folk_runtime_embed/
runtime.rs1use anyhow::Result;
7use async_trait::async_trait;
8use folk_core::runtime::{Runtime, WorkerHandle};
9use tracing::{debug, info};
10
11use crate::handle::EmbedWorkerHandle;
12use crate::php::PhpInstance;
13use crate::worker::spawn_worker_thread;
14
15#[derive(Debug, Clone)]
17pub struct EmbedConfig {
18 pub script: Option<String>,
21
22 pub warmup_files: Vec<String>,
26}
27
28pub struct EmbedRuntime {
30 config: EmbedConfig,
31 warmup_done: bool,
32}
33
34impl EmbedRuntime {
35 pub fn new(config: EmbedConfig) -> Self {
36 Self {
37 config,
38 warmup_done: false,
39 }
40 }
41
42 pub async fn warmup(&mut self) -> Result<()> {
48 if self.config.warmup_files.is_empty() {
49 debug!("no warmup files configured, skipping `OPcache` warmup");
50 self.warmup_done = true;
51 return Ok(());
52 }
53
54 let files = self.config.warmup_files.clone();
55
56 let result = tokio::task::spawn_blocking(move || warmup_opcache(&files)).await?;
57
58 self.warmup_done = true;
59 result
60 }
61}
62
63#[async_trait]
64impl Runtime for EmbedRuntime {
65 async fn spawn(&self) -> Result<Box<dyn WorkerHandle>> {
66 if !self.warmup_done && !self.config.warmup_files.is_empty() {
67 debug!("`OPcache` warmup not run yet — workers will compile on first request");
68 }
69
70 let (thread, cmd_tx, task_resp_rx, control_rx, worker_id) =
71 spawn_worker_thread(self.config.script.clone());
72
73 Ok(Box::new(EmbedWorkerHandle::new(
74 worker_id,
75 cmd_tx,
76 task_resp_rx,
77 control_rx,
78 thread,
79 )))
80 }
81}
82
83fn warmup_opcache(files: &[String]) -> Result<()> {
85 info!(files = ?files, "starting `OPcache` warmup");
86
87 let mut php = PhpInstance::boot_custom_sapi()?;
88
89 php.request_startup()?;
90
91 for file in files {
92 debug!(file, "warming `OPcache`");
93 let code = format!("require_once '{}';", file.replace('\'', "\\'"));
95 if let Err(e) = php.eval(&code) {
96 tracing::warn!(file, error = ?e, "warmup file failed (non-fatal)");
97 }
98 }
99
100 let status = php.eval(
102 "if (function_exists('opcache_get_status')) { \
103 $s = opcache_get_status(false); \
104 echo json_encode(['cached_scripts' => $s['opcache_statistics']['num_cached_scripts'] ?? 0, \
105 'hits' => $s['opcache_statistics']['hits'] ?? 0, \
106 'memory_used' => $s['memory_usage']['used_memory'] ?? 0]); \
107 } else { echo '{}'; }",
108 );
109
110 if let Ok(result) = status {
111 info!(opcache_status = %result.output, "`OPcache` warmup complete");
112 }
113
114 php.request_shutdown();
115 info!("`OPcache` warmup finished — SHM bytecode available for workers");
118 Ok(())
119}