Skip to content

Commit 034ef1a

Browse files
committed
test: add basic multithreaded SAPI test
1 parent 3f6595d commit 034ef1a

File tree

1 file changed

+104
-1
lines changed

1 file changed

+104
-1
lines changed

tests/sapi.rs

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

1111
use ext_php_rs::builders::SapiBuilder;
12-
use ext_php_rs::embed::{Embed, ext_php_rs_sapi_startup};
12+
use ext_php_rs::embed::{
13+
Embed, ext_php_rs_sapi_per_thread_init, ext_php_rs_sapi_shutdown, ext_php_rs_sapi_startup,
14+
};
1315
use ext_php_rs::ffi::{
1416
ZEND_RESULT_CODE_SUCCESS, php_module_shutdown, php_module_startup, php_request_shutdown,
1517
php_request_startup, sapi_shutdown, sapi_startup,
1618
};
1719
use ext_php_rs::prelude::*;
1820
use ext_php_rs::zend::try_catch_first;
1921
use std::ffi::c_char;
22+
use std::sync::{Arc, Mutex};
23+
use std::thread;
2024

2125
static mut LAST_OUTPUT: String = String::new();
2226

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

3645
#[test]
3746
fn test_sapi() {
47+
let _guard = SAPI_TEST_MUTEX.lock().unwrap();
48+
3849
let mut builder = SapiBuilder::new("test", "Test");
3950
builder = builder.ub_write_function(output_tester);
4051

@@ -86,6 +97,10 @@ fn test_sapi() {
8697
unsafe {
8798
sapi_shutdown();
8899
}
100+
101+
unsafe {
102+
ext_php_rs_sapi_shutdown();
103+
}
89104
}
90105

91106
/// Gives you a nice greeting!
@@ -102,3 +117,91 @@ pub fn hello_world(name: String) -> String {
102117
pub fn module(module: ModuleBuilder) -> ModuleBuilder {
103118
module.function(wrap_function!(hello_world))
104119
}
120+
121+
#[test]
122+
fn test_sapi_multithread() {
123+
let _guard = SAPI_TEST_MUTEX.lock().unwrap();
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(e) => panic!("Evaluation failed in thread {i}: {e:?}"),
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!(
190+
results
191+
.iter()
192+
.any(|(idx, output)| { *idx == i && output == &format!("Hello, thread-{i}!") })
193+
);
194+
}
195+
196+
unsafe {
197+
php_module_shutdown();
198+
}
199+
200+
unsafe {
201+
sapi_shutdown();
202+
}
203+
204+
unsafe {
205+
ext_php_rs_sapi_shutdown();
206+
}
207+
}

0 commit comments

Comments
 (0)