7d714040dca2af728cad053d1a1672220793fef4
[m6w6/ext-raphf] / php_raphf.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: raphf |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2013, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include "php.h"
18 #include "php_ini.h"
19 #include "ext/standard/info.h"
20 #include "php_raphf.h"
21
22 struct php_persistent_handle_globals {
23 ulong limit;
24 HashTable hash;
25 };
26
27 ZEND_BEGIN_MODULE_GLOBALS(raphf)
28 struct php_persistent_handle_globals persistent_handle;
29 ZEND_END_MODULE_GLOBALS(raphf)
30
31 #ifdef ZTS
32 # define PHP_RAPHF_G ((zend_raphf_globals *) \
33 (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(raphf_globals_id)])
34 #else
35 # define PHP_RAPHF_G (&raphf_globals)
36 #endif
37
38 ZEND_DECLARE_MODULE_GLOBALS(raphf)
39
40 #ifndef PHP_RAPHF_DEBUG_PHANDLES
41 # define PHP_RAPHF_DEBUG_PHANDLES 0
42 #endif
43 #if PHP_RAPHF_DEBUG_PHANDLES
44 # undef inline
45 # define inline
46 #endif
47
48 php_resource_factory_t *php_resource_factory_init(php_resource_factory_t *f,
49 php_resource_factory_ops_t *fops, void *data, void (*dtor)(void *data))
50 {
51 if (!f) {
52 f = emalloc(sizeof(*f));
53 }
54 memset(f, 0, sizeof(*f));
55
56 memcpy(&f->fops, fops, sizeof(*fops));
57
58 f->data = data;
59 f->dtor = dtor;
60
61 f->refcount = 1;
62
63 return f;
64 }
65
66 unsigned php_resource_factory_addref(php_resource_factory_t *rf)
67 {
68 return ++rf->refcount;
69 }
70
71 void php_resource_factory_dtor(php_resource_factory_t *f)
72 {
73 if (!--f->refcount) {
74 if (f->dtor) {
75 f->dtor(f->data);
76 }
77 }
78 }
79
80 void php_resource_factory_free(php_resource_factory_t **f)
81 {
82 if (*f) {
83 php_resource_factory_dtor(*f);
84 if (!(*f)->refcount) {
85 efree(*f);
86 *f = NULL;
87 }
88 }
89 }
90
91 void *php_resource_factory_handle_ctor(php_resource_factory_t *f,
92 void *init_arg TSRMLS_DC)
93 {
94 if (f->fops.ctor) {
95 return f->fops.ctor(f->data, init_arg TSRMLS_CC);
96 }
97 return NULL;
98 }
99
100 void *php_resource_factory_handle_copy(php_resource_factory_t *f,
101 void *handle TSRMLS_DC)
102 {
103 if (f->fops.copy) {
104 return f->fops.copy(f->data, handle TSRMLS_CC);
105 }
106 return NULL;
107 }
108
109 void php_resource_factory_handle_dtor(php_resource_factory_t *f,
110 void *handle TSRMLS_DC)
111 {
112 if (f->fops.dtor) {
113 f->fops.dtor(f->data, handle TSRMLS_CC);
114 }
115 }
116
117 static inline php_persistent_handle_list_t *php_persistent_handle_list_init(
118 php_persistent_handle_list_t *list)
119 {
120 list = pemalloc(sizeof(*list), 1);
121 list->used = 0;
122 zend_hash_init(&list->free, 0, NULL, NULL, 1);
123
124 return list;
125 }
126
127 static int php_persistent_handle_apply_stat(zval *p TSRMLS_DC, int argc,
128 va_list argv, zend_hash_key *key)
129 {
130 php_persistent_handle_list_t *list = Z_PTR_P(p);
131 zval zsubentry, *zentry = va_arg(argv, zval *);
132
133 array_init(&zsubentry);
134 add_assoc_long_ex(&zsubentry, ZEND_STRS("used"), list->used);
135 add_assoc_long_ex(&zsubentry, ZEND_STRS("free"),
136 zend_hash_num_elements(&list->free));
137 add_assoc_zval_ex(zentry, key->key->val, key->key->len, &zsubentry);
138
139 return ZEND_HASH_APPLY_KEEP;
140 }
141
142 static int php_persistent_handle_apply_statall(zval *p TSRMLS_DC, int argc,
143 va_list argv, zend_hash_key *key)
144 {
145 php_persistent_handle_provider_t *provider = Z_PTR_P(p);
146 HashTable *ht = va_arg(argv, HashTable *);
147 zval zentry;
148
149 array_init(&zentry);
150
151 zend_hash_apply_with_arguments(&provider->list.free TSRMLS_CC,
152 php_persistent_handle_apply_stat, 1, &zentry);
153 zend_symtable_update(ht, key->key, &zentry);
154
155 return ZEND_HASH_APPLY_KEEP;
156 }
157
158 static int php_persistent_handle_apply_cleanup_ex(zval *p, void *arg TSRMLS_DC)
159 {
160 php_resource_factory_t *rf = arg;
161 void *handle = Z_PTR_P(p);
162
163 #if PHP_RAPHF_DEBUG_PHANDLES
164 fprintf(stderr, "DESTROY: %p\n", handle);
165 #endif
166 php_resource_factory_handle_dtor(rf, handle TSRMLS_CC);
167 return ZEND_HASH_APPLY_REMOVE;
168 }
169
170 static int php_persistent_handle_apply_cleanup(zval *p, void *arg TSRMLS_DC)
171 {
172 php_resource_factory_t *rf = arg;
173 php_persistent_handle_list_t *list = Z_PTR_P(p);
174
175 zend_hash_apply_with_argument(&list->free,
176 php_persistent_handle_apply_cleanup_ex, rf TSRMLS_CC);
177 if (list->used) {
178 return ZEND_HASH_APPLY_KEEP;
179 }
180 zend_hash_destroy(&list->free);
181 #if PHP_RAPHF_DEBUG_PHANDLES
182 fprintf(stderr, "LSTFREE: %p\n", list);
183 #endif
184 pefree(list, 1);
185 return ZEND_HASH_APPLY_REMOVE;
186 }
187
188 static inline void php_persistent_handle_list_dtor(
189 php_persistent_handle_list_t *list,
190 php_persistent_handle_provider_t *provider TSRMLS_DC)
191 {
192 #if PHP_RAPHF_DEBUG_PHANDLES
193 fprintf(stderr, "LSTDTOR: %p\n", list);
194 #endif
195 zend_hash_apply_with_argument(&list->free,
196 php_persistent_handle_apply_cleanup_ex, &provider->rf TSRMLS_CC);
197 zend_hash_destroy(&list->free);
198 }
199
200 static inline void php_persistent_handle_list_free(
201 php_persistent_handle_list_t **list,
202 php_persistent_handle_provider_t *provider TSRMLS_DC)
203 {
204 php_persistent_handle_list_dtor(*list, provider TSRMLS_CC);
205 #if PHP_RAPHF_DEBUG_PHANDLES
206 fprintf(stderr, "LSTFREE: %p\n", *list);
207 #endif
208 pefree(*list, 1);
209 *list = NULL;
210 }
211
212 static int php_persistent_handle_list_apply_dtor(zval *p,
213 void *provider TSRMLS_DC)
214 {
215 php_persistent_handle_list_t *list = Z_PTR_P(p);
216 php_persistent_handle_list_free(&list, provider TSRMLS_CC);
217 return ZEND_HASH_APPLY_REMOVE;
218 }
219
220 static inline php_persistent_handle_list_t *php_persistent_handle_list_find(
221 php_persistent_handle_provider_t *provider, const char *ident_str,
222 size_t ident_len TSRMLS_DC)
223 {
224 php_persistent_handle_list_t *list;
225
226 list = zend_symtable_str_find_ptr(&provider->list.free, ident_str,
227 ident_len + 1);
228
229 if (list) {
230 #if PHP_RAPHF_DEBUG_PHANDLES
231 fprintf(stderr, "LSTFIND: %p\n", list);
232 #endif
233 return list;
234 }
235
236 if ((list = php_persistent_handle_list_init(NULL))) {
237 zval p;
238
239 ZVAL_PTR(&p, list);
240 if (zend_symtable_str_update(&provider->list.free, ident_str, ident_len+1,
241 &p)) {
242 #if PHP_RAPHF_DEBUG_PHANDLES
243 fprintf(stderr, "LSTFIND: %p (new)\n", list);
244 #endif
245 return list;
246 }
247 php_persistent_handle_list_free(&list, provider TSRMLS_CC);
248 }
249
250 return NULL;
251 }
252
253 static int php_persistent_handle_apply_cleanup_all(zval *p TSRMLS_DC, int argc,
254 va_list argv, zend_hash_key *key)
255 {
256 php_persistent_handle_provider_t *provider = Z_PTR_P(p);
257 const char *ident_str = va_arg(argv, const char *);
258 size_t ident_len = va_arg(argv, size_t);
259 php_persistent_handle_list_t *list;
260
261 if (ident_str && ident_len) {
262 if ((list = php_persistent_handle_list_find(provider, ident_str,
263 ident_len TSRMLS_CC))) {
264 zend_hash_apply_with_argument(&list->free,
265 php_persistent_handle_apply_cleanup_ex,
266 &provider->rf TSRMLS_CC);
267 }
268 } else {
269 zend_hash_apply_with_argument(&provider->list.free,
270 php_persistent_handle_apply_cleanup, &provider->rf TSRMLS_CC);
271 }
272
273 return ZEND_HASH_APPLY_KEEP;
274 }
275
276 static void php_persistent_handle_hash_dtor(zval *p)
277 {
278 php_persistent_handle_provider_t *provider = Z_PTR_P(p);
279 TSRMLS_FETCH();
280
281 zend_hash_apply_with_argument(&provider->list.free,
282 php_persistent_handle_list_apply_dtor, provider TSRMLS_CC);
283 zend_hash_destroy(&provider->list.free);
284 php_resource_factory_dtor(&provider->rf);
285 pefree(provider, 1);
286 }
287
288 ZEND_RESULT_CODE php_persistent_handle_provide(const char *name_str,
289 size_t name_len, php_resource_factory_ops_t *fops, void *data,
290 void (*dtor)(void *) TSRMLS_DC)
291 {
292 php_persistent_handle_provider_t *provider = pemalloc(sizeof(*provider), 1);
293
294 if (php_persistent_handle_list_init(&provider->list)) {
295 if (php_resource_factory_init(&provider->rf, fops, data, dtor)) {
296 zval p;
297
298 #if PHP_RAPHF_DEBUG_PHANDLES
299 fprintf(stderr, "PROVIDE: %p %s\n", PHP_RAPHF_G, name_str);
300 #endif
301
302 ZVAL_PTR(&p, provider);
303 if (zend_symtable_str_update(&PHP_RAPHF_G->persistent_handle.hash,
304 name_str, name_len+1, &p)) {
305 return SUCCESS;
306 }
307 php_resource_factory_dtor(&provider->rf);
308 }
309 }
310
311 return FAILURE;
312 }
313
314 php_persistent_handle_factory_t *php_persistent_handle_concede(
315 php_persistent_handle_factory_t *a, const char *name_str,
316 size_t name_len, const char *ident_str, size_t ident_len,
317 php_persistent_handle_wakeup_t wakeup,
318 php_persistent_handle_retire_t retire TSRMLS_DC)
319 {
320 php_persistent_handle_factory_t *free_a = NULL;
321
322 if (!a) {
323 free_a = a = emalloc(sizeof(*a));
324 }
325 memset(a, 0, sizeof(*a));
326
327 a->provider = zend_symtable_str_find_ptr(&PHP_RAPHF_G->persistent_handle.hash,
328 name_str, name_len+1);
329
330 if (a->provider) {
331 a->ident.str = estrndup(ident_str, ident_len);
332 a->ident.len = ident_len;
333
334 a->wakeup = wakeup;
335 a->retire = retire;
336
337 if (free_a) {
338 a->free_on_abandon = 1;
339 }
340 } else {
341 if (free_a) {
342 efree(free_a);
343 }
344 a = NULL;
345 }
346
347 #if PHP_RAPHF_DEBUG_PHANDLES
348 fprintf(stderr, "CONCEDE: %p %p (%s) (%s)\n", PHP_RAPHF_G,
349 a ? a->provider : NULL, name_str, ident_str);
350 #endif
351
352 return a;
353 }
354
355 void php_persistent_handle_abandon(php_persistent_handle_factory_t *a)
356 {
357 zend_bool f = a->free_on_abandon;
358
359 #if PHP_RAPHF_DEBUG_PHANDLES
360 fprintf(stderr, "ABANDON: %p\n", a->provider);
361 #endif
362
363 if (a->ident.str) {
364 efree(a->ident.str);
365 }
366 memset(a, 0, sizeof(*a));
367 if (f) {
368 efree(a);
369 }
370 }
371
372 void *php_persistent_handle_acquire(php_persistent_handle_factory_t *a,
373 void *init_arg TSRMLS_DC)
374 {
375 int key;
376 zval *p;
377 ulong index;
378 void *handle = NULL;
379 php_persistent_handle_list_t *list;
380
381 list = php_persistent_handle_list_find(a->provider, a->ident.str,
382 a->ident.len TSRMLS_CC);
383 if (list) {
384 zend_hash_internal_pointer_end(&list->free);
385 key = zend_hash_get_current_key(&list->free, NULL, &index, 0);
386 p = zend_hash_get_current_data(&list->free);
387 if (p && HASH_KEY_NON_EXISTENT != key) {
388 handle = Z_PTR_P(p);
389 if (a->wakeup) {
390 a->wakeup(a, &handle TSRMLS_CC);
391 }
392 zend_hash_index_del(&list->free, index);
393 } else {
394 handle = php_resource_factory_handle_ctor(&a->provider->rf,
395 init_arg TSRMLS_CC);
396 }
397 #if PHP_RAPHF_DEBUG_PHANDLES
398 fprintf(stderr, "CREATED: %p\n", *handle);
399 #endif
400 if (handle) {
401 ++a->provider->list.used;
402 ++list->used;
403 }
404 }
405
406 return handle;
407 }
408
409 void *php_persistent_handle_accrete(php_persistent_handle_factory_t *a,
410 void *handle TSRMLS_DC)
411 {
412 void *new_handle = NULL;
413 php_persistent_handle_list_t *list;
414
415 new_handle = php_resource_factory_handle_copy(&a->provider->rf,
416 handle TSRMLS_CC);
417 if (handle) {
418 list = php_persistent_handle_list_find(a->provider, a->ident.str,
419 a->ident.len TSRMLS_CC);
420 if (list) {
421 ++list->used;
422 }
423 ++a->provider->list.used;
424 }
425
426 return new_handle;
427 }
428
429 void php_persistent_handle_release(php_persistent_handle_factory_t *a,
430 void *handle TSRMLS_DC)
431 {
432 php_persistent_handle_list_t *list;
433
434 list = php_persistent_handle_list_find(a->provider, a->ident.str,
435 a->ident.len TSRMLS_CC);
436 if (list) {
437 if (a->provider->list.used >= PHP_RAPHF_G->persistent_handle.limit) {
438 #if PHP_RAPHF_DEBUG_PHANDLES
439 fprintf(stderr, "DESTROY: %p\n", *handle);
440 #endif
441 php_resource_factory_handle_dtor(&a->provider->rf, handle TSRMLS_CC);
442 } else {
443 if (a->retire) {
444 a->retire(a, &handle TSRMLS_CC);
445 }
446 zend_hash_next_index_insert_ptr(&list->free, handle);
447 }
448
449 --a->provider->list.used;
450 --list->used;
451 }
452 }
453
454 void php_persistent_handle_cleanup(const char *name_str, size_t name_len,
455 const char *ident_str, size_t ident_len TSRMLS_DC)
456 {
457 php_persistent_handle_provider_t *provider;
458 php_persistent_handle_list_t *list;
459
460 if (name_str && name_len) {
461 provider = zend_symtable_str_find_ptr(&PHP_RAPHF_G->persistent_handle.hash,
462 name_str, name_len+1);
463
464 if (provider) {
465 if (ident_str && ident_len) {
466 list = php_persistent_handle_list_find(provider, ident_str,
467 ident_len TSRMLS_CC);
468 if (list) {
469 zend_hash_apply_with_argument(&list->free,
470 php_persistent_handle_apply_cleanup_ex,
471 &provider->rf TSRMLS_CC);
472 }
473 } else {
474 zend_hash_apply_with_argument(&provider->list.free,
475 php_persistent_handle_apply_cleanup,
476 &provider->rf TSRMLS_CC);
477 }
478 }
479 } else {
480 zend_hash_apply_with_arguments(
481 &PHP_RAPHF_G->persistent_handle.hash TSRMLS_CC,
482 php_persistent_handle_apply_cleanup_all, 2, ident_str,
483 ident_len);
484 }
485 }
486
487 HashTable *php_persistent_handle_statall(HashTable *ht TSRMLS_DC)
488 {
489 if (zend_hash_num_elements(&PHP_RAPHF_G->persistent_handle.hash)) {
490 if (!ht) {
491 ALLOC_HASHTABLE(ht);
492 zend_hash_init(ht, 0, NULL, ZVAL_PTR_DTOR, 0);
493 }
494 zend_hash_apply_with_arguments(
495 &PHP_RAPHF_G->persistent_handle.hash TSRMLS_CC,
496 php_persistent_handle_apply_statall, 1, ht);
497 } else if (ht) {
498 ht = NULL;
499 }
500
501 return ht;
502 }
503
504 static php_resource_factory_ops_t php_persistent_handle_resource_factory_ops = {
505 (php_resource_factory_handle_ctor_t) php_persistent_handle_acquire,
506 (php_resource_factory_handle_copy_t) php_persistent_handle_accrete,
507 (php_resource_factory_handle_dtor_t) php_persistent_handle_release
508 };
509
510 php_resource_factory_ops_t *php_persistent_handle_get_resource_factory_ops(void)
511 {
512 return &php_persistent_handle_resource_factory_ops;
513 }
514
515 ZEND_BEGIN_ARG_INFO_EX(ai_raphf_stat_persistent_handles, 0, 0, 0)
516 ZEND_END_ARG_INFO();
517 static PHP_FUNCTION(raphf_stat_persistent_handles)
518 {
519 if (SUCCESS == zend_parse_parameters_none()) {
520 object_init(return_value);
521 if (php_persistent_handle_statall(HASH_OF(return_value) TSRMLS_CC)) {
522 return;
523 }
524 zval_dtor(return_value);
525 }
526 RETURN_FALSE;
527 }
528
529 ZEND_BEGIN_ARG_INFO_EX(ai_raphf_clean_persistent_handles, 0, 0, 0)
530 ZEND_ARG_INFO(0, name)
531 ZEND_ARG_INFO(0, ident)
532 ZEND_END_ARG_INFO();
533 static PHP_FUNCTION(raphf_clean_persistent_handles)
534 {
535 char *name_str = NULL, *ident_str = NULL;
536 int name_len = 0, ident_len = 0;
537
538 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!",
539 &name_str, &name_len, &ident_str, &ident_len)) {
540 php_persistent_handle_cleanup(name_str, name_len, ident_str,
541 ident_len TSRMLS_CC);
542 }
543 }
544
545 static const zend_function_entry raphf_functions[] = {
546 ZEND_NS_FENTRY("raphf", stat_persistent_handles,
547 ZEND_FN(raphf_stat_persistent_handles),
548 ai_raphf_stat_persistent_handles, 0)
549 ZEND_NS_FENTRY("raphf", clean_persistent_handles,
550 ZEND_FN(raphf_clean_persistent_handles),
551 ai_raphf_clean_persistent_handles, 0)
552 {0}
553 };
554
555 PHP_INI_BEGIN()
556 STD_PHP_INI_ENTRY("raphf.persistent_handle.limit", "-1", PHP_INI_SYSTEM,
557 OnUpdateLong, persistent_handle.limit, zend_raphf_globals,
558 raphf_globals)
559 PHP_INI_END()
560
561 static HashTable *php_persistent_handles_global_hash;
562
563 static PHP_GINIT_FUNCTION(raphf)
564 {
565 raphf_globals->persistent_handle.limit = -1;
566
567 zend_hash_init(&raphf_globals->persistent_handle.hash, 0, NULL,
568 php_persistent_handle_hash_dtor, 1);
569 if (php_persistent_handles_global_hash) {
570 zend_hash_copy(&raphf_globals->persistent_handle.hash,
571 php_persistent_handles_global_hash, NULL);
572 }
573 }
574
575 static PHP_GSHUTDOWN_FUNCTION(raphf)
576 {
577 zend_hash_destroy(&raphf_globals->persistent_handle.hash);
578 }
579
580 PHP_MINIT_FUNCTION(raphf)
581 {
582 php_persistent_handles_global_hash = &PHP_RAPHF_G->persistent_handle.hash;
583 REGISTER_INI_ENTRIES();
584 return SUCCESS;
585 }
586
587 PHP_MSHUTDOWN_FUNCTION(raphf)
588 {
589 UNREGISTER_INI_ENTRIES();
590 php_persistent_handles_global_hash = NULL;
591 return SUCCESS;
592 }
593
594 static int php_persistent_handle_apply_info_ex(zval *p TSRMLS_DC, int argc,
595 va_list argv, zend_hash_key *key)
596 {
597 php_persistent_handle_list_t *list = Z_PTR_P(p);
598 zend_hash_key *super_key = va_arg(argv, zend_hash_key *);
599 char used[21], free[21];
600
601 slprintf(used, sizeof(used), "%u", list->used);
602 slprintf(free, sizeof(free), "%d", zend_hash_num_elements(&list->free));
603
604 php_info_print_table_row(4, super_key->key->val, key->key->val, used, free);
605
606 return ZEND_HASH_APPLY_KEEP;
607 }
608
609 static int php_persistent_handle_apply_info(zval *p TSRMLS_DC, int argc,
610 va_list argv, zend_hash_key *key)
611 {
612 php_persistent_handle_provider_t *provider = Z_PTR_P(p);
613
614 zend_hash_apply_with_arguments(&provider->list.free TSRMLS_CC,
615 php_persistent_handle_apply_info_ex, 1, key);
616
617 return ZEND_HASH_APPLY_KEEP;
618 }
619
620 PHP_MINFO_FUNCTION(raphf)
621 {
622 php_info_print_table_start();
623 php_info_print_table_header(2,
624 "Resource and persistent handle factory support", "enabled");
625 php_info_print_table_row(2, "Extension version", PHP_RAPHF_VERSION);
626 php_info_print_table_end();
627
628 php_info_print_table_start();
629 php_info_print_table_colspan_header(4, "Persistent handles in this "
630 #ifdef ZTS
631 "thread"
632 #else
633 "process"
634 #endif
635 );
636 php_info_print_table_header(4, "Provider", "Ident", "Used", "Free");
637 zend_hash_apply_with_arguments(
638 &PHP_RAPHF_G->persistent_handle.hash TSRMLS_CC,
639 php_persistent_handle_apply_info, 0);
640 php_info_print_table_end();
641
642 DISPLAY_INI_ENTRIES();
643 }
644
645 zend_module_entry raphf_module_entry = {
646 STANDARD_MODULE_HEADER,
647 "raphf",
648 raphf_functions,
649 PHP_MINIT(raphf),
650 PHP_MSHUTDOWN(raphf),
651 NULL,
652 NULL,
653 PHP_MINFO(raphf),
654 PHP_RAPHF_VERSION,
655 ZEND_MODULE_GLOBALS(raphf),
656 PHP_GINIT(raphf),
657 PHP_GSHUTDOWN(raphf),
658 NULL,
659 STANDARD_MODULE_PROPERTIES_EX
660 };
661 /* }}} */
662
663 #ifdef COMPILE_DL_RAPHF
664 ZEND_GET_MODULE(raphf)
665 #endif
666
667
668 /*
669 * Local variables:
670 * tab-width: 4
671 * c-basic-offset: 4
672 * End:
673 * vim600: noet sw=4 ts=4 fdm=marker
674 * vim<600: noet sw=4 ts=4
675 */