1 /*
2 +----------------------------------------------------------------------+
3 | Swoole |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 2.0 of the Apache license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | http://www.apache.org/licenses/LICENSE-2.0.html |
9 | If you did not receive a copy of the Apache2.0 license and are unable|
10 | to obtain it through the world-wide-web, please send a note to |
11 | license@swoole.com so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Tianfeng Han <mikan.tenny@gmail.com> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include "php_swoole_private.h"
18 #include "swoole_memory.h"
19
20 #ifdef HAVE_FUTEX
21 #include <linux/futex.h>
22 #include <syscall.h>
23
swoole_futex_wait(sw_atomic_t * atomic,double timeout)24 static sw_inline int swoole_futex_wait(sw_atomic_t *atomic, double timeout) {
25 if (sw_atomic_cmp_set(atomic, 1, 0)) {
26 return SW_OK;
27 }
28
29 int ret;
30 struct timespec _timeout;
31
32 if (timeout > 0) {
33 _timeout.tv_sec = (long) timeout;
34 _timeout.tv_nsec = (timeout - _timeout.tv_sec) * 1000 * 1000 * 1000;
35 ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, &_timeout, nullptr, 0);
36 } else {
37 ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, nullptr, nullptr, 0);
38 }
39 if (ret == SW_OK && sw_atomic_cmp_set(atomic, 1, 0)) {
40 return SW_OK;
41 } else {
42 return SW_ERR;
43 }
44 }
45
swoole_futex_wakeup(sw_atomic_t * atomic,int n)46 static sw_inline int swoole_futex_wakeup(sw_atomic_t *atomic, int n) {
47 if (sw_atomic_cmp_set(atomic, 0, 1)) {
48 return syscall(SYS_futex, atomic, FUTEX_WAKE, n, nullptr, nullptr, 0);
49 } else {
50 return SW_OK;
51 }
52 }
53
54 #else
swoole_atomic_wait(sw_atomic_t * atomic,double timeout)55 static sw_inline int swoole_atomic_wait(sw_atomic_t *atomic, double timeout) {
56 if (sw_atomic_cmp_set(atomic, (sw_atomic_t) 1, (sw_atomic_t) 0)) {
57 return SW_OK;
58 }
59 timeout = timeout <= 0 ? INT_MAX : timeout;
60 int32_t i = (int32_t) sw_atomic_sub_fetch(atomic, 1);
61 while (timeout > 0) {
62 if ((int32_t) *atomic > i) {
63 return SW_OK;
64 } else {
65 usleep(1000);
66 timeout -= 0.001;
67 }
68 }
69 sw_atomic_fetch_add(atomic, 1);
70 return SW_ERR;
71 }
72
swoole_atomic_wakeup(sw_atomic_t * atomic,int n)73 static sw_inline int swoole_atomic_wakeup(sw_atomic_t *atomic, int n) {
74 if (1 == (int32_t) *atomic) {
75 return SW_OK;
76 }
77 sw_atomic_fetch_add(atomic, n);
78 return SW_OK;
79 }
80 #endif
81
82 zend_class_entry *swoole_atomic_ce;
83 static zend_object_handlers swoole_atomic_handlers;
84
85 zend_class_entry *swoole_atomic_long_ce;
86 static zend_object_handlers swoole_atomic_long_handlers;
87
88 struct AtomicObject {
89 sw_atomic_t *ptr;
90 zend_object std;
91 };
92
php_swoole_atomic_fetch_object(zend_object * obj)93 static sw_inline AtomicObject *php_swoole_atomic_fetch_object(zend_object *obj) {
94 return (AtomicObject *) ((char *) obj - swoole_atomic_handlers.offset);
95 }
96
php_swoole_atomic_get_ptr(zval * zobject)97 static sw_atomic_t *php_swoole_atomic_get_ptr(zval *zobject) {
98 return php_swoole_atomic_fetch_object(Z_OBJ_P(zobject))->ptr;
99 }
100
php_swoole_atomic_set_ptr(zval * zobject,sw_atomic_t * ptr)101 void php_swoole_atomic_set_ptr(zval *zobject, sw_atomic_t *ptr) {
102 php_swoole_atomic_fetch_object(Z_OBJ_P(zobject))->ptr = ptr;
103 }
104
php_swoole_atomic_free_object(zend_object * object)105 static void php_swoole_atomic_free_object(zend_object *object) {
106 sw_mem_pool()->free((void *) php_swoole_atomic_fetch_object(object)->ptr);
107 zend_object_std_dtor(object);
108 }
109
php_swoole_atomic_create_object(zend_class_entry * ce)110 static zend_object *php_swoole_atomic_create_object(zend_class_entry *ce) {
111 AtomicObject *atomic = (AtomicObject *) zend_object_alloc(sizeof(AtomicObject), ce);
112 if (atomic == nullptr) {
113 zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL);
114 }
115
116 zend_object_std_init(&atomic->std, ce);
117 object_properties_init(&atomic->std, ce);
118 atomic->std.handlers = &swoole_atomic_handlers;
119 atomic->ptr = (sw_atomic_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_t));
120 if (atomic->ptr == nullptr) {
121 zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL);
122 }
123
124 return &atomic->std;
125 }
126
127 struct AtomicLongObject {
128 sw_atomic_long_t *ptr;
129 zend_object std;
130 };
131
php_swoole_atomic_long_fetch_object(zend_object * obj)132 static sw_inline AtomicLongObject *php_swoole_atomic_long_fetch_object(zend_object *obj) {
133 return (AtomicLongObject *) ((char *) obj - swoole_atomic_long_handlers.offset);
134 }
135
php_swoole_atomic_long_get_ptr(zval * zobject)136 static sw_atomic_long_t *php_swoole_atomic_long_get_ptr(zval *zobject) {
137 return php_swoole_atomic_long_fetch_object(Z_OBJ_P(zobject))->ptr;
138 }
139
php_swoole_atomic_long_set_ptr(zval * zobject,sw_atomic_long_t * ptr)140 void php_swoole_atomic_long_set_ptr(zval *zobject, sw_atomic_long_t *ptr) {
141 php_swoole_atomic_long_fetch_object(Z_OBJ_P(zobject))->ptr = ptr;
142 }
143
php_swoole_atomic_long_free_object(zend_object * object)144 static void php_swoole_atomic_long_free_object(zend_object *object) {
145 sw_mem_pool()->free((void *) php_swoole_atomic_long_fetch_object(object)->ptr);
146 zend_object_std_dtor(object);
147 }
148
php_swoole_atomic_long_create_object(zend_class_entry * ce)149 static zend_object *php_swoole_atomic_long_create_object(zend_class_entry *ce) {
150 AtomicLongObject *atomic_long = (AtomicLongObject *) zend_object_alloc(sizeof(AtomicLongObject), ce);
151 if (atomic_long == nullptr) {
152 zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL);
153 }
154
155 zend_object_std_init(&atomic_long->std, ce);
156 object_properties_init(&atomic_long->std, ce);
157 atomic_long->std.handlers = &swoole_atomic_long_handlers;
158
159 atomic_long->ptr = (sw_atomic_long_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_long_t));
160 if (atomic_long->ptr == nullptr) {
161 zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL);
162 }
163
164 return &atomic_long->std;
165 }
166
167 SW_EXTERN_C_BEGIN
168 static PHP_METHOD(swoole_atomic, __construct);
169 static PHP_METHOD(swoole_atomic, add);
170 static PHP_METHOD(swoole_atomic, sub);
171 static PHP_METHOD(swoole_atomic, get);
172 static PHP_METHOD(swoole_atomic, set);
173 static PHP_METHOD(swoole_atomic, cmpset);
174 static PHP_METHOD(swoole_atomic, wait);
175 static PHP_METHOD(swoole_atomic, wakeup);
176
177 static PHP_METHOD(swoole_atomic_long, __construct);
178 static PHP_METHOD(swoole_atomic_long, add);
179 static PHP_METHOD(swoole_atomic_long, sub);
180 static PHP_METHOD(swoole_atomic_long, get);
181 static PHP_METHOD(swoole_atomic_long, set);
182 static PHP_METHOD(swoole_atomic_long, cmpset);
183 SW_EXTERN_C_END
184
185 // clang-format off
186
187 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_construct, 0, 0, 0)
188 ZEND_ARG_INFO(0, value)
189 ZEND_END_ARG_INFO()
190
191 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_add, 0, 0, 0)
192 ZEND_ARG_INFO(0, add_value)
193 ZEND_END_ARG_INFO()
194
195 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_sub, 0, 0, 0)
196 ZEND_ARG_INFO(0, sub_value)
197 ZEND_END_ARG_INFO()
198
199 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_get, 0, 0, 0)
200 ZEND_END_ARG_INFO()
201
202 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_set, 0, 0, 1)
203 ZEND_ARG_INFO(0, value)
204 ZEND_END_ARG_INFO()
205
206 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_cmpset, 0, 0, 2)
207 ZEND_ARG_INFO(0, cmp_value)
208 ZEND_ARG_INFO(0, new_value)
209 ZEND_END_ARG_INFO()
210
211 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_wait, 0, 0, 0)
212 ZEND_ARG_INFO(0, timeout)
213 ZEND_END_ARG_INFO()
214
215 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_atomic_wakeup, 0, 0, 0)
216 ZEND_ARG_INFO(0, count)
217 ZEND_END_ARG_INFO()
218
219 static const zend_function_entry swoole_atomic_methods[] =
220 {
221 PHP_ME(swoole_atomic, __construct, arginfo_swoole_atomic_construct, ZEND_ACC_PUBLIC)
222 PHP_ME(swoole_atomic, add, arginfo_swoole_atomic_add, ZEND_ACC_PUBLIC)
223 PHP_ME(swoole_atomic, sub, arginfo_swoole_atomic_sub, ZEND_ACC_PUBLIC)
224 PHP_ME(swoole_atomic, get, arginfo_swoole_atomic_get, ZEND_ACC_PUBLIC)
225 PHP_ME(swoole_atomic, set, arginfo_swoole_atomic_set, ZEND_ACC_PUBLIC)
226 PHP_ME(swoole_atomic, wait, arginfo_swoole_atomic_wait, ZEND_ACC_PUBLIC)
227 PHP_ME(swoole_atomic, wakeup, arginfo_swoole_atomic_wakeup, ZEND_ACC_PUBLIC)
228 PHP_ME(swoole_atomic, cmpset, arginfo_swoole_atomic_cmpset, ZEND_ACC_PUBLIC)
229 PHP_FE_END
230 };
231
232 static const zend_function_entry swoole_atomic_long_methods[] =
233 {
234 PHP_ME(swoole_atomic_long, __construct, arginfo_swoole_atomic_construct, ZEND_ACC_PUBLIC)
235 PHP_ME(swoole_atomic_long, add, arginfo_swoole_atomic_add, ZEND_ACC_PUBLIC)
236 PHP_ME(swoole_atomic_long, sub, arginfo_swoole_atomic_sub, ZEND_ACC_PUBLIC)
237 PHP_ME(swoole_atomic_long, get, arginfo_swoole_atomic_get, ZEND_ACC_PUBLIC)
238 PHP_ME(swoole_atomic_long, set, arginfo_swoole_atomic_set, ZEND_ACC_PUBLIC)
239 PHP_ME(swoole_atomic_long, cmpset, arginfo_swoole_atomic_cmpset, ZEND_ACC_PUBLIC)
240 PHP_FE_END
241 };
242
243 // clang-format on
244
php_swoole_atomic_minit(int module_number)245 void php_swoole_atomic_minit(int module_number) {
246 SW_INIT_CLASS_ENTRY(swoole_atomic, "Swoole\\Atomic", "swoole_atomic", nullptr, swoole_atomic_methods);
247 SW_SET_CLASS_NOT_SERIALIZABLE(swoole_atomic);
248 SW_SET_CLASS_CLONEABLE(swoole_atomic, sw_zend_class_clone_deny);
249 SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_atomic, sw_zend_class_unset_property_deny);
250 SW_SET_CLASS_CUSTOM_OBJECT(
251 swoole_atomic, php_swoole_atomic_create_object, php_swoole_atomic_free_object, AtomicObject, std);
252
253 SW_INIT_CLASS_ENTRY(
254 swoole_atomic_long, "Swoole\\Atomic\\Long", "swoole_atomic_long", nullptr, swoole_atomic_long_methods);
255 SW_SET_CLASS_NOT_SERIALIZABLE(swoole_atomic_long);
256 SW_SET_CLASS_CLONEABLE(swoole_atomic_long, sw_zend_class_clone_deny);
257 SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_atomic_long, sw_zend_class_unset_property_deny);
258 SW_SET_CLASS_CUSTOM_OBJECT(swoole_atomic_long,
259 php_swoole_atomic_long_create_object,
260 php_swoole_atomic_long_free_object,
261 AtomicLongObject,
262 std);
263 }
264
PHP_METHOD(swoole_atomic,__construct)265 PHP_METHOD(swoole_atomic, __construct) {
266 sw_atomic_t *atomic = php_swoole_atomic_get_ptr(ZEND_THIS);
267 zend_long value = 0;
268
269 ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1)
270 Z_PARAM_OPTIONAL
271 Z_PARAM_LONG(value)
272 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
273
274 *atomic = (sw_atomic_t) value;
275 }
276
PHP_METHOD(swoole_atomic,add)277 PHP_METHOD(swoole_atomic, add) {
278 sw_atomic_t *atomic = php_swoole_atomic_get_ptr(ZEND_THIS);
279 zend_long add_value = 1;
280
281 ZEND_PARSE_PARAMETERS_START(0, 1)
282 Z_PARAM_OPTIONAL
283 Z_PARAM_LONG(add_value)
284 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
285
286 RETURN_LONG(sw_atomic_add_fetch(atomic, (uint32_t) add_value));
287 }
288
PHP_METHOD(swoole_atomic,sub)289 PHP_METHOD(swoole_atomic, sub) {
290 sw_atomic_t *atomic = php_swoole_atomic_get_ptr(ZEND_THIS);
291 zend_long sub_value = 1;
292
293 ZEND_PARSE_PARAMETERS_START(0, 1)
294 Z_PARAM_OPTIONAL
295 Z_PARAM_LONG(sub_value)
296 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
297
298 RETURN_LONG(sw_atomic_sub_fetch(atomic, (uint32_t) sub_value));
299 }
300
PHP_METHOD(swoole_atomic,get)301 PHP_METHOD(swoole_atomic, get) {
302 sw_atomic_t *atomic = php_swoole_atomic_get_ptr(ZEND_THIS);
303 RETURN_LONG(*atomic);
304 }
305
PHP_METHOD(swoole_atomic,set)306 PHP_METHOD(swoole_atomic, set) {
307 sw_atomic_t *atomic = php_swoole_atomic_get_ptr(ZEND_THIS);
308 zend_long set_value;
309
310 ZEND_PARSE_PARAMETERS_START(1, 1)
311 Z_PARAM_LONG(set_value)
312 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
313
314 *atomic = (uint32_t) set_value;
315 }
316
PHP_METHOD(swoole_atomic,cmpset)317 PHP_METHOD(swoole_atomic, cmpset) {
318 sw_atomic_t *atomic = php_swoole_atomic_get_ptr(ZEND_THIS);
319 zend_long cmp_value, set_value;
320
321 ZEND_PARSE_PARAMETERS_START(2, 2)
322 Z_PARAM_LONG(cmp_value)
323 Z_PARAM_LONG(set_value)
324 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
325
326 RETURN_BOOL(sw_atomic_cmp_set(atomic, (sw_atomic_t) cmp_value, (sw_atomic_t) set_value));
327 }
328
PHP_METHOD(swoole_atomic,wait)329 PHP_METHOD(swoole_atomic, wait) {
330 sw_atomic_t *atomic = php_swoole_atomic_get_ptr(ZEND_THIS);
331 double timeout = 1.0;
332
333 ZEND_PARSE_PARAMETERS_START(0, 1)
334 Z_PARAM_OPTIONAL
335 Z_PARAM_DOUBLE(timeout)
336 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
337
338 #ifdef HAVE_FUTEX
339 SW_CHECK_RETURN(swoole_futex_wait(atomic, timeout));
340 #else
341 SW_CHECK_RETURN(swoole_atomic_wait(atomic, timeout));
342 #endif
343 }
344
PHP_METHOD(swoole_atomic,wakeup)345 PHP_METHOD(swoole_atomic, wakeup) {
346 sw_atomic_t *atomic = php_swoole_atomic_get_ptr(ZEND_THIS);
347 zend_long n = 1;
348
349 ZEND_PARSE_PARAMETERS_START(0, 1)
350 Z_PARAM_OPTIONAL
351 Z_PARAM_LONG(n)
352 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
353
354 #ifdef HAVE_FUTEX
355 SW_CHECK_RETURN(swoole_futex_wakeup(atomic, (int) n));
356 #else
357 SW_CHECK_RETURN(swoole_atomic_wakeup(atomic, n));
358 #endif
359 }
360
PHP_METHOD(swoole_atomic_long,__construct)361 PHP_METHOD(swoole_atomic_long, __construct) {
362 sw_atomic_long_t *atomic_long = php_swoole_atomic_long_get_ptr(ZEND_THIS);
363 zend_long value = 0;
364
365 ZEND_PARSE_PARAMETERS_START(0, 1)
366 Z_PARAM_OPTIONAL
367 Z_PARAM_LONG(value)
368 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
369
370 *atomic_long = (sw_atomic_long_t) value;
371 RETURN_TRUE;
372 }
373
PHP_METHOD(swoole_atomic_long,add)374 PHP_METHOD(swoole_atomic_long, add) {
375 sw_atomic_long_t *atomic_long = php_swoole_atomic_long_get_ptr(ZEND_THIS);
376 zend_long add_value = 1;
377
378 ZEND_PARSE_PARAMETERS_START(0, 1)
379 Z_PARAM_OPTIONAL
380 Z_PARAM_LONG(add_value)
381 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
382
383 RETURN_LONG(sw_atomic_add_fetch(atomic_long, (sw_atomic_long_t) add_value));
384 }
385
PHP_METHOD(swoole_atomic_long,sub)386 PHP_METHOD(swoole_atomic_long, sub) {
387 sw_atomic_long_t *atomic_long = php_swoole_atomic_long_get_ptr(ZEND_THIS);
388 zend_long sub_value = 1;
389
390 ZEND_PARSE_PARAMETERS_START(0, 1)
391 Z_PARAM_OPTIONAL
392 Z_PARAM_LONG(sub_value)
393 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
394
395 RETURN_LONG(sw_atomic_sub_fetch(atomic_long, (sw_atomic_long_t) sub_value));
396 }
397
PHP_METHOD(swoole_atomic_long,get)398 PHP_METHOD(swoole_atomic_long, get) {
399 sw_atomic_long_t *atomic_long = php_swoole_atomic_long_get_ptr(ZEND_THIS);
400 RETURN_LONG(*atomic_long);
401 }
402
PHP_METHOD(swoole_atomic_long,set)403 PHP_METHOD(swoole_atomic_long, set) {
404 sw_atomic_long_t *atomic_long = php_swoole_atomic_long_get_ptr(ZEND_THIS);
405 zend_long set_value;
406
407 ZEND_PARSE_PARAMETERS_START(1, 1)
408 Z_PARAM_LONG(set_value)
409 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
410
411 *atomic_long = (sw_atomic_long_t) set_value;
412 }
413
PHP_METHOD(swoole_atomic_long,cmpset)414 PHP_METHOD(swoole_atomic_long, cmpset) {
415 sw_atomic_long_t *atomic_long = php_swoole_atomic_long_get_ptr(ZEND_THIS);
416 zend_long cmp_value, set_value;
417
418 ZEND_PARSE_PARAMETERS_START(2, 2)
419 Z_PARAM_LONG(cmp_value)
420 Z_PARAM_LONG(set_value)
421 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
422
423 RETURN_BOOL(sw_atomic_cmp_set(atomic_long, (sw_atomic_long_t) cmp_value, (sw_atomic_long_t) set_value));
424 }
425