diff --git a/CHANGELOG.en.md b/CHANGELOG.en.md index abad5a5..ba528fe 100644 --- a/CHANGELOG.en.md +++ b/CHANGELOG.en.md @@ -20,7 +20,25 @@ Chinese is the primary changelog and this file is its maintained English mirror. explicitly unapproved for distribution without changing the XCFramework Release's RootFS exclusion policy. -## v0.4.0-abi.11 (planned Stage1 maintenance prerelease) +## v0.4.0-abi.12 (planned Stage1 maintenance prerelease) + +This is a compatibility maintenance release after `v0.4.0-abi.11`. It remains +a prerelease and is **not stable v0.4.0**. + +- `third_party/ish` advances to `6b599fe7`. The internal Apple-platform + `wrlock_t` is now writer-preferring: once a writer queues, later readers + cannot barge indefinitely and starve code-dirty invalidation under sustained + guest read load. +- If `WNOWAIT` has observed a zombie but destructive `waitpid(WNOHANG)` + temporarily returns zero, the guest supervisor retries within the existing + cleanup deadline while the thread group finishes releasing resources rather + than incorrectly fail-closing the VM. +- Regression coverage now includes writer preference, deferred supervisor + reap, and platform-independent test access to lock state. The public C ABI + remains version 1, wire protocol remains v4, and Swift API is unchanged. + RootFS remains outside the Release. + +## v0.4.0-abi.11 (published Stage1 maintenance prerelease) This is a compatibility maintenance release after `v0.4.0-abi.10`. It remains a prerelease and is **not stable v0.4.0**. diff --git a/CHANGELOG.md b/CHANGELOG.md index e22924d..7e1e334 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,20 @@ `fs.tar.gz`,环境差异仍保留在外部证据中。CI 不上传制品,候选仍明确标记为未获分发 批准,不改变 XCFramework Release 的 RootFS 排除策略。 -## v0.4.0-abi.11(计划中的 Stage1 维护预发布) +## v0.4.0-abi.12(计划中的 Stage1 维护预发布) + +这是 `v0.4.0-abi.11` 之后的兼容性维护版本,仍是 prerelease,**不是稳定 +v0.4.0**。 + +- `third_party/ish` 更新到 `6b599fe7`。Apple 平台的内部 `wrlock_t` 改为写者优先实现; + 一旦写者排队,后到读者不能持续插队,避免代码脏页失效在持续 guest 读负载下饥饿。 +- guest supervisor 在 `WNOWAIT` 已观察到 zombie、但 destructive + `waitpid(WNOHANG)` 暂时返回 0 时,会在原清理期限内重试,等待 thread group 完成资源 + 静止,而不是错误地 fail-close VM。 +- 新增锁写者优先、supervisor 延迟 reap 与平台无关测试访问的回归覆盖。公开 C ABI 仍为 + 1,wire protocol 仍为 v4,Swift API 不变;RootFS 不进入 Release。 + +## v0.4.0-abi.11(已发布的 Stage1 维护预发布) 这是 `v0.4.0-abi.10` 之后的兼容性维护版本,仍是 prerelease,**不是稳定 v0.4.0**。 diff --git a/README.en.md b/README.en.md index 36b78af..8694d93 100644 --- a/README.en.md +++ b/README.en.md @@ -15,16 +15,16 @@ and simulator slices are arm64. ## Current phase: native ABI transition -The default branch has published `v0.4.0-abi.10` and is preparing the compatible -maintenance prerelease `v0.4.0-abi.11`. Both belong to the Stage1 **native ABI +The default branch has published `v0.4.0-abi.11` and is preparing the compatible +maintenance prerelease `v0.4.0-abi.12`. Both belong to the Stage1 **native ABI transition**. Neither is stable `v0.4.0` or the complete v0.4 Swift API. Keep these four version surfaces distinct: -| Surface | Current `v0.4.0-abi.10` | Planned `v0.4.0-abi.11` | +| Surface | Current `v0.4.0-abi.11` | Planned `v0.4.0-abi.12` | | --- | --- | --- | -| Public C ABI | `ISH_EMBED_ABI_VERSION == 1`; atomic rename, finite-stdin deadlines, per-call stdin timeouts, and forced-teardown lifetime fixes are public | Still ABI 1; only the internal destructive-wait/reap boundary changes | +| Public C ABI | `ISH_EMBED_ABI_VERSION == 1`; forced-teardown and destructive-wait/reap lifetime fixes are public | Still ABI 1; only the internal Apple rwlock and deferred supervisor reap change | | Internal wire protocol | exact-match v4 between host and embedded supervisor | still v4; this is not the public C ABI version | -| `Package.swift` | pins the public `v0.4.0-abi.10` URL/checksum | the release transaction creates a manifest-only release commit pinned to the maintenance binary | +| `Package.swift` | pins the public `v0.4.0-abi.11` URL/checksum | the release transaction creates a manifest-only release commit pinned to the maintenance binary | | Swift source | remains v0.3.3-ABI compatible and includes typed rename plus per-call stdin timeouts | unchanged | Stage1 native code adds session retain/release, a joinable kernel thread, @@ -90,15 +90,15 @@ gives those narrow differences independent PRs, CI, and an exact gitlink, making PocketRoot builds and releases reproducible. We do not directly rewrite somebody else's local upstream repository; generally useful fixes can still be contributed to [iSH upstream](https://github.com/ish-app/ish), while the fork carries project -gates until upstream accepts and releases them. The current `v0.4.0-abi.11` +gates until upstream accepts and releases them. The current `v0.4.0-abi.12` source change includes neither RootFS content nor any prebuilt XCFramework/guest binary; binaries may be produced and published only by a later release transaction after its gates pass. ## Installation status -`v0.4.0-abi.10` is public and [`Package.swift`](Package.swift) currently pins it. -Until `v0.4.0-abi.11` is published, the manifest keeps pointing at that verified +`v0.4.0-abi.11` is public and [`Package.swift`](Package.swift) currently pins it. +Until `v0.4.0-abi.12` is published, the manifest keeps pointing at that verified asset instead of advertising a future 404 URL. Use Xcode's **File → Add Package Dependencies…** with: @@ -110,12 +110,11 @@ Select a version whose tag, `libIshKernel.xcframework.zip`, Corresponding Source and manifest URL/checksum all match. Consumer projects do not need Meson, Zig, or LLVM. -`v0.4.0-abi.10` provides guest-atomic rename without a shell, bounded stdin -deadlines, per-call write/close timeouts, and lifetime fixes between forced -guest-task teardown and interval timers, shared fds, blocking syscalls, and -native output workers. `v0.4.0-abi.11` delays destructive waits until the full -thread group is quiescent, preventing sequential guest startup from reusing -runtime resources that the previous process still owns. It does not +`v0.4.0-abi.11` provides guest-atomic rename without a shell, bounded stdin +deadlines, per-call write/close timeouts, forced guest-task teardown, and delays +destructive waits until the full thread group is quiescent. `v0.4.0-abi.12` +further prevents writer starvation in the Apple-platform rwlock and lets the +supervisor boundedly wait for destructive reap after observing a zombie. It does not implement a native Agent Loop or install Codex CLI in the app. Node.js/npm remain optional choices of the RootFS/guest package-management flow, diff --git a/README.md b/README.md index e302c8c..5c0169f 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,15 @@ RootFS 安装、产品级命令策略、Swift Concurrency 隔离和界面。项 ## 当前阶段:Native ABI 过渡 -当前默认分支已发布 `v0.4.0-abi.10`,正在准备兼容性维护预发布 -`v0.4.0-abi.11`。它们都属于 **Stage1 native ABI 过渡**,不是稳定 `v0.4.0`, +当前默认分支已发布 `v0.4.0-abi.11`,正在准备兼容性维护预发布 +`v0.4.0-abi.12`。它们都属于 **Stage1 native ABI 过渡**,不是稳定 `v0.4.0`, 也不是完整 v0.4 Swift API。请同时区分下面四个版本面: -| 版本面 | 当前 `v0.4.0-abi.10` | 计划中的 `v0.4.0-abi.11` | +| 版本面 | 当前 `v0.4.0-abi.11` | 计划中的 `v0.4.0-abi.12` | | --- | --- | --- | -| 公开 C ABI | `ISH_EMBED_ABI_VERSION == 1`;原子 rename、有限 stdin deadline、单次 stdin timeout 与强制 teardown 生命周期修复已发布 | 仍为 ABI 1;只收紧内部 destructive wait/reap 边界 | +| 公开 C ABI | `ISH_EMBED_ABI_VERSION == 1`;强制 teardown 与 destructive wait/reap 生命周期修复已发布 | 仍为 ABI 1;只收紧 Apple 内部读写锁与 supervisor 延迟 reap | | 内部 wire protocol | host 与内嵌 supervisor 精确匹配 v4 | 仍为 v4;它不是公开 C ABI 版本 | -| `Package.swift` | 固定已公开的 `v0.4.0-abi.10` URL/checksum | 发布事务生成只改 manifest 的 release commit,固定到维护二进制 | +| `Package.swift` | 固定已公开的 `v0.4.0-abi.11` URL/checksum | 发布事务生成只改 manifest 的 release commit,固定到维护二进制 | | Swift 源 | 保持 v0.3.3 ABI 兼容,已提供类型化 rename 与单次 stdin timeout | 不变 | Stage1 的 native runtime 已加入 session retain/release、可等待 kernel 线程、soft-halt、 @@ -69,13 +69,13 @@ JIT 脏页一致性必须修改模拟器核心,无法只在 outer package 或 窄差异拥有独立 PR、CI 和精确 gitlink,PocketRoot 的构建与发布也因此可复现。我们不会在 本地直接改写别人维护的上游仓库;适合通用化的修复仍可回馈 [iSH upstream](https://github.com/ish-app/ish),但在上游接受并发布前由 fork 承担项目门禁。 -当前 `v0.4.0-abi.11` 源码变更不纳入 RootFS,也不提交任何预构建 +当前 `v0.4.0-abi.12` 源码变更不纳入 RootFS,也不提交任何预构建 XCFramework/guest binary;二进制只能在后续发布事务通过后生成和发布。 ## 安装状态 -`v0.4.0-abi.10` 已公开且当前 [`Package.swift`](Package.swift) 固定到它。 -`v0.4.0-abi.11` 发布前,manifest 继续指向这个已验证的资产,不会提前引用 404 URL。 +`v0.4.0-abi.11` 已公开且当前 [`Package.swift`](Package.swift) 固定到它。 +`v0.4.0-abi.12` 发布前,manifest 继续指向这个已验证的资产,不会提前引用 404 URL。 在 Xcode 的 **File → Add Package Dependencies…** 中使用: ```text @@ -85,11 +85,10 @@ https://github.com/jacklv-coder/ish-arm64-pkg 请选择明确包含 `libIshKernel.xcframework.zip`、对应源码归档,并且 manifest URL/checksum 与同一标签匹配的版本。业务工程不需要安装 Meson、Zig 或 LLVM。 -`v0.4.0-abi.10` 已提供无 shell 的 guest 原子重命名、有界 stdin deadline、单次 -write/close timeout,以及强制 guest task teardown 与 interval timer、共享 fd、阻塞 -syscall 和 native output worker 之间的生命周期修复。`v0.4.0-abi.11` 会让破坏性 wait -在完整 thread group 静止后才发布进程退出,避免连续启动 guest process 时复用尚未释放的 -runtime 资源。它不实现原生 +`v0.4.0-abi.11` 已提供无 shell 的 guest 原子重命名、有界 stdin deadline、单次 +write/close timeout、强制 guest task teardown,并让破坏性 wait 在完整 thread group +静止后才发布进程退出。`v0.4.0-abi.12` 进一步避免 Apple 平台读写锁的写者饥饿,并允许 +supervisor 在已观察 zombie 后有界等待 destructive reap 真正可用。它不实现原生 Agent Loop,也不会 在 App 内安装 Codex CLI。Node.js/npm 如有需要仍由 RootFS/guest 包管理流程选择,不属于 runtime 的强制依赖。 diff --git a/c-tests/dirty_page_test.c b/c-tests/dirty_page_test.c index cd11d85..363f5e2 100644 --- a/c-tests/dirty_page_test.c +++ b/c-tests/dirty_page_test.c @@ -878,11 +878,12 @@ struct drain_context { static void confirm_dirty_reader_would_block(struct asbestos *asbestos, pthread_mutex_t *mutex, pthread_cond_t *cond, bool *confirmed) { - // Probe the same underlying rwlock immediately before entering the - // production invalidation path. The compiler writer is already held, so - // EBUSY proves this worker has reached the lock and its next blocking read - // cannot pass until the stale block has been published. - assert(pthread_rwlock_tryrdlock(&asbestos->dirty_coherence_lock.l) == EBUSY); + // Observe only the wrlock API's implementation-independent state before + // entering the production invalidation path. The compiler writer is + // already held, so this worker's next blocking read cannot pass until the + // stale block has been published. Do not reach into the platform-specific + // pthread/custom lock representation here. + assert(asbestos->dirty_coherence_lock.val == -1); assert(pthread_mutex_lock(mutex) == 0); *confirmed = true; assert(pthread_cond_signal(cond) == 0); diff --git a/c-tests/supervisor_stdin_test.c b/c-tests/supervisor_stdin_test.c index c9f81a8..f3ea577 100644 --- a/c-tests/supervisor_stdin_test.c +++ b/c-tests/supervisor_stdin_test.c @@ -893,6 +893,7 @@ struct waitpid_event { static struct waitpid_event test_waitpid_events[4]; static size_t test_waitpid_event_count; static size_t test_waitpid_event_index; +static size_t test_waitpid_deferred_count; static int test_waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) { @@ -923,9 +924,14 @@ static pid_t test_waitpid(pid_t requested, int *status, int options) { CHECK(options == WNOHANG); CHECK(test_waitpid_event_index < test_waitpid_event_count); struct waitpid_event event = - test_waitpid_events[test_waitpid_event_index++]; + test_waitpid_events[test_waitpid_event_index]; CHECK(requested == event.pid); CHECK(event.pid > 0); + if (test_waitpid_deferred_count > 0) { + test_waitpid_deferred_count--; + return 0; + } + test_waitpid_event_index++; if (event.pid > 0 && status) *status = event.status; return event.pid; } @@ -963,6 +969,7 @@ static void reset_process_hooks(void) { ishsv_test_tcgetpgrp_hook = NULL; test_waitpid_event_count = 0; test_waitpid_event_index = 0; + test_waitpid_deferred_count = 0; test_kill_count = 0; test_foreground_pgid = 0; g_instance_fail_closed = 0; @@ -971,6 +978,49 @@ static void reset_process_hooks(void) { #endif } +/* WNOWAIT may observe the leader zombie before the embedded kernel permits a + * destructive reap: the final guest host pthread can still own shared task or + * address-space state. The supervisor must keep the numeric identity pinned + * and retry within its deadline, not fail-close the complete VM. */ +static void test_observed_zombie_waits_for_thread_group_quiescence(void) { + const pid_t leader_pid = 4050; + memset(g_sessions, 0, sizeof(g_sessions)); + reset_process_hooks(); + ishsv_test_waitid_hook = test_waitid; + ishsv_test_waitpid_hook = test_waitpid; + ishsv_test_kill_hook = test_kill; + + struct session *s = &g_sessions[0]; + s->in_use = 1; + s->id = 800; + s->pid = leader_pid; + s->pgid = leader_pid; + s->pgid_validated = 1; + s->stdin_fd = -1; + s->stdout_fd = -1; + s->stderr_fd = -1; + + const struct waitpid_event events[] = { + {leader_pid, 9 << 8}, + {0, 0}, + }; + set_waitpid_events(events, sizeof(events) / sizeof(events[0])); + test_waitpid_deferred_count = 3; + + reap_children_until(monotonic_ms() + 1000u); + + CHECK(!g_instance_fail_closed); + CHECK(test_waitpid_deferred_count == 0); + CHECK(s->reaped && s->pid == 0 && s->pgid == 0 && + !s->pgid_validated && s->exit_code == 9); + CHECK(test_kill_count == 1); + CHECK(test_kill_pids[0] == -leader_pid); + CHECK(test_kill_signals[0] == SIGKILL); + + free_session(s); + reset_process_hooks(); +} + static void test_host_build_requires_hook_for_instance_cleanup(void) { reset_process_hooks(); errno = 0; @@ -1513,6 +1563,7 @@ int main(void) { test_host_build_requires_hook_for_instance_cleanup(); test_incomplete_shutdown_is_not_acknowledged(); test_cleanup_failures_do_not_report_success(); + test_observed_zombie_waits_for_thread_group_quiescence(); test_reaped_pid_is_not_reused_as_session_identity(); test_tty_foreground_close_terminates_complete_session(); test_reaped_leader_cannot_leave_background_group_running(); diff --git a/docs/README.en.md b/docs/README.en.md index ae43426..42a5904 100644 --- a/docs/README.en.md +++ b/docs/README.en.md @@ -14,13 +14,13 @@ primary language; the English mirrors support collaboration and release review. authoritative. Stage1 uses exact-match v4 between host and embedded supervisor. 3. **Swift source and manifest binary**: Stage1 Swift remains v0.3.3-ABI compatible and does not call retain/release. The manifest currently pins the - public `v0.4.0-abi.10`; the release transaction moves it to the - `v0.4.0-abi.11` maintenance binary only after those assets are public and + public `v0.4.0-abi.11`; the release transaction moves it to the + `v0.4.0-abi.12` maintenance binary only after those assets are public and verified. 4. **RootFS and PocketRoot**: RootFS is an independent asset and PocketRoot is the product layer. Neither is completed automatically by a runtime PR or Release. -`v0.4.0-abi.10` and its `v0.4.0-abi.11` maintenance release are native-first +`v0.4.0-abi.11` and its `v0.4.0-abi.12` maintenance release are native-first transition prereleases, not stable v0.4. The complete Swift lifecycle, typed statuses, Terminal callback queue, and VT parser changes belong to Stage2. @@ -44,7 +44,7 @@ statuses, Terminal callback queue, and VT parser changes belong to Stage2. | [Architecture and lifecycle](architecture.en.md) | How is the runtime implemented? How do ABI 1 and wire v4 differ? | | [Testing and acceptance](testing.en.md) | What do native, sanitizer, Swift/iOS 18, documentation, and supply-chain gates prove? | | [Troubleshooting](troubleshooting.en.md) | Where should boot, link, protocol, output, shutdown, or release diagnosis start? | -| [Release transaction](releasing.en.md) | Why does the merged maintenance source still pin `v0.4.0-abi.10`, and when does it become `v0.4.0-abi.11`? | +| [Release transaction](releasing.en.md) | Why does the merged maintenance source still pin `v0.4.0-abi.11`, and when does it become `v0.4.0-abi.12`? | | [Changelog](../CHANGELOG.en.md) | What is Stage1's scope and compatibility boundary? | ## Architecture on one page @@ -99,7 +99,7 @@ fixes can still go upstream. See the A direct-chain or RET target returns to the dispatcher only when it intersects pending dirty code pages; data-only writes may keep chaining. - RootFS is outside the package/Release and must not enter Corresponding Source. -- The current `v0.4.0-abi.11` source change carries no RootFS or prebuilt binary; +- The current `v0.4.0-abi.12` source change carries no RootFS or prebuilt binary; a later release transaction must produce the XCFramework. ## Authoritative sources diff --git a/docs/README.md b/docs/README.md index 487e55f..fdbcc62 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,12 +12,12 @@ 2. **内部 wire protocol**:权威定义为 [`protocol/proto.h`](../protocol/proto.h), Stage1 是 host 与内嵌 supervisor 精确匹配的 v4。 3. **Swift 源与 manifest binary**:Stage1 Swift 保持 v0.3.3 ABI 兼容且不调用 - retain/release。manifest 当前固定已公开的 `v0.4.0-abi.10`;只有 - `v0.4.0-abi.11` 资产公开并验证后,发布事务才将其切到维护 binary。 + retain/release。manifest 当前固定已公开的 `v0.4.0-abi.11`;只有 + `v0.4.0-abi.12` 资产公开并验证后,发布事务才将其切到维护 binary。 4. **RootFS 与 PocketRoot**:RootFS 是独立资产;PocketRoot 是上层产品。两者均不因 runtime PR 或 Release 自动完成。 -`v0.4.0-abi.10` 与维护版本 `v0.4.0-abi.11` 都是 native-first 过渡预发布,不是稳定 +`v0.4.0-abi.11` 与维护版本 `v0.4.0-abi.12` 都是 native-first 过渡预发布,不是稳定 v0.4。完整 Swift lifecycle、类型化状态、Terminal callback 队列和 VT parser 改造 属于 Stage2。 @@ -39,7 +39,7 @@ v0.4。完整 Swift lifecycle、类型化状态、Terminal callback 队列和 VT | [架构与生命周期](architecture.md) | runtime 怎样实现?ABI 1 与 wire v4 有什么区别? | | [测试与验收](testing.md) | native、sanitizer、Swift/iOS 18、文档与供应链门禁各证明什么? | | [故障排查](troubleshooting.md) | boot、链接、协议、输出、shutdown 或发布失败时从哪里查? | -| [发布事务](releasing.md) | 为什么维护源码合入后 manifest 仍是 `v0.4.0-abi.10`?何时变成 `v0.4.0-abi.11`? | +| [发布事务](releasing.md) | 为什么维护源码合入后 manifest 仍是 `v0.4.0-abi.11`?何时变成 `v0.4.0-abi.12`? | | [变更日志](../CHANGELOG.md) | Stage1 的范围和兼容边界是什么? | ## 一页架构 @@ -85,7 +85,7 @@ Swift 对象不是另一套 runtime。它们包装 C handle;C 层管理线程 - JIT 单页写及显式 `invalidate_page` 按精确页过滤;仅多页哈希位图可能因碰撞保守多 失效。下一直链/RET 目标命中待处理代码脏页时才回到 dispatcher;纯数据写可继续直链。 - RootFS 不属于 package/release,禁止将其混入 Corresponding Source。 -- 当前 `v0.4.0-abi.11` 源码变更不携带 RootFS 或预构建二进制;XCFramework 必须由 +- 当前 `v0.4.0-abi.12` 源码变更不携带 RootFS 或预构建二进制;XCFramework 必须由 后续发布事务生成。 ## 权威来源 diff --git a/docs/releasing.en.md b/docs/releasing.en.md index cc3d065..53aaaca 100644 --- a/docs/releasing.en.md +++ b/docs/releasing.en.md @@ -3,32 +3,33 @@ [简体中文](releasing.md) | English This guide is for maintainers publishing the XCFramework and matching -Corresponding Source. `v0.4.0-abi.10` is already public; the only next tag +Corresponding Source. `v0.4.0-abi.11` is already public; the only next tag allowed for preparation is the compatible maintenance release -`v0.4.0-abi.11`. It remains an ABI-transition prerelease that is **not stable +`v0.4.0-abi.12`. It remains an ABI-transition prerelease that is **not stable v0.4.0**. Publication creates a public GitHub Release and updates the default branch, so run it only with explicit release authorization. -## State before and after `v0.4.0-abi.11` publication +## State before and after `v0.4.0-abi.12` publication ### After the maintenance PR merges, before Release publication -- `Package.swift` still pins the published `v0.4.0-abi.10` URL/checksum; +- `Package.swift` still pins the published `v0.4.0-abi.11` URL/checksum; - Swift source remains v0.3.3-ABI compatible and does not call retain/release; - repository source contains the published abi.2 procfs/task lifecycle fixes, abi.3 guest `uname` field bounds, abi.4 internal-SIGUSR1 fix, abi.5 finite streaming control-path deadline, abi.6 Swift-marshalling/stdin-close deadline reuse, abi.7 guest-atomic no-replace rename, abi.8 finite stdin-write deadline reuse, abi.9 per-call stdin write/close timeout APIs, abi.10 forced - task-teardown/address-space lifetime fixes, and the pending abi.11 - destructive-wait/reap boundary after thread-group quiescence; -- there is no installable `v0.4.0-abi.11` binary. + task-teardown/address-space lifetime fixes, abi.11 destructive-wait/reap + boundary after thread-group quiescence, and the pending abi.12 Apple + writer-preferring lock plus deferred supervisor reap; +- there is no installable `v0.4.0-abi.12` binary. This intermediate state is intentional: the default branch never advertises an unpublished asset URL that returns 404. -### After successful `v0.4.0-abi.11` publication +### After successful `v0.4.0-abi.12` publication - the release commit changes only `Package.swift`, pinning the new XCFramework URL/checksum; @@ -78,7 +79,7 @@ scripts/test-swift-ios.sh --local-binary ``` `--manifest-binary` proves that Stage1 Swift still links the currently pinned -`v0.4.0-abi.10` binary. `--local-binary` proves that the same Swift source links +`v0.4.0-abi.11` binary. `--local-binary` proves that the same Swift source links the maintenance XCFramework. Both boundaries are required. ## Execute @@ -86,7 +87,7 @@ the maintenance XCFramework. Both boundaries are required. After confirming that the tag is absent and publication is authorized: ```sh -scripts/release.sh v0.4.0-abi.11 +scripts/release.sh v0.4.0-abi.12 ``` The script derives GitHub `prerelease=true` from the SemVer suffix. Only @@ -94,7 +95,7 @@ The script derives GitHub `prerelease=true` from the SemVer suffix. Only that this is not stable v0.4, describes native lifecycle/retain-release/ join-soft-halt/wire v4, and records the Swift and RootFS boundaries. In addition to strict SemVer validation, the Stage1 policy rejects every tag -except `v0.4.0-abi.11`. Reusing `v0.4.0-abi.10` or accidentally entering +except `v0.4.0-abi.12`. Reusing `v0.4.0-abi.11` or accidentally entering `v0.4.0` therefore fails before any tag, draft, or asset is written. Do not substitute `v0.4.0`. A stable tag must wait for a separate decision after @@ -151,9 +152,9 @@ and explicit owner authorization. ## Post-publication acceptance ```sh -gh release view v0.4.0-abi.11 --repo jacklv-coder/ish-arm64-pkg +gh release view v0.4.0-abi.12 --repo jacklv-coder/ish-arm64-pkg git fetch origin --tags -git show v0.4.0-abi.11:Package.swift +git show v0.4.0-abi.12:Package.swift git pull --ff-only origin main scripts/test-swift-ios.sh --manifest-binary ``` @@ -187,8 +188,8 @@ manual recovery. ## PocketRoot upgrade gate -PocketRoot may move its dependency from `v0.4.0-abi.10` to the maintenance -release only after the public `v0.4.0-abi.11` assets, manifest update, and +PocketRoot may move its dependency from `v0.4.0-abi.11` to the maintenance +release only after the public `v0.4.0-abi.12` assets, manifest update, and post-publication real link all pass, followed by its Xcode 16/iOS 18 gates. Stage2 and a native Agent Loop are outside this release. Either still requires an independent plan, review, tests, documentation, and release decision. diff --git a/docs/releasing.md b/docs/releasing.md index 0ae851c..b4242e1 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -2,28 +2,29 @@ 简体中文|[English](releasing.en.md) -本文用于维护者发布 XCFramework 与匹配的 Corresponding Source。`v0.4.0-abi.10` -已经公开;当前唯一允许准备的下一个标签是兼容性维护版本 `v0.4.0-abi.11`。它仍是 ABI +本文用于维护者发布 XCFramework 与匹配的 Corresponding Source。`v0.4.0-abi.11` +已经公开;当前唯一允许准备的下一个标签是兼容性维护版本 `v0.4.0-abi.12`。它仍是 ABI 过渡 prerelease,**不是稳定 v0.4.0**。执行发布会创建公开 GitHub Release 和更新 默认分支,必须在获得明确发布授权后进行。 -## `v0.4.0-abi.11` 发布前后状态 +## `v0.4.0-abi.12` 发布前后状态 ### 维护 PR 合入后、Release 发布前 -- `Package.swift` 仍固定已发布的 `v0.4.0-abi.10` URL/checksum; +- `Package.swift` 仍固定已发布的 `v0.4.0-abi.11` URL/checksum; - Swift 源保持 v0.3.3 ABI 兼容,不调用 retain/release; - 仓库源码包含已发布的 abi.2 procfs/task 生命周期修复、abi.3 guest `uname` 字段边界修复、abi.4 内部 SIGUSR1 解屏蔽修复、abi.5 有限 streaming control-path deadline、abi.6 Swift 参数封送与 stdin-close deadline 复用、abi.7 guest 原子 no-replace rename、abi.8 有限 stdin-write deadline 复用、abi.9 单次 stdin - write/close timeout API、abi.10 强制 task teardown/address-space 生命周期修复, - 以及待发布 abi.11 的 thread-group 静止后 destructive wait/reap 边界; -- 没有可供使用方安装的 `v0.4.0-abi.11` binary。 + write/close timeout API、abi.10 强制 task teardown/address-space 生命周期修复、 + abi.11 thread-group 静止后 destructive wait/reap 边界,以及待发布 abi.12 的 Apple + 写者优先锁与 supervisor 延迟 reap; +- 没有可供使用方安装的 `v0.4.0-abi.12` binary。 这个中间状态是刻意设计的:默认分支不会先暴露一个尚未公开、会返回 404 的资产 URL。 -### `v0.4.0-abi.11` 成功发布后 +### `v0.4.0-abi.12` 成功发布后 - release commit 只改 `Package.swift`,固定到新 XCFramework URL/checksum; - GitHub prerelease 包含 `libIshKernel.xcframework.zip` 与 @@ -66,7 +67,7 @@ scripts/verify-ios-artifact.sh scripts/test-swift-ios.sh --local-binary ``` -`--manifest-binary` 证明 Stage1 Swift 仍能链接当前固定的 `v0.4.0-abi.10` binary; +`--manifest-binary` 证明 Stage1 Swift 仍能链接当前固定的 `v0.4.0-abi.11` binary; `--local-binary` 证明相同 Swift 能链接待发布的 ABI 过渡 XCFramework。两者缺一不可。 ## 执行 @@ -74,14 +75,14 @@ scripts/test-swift-ios.sh --local-binary 确认标签不存在且获得发布授权后: ```sh -scripts/release.sh v0.4.0-abi.11 +scripts/release.sh v0.4.0-abi.12 ``` 脚本根据 SemVer 后缀设置 GitHub `prerelease=true`。只有 `v*-abi.*` 标签会附加专用 中英文说明,明确它不是稳定 v0.4,列出 native lifecycle/retain-release/ join-soft-halt/wire v4,并说明 Swift 和 RootFS 边界。 -除了 SemVer 检查,Stage1 版本策略还会硬性拒绝除 `v0.4.0-abi.11` 以外的任何标签。 -因此重用 `v0.4.0-abi.10` 或误输入 `v0.4.0`,都会在任何 tag、draft 或资产写入前失败。 +除了 SemVer 检查,Stage1 版本策略还会硬性拒绝除 `v0.4.0-abi.12` 以外的任何标签。 +因此重用 `v0.4.0-abi.11` 或误输入 `v0.4.0`,都会在任何 tag、draft 或资产写入前失败。 不要用 `v0.4.0` 代替过渡标签。稳定标签必须等 Stage2 合入、迁移与回归完成后另行决定。 @@ -128,9 +129,9 @@ LICENSE/NOTICE、对应源码、SBOM、PocketRoot manifest 更新和负责人明 ## 发布后验收 ```sh -gh release view v0.4.0-abi.11 --repo jacklv-coder/ish-arm64-pkg +gh release view v0.4.0-abi.12 --repo jacklv-coder/ish-arm64-pkg git fetch origin --tags -git show v0.4.0-abi.11:Package.swift +git show v0.4.0-abi.12:Package.swift git pull --ff-only origin main scripts/test-swift-ios.sh --manifest-binary ``` @@ -161,7 +162,7 @@ scripts/test-swift-ios.sh --manifest-binary ## PocketRoot 升级条件 -只有 `v0.4.0-abi.11` 公开资产、manifest 更新和发布后真链接全部通过,PocketRoot 才能 -把依赖从 `v0.4.0-abi.10` 更新到该维护版本并重跑 Xcode 16/iOS 18 门禁。Stage2 与 +只有 `v0.4.0-abi.12` 公开资产、manifest 更新和发布后真链接全部通过,PocketRoot 才能 +把依赖从 `v0.4.0-abi.11` 更新到该维护版本并重跑 Xcode 16/iOS 18 门禁。Stage2 与 原生 Agent Loop 均不在本次发布范围内;未来启用时仍需独立计划、CR、测试、文档和 发布决策。 diff --git a/docs/testing.en.md b/docs/testing.en.md index 4072c3b..5137fe9 100644 --- a/docs/testing.en.md +++ b/docs/testing.en.md @@ -16,25 +16,25 @@ wire v4, and release supply chain. | Native integration | `internal-signal-mask`, `procfs_test`, `ishembed_smoke` | internal SIGUSR1 masks on embedded/guest task threads, fakefs, spawn, procfs, a real guest `uname -a`, and the general command path | RootFS provenance/license is trustworthy; compatibility with a particular user tool | | Sanitizers | ASan/UBSan and TSan where applicable | bounds, UAF, undefined behavior, and races on covered paths | every schedule is defect-free | | RootFS-free Swift | instance/session gates, shutdown retry, public API smoke | oneshot/session leases prevent old-ABI UAF, failure keeps the handle, old public signatures compile | every C call is cancellable or close is always bounded | -| Swift manifest real link | `test-swift-ios.sh --manifest-binary` | Stage1 Swift links the current `v0.4.0-abi.10` binary | `v0.4.0-abi.11` fixes are public | +| Swift manifest real link | `test-swift-ios.sh --manifest-binary` | Stage1 Swift links the current `v0.4.0-abi.11` binary | `v0.4.0-abi.12` fixes are public | | Swift local real link | `test-swift-ios.sh --local-binary` | the same Swift links the maintenance XCFramework | GitHub assets are published | | XCFramework | `build-ios.sh` plus symbol/final-link checks | device/simulator arm64, minimum iOS 18, required symbols | product app behavior | | Docs/scripts | positive/negative docs gates, shell syntax, policy tests | bilingual links, diagnostics, release-notes/version/tag/source policy | documentation equals implementation | ## Confirm the Stage1 state first -Before `v0.4.0-abi.11` publication, all of these should be true: +Before `v0.4.0-abi.12` publication, all of these should be true: - `ISH_EMBED_ABI_VERSION` is 1; - `ISH_PROTO_VERSION` is 4; - Swift source does not call `ish_embed_session_retain/release`; -- `Package.swift` still pins the public `v0.4.0-abi.10`; +- `Package.swift` still pins the public `v0.4.0-abi.11`; - the locally built XCFramework exports retain/release and required join/soft-halt symbols; - RootFS content is absent from Git diff, XCFramework, source archive, and Release manifest. -Only after publication should “manifest pins `v0.4.0-abi.11`” become the expected +Only after publication should “manifest pins `v0.4.0-abi.12`” become the expected state. Do not apply that expectation to a correct pre-publication tree that -still references the verified `v0.4.0-abi.10`. +still references the verified `v0.4.0-abi.11`. ## Fast metadata and script gates diff --git a/docs/testing.md b/docs/testing.md index 666e125..ad7451a 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -15,24 +15,24 @@ iOS 18 二进制、内部 wire v4 和发布供应链处于同一可解释状态 | native 集成 | `internal-signal-mask`、`procfs_test`、`ishembed_smoke` | 嵌入/guest task 内部 SIGUSR1 mask、fakefs、spawn、procfs、真实 guest `uname -a`、通用命令链路 | RootFS 来源/许可可信;特定用户工具兼容性 | | sanitizer | ASan/UBSan,必要时 TSan | 已覆盖路径上的越界、UAF、未定义行为和数据竞争 | 所有调度组合都无缺陷 | | Swift RootFS-free | instance/session gate、shutdown retry、公开 API smoke | oneshot/session lease 阻止旧 ABI UAF、失败保留 handle、旧公开签名可编译 | 任意 C 调用都可取消或 close 始终有界 | -| Swift manifest 真链接 | `test-swift-ios.sh --manifest-binary` | Stage1 Swift 与当前 `v0.4.0-abi.10` binary 链接 | `v0.4.0-abi.11` 修复已公开 | +| Swift manifest 真链接 | `test-swift-ios.sh --manifest-binary` | Stage1 Swift 与当前 `v0.4.0-abi.11` binary 链接 | `v0.4.0-abi.12` 修复已公开 | | Swift local 真链接 | `test-swift-ios.sh --local-binary` | 同一 Swift 与待发布维护 XCFramework 链接 | GitHub 资产已发布 | | XCFramework | `build-ios.sh` + symbol/final-link 检查 | device/simulator arm64、最低 iOS 18、必需符号 | App 产品逻辑 | | 文档/脚本 | docs 正负门禁、shell syntax、策略测试 | 双语链接、失败诊断、release notes/version/tag/source policy | 文档本身等于实现 | ## 先确认 Stage1 状态 -`v0.4.0-abi.11` 发布前应同时满足: +`v0.4.0-abi.12` 发布前应同时满足: - `ISH_EMBED_ABI_VERSION` 为 1; - `ISH_PROTO_VERSION` 为 4; - Swift 源不调用 `ish_embed_session_retain/release`; -- `Package.swift` 仍固定已公开的 `v0.4.0-abi.10`; +- `Package.swift` 仍固定已公开的 `v0.4.0-abi.11`; - 本地构建的新 XCFramework 导出 retain/release、join/soft-halt 等必需符号; - RootFS 没有出现在 Git diff、XCFramework、source archive 或 Release 清单中。 -发布后才把“manifest 固定到 `v0.4.0-abi.11`”加入预期。不能用发布后的预期否定 -发布前仍引用已验证 `v0.4.0-abi.10` 的正确状态。 +发布后才把“manifest 固定到 `v0.4.0-abi.12`”加入预期。不能用发布后的预期否定 +发布前仍引用已验证 `v0.4.0-abi.11` 的正确状态。 ## 快速元数据与脚本门禁 diff --git a/docs/troubleshooting.en.md b/docs/troubleshooting.en.md index 28e7659..0b87c9b 100644 --- a/docs/troubleshooting.en.md +++ b/docs/troubleshooting.en.md @@ -29,8 +29,8 @@ checksum, and RootFS provenance, size, and SHA-256. Without this matrix, logs may describe different states. The current correct combination is C ABI 1, wire v4, Swift not calling -retain/release, and a manifest pointing at the public `v0.4.0-abi.10`. After -`v0.4.0-abi.11` publication, only the manifest URL/checksum should switch to the +retain/release, and a manifest pointing at the public `v0.4.0-abi.11`. After +`v0.4.0-abi.12` publication, only the manifest URL/checksum should switch to the maintenance asset. ## Missing retain/release or other link symbols @@ -47,24 +47,24 @@ nm -gU path/to/libIshKernel.a | awk '{print $NF}' | sort -u \ old-ABI-compatible Swift layer rather than expanding this maintenance release. - If a local Stage1 XCFramework lacks them, it was built from an old commit, gitlink, or cache. Rebuild in an isolated path. -- If `v0.4.0-abi.11` is public but the manifest is still `v0.4.0-abi.10`, inspect +- If `v0.4.0-abi.12` is public but the manifest is still `v0.4.0-abi.11`, inspect whether the release commit/default-branch fast-forward completed. Never guess a checksum. ## `Package.swift` looks “not updated” -Between the maintenance PR merge and Release publication, a `v0.4.0-abi.10` +Between the maintenance PR merge and Release publication, a `v0.4.0-abi.11` manifest pin is expected. The release script rebuilds and validates assets from the merged commit, creates a manifest-only release commit, publishes and verifies assets, then fast-forwards the default branch. Thus the branch never advertises a 404 URL. -Only after confirming that the `v0.4.0-abi.11` Release is public is an old +Only after confirming that the `v0.4.0-abi.12` Release is public is an old manifest abnormal: ```sh -gh release view v0.4.0-abi.11 --repo jacklv-coder/ish-arm64-pkg -git ls-remote --tags origin refs/tags/v0.4.0-abi.11 +gh release view v0.4.0-abi.12 --repo jacklv-coder/ish-arm64-pkg +git ls-remote --tags origin refs/tags/v0.4.0-abi.12 git fetch origin git log --oneline --decorate -5 origin/main ``` @@ -115,8 +115,10 @@ cannot confirm cleanup, or a later command cannot recover. `v0.4.0-abi.7` adds guest-atomic no-replace rename. `v0.4.0-abi.8` makes finite-session stdin write/close share one absolute deadline. `v0.4.0-abi.9` adds per-call stdin write/close timeout APIs. Published `v0.4.0-abi.10` fixes - forced task-teardown lifetime races. Planned `v0.4.0-abi.11` delays a - destructive wait until the full thread group is quiescent; none changes guest + forced task-teardown lifetime races. Published `v0.4.0-abi.11` delays a + destructive wait until the full thread group is quiescent. Planned + `v0.4.0-abi.12` fixes Apple writer starvation and retries a briefly deferred + destructive reap; none changes guest signal semantics. - Source validation should run iSH's `internal-signal-mask` test, package-level host/iOS tests, and a real-RootFS “cancel → native termination confirmed → diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 855c207..7cc8249 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -26,7 +26,7 @@ git status --short 来源、大小和 SHA-256。没有这组信息,后续日志可能来自不同状态。 当前正确组合是:C ABI 1、wire v4、Swift 不调用 retain/release、manifest 指向已公开 -的 `v0.4.0-abi.10`。发布 `v0.4.0-abi.11` 后,只有 manifest URL/checksum 应切到维护资产。 +的 `v0.4.0-abi.11`。发布 `v0.4.0-abi.12` 后,只有 manifest URL/checksum 应切到维护资产。 ## 链接缺少 retain/release 或其他符号 @@ -44,20 +44,20 @@ nm -gU path/to/libIshKernel.a | awk '{print $NF}' | sort -u \ 旧 ABI 兼容 Swift,而不是扩大本次维护发布范围。 - 如果检查的是本地 Stage1 XCFramework 而符号缺失,说明构建用了旧 commit、旧 gitlink 或缓存;用隔离 build 路径重建。 -- 如果 `v0.4.0-abi.11` 已发布但 manifest 仍是 `v0.4.0-abi.10`,检查 release +- 如果 `v0.4.0-abi.12` 已发布但 manifest 仍是 `v0.4.0-abi.11`,检查 release commit/default branch fast-forward 是否完成;不要手工猜 checksum。 ## Package.swift 看起来“还没更新” -维护 PR 合入到 Release 发布前,manifest 固定 `v0.4.0-abi.10` 是预期状态。发布脚本先 +维护 PR 合入到 Release 发布前,manifest 固定 `v0.4.0-abi.11` 是预期状态。发布脚本先 从合入 commit 重建/验证资产,再创建只更新 manifest 的 release commit,先公开并校验 资产,最后 fast-forward 默认分支。这样默认分支不会引用 404 URL。 -只有已经确认 `v0.4.0-abi.11` Release 公开后,manifest 仍旧才是异常。此时检查: +只有已经确认 `v0.4.0-abi.12` Release 公开后,manifest 仍旧才是异常。此时检查: ```sh -gh release view v0.4.0-abi.11 --repo jacklv-coder/ish-arm64-pkg -git ls-remote --tags origin refs/tags/v0.4.0-abi.11 +gh release view v0.4.0-abi.12 --repo jacklv-coder/ish-arm64-pkg +git ls-remote --tags origin refs/tags/v0.4.0-abi.12 git fetch origin git log --oneline --decorate -5 origin/main ``` @@ -98,7 +98,8 @@ Linux `new_utsname` 65 字节字段宽度的宿主 hostname 触发。 的复用;`v0.4.0-abi.7` 新增 guest 原子 no-replace rename;`v0.4.0-abi.8` 让有限 session 的 stdin write/close 共享同一绝对 deadline;`v0.4.0-abi.9` 新增单次 stdin write/close timeout API;已发布的 `v0.4.0-abi.10` 修复强制 task teardown 生命周期 - 竞态;待发布的 `v0.4.0-abi.11` 会等完整 thread group 静止后才允许破坏性 wait 回收; + 竞态;已发布的 `v0.4.0-abi.11` 会等完整 thread group 静止后才允许破坏性 wait 回收; + 待发布的 `v0.4.0-abi.12` 修复 Apple 写者饥饿并对短暂延迟的 destructive reap 重试; 这些版本 都不改变 guest signal 语义。 - 源码验证应运行 iSH 的 `internal-signal-mask` 测试、包级 host/iOS 测试,并在真实 diff --git a/scripts/release-version-policy.sh b/scripts/release-version-policy.sh index c2fa7d5..9dbe424 100644 --- a/scripts/release-version-policy.sh +++ b/scripts/release-version-policy.sh @@ -5,7 +5,7 @@ # existing tag and accidentally creating a stable tag before Stage2 is # integrated and separately authorized. ish_release_stage1_version_allowed() { - [[ "$1" == "v0.4.0-abi.11" ]] + [[ "$1" == "v0.4.0-abi.12" ]] } # Call only after the release entry point has validated strict SemVer. @@ -47,6 +47,10 @@ pthread 终止手段。 本版本还让破坏性 guest wait 保留 zombie,直到所有强制分离的 host pthread 与较晚的 thread-group member 都释放旧进程资源。`WNOWAIT` 仍可用于观察,最后一个 owner 会唤醒 waiter,避免 replacement embedded process 过早复用 runtime。 +本维护版进一步为 Apple 平台采用写者优先的内部读写锁:已有写者排队时,后到的读者 +不能持续插队,从而避免代码脏页失效路径在高并发 guest 读操作下长期阻塞。supervisor +在 `WNOWAIT` 已观察到 zombie、但 destructive `waitpid(WNOHANG)` 暂时返回 0 时,也会在 +原清理期限内重试,等待 guest thread group 完成资源静止,而不是错误地 fail-close VM。 公开 C ABI 版本仍为 1;这些变更向后兼容。Swift 源仍不调用 retain/release; 完整 Swift lifecycle、类型化状态与 Terminal/VT 改造将在 Stage2 交付。 本 Release 不包含 RootFS,发布脚本也不会上传 RootFS。 @@ -80,6 +84,13 @@ Destructive guest waits now keep a zombie process visible until every force-detached host pthread and late thread-group member has released the old process. Observational `WNOWAIT` remains available, while the final owner wakes the waiter before a replacement embedded process can start. +This maintenance release also uses an internal writer-preferring rwlock on +Apple platforms. Once a writer is queued, later readers cannot barge forever, +so code-dirty invalidation cannot starve behind sustained concurrent guest +reads. If `WNOWAIT` has observed a zombie but destructive `waitpid(WNOHANG)` +temporarily returns zero, the supervisor now retries within the existing +cleanup deadline while the guest thread group finishes releasing resources, +rather than incorrectly fail-closing the VM. The public C ABI remains version 1; these changes are backward compatible. Swift source still does not call retain/release; the complete Swift diff --git a/scripts/release.sh b/scripts/release.sh index 704d24c..72c3951 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -30,7 +30,7 @@ Environment equivalents: Every release contains libIshKernel.xcframework.zip and its matching IshEmbed-corresponding-source.tar.gz. This script never publishes a RootFS; RootFS provenance, hashing, licensing, and distribution remain a separate flow. -The Stage1 policy accepts only v0.4.0-abi.11 and publishes it as a prerelease. +The Stage1 policy accepts only v0.4.0-abi.12 and publishes it as a prerelease. A suffix-free stable v0.4.0 remains blocked until the Stage2 policy replaces this transition gate. EOF @@ -110,7 +110,7 @@ VERSION_POLICY="$(cd "$(dirname "$0")" && pwd)/release-version-policy.sh" # shellcheck source=release-version-policy.sh source "$VERSION_POLICY" ish_release_stage1_version_allowed "$VERSION" \ - || fail "Stage1 release policy allows only v0.4.0-abi.11; stable v0.4.0 requires Stage2" + || fail "Stage1 release policy allows only v0.4.0-abi.12; stable v0.4.0 requires Stage2" GITHUB_PRERELEASE="$(ish_release_github_prerelease "$VERSION")" if [[ "$VERSION" == *-* ]]; then PRERELEASE="${VERSION#*-}" diff --git a/scripts/test-release-version-policy.sh b/scripts/test-release-version-policy.sh index dbe5bb6..f7f7c68 100755 --- a/scripts/test-release-version-policy.sh +++ b/scripts/test-release-version-policy.sh @@ -6,11 +6,11 @@ PKG_ROOT="$(cd "$(dirname "$0")/.." && pwd)" # shellcheck source=release-version-policy.sh source "$PKG_ROOT/scripts/release-version-policy.sh" -ish_release_stage1_version_allowed v0.4.0-abi.11 || { +ish_release_stage1_version_allowed v0.4.0-abi.12 || { printf 'error: authorized Stage1 maintenance tag was rejected\n' >&2 exit 1 } -for forbidden in v0.4.0 v0.4.0-abi.1 v0.4.0-abi.2 v0.4.0-abi.3 v0.4.0-abi.4 v0.4.0-abi.5 v0.4.0-abi.6 v0.4.0-abi.7 v0.4.0-abi.8 v0.4.0-abi.9 v0.4.0-abi.10 v0.4.0-rc.1 v1.2.3; do +for forbidden in v0.4.0 v0.4.0-abi.1 v0.4.0-abi.2 v0.4.0-abi.3 v0.4.0-abi.4 v0.4.0-abi.5 v0.4.0-abi.6 v0.4.0-abi.7 v0.4.0-abi.8 v0.4.0-abi.9 v0.4.0-abi.10 v0.4.0-abi.11 v0.4.0-rc.1 v1.2.3; do if ish_release_stage1_version_allowed "$forbidden"; then printf 'error: Stage1 policy unexpectedly allowed %s\n' "$forbidden" >&2 exit 1 @@ -29,12 +29,12 @@ assert_prerelease_flag() { } } -assert_prerelease_flag v0.4.0-abi.11 true +assert_prerelease_flag v0.4.0-abi.12 true assert_prerelease_flag v1.2.3-rc.1 true assert_prerelease_flag v0.4.0 false assert_prerelease_flag v1.2.3 false -abi_notes="$(ish_release_abi_transition_notes v0.4.0-abi.11)" +abi_notes="$(ish_release_abi_transition_notes v0.4.0-abi.12)" for expected in \ '这不是稳定 v0.4' \ 'This is not stable v0.4' \ @@ -65,6 +65,13 @@ for expected in \ 'Destructive guest waits' \ 'Observational `WNOWAIT`' \ 'replacement embedded process' \ + '写者优先的内部读写锁' \ + '后到的读者' \ + 'writer-preferring rwlock' \ + 'later readers cannot barge' \ + 'waitpid(WNOHANG)' \ + 'cleanup deadline' \ + 'fail-closing the VM' \ 'public C ABI remains version 1' \ 'Stage2' \ 'does not contain a RootFS'; do diff --git a/supervisor/ishsv.c b/supervisor/ishsv.c index 57103d0..6c69597 100644 --- a/supervisor/ishsv.c +++ b/supervisor/ishsv.c @@ -1550,15 +1550,31 @@ static void reap_children_until(uint64_t cleanup_deadline_ms) { int status; pid_t reaped; - do { - reaped = supervisor_waitpid(pid, &status, WNOHANG); - } while (reaped < 0 && errno == EINTR); - if (reaped != pid) { - if (reaped < 0 && errno != ECHILD) - slogf("ishsv: waitpid(%d) failed: %s\n", - (int)pid, strerror(errno)); - fail_close_instance("cannot reap an observed guest child"); - return; + for (;;) { + do { + reaped = supervisor_waitpid(pid, &status, WNOHANG); + } while (reaped < 0 && errno == EINTR); + if (reaped == pid) break; + if (reaped < 0) { + if (errno != ECHILD) + slogf("ishsv: waitpid(%d) failed: %s\n", + (int)pid, strerror(errno)); + fail_close_instance("cannot reap an observed guest child"); + return; + } + + /* The embedded kernel can publish a zombie to WNOWAIT before the + * final host pthread in that guest thread group has released its + * borrowed task/address-space state. A destructive WNOHANG wait + * deliberately returns 0 until that ownership is quiescent. Keep + * the zombie (and therefore its PID/PGID) pinned, then retry under + * the same cleanup deadline instead of treating this short-lived + * state as a contradictory wait result. */ + if (monotonic_ms() >= cleanup_deadline_ms) { + fail_close_instance("timed out reaping an observed guest child"); + return; + } + usleep(1000); } if (!s) continue; diff --git a/third_party/ish b/third_party/ish index 7564928..6b599fe 160000 --- a/third_party/ish +++ b/third_party/ish @@ -1 +1 @@ -Subproject commit 7564928c7b67c108768b5cf20bab1fdb4df54749 +Subproject commit 6b599fe7cd4b2dd90eaa7b168cb8b8833fdd98ba