folk_ext/worker.rs
1//! ZTS worker threads — each thread runs an independent PHP context.
2//!
3//! A worker thread:
4//! 1. Registers with TSRM via `ZtsThreadGuard`
5//! 2. Starts a PHP request (`request_startup`)
6//! 3. Executes the PHP worker script (which registers handlers)
7//! 4. Enters the recv/send loop via `bridge::do_recv()`/`bridge::do_send()`
8//! 5. Cleans up on exit
9
10use std::sync::mpsc;
11use std::thread;
12
13use tracing::{error, info};
14
15use crate::bridge;
16use crate::zts;
17
18/// Spawn a ZTS worker thread that executes the given PHP script.
19///
20/// The worker thread receives tasks via `task_rx` and signals
21/// readiness via `ready_tx`. The script is expected to call
22/// `folk_worker_ready()` and then loop on `folk_worker_recv()`/`folk_worker_send()`.
23pub fn spawn_zts_worker(
24 worker_id: u32,
25 script: String,
26 task_rx: mpsc::Receiver<bridge::TaskRequest>,
27 ready_tx: mpsc::SyncSender<()>,
28) -> thread::JoinHandle<()> {
29 thread::Builder::new()
30 .name(format!("folk-worker-{worker_id}"))
31 .spawn(move || {
32 if let Err(e) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
33 run_zts_worker(worker_id, &script, task_rx, ready_tx);
34 })) {
35 error!(worker_id, "ZTS worker thread panicked: {e:?}");
36 }
37 })
38 .expect("failed to spawn worker thread")
39}
40
41fn run_zts_worker(
42 worker_id: u32,
43 script: &str,
44 task_rx: mpsc::Receiver<bridge::TaskRequest>,
45 ready_tx: mpsc::SyncSender<()>,
46) {
47 // 1. Register with TSRM.
48 let _guard = zts::ZtsThreadGuard::new();
49
50 // 2. Start PHP request.
51 if let Err(e) = zts::request_startup() {
52 error!(worker_id, error = ?e, "php_request_startup failed");
53 return;
54 }
55
56 // 3. Install worker bridge channels (thread-local).
57 bridge::init_worker_state(worker_id, task_rx, ready_tx);
58
59 // 4. Execute the PHP worker script.
60 // The script calls folk_worker_ready() and enters the recv/send loop.
61 // This call blocks until the script exits (channel closed).
62 if let Err(e) = zts::execute_script(script) {
63 error!(worker_id, error = ?e, "worker script failed");
64 }
65
66 // 5. Clean up.
67 // VCWD restore happens inside run_dispatch_loop (bridge.rs).
68 bridge::cleanup_worker_state();
69 zts::request_shutdown();
70 // ZtsThreadGuard drop handles ts_free_thread().
71
72 info!(worker_id, "ZTS worker thread exiting");
73}