Skip to content

Commit d1156a8

Browse files
committed
test: add basic multithreaded SAPI test
1 parent faf0efb commit d1156a8

File tree

1 file changed

+102
-1
lines changed

1 file changed

+102
-1
lines changed

tests/sapi.rs

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,23 @@
99
extern crate ext_php_rs;
1010

1111
use ext_php_rs::builders::SapiBuilder;
12-
use ext_php_rs::embed::{ext_php_rs_sapi_startup, Embed};
12+
use ext_php_rs::embed::{ext_php_rs_sapi_per_thread_init, ext_php_rs_sapi_shutdown, ext_php_rs_sapi_startup, Embed};
1313
use ext_php_rs::ffi::{
1414
php_module_shutdown, php_module_startup, php_request_shutdown, php_request_startup,
1515
sapi_shutdown, sapi_startup, ZEND_RESULT_CODE_SUCCESS,
1616
};
1717
use ext_php_rs::prelude::*;
1818
use ext_php_rs::zend::try_catch_first;
1919
use std::ffi::c_char;
20+
use std::sync::Mutex;
2021

2122
static mut LAST_OUTPUT: String = String::new();
2223

24+
// Global mutex to ensure SAPI tests don't run concurrently. PHP does not allow
25+
// multiple SAPIs to exist at the same time. This prevents the tests from
26+
// overwriting each other's state.
27+
static SAPI_TEST_MUTEX: Mutex<()> = Mutex::new(());
28+
2329
extern "C" fn output_tester(str: *const c_char, str_length: usize) -> usize {
2430
let char = unsafe { std::slice::from_raw_parts(str.cast::<u8>(), str_length) };
2531
let string = String::from_utf8_lossy(char);
@@ -35,6 +41,8 @@ extern "C" fn output_tester(str: *const c_char, str_length: usize) -> usize {
3541

3642
#[test]
3743
fn test_sapi() {
44+
let _guard = SAPI_TEST_MUTEX.lock().unwrap();
45+
3846
let mut builder = SapiBuilder::new("test", "Test");
3947
builder = builder.ub_write_function(output_tester);
4048

@@ -86,6 +94,10 @@ fn test_sapi() {
8694
unsafe {
8795
sapi_shutdown();
8896
}
97+
98+
unsafe {
99+
ext_php_rs_sapi_shutdown();
100+
}
89101
}
90102

91103
/// Gives you a nice greeting!
@@ -102,3 +114,92 @@ pub fn hello_world(name: String) -> String {
102114
pub fn module(module: ModuleBuilder) -> ModuleBuilder {
103115
module.function(wrap_function!(hello_world))
104116
}
117+
118+
#[test]
119+
fn test_sapi_multithread() {
120+
let _guard = SAPI_TEST_MUTEX.lock().unwrap();
121+
122+
use std::sync::{Arc, Mutex};
123+
use std::thread;
124+
125+
let mut builder = SapiBuilder::new("test-mt", "Test Multi-threaded");
126+
builder = builder.ub_write_function(output_tester);
127+
128+
let sapi = builder.build().unwrap().into_raw();
129+
let module = get_module();
130+
131+
unsafe {
132+
ext_php_rs_sapi_startup();
133+
}
134+
135+
unsafe {
136+
sapi_startup(sapi);
137+
}
138+
139+
unsafe {
140+
php_module_startup(sapi, module);
141+
}
142+
143+
let results = Arc::new(Mutex::new(Vec::new()));
144+
let mut handles = vec![];
145+
146+
for i in 0..4 {
147+
let results = Arc::clone(&results);
148+
149+
let handle = thread::spawn(move || {
150+
unsafe {
151+
ext_php_rs_sapi_per_thread_init();
152+
}
153+
154+
let result = unsafe { php_request_startup() };
155+
assert_eq!(result, ZEND_RESULT_CODE_SUCCESS);
156+
157+
let _ = try_catch_first(|| {
158+
let eval_result = Embed::eval(&format!("hello_world('thread-{}');", i));
159+
160+
match eval_result {
161+
Ok(zval) => {
162+
assert!(zval.is_string());
163+
let string = zval.string().unwrap();
164+
let output = string.to_string();
165+
assert_eq!(output, format!("Hello, thread-{}!", i));
166+
167+
results.lock().unwrap().push((i, output));
168+
}
169+
Err(_) => panic!("Evaluation failed in thread {}", i),
170+
}
171+
});
172+
173+
unsafe {
174+
php_request_shutdown(std::ptr::null_mut());
175+
}
176+
});
177+
178+
handles.push(handle);
179+
}
180+
181+
for handle in handles {
182+
handle.join().expect("Thread panicked");
183+
}
184+
185+
let results = results.lock().unwrap();
186+
assert_eq!(results.len(), 4);
187+
188+
for i in 0..4 {
189+
assert!(results.iter().any(|(idx, output)| {
190+
*idx == i && output == &format!("Hello, thread-{}!", i)
191+
}));
192+
}
193+
194+
unsafe {
195+
php_module_shutdown();
196+
}
197+
198+
unsafe {
199+
sapi_shutdown();
200+
}
201+
202+
unsafe {
203+
ext_php_rs_sapi_shutdown();
204+
}
205+
}

0 commit comments

Comments
 (0)