diff --git a/swift/IceStorm/clock/Package.swift b/swift/IceStorm/clock/Package.swift new file mode 100644 index 000000000..1d9ecf493 --- /dev/null +++ b/swift/IceStorm/clock/Package.swift @@ -0,0 +1,30 @@ +// swift-tools-version: 5.9 + +import Foundation +import PackageDescription + +guard let iceHome = ProcessInfo.processInfo.environment["ICE_HOME"] else { + fatalError("ICE_HOME environment variable not set") +} + +let package = Package( + name: "clock", + platforms: [ + .macOS(.v14) + ], + dependencies: [.package(name: "ice", path: iceHome)], + targets: [ + .executableTarget( + name: "Publisher", + dependencies: [.product(name: "Ice", package: "ice"), .product(name: "IceStorm", package: "ice")], + exclude: ["slice-plugin.json"], + plugins: [.plugin(name: "CompileSlice", package: "ice")] + ), + .executableTarget( + name: "Subscriber", + dependencies: [.product(name: "Ice", package: "ice"), .product(name: "IceStorm", package: "ice")], + exclude: ["slice-plugin.json"], + plugins: [.plugin(name: "CompileSlice", package: "ice")] + ), + ] +) diff --git a/swift/IceStorm/clock/Sources/Publisher/main.swift b/swift/IceStorm/clock/Sources/Publisher/main.swift index 36f691ae4..48390b118 100644 --- a/swift/IceStorm/clock/Sources/Publisher/main.swift +++ b/swift/IceStorm/clock/Sources/Publisher/main.swift @@ -1,7 +1,6 @@ // Copyright (c) ZeroC, Inc. import Foundation -import PromiseKit import Ice import IceStorm @@ -19,29 +18,15 @@ enum Option: String { case oneway = "--oneway" } -func run() -> Int32 { +func run(ctrlCHandler: Ice.CtrlCHandler) async -> Int32 { do { var args = [String](CommandLine.arguments.dropFirst()) - signal(SIGTERM, SIG_IGN) - signal(SIGINT, SIG_IGN) let communicator = try Ice.initialize(args: &args, configFile: "config.pub") defer { communicator.destroy() } - let sigintSource = DispatchSource.makeSignalSource(signal: SIGINT, queue: DispatchQueue.main) - sigintSource.setEventHandler { - communicator.destroy() - } - sigintSource.resume() - - let sigtermSource = DispatchSource.makeSignalSource(signal: SIGTERM, queue: DispatchQueue.main) - sigtermSource.setEventHandler { - communicator.destroy() - } - sigtermSource.resume() - var option: Option = .none var topicName = "time" @@ -67,7 +52,7 @@ func run() -> Int32 { } guard let base = try communicator.propertyToProxy("TopicManager.Proxy"), - let manager = try checkedCast(prx: base, type: IceStorm.TopicManagerPrx.self) else { + let manager = try await checkedCast(prx: base, type: IceStorm.TopicManagerPrx.self) else { print("invalid proxy") return 1 } @@ -77,10 +62,10 @@ func run() -> Int32 { // var topic: IceStorm.TopicPrx! do { - topic = try manager.retrieve(topicName) + topic = try await manager.retrieve(topicName) } catch is IceStorm.NoSuchTopic { do { - topic = try manager.create(topicName) + topic = try await manager.create(topicName) } catch is IceStorm.TopicExists { print("temporary error. try again.") return 1 @@ -91,7 +76,7 @@ func run() -> Int32 { // Get the topic's publisher object, and create a Clock proxy with // the mode specified as an argument of this application. // - guard var publisher = try topic.getPublisher() else { + guard var publisher = try await topic.getPublisher() else { print("Error getting publisher proxy") return 1 } @@ -113,27 +98,34 @@ func run() -> Int32 { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "dd/MM/YYYY HH:mm:ss" - let t = DispatchSource.makeTimerSource() - t.schedule(deadline: .now(), repeating: .seconds(1)) - t.setEventHandler { - do { - try clock.tick(dateFormatter.string(from: Date())) - } catch is CommunicatorDestroyedException { - t.suspend() - exit(0) - } catch { - t.suspend() - print("Error: \(error)\n") - communicator.destroy() - exit(1) + // Send a tick every second until cancelled + let task = Task { + while true { + do { + try await clock.tick(dateFormatter.string(from: Date())) + } catch let error as Ice.LocalException { + print("tick invocation failed with: \(error.message)") + } + try await Task.sleep(for: .seconds(1)) } } - t.activate() - Dispatch.dispatchMain() + + let signal = await ctrlCHandler.catchSignal() + print("Caught signal \(signal), exiting...") + + task.cancel() + do { + try await task.value + } catch is CancellationError { + // expected + } + + return 0 } catch { print("Error: \(error)\n") return 1 } } -exit(run()) +let ctrlCHandler = Ice.CtrlCHandler() +exit(await run(ctrlCHandler: ctrlCHandler)) diff --git a/swift/IceStorm/clock/Sources/Publisher/slice-plugin.json b/swift/IceStorm/clock/Sources/Publisher/slice-plugin.json new file mode 100644 index 000000000..c3593e550 --- /dev/null +++ b/swift/IceStorm/clock/Sources/Publisher/slice-plugin.json @@ -0,0 +1,3 @@ +{ + "sources": ["../../slice/Clock.ice"] +} diff --git a/swift/IceStorm/clock/Sources/Subscriber/main.swift b/swift/IceStorm/clock/Sources/Subscriber/main.swift index fefec40e8..af5391129 100644 --- a/swift/IceStorm/clock/Sources/Subscriber/main.swift +++ b/swift/IceStorm/clock/Sources/Subscriber/main.swift @@ -25,26 +25,14 @@ enum Option: String { case oneway = "--oneway" } -func run() -> Int32 { +func run(ctrlCHandler: CtrlCHandler) async -> Int32 { do { var args = [String](CommandLine.arguments.dropFirst()) - signal(SIGTERM, SIG_IGN) - signal(SIGINT, SIG_IGN) - let communicator = try Ice.initialize(args: &args, configFile: "config.sub") defer { communicator.destroy() } - let sigintSource = DispatchSource.makeSignalSource(signal: SIGINT, - queue: DispatchQueue.global()) - let sigtermSource = DispatchSource.makeSignalSource(signal: SIGTERM, - queue: DispatchQueue.global()) - sigintSource.setEventHandler { communicator.shutdown() } - sigtermSource.setEventHandler { communicator.shutdown() } - sigintSource.resume() - sigtermSource.resume() - args = try communicator.getProperties().parseCommandLineOptions(prefix: "Clock", options: args) var topicName = "time" @@ -98,7 +86,7 @@ func run() -> Int32 { } guard let base = try communicator.propertyToProxy("TopicManager.Proxy"), - let manager = try checkedCast(prx: base, type: IceStorm.TopicManagerPrx.self) else { + let manager = try await checkedCast(prx: base, type: IceStorm.TopicManagerPrx.self) else { print("invalid proxy") return 1 } @@ -108,10 +96,10 @@ func run() -> Int32 { // let topic: IceStorm.TopicPrx! do { - topic = try manager.retrieve(topicName) + topic = try await manager.retrieve(topicName) } catch is IceStorm.NoSuchTopic { do { - topic = try manager.create(topicName) + topic = try await manager.create(topicName) } catch is IceStorm.TopicExists { print("temporary error. try again.") return 1 @@ -161,15 +149,20 @@ func run() -> Int32 { } do { - _ = try topic.subscribeAndGetPublisher(theQoS: qos, subscriber: subscriber) + _ = try await topic.subscribeAndGetPublisher(theQoS: qos, subscriber: subscriber) } catch is IceStorm.AlreadySubscribed { // Must never happen when subscribing with an UUID precondition(id != nil) print("reactivating persistent subscriber") } + + let signal = await ctrlCHandler.catchSignal() + print("Caught signal \(signal), exiting...") + + communicator.shutdown() communicator.waitForShutdown() - try topic.unsubscribe(subscriber) + try await topic.unsubscribe(subscriber) return 0 } catch { print("Error: \(error)\n") @@ -177,4 +170,5 @@ func run() -> Int32 { } } -exit(run()) +let ctrlCHandler = CtrlCHandler() +exit(await run(ctrlCHandler: ctrlCHandler)) diff --git a/swift/IceStorm/clock/Sources/Subscriber/slice-plugin.json b/swift/IceStorm/clock/Sources/Subscriber/slice-plugin.json new file mode 100644 index 000000000..c3593e550 --- /dev/null +++ b/swift/IceStorm/clock/Sources/Subscriber/slice-plugin.json @@ -0,0 +1,3 @@ +{ + "sources": ["../../slice/Clock.ice"] +} diff --git a/swift/IceStorm/clock/Sources/Clock.ice b/swift/IceStorm/clock/slice/Clock.ice similarity index 100% rename from swift/IceStorm/clock/Sources/Clock.ice rename to swift/IceStorm/clock/slice/Clock.ice