Skip to main content

folk_runtime_fork/
runtime.rs

1//! `ForkRuntime`: spawns PHP workers via prefork master + `SCM_RIGHTS`.
2
3use std::os::unix::io::{FromRawFd, IntoRawFd};
4use std::sync::Arc;
5
6use anyhow::Result;
7use async_trait::async_trait;
8use folk_core::runtime::{Runtime, WorkerHandle};
9use folk_runtime_pipe::socket::create_socketpair;
10use tokio::net::UnixStream;
11use tokio::sync::Mutex;
12
13use crate::handle::ForkWorkerHandle;
14use crate::master::PreforkMaster;
15
16/// Configuration for the fork runtime.
17#[derive(Clone)]
18pub struct ForkConfig {
19    pub php: String,
20    pub script: String,
21    pub boot_timeout: std::time::Duration,
22}
23
24/// Fork-based runtime. Holds a shared reference to the prefork master.
25pub struct ForkRuntime {
26    // Stored for master recycling (future use).
27    _config: ForkConfig,
28    master: Mutex<Option<PreforkMaster>>,
29}
30
31impl ForkRuntime {
32    /// Create a new fork runtime and spawn the prefork master.
33    pub async fn new(config: ForkConfig) -> Result<Arc<Self>> {
34        let master = PreforkMaster::spawn(&config.php, &config.script, config.boot_timeout).await?;
35
36        Ok(Arc::new(Self {
37            _config: config,
38            master: Mutex::new(Some(master)),
39        }))
40    }
41}
42
43#[async_trait]
44#[allow(unsafe_code)]
45impl Runtime for ForkRuntime {
46    async fn spawn(&self) -> Result<Box<dyn WorkerHandle>> {
47        let (task_master, task_child) = create_socketpair()?;
48        let (ctrl_master, ctrl_child) = create_socketpair()?;
49
50        let child_pid = {
51            let mut master_guard = self.master.lock().await;
52            let master = master_guard
53                .as_mut()
54                .ok_or_else(|| anyhow::anyhow!("prefork master not running"))?;
55            master.fork_worker(&task_child, &ctrl_child).await?
56        };
57
58        // Child now owns copies of these FDs; drop ours.
59        drop(task_child);
60        drop(ctrl_child);
61
62        // Convert master FDs to tokio UnixStreams.
63        let task_stream = {
64            let std_sock =
65                unsafe { std::os::unix::net::UnixStream::from_raw_fd(task_master.into_raw_fd()) };
66            std_sock.set_nonblocking(true)?;
67            UnixStream::from_std(std_sock)?
68        };
69        let ctrl_stream = {
70            let std_sock =
71                unsafe { std::os::unix::net::UnixStream::from_raw_fd(ctrl_master.into_raw_fd()) };
72            std_sock.set_nonblocking(true)?;
73            UnixStream::from_std(std_sock)?
74        };
75
76        Ok(Box::new(ForkWorkerHandle::new(
77            child_pid,
78            task_stream,
79            ctrl_stream,
80        )))
81    }
82}