9
9
extern crate ext_php_rs;
10
10
11
11
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 } ;
13
13
use ext_php_rs:: ffi:: {
14
14
php_module_shutdown, php_module_startup, php_request_shutdown, php_request_startup,
15
15
sapi_shutdown, sapi_startup, ZEND_RESULT_CODE_SUCCESS ,
16
16
} ;
17
17
use ext_php_rs:: prelude:: * ;
18
18
use ext_php_rs:: zend:: try_catch_first;
19
19
use std:: ffi:: c_char;
20
+ use std:: sync:: Mutex ;
20
21
21
22
static mut LAST_OUTPUT : String = String :: new ( ) ;
22
23
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
+
23
29
extern "C" fn output_tester ( str : * const c_char , str_length : usize ) -> usize {
24
30
let char = unsafe { std:: slice:: from_raw_parts ( str. cast :: < u8 > ( ) , str_length) } ;
25
31
let string = String :: from_utf8_lossy ( char) ;
@@ -35,6 +41,8 @@ extern "C" fn output_tester(str: *const c_char, str_length: usize) -> usize {
35
41
36
42
#[ test]
37
43
fn test_sapi ( ) {
44
+ let _guard = SAPI_TEST_MUTEX . lock ( ) . unwrap ( ) ;
45
+
38
46
let mut builder = SapiBuilder :: new ( "test" , "Test" ) ;
39
47
builder = builder. ub_write_function ( output_tester) ;
40
48
@@ -86,6 +94,10 @@ fn test_sapi() {
86
94
unsafe {
87
95
sapi_shutdown ( ) ;
88
96
}
97
+
98
+ unsafe {
99
+ ext_php_rs_sapi_shutdown ( ) ;
100
+ }
89
101
}
90
102
91
103
/// Gives you a nice greeting!
@@ -102,3 +114,92 @@ pub fn hello_world(name: String) -> String {
102
114
pub fn module ( module : ModuleBuilder ) -> ModuleBuilder {
103
115
module. function ( wrap_function ! ( hello_world) )
104
116
}
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