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