Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use CtrlCHandler in IceStorm/clock (Swift) #260

Merged
merged 4 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions swift/IceStorm/clock/Package.swift
Original file line number Diff line number Diff line change
@@ -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")]
),
]
)
64 changes: 28 additions & 36 deletions swift/IceStorm/clock/Sources/Publisher/main.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) ZeroC, Inc.

import Foundation
import PromiseKit
import Ice
import IceStorm

Expand All @@ -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"

Expand All @@ -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
}
Expand All @@ -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
Expand All @@ -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
}
Expand All @@ -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)")
Copy link
Member Author

@bernardnormier bernardnormier Feb 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plain \(error) for a local exception displays the full C++ stack trace. We should double-check that's the desired behavior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's undesirable.

}
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))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not fond of this "exit(run)" pattern, we should replace it.

With this pattern, it's critical to create the CtrlCHandler before the first await call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's also unnecessary

3 changes: 3 additions & 0 deletions swift/IceStorm/clock/Sources/Publisher/slice-plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"sources": ["../../slice/Clock.ice"]
}
32 changes: 13 additions & 19 deletions swift/IceStorm/clock/Sources/Subscriber/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand All @@ -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
Expand Down Expand Up @@ -161,20 +149,26 @@ 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")
return 1
}
}

exit(run())
let ctrlCHandler = CtrlCHandler()
exit(await run(ctrlCHandler: ctrlCHandler))
3 changes: 3 additions & 0 deletions swift/IceStorm/clock/Sources/Subscriber/slice-plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"sources": ["../../slice/Clock.ice"]
}