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:: {
13
+ ext_php_rs_sapi_per_thread_init, ext_php_rs_sapi_shutdown, ext_php_rs_sapi_startup, Embed ,
14
+ } ;
13
15
use ext_php_rs:: ffi:: {
14
16
php_module_shutdown, php_module_startup, php_request_shutdown, php_request_startup,
15
17
sapi_shutdown, sapi_startup, ZEND_RESULT_CODE_SUCCESS ,
16
18
} ;
17
19
use ext_php_rs:: prelude:: * ;
18
20
use ext_php_rs:: zend:: try_catch_first;
19
21
use std:: ffi:: c_char;
22
+ use std:: sync:: Mutex ;
20
23
21
24
static mut LAST_OUTPUT : String = String :: new ( ) ;
22
25
26
+ // Global mutex to ensure SAPI tests don't run concurrently. PHP does not allow
27
+ // multiple SAPIs to exist at the same time. This prevents the tests from
28
+ // overwriting each other's state.
29
+ static SAPI_TEST_MUTEX : Mutex < ( ) > = Mutex :: new ( ( ) ) ;
30
+
23
31
extern "C" fn output_tester ( str : * const c_char , str_length : usize ) -> usize {
24
32
let char = unsafe { std:: slice:: from_raw_parts ( str. cast :: < u8 > ( ) , str_length) } ;
25
33
let string = String :: from_utf8_lossy ( char) ;
@@ -35,6 +43,8 @@ extern "C" fn output_tester(str: *const c_char, str_length: usize) -> usize {
35
43
36
44
#[ test]
37
45
fn test_sapi ( ) {
46
+ let _guard = SAPI_TEST_MUTEX . lock ( ) . unwrap ( ) ;
47
+
38
48
let mut builder = SapiBuilder :: new ( "test" , "Test" ) ;
39
49
builder = builder. ub_write_function ( output_tester) ;
40
50
@@ -86,6 +96,10 @@ fn test_sapi() {
86
96
unsafe {
87
97
sapi_shutdown ( ) ;
88
98
}
99
+
100
+ unsafe {
101
+ ext_php_rs_sapi_shutdown ( ) ;
102
+ }
89
103
}
90
104
91
105
/// Gives you a nice greeting!
@@ -102,3 +116,92 @@ pub fn hello_world(name: String) -> String {
102
116
pub fn module ( module : ModuleBuilder ) -> ModuleBuilder {
103
117
module. function ( wrap_function ! ( hello_world) )
104
118
}
119
+
120
+ #[ test]
121
+ fn test_sapi_multithread ( ) {
122
+ let _guard = SAPI_TEST_MUTEX . lock ( ) . unwrap ( ) ;
123
+
124
+ use std:: sync:: { Arc , Mutex } ;
125
+ use std:: thread;
126
+
127
+ let mut builder = SapiBuilder :: new ( "test-mt" , "Test Multi-threaded" ) ;
128
+ builder = builder. ub_write_function ( output_tester) ;
129
+
130
+ let sapi = builder. build ( ) . unwrap ( ) . into_raw ( ) ;
131
+ let module = get_module ( ) ;
132
+
133
+ unsafe {
134
+ ext_php_rs_sapi_startup ( ) ;
135
+ }
136
+
137
+ unsafe {
138
+ sapi_startup ( sapi) ;
139
+ }
140
+
141
+ unsafe {
142
+ php_module_startup ( sapi, module) ;
143
+ }
144
+
145
+ let results = Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ;
146
+ let mut handles = vec ! [ ] ;
147
+
148
+ for i in 0 ..4 {
149
+ let results = Arc :: clone ( & results) ;
150
+
151
+ let handle = thread:: spawn ( move || {
152
+ unsafe {
153
+ ext_php_rs_sapi_per_thread_init ( ) ;
154
+ }
155
+
156
+ let result = unsafe { php_request_startup ( ) } ;
157
+ assert_eq ! ( result, ZEND_RESULT_CODE_SUCCESS ) ;
158
+
159
+ let _ = try_catch_first ( || {
160
+ let eval_result = Embed :: eval ( & format ! ( "hello_world('thread-{}');" , i) ) ;
161
+
162
+ match eval_result {
163
+ Ok ( zval) => {
164
+ assert ! ( zval. is_string( ) ) ;
165
+ let string = zval. string ( ) . unwrap ( ) ;
166
+ let output = string. to_string ( ) ;
167
+ assert_eq ! ( output, format!( "Hello, thread-{}!" , i) ) ;
168
+
169
+ results. lock ( ) . unwrap ( ) . push ( ( i, output) ) ;
170
+ }
171
+ Err ( _) => panic ! ( "Evaluation failed in thread {}" , i) ,
172
+ }
173
+ } ) ;
174
+
175
+ unsafe {
176
+ php_request_shutdown ( std:: ptr:: null_mut ( ) ) ;
177
+ }
178
+ } ) ;
179
+
180
+ handles. push ( handle) ;
181
+ }
182
+
183
+ for handle in handles {
184
+ handle. join ( ) . expect ( "Thread panicked" ) ;
185
+ }
186
+
187
+ let results = results. lock ( ) . unwrap ( ) ;
188
+ assert_eq ! ( results. len( ) , 4 ) ;
189
+
190
+ for i in 0 ..4 {
191
+ assert ! ( results
192
+ . iter( )
193
+ . any( |( idx, output) | { * idx == i && output == & format!( "Hello, thread-{}!" , i) } ) ) ;
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