use anyhow::Result;
use async_trait::async_trait;
use folk_core::runtime::{Runtime, WorkerHandle};
use tracing::{debug, info};
use crate::handle::EmbedWorkerHandle;
use crate::php::PhpInstance;
use crate::worker::spawn_worker_thread;
#[derive(Debug, Clone)]
pub struct EmbedConfig {
pub script: Option<String>,
pub warmup_files: Vec<String>,
}
pub struct EmbedRuntime {
config: EmbedConfig,
warmup_done: bool,
}
impl EmbedRuntime {
pub fn new(config: EmbedConfig) -> Self {
Self {
config,
warmup_done: false,
}
}
pub async fn warmup(&mut self) -> Result<()> {
if self.config.warmup_files.is_empty() {
debug!("no warmup files configured, skipping `OPcache` warmup");
self.warmup_done = true;
return Ok(());
}
let files = self.config.warmup_files.clone();
let result = tokio::task::spawn_blocking(move || warmup_opcache(&files)).await?;
self.warmup_done = true;
result
}
}
#[async_trait]
impl Runtime for EmbedRuntime {
async fn spawn(&self) -> Result<Box<dyn WorkerHandle>> {
if !self.warmup_done && !self.config.warmup_files.is_empty() {
debug!("`OPcache` warmup not run yet — workers will compile on first request");
}
let (thread, cmd_tx, task_resp_rx, control_rx, worker_id) =
spawn_worker_thread(self.config.script.clone());
Ok(Box::new(EmbedWorkerHandle::new(
worker_id,
cmd_tx,
task_resp_rx,
control_rx,
thread,
)))
}
}
fn warmup_opcache(files: &[String]) -> Result<()> {
info!(files = ?files, "starting `OPcache` warmup");
let mut php = PhpInstance::boot_custom_sapi()?;
php.request_startup()?;
for file in files {
debug!(file, "warming `OPcache`");
let code = format!("require_once '{}';", file.replace('\'', "\\'"));
if let Err(e) = php.eval(&code) {
tracing::warn!(file, error = ?e, "warmup file failed (non-fatal)");
}
}
let status = php.eval(
"if (function_exists('opcache_get_status')) { \
$s = opcache_get_status(false); \
echo json_encode(['cached_scripts' => $s['opcache_statistics']['num_cached_scripts'] ?? 0, \
'hits' => $s['opcache_statistics']['hits'] ?? 0, \
'memory_used' => $s['memory_usage']['used_memory'] ?? 0]); \
} else { echo '{}'; }",
);
if let Ok(result) = status {
info!(opcache_status = %result.output, "`OPcache` warmup complete");
}
php.request_shutdown();
info!("`OPcache` warmup finished — SHM bytecode available for workers");
Ok(())
}