Skip to content

TestApp: graceful quit; debugger integration#383

Merged
eagleoflqj merged 2 commits into
masterfrom
dev
Jun 20, 2026
Merged

TestApp: graceful quit; debugger integration#383
eagleoflqj merged 2 commits into
masterfrom
dev

Conversation

@eagleoflqj

@eagleoflqj eagleoflqj commented Jun 20, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • Bug Fixes
    • Improved app shutdown behavior by adding explicit handling for termination and interrupt signals.
    • Ensures background processing is properly started during launch and cleanly stopped when the app terminates.
    • Updated termination signal handling to use safer main-thread dispatch, improving reliability of process lifecycle responses.

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2d866940-38a3-458e-830a-8cd86d0e3237

📥 Commits

Reviewing files that changed from the base of the PR and between d6a634a and d9f7ff3.

📒 Files selected for processing (2)
  • appium/entry.swift
  • src/server.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • appium/entry.swift

📝 Walkthrough

Walkthrough

Refactors POSIX signal handling in both TestAppDelegate and the main server AppDelegate from legacy signal(2) functions to DispatchSourceSignal-based handlers, enabling graceful shutdown and main-actor-safe termination. TestApp now cleanly stops the fcitx thread on exit. A .vscode/launch.json debug configuration is added for LLDB debugging of the TestApp binary.

Changes

Signal handling and app lifecycle management

Layer / File(s) Summary
TestApp signal handling and fcitx lifecycle
appium/entry.swift
Adds sigintSource and sigtermSource properties to store dispatch signal sources. New installSignalHandlers() method ignores SIGINT/SIGTERM, sets up dispatch sources on the main queue, and schedules NSApp.terminate(nil) on the main actor when either signal fires. applicationDidFinishLaunching now calls installSignalHandlers() before starting the fcitx thread. New applicationWillTerminate method stops the fcitx thread cleanly.
Server signal handling refactoring
src/server.swift
Removes the legacy file-scope signalHandler function. AppDelegate now stores a sigtermSource dispatch signal property and introduces installSignalHandlers() to configure SIGTERM handling via DispatchSourceSignal instead of signal(2). The dispatch handler invokes restartProcess() from a main-actor Task. applicationDidFinishLaunching calls installSignalHandlers() instead of the old signal registration.
VS Code LLDB debug configuration
.vscode/launch.json
New file with a single "Debug TestApp" launch configuration using LLDB, pointing program to build/arm64/appium/FcitxTestApp.app/Contents/MacOS/FcitxTestApp.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 The signals now dance on dispatch queues bright,
No legacy handlers to dim the light!
SIGINT and SIGTERM find their graceful way,
Via main-actor tasks that end the fray.
With LLDB ready and fcitx laid to rest,
This Swift app refactor passes the test! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main changes: adding graceful quit handling via signal handlers and VS Code debugger integration via launch.json.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@appium/entry.swift`:
- Around line 4-13: The signalHandler function and the signal() calls in
applicationDidFinishLaunching are using POSIX signal handlers to perform
non-async-signal-safe operations like Task creation and NSApp.terminate(), which
violates safety rules. Replace the signal(SIGINT, signalHandler) and
signal(SIGTERM, signalHandler) calls with DispatchSourceSignal instances
configured on the main dispatch queue. Create separate DispatchSourceSignal
objects for SIGINT and SIGTERM, set their event handler to perform the
NSApp.terminate(nil) operation directly on the main queue (without the Task
wrapper), and resume each source. Remove or repurpose the signalHandler function
since it will no longer be needed as a POSIX signal callback.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c7dfa42b-c230-428f-99bb-66f0f4e604a5

📥 Commits

Reviewing files that changed from the base of the PR and between f4b3907 and d6a634a.

📒 Files selected for processing (2)
  • .vscode/launch.json
  • appium/entry.swift

Comment thread appium/entry.swift Outdated
Comment on lines +4 to +13
private func signalHandler(signal: Int32) {
Task { @MainActor in
NSApp.terminate(nil)
}
}

class TestAppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
signal(SIGINT, signalHandler)
signal(SIGTERM, signalHandler)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

POSIX async-signal-safe rules for signal handlers, and Apple guidance for handling SIGINT/SIGTERM in macOS apps with DispatchSourceSignal.

💡 Result:

POSIX defines a specific set of functions that are async-signal-safe, meaning they can be safely called from within a traditional signal handler [1][2][3]. A function is async-signal-safe if it is either reentrant or atomic with respect to signals [1][4]. If a signal handler interrupts an unsafe function and then calls another unsafe function, the program's behavior is undefined [2][3]. The list of POSIX-mandated async-signal-safe functions includes, but is not limited to, _exit, close, getpid, kill, open, read, write, and sigprocmask [1][3]. Notably, common functions like malloc, printf, and exit are not async-signal-safe and should never be called inside a signal handler [5][3]. For macOS applications, Apple recommends using DispatchSourceSignal as the preferred mechanism for handling signals like SIGINT or SIGTERM instead of traditional synchronous signal handlers [6][5]. Key guidance for macOS applications: 1. Avoid Synchronous Handlers: Traditional signal handlers (via sigaction) run in a highly restricted environment where almost any non-trivial work is unsafe [5]. 2. Use DispatchSourceSignal: This GCD-based approach allows you to monitor signal arrival asynchronously on a dispatch queue [6][7]. Because the event handler executes on a dispatch queue rather than interrupting the current thread, it is not subject to async-signal-safety restrictions [8][5]. 3. Implementation Steps: - Use signal or sigaction to ignore the signal (e.g., signal(SIGTERM, SIG_IGN)) so that the process is not terminated immediately [6][8][5][9]. - Create a dispatch source of type DISPATCH_SOURCE_TYPE_SIGNAL for the desired signal [7][8][5]. - Set an event handler on that source to perform your cleanup logic [6][5]. - Call dispatch_resume to start monitoring [6][9]. 4. Limitations: Dispatch sources cannot monitor hardware-generated exceptions like SIGILL, SIGBUS, or SIGSEGV [6]. They are intended for process notification, not as a replacement for low-level fault handling [6]. Top results: [1][6][8][5][3]

Citations:


Replace signal() callback with DispatchSourceSignal on the main queue.

The current handler performs non-async-signal-safe work—Task creation and NSApp.terminate(nil)—from a POSIX signal context. This violates async-signal-safety rules and can cause deadlock or crash on SIGINT/SIGTERM. Apple recommends using DispatchSourceSignal to defer signal handling to the main dispatch queue, where unsafe operations are permitted.

Suggested fix
-private func signalHandler(signal: Int32) {
-  Task { `@MainActor` in
-    NSApp.terminate(nil)
-  }
-}
-
 class TestAppDelegate: NSObject, NSApplicationDelegate {
+  private var sigintSource: DispatchSourceSignal?
+  private var sigtermSource: DispatchSourceSignal?
+
+  private func installSignalHandlers() {
+    signal(SIGINT, SIG_IGN)
+    signal(SIGTERM, SIG_IGN)
+
+    sigintSource = DispatchSource.makeSignalSource(signal: SIGINT, queue: .main)
+    sigtermSource = DispatchSource.makeSignalSource(signal: SIGTERM, queue: .main)
+
+    sigintSource?.setEventHandler { NSApp.terminate(nil) }
+    sigtermSource?.setEventHandler { NSApp.terminate(nil) }
+
+    sigintSource?.resume()
+    sigtermSource?.resume()
+  }
+
   func applicationDidFinishLaunching(_ notification: Notification) {
-    signal(SIGINT, signalHandler)
-    signal(SIGTERM, signalHandler)
+    installSignalHandlers()
     start_fcitx_thread("")
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@appium/entry.swift` around lines 4 - 13, The signalHandler function and the
signal() calls in applicationDidFinishLaunching are using POSIX signal handlers
to perform non-async-signal-safe operations like Task creation and
NSApp.terminate(), which violates safety rules. Replace the signal(SIGINT,
signalHandler) and signal(SIGTERM, signalHandler) calls with
DispatchSourceSignal instances configured on the main dispatch queue. Create
separate DispatchSourceSignal objects for SIGINT and SIGTERM, set their event
handler to perform the NSApp.terminate(nil) operation directly on the main queue
(without the Task wrapper), and resume each source. Remove or repurpose the
signalHandler function since it will no longer be needed as a POSIX signal
callback.

@github-actions

Copy link
Copy Markdown

arm64 comparison

No difference.

x86_64 comparison

No difference.

@eagleoflqj eagleoflqj merged commit 012bcef into master Jun 20, 2026
8 checks passed
@eagleoflqj eagleoflqj deleted the dev branch June 20, 2026 01:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant