diff --git a/docs/guides/applications.md b/docs/guides/applications.md index 0e7c7dc7f..785fd81ba 100644 --- a/docs/guides/applications.md +++ b/docs/guides/applications.md @@ -89,7 +89,7 @@ On macOS/Linux, Ctrl+C or `SIGTERM` requests shutdown through the application's After task completion and the active callback stack returns, the manager waits for every module to report `ready_to_shut()`, then synchronously invokes shutdown hooks and unloading. A module must use its owner-frame readiness check to stop/drain native work and confirm native thread exit before allowing unload. When all hooks succeed, Timers logs `PreShut` and then `Shut`, and the launcher exits. A hook may block; the handler-count budget is not a shutdown-duration bound. -If a shutdown hook returns `false` or throws, the launcher reports its phase, module and available exception details once, keeps the process/resources alive, and does not continue unloading. `plugin_manager::update()` returning `false` or throwing during drain also locks the launcher into retention; owner updates continue where possible, but later completion cannot resume unloading. +If a shutdown hook returns `false` or throws, the launcher reports its phase, module and available exception details once, keeps the process/resources alive, and does not continue unloading. A failed native library close similarly reports `unload: `, retains that library handle and wrapper, and stops before closing later libraries. Repeated `stop()` stays failed without repeating plugin exits or close calls. Earlier shutdown work and successful library closes are not rolled back. A successful `FreeLibrary` or `dlclose` call does not prove that the OS library image was unmapped or that arbitrary hot update is safe. `plugin_manager::update()` returning `false` or throwing during drain also locks the launcher into retention; owner updates continue where possible, but later completion cannot resume unloading. `--shutdown_timeout_ms=N` configures a non-negative int64 wait in milliseconds; the default `0` waits indefinitely. A positive limit uses a monotonic clock and reports elapsed time, pending managed-task count and the waiting phase once. On expiry the process and resources stay alive, owner updates continue, and unloading remains disabled even if work completes later. This is a cooperative owner-loop wait limit: it cannot interrupt a blocking user `update()`, readiness check, shutdown hook or unload call. It is not a hard process-stop deadline, cancellation policy or successful shutdown. This managed-task drain does not prove that arbitrary callbacks, native pool workers or background cleanup have finished; see [module task integration](tasks.md#shared-business-executor-for-modules). Timers's logs alone are not a readiness or shutdown guarantee for every plugin combination. diff --git a/docs/guides/applications_CN.md b/docs/guides/applications_CN.md index 8d18350d9..abf3977fd 100644 --- a/docs/guides/applications_CN.md +++ b/docs/guides/applications_CN.md @@ -89,7 +89,7 @@ macOS/Linux 可使用 Ctrl+C 或 `SIGTERM`,由应用主循环处理停止请 任务完成且当前回调返回后,管理器等待所有模块的 `ready_to_shut()` 返回就绪,再同步执行关闭钩子与卸载。模块应在 owner frame 的就绪检查中停止并排空原生工作,确认原生线程退出后才允许卸载。所有钩子成功时,Timers 依次输出 `PreShut` 和 `Shut`,随后启动器退出。钩子可能阻塞;handler 数量预算不代表停机耗时上限。 -关闭钩子返回 `false` 或抛出异常时,启动器输出一次阶段、模块及可用的异常信息,保留进程和资源,不再继续卸载。排空期间的 `plugin_manager::update()` 返回 `false` 或抛出异常,也会锁定资源保留状态;启动器尽可能继续 owner 更新,但后续完成不能恢复卸载。 +关闭钩子返回 `false` 或抛出异常时,启动器输出一次阶段、模块及可用的异常信息,保留进程和资源,不再继续卸载。原生动态库关闭失败时同样报告 `unload: `,保留该库句柄与包装对象,并停止关闭后续库。重复 `stop()` 继续返回失败,不重复执行插件出口或关闭调用。先前完成的停机工作与成功关闭的库不会回滚。`FreeLibrary` 或 `dlclose` 成功不证明操作系统已移除库映像,也不证明任意热更新安全。排空期间的 `plugin_manager::update()` 返回 `false` 或抛出异常,也会锁定资源保留状态;启动器尽可能继续 owner 更新,但后续完成不能恢复卸载。 `--shutdown_timeout_ms=N` 设置等待时限,单位为毫秒,接受非负 int64 整数;默认值 `0` 表示无限等待。正数使用单调时钟计时,超时后输出一次实际等待时间、待完成的业务任务数和等待阶段。进程与资源继续保留,owner 更新继续运行;即使工作随后完成,也不再卸载。这是 owner loop 的协作式等待时限,不能中断阻塞中的用户 `update()`、就绪检查、关闭钩子或卸载调用,不构成强制进程退出期限、自动取消策略或成功停机。业务任务排空不证明外部回调、原生连接池线程或后台清理已经完成,详见[模块任务集成](tasks_CN.md#模块共用业务执行器)。仅凭 Timers 的日志,不能推断所有插件组合都具备相同的就绪条件或停止保证。 diff --git a/docs/guides/tasks.md b/docs/guides/tasks.md index 8e3f6c0d6..f8e566ba0 100644 --- a/docs/guides/tasks.md +++ b/docs/guides/tasks.md @@ -72,11 +72,13 @@ Private database bridges use `manager.get_task_runtime()` to share that exact ex | --- | --- | | `running` | Update normally and accept tasks | | `draining` | Continue module updates and the central poll; retain business state and services | -| `failed` | A shutdown hook returned failure or threw; do not continue unloading, and retain process/resources | -| `stopped` | Shutdown hooks completed; repeated `stop()` returns `true` | +| `failed` | A shutdown hook failed/threw or a native library close failed; stop further unloading and retain remaining resources | +| `stopped` | Shutdown hooks and native close calls succeeded; repeated `stop()` returns `true` | Use `get_shutdown_state()`, `get_shutdown_failure()` and `get_shutdown_exception()` to distinguish waiting from failure. The launcher keeps pumping while tasks remain, and reports a hook failure once while retaining the process. There is no implicit shutdown timeout, cancellation, detach or frame destruction as a drain substitute. The pending-task check does not wait for completion. The poll budget limits handler count, not execution time, and does not bound user-hook or total shutdown latency. +`dynamic_library::unload()` returns `false` on a native close failure and keeps its handle. Success clears the handle; unloading an empty or already closed wrapper returns `true` without another native call. Manager failure records `unload: ` and latches the existing failed state, retaining the failed wrapper and remaining libraries. It does not retry plugin exits or closes on a later `stop()`, roll back previous teardown, or establish OS image removal. + Use the manager and its groups on their constructing thread. Do not mutate module registries or recursively update from update/shutdown hooks or executor callbacks. While any managed task is pending, no module can unregister: another module’s task may still borrow that provider. Destroying a manager with tasks, on an active callback stack or from another thread terminates; destruction is not a drain entry point.