diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index ed683d65fa221..626bbe7b31b34 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -334,6 +334,8 @@ struct _zend_mm_heap { void *(*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); void *(*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + size_t (*_gc)(void); + void (*_shutdown)(bool full, bool silent); } custom_heap; HashTable *tracked_allocs; #endif @@ -2119,6 +2121,10 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap) #if ZEND_MM_CUSTOM if (heap->use_custom_heap) { + size_t (*gc)(void) = heap->custom_heap._gc; + if (gc) { + return gc(); + } return 0; } #endif @@ -2421,10 +2427,10 @@ static void zend_mm_check_leaks(zend_mm_heap *heap) #if ZEND_MM_CUSTOM static void *tracked_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); -static void tracked_free_all(void); +static void tracked_free_all(zend_mm_heap *heap); #endif -void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) +ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) { zend_mm_chunk *p; zend_mm_huge_list *list; @@ -2433,7 +2439,7 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) if (heap->use_custom_heap) { if (heap->custom_heap._malloc == tracked_malloc) { if (silent) { - tracked_free_all(); + tracked_free_all(heap); } zend_hash_clean(heap->tracked_allocs); if (full) { @@ -2445,9 +2451,16 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) heap->size = 0; } + void (*shutdown)(bool, bool) = heap->custom_heap._shutdown; + if (full) { heap->custom_heap._free(heap ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC); } + + if (shutdown) { + shutdown(full, silent); + } + return; } #endif @@ -3039,8 +3052,8 @@ static void *tracked_realloc(void *ptr, size_t new_size ZEND_FILE_LINE_DC ZEND_F return ptr; } -static void tracked_free_all(void) { - HashTable *tracked_allocs = AG(mm_heap)->tracked_allocs; +static void tracked_free_all(zend_mm_heap *heap) { + HashTable *tracked_allocs = heap->tracked_allocs; zend_ulong h; ZEND_HASH_FOREACH_NUM_KEY(tracked_allocs, h) { void *ptr = (void *) (uintptr_t) (h << ZEND_MM_ALIGNMENT_LOG2); @@ -3138,6 +3151,18 @@ ZEND_API void zend_mm_set_custom_handlers(zend_mm_heap *heap, void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)) { +#if ZEND_MM_CUSTOM + zend_mm_set_custom_handlers_ex(heap, _malloc, _free, _realloc, NULL, NULL); +#endif +} + +ZEND_API void zend_mm_set_custom_handlers_ex(zend_mm_heap *heap, + void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + size_t (*_gc)(void), + void (*_shutdown)(bool, bool)) +{ #if ZEND_MM_CUSTOM zend_mm_heap *_heap = (zend_mm_heap*)heap; @@ -3148,14 +3173,28 @@ ZEND_API void zend_mm_set_custom_handlers(zend_mm_heap *heap, _heap->custom_heap._malloc = _malloc; _heap->custom_heap._free = _free; _heap->custom_heap._realloc = _realloc; + _heap->custom_heap._gc = _gc; + _heap->custom_heap._shutdown = _shutdown; } #endif } ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap, - void* (**_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), - void (**_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), - void* (**_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)) + void* (**_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void (**_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void* (**_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)) +{ +#if ZEND_MM_CUSTOM + zend_mm_get_custom_handlers_ex(heap, _malloc, _free, _realloc, NULL, NULL); +#endif +} + +ZEND_API void zend_mm_get_custom_handlers_ex(zend_mm_heap *heap, + void* (**_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void (**_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void* (**_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + size_t (**_gc)(void), + void (**_shutdown)(bool, bool)) { #if ZEND_MM_CUSTOM zend_mm_heap *_heap = (zend_mm_heap*)heap; @@ -3164,15 +3203,29 @@ ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap, *_malloc = _heap->custom_heap._malloc; *_free = _heap->custom_heap._free; *_realloc = _heap->custom_heap._realloc; + if (_gc != NULL) { + *_gc = _heap->custom_heap._gc; + } + if (_shutdown != NULL) { + *_shutdown = _heap->custom_heap._shutdown; + } } else { *_malloc = NULL; *_free = NULL; *_realloc = NULL; + if (_gc != NULL) { + *_gc = NULL; + } + if (_shutdown != NULL) { + *_shutdown = NULL; + } } #else *_malloc = NULL; *_free = NULL; *_realloc = NULL; + *_gc = NULL; + *_shutdown = NULL; #endif } diff --git a/Zend/zend_alloc.h b/Zend/zend_alloc.h index 2965de2b931cf..541989a2a13e0 100644 --- a/Zend/zend_alloc.h +++ b/Zend/zend_alloc.h @@ -274,13 +274,25 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap); ZEND_API bool zend_mm_is_custom_heap(zend_mm_heap *new_heap); ZEND_API void zend_mm_set_custom_handlers(zend_mm_heap *heap, - void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), - void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), - void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)); + void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)); +ZEND_API void zend_mm_set_custom_handlers_ex(zend_mm_heap *heap, + void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + size_t (*_gc)(void), + void (*_shutdown)(bool, bool)); ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap, - void* (**_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), - void (**_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), - void* (**_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)); + void* (**_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void (**_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void* (**_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)); +ZEND_API void zend_mm_get_custom_handlers_ex(zend_mm_heap *heap, + void* (**_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void (**_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void* (**_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + size_t (**_gc)(void), + void (**_shutdown)(bool, bool)); typedef struct _zend_mm_storage zend_mm_storage; diff --git a/ext/zend_test/config.m4 b/ext/zend_test/config.m4 index 4b15db47750cb..1aff6f0e58319 100644 --- a/ext/zend_test/config.m4 +++ b/ext/zend_test/config.m4 @@ -4,5 +4,5 @@ PHP_ARG_ENABLE([zend-test], [Enable zend_test extension])]) if test "$PHP_ZEND_TEST" != "no"; then - PHP_NEW_EXTENSION(zend_test, test.c observer.c fiber.c iterators.c object_handlers.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) + PHP_NEW_EXTENSION(zend_test, test.c observer.c fiber.c iterators.c object_handlers.c zend_mm_custom_handlers.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) fi diff --git a/ext/zend_test/config.w32 b/ext/zend_test/config.w32 index e066fdeb243ea..65fe6d359ba72 100644 --- a/ext/zend_test/config.w32 +++ b/ext/zend_test/config.w32 @@ -3,6 +3,6 @@ ARG_ENABLE("zend-test", "enable zend_test extension", "no"); if (PHP_ZEND_TEST != "no") { - EXTENSION("zend_test", "test.c observer.c fiber.c iterators.c object_handlers.c", PHP_ZEND_TEST_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); + EXTENSION("zend_test", "test.c observer.c fiber.c iterators.c object_handlers.c zend_mm_custom_handlers.c", PHP_ZEND_TEST_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); ADD_FLAG("CFLAGS_ZEND_TEST", "/D PHP_ZEND_TEST_EXPORTS "); } diff --git a/ext/zend_test/php_test.h b/ext/zend_test/php_test.h index 89570d1155cea..09391d8137aa6 100644 --- a/ext/zend_test/php_test.h +++ b/ext/zend_test/php_test.h @@ -62,6 +62,19 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test) zend_long quantity_value; zend_string *str_test; zend_string *not_empty_str_test; + int zend_mm_custom_handlers_enabled; + + // the previous heap that was found in ZendMM + zend_mm_heap* original_heap; + // the custom handlers that might have been found in the previous heap + void* (*custom_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + void (*custom_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + void* (*custom_realloc)(void *, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + size_t (*custom_gc)(void); + void (*custom_shutdown)(bool, bool); + // this is our heap that we install our custom handlers on and inject into + // ZendMM + zend_mm_heap* observed_heap; ZEND_END_MODULE_GLOBALS(zend_test) extern ZEND_DECLARE_MODULE_GLOBALS(zend_test) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 78adc05ccca5e..bdd97d69908da 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -14,6 +14,7 @@ +----------------------------------------------------------------------+ */ +#include "zend_modules.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -35,6 +36,7 @@ #include "test_arginfo.h" #include "zend_call_stack.h" #include "zend_exceptions.h" +#include "zend_mm_custom_handlers.h" // `php.h` sets `NDEBUG` when not `PHP_DEBUG` which will make `assert()` from // assert.h a no-op. In order to have `assert()` working on NDEBUG builds, we @@ -1260,6 +1262,7 @@ PHP_MINIT_FUNCTION(zend_test) } zend_test_observer_init(INIT_FUNC_ARGS_PASSTHRU); + zend_test_mm_custom_handlers_minit(INIT_FUNC_ARGS_PASSTHRU); zend_test_fiber_init(); zend_test_iterators_init(); zend_test_object_handlers_init(); @@ -1288,6 +1291,7 @@ PHP_RINIT_FUNCTION(zend_test) { zend_hash_init(&ZT_G(global_weakmap), 8, NULL, ZVAL_PTR_DTOR, 0); ZT_G(observer_nesting_depth) = 0; + zend_test_mm_custom_handlers_rinit(); return SUCCESS; } @@ -1305,6 +1309,7 @@ PHP_RSHUTDOWN_FUNCTION(zend_test) zend_mm_set_heap(ZT_G(zend_orig_heap)); } + zend_test_mm_custom_handlers_rshutdown(); return SUCCESS; } diff --git a/ext/zend_test/tests/zend_mm_custom_handlers_alloc_01.phpt b/ext/zend_test/tests/zend_mm_custom_handlers_alloc_01.phpt new file mode 100644 index 0000000000000..cb6490d4c5d31 --- /dev/null +++ b/ext/zend_test/tests/zend_mm_custom_handlers_alloc_01.phpt @@ -0,0 +1,12 @@ +--TEST-- +ZendMM Custom Handlers: garbage collection +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECTREGEX-- +.*Allocated \d+ bytes at.* diff --git a/ext/zend_test/tests/zend_mm_custom_handlers_free_01.phpt b/ext/zend_test/tests/zend_mm_custom_handlers_free_01.phpt new file mode 100644 index 0000000000000..ae9dacb999509 --- /dev/null +++ b/ext/zend_test/tests/zend_mm_custom_handlers_free_01.phpt @@ -0,0 +1,13 @@ +--TEST-- +ZendMM Custom Handlers: garbage collection +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECTREGEX-- +.*Freed memory at.* diff --git a/ext/zend_test/tests/zend_mm_custom_handlers_gc_01.phpt b/ext/zend_test/tests/zend_mm_custom_handlers_gc_01.phpt new file mode 100644 index 0000000000000..c6e498ba55f80 --- /dev/null +++ b/ext/zend_test/tests/zend_mm_custom_handlers_gc_01.phpt @@ -0,0 +1,12 @@ +--TEST-- +ZendMM Custom Handlers: garbage collection +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECTREGEX-- +.*ZendMM GC freed \d+ bytes.* diff --git a/ext/zend_test/zend_mm_custom_handlers.c b/ext/zend_test/zend_mm_custom_handlers.c new file mode 100644 index 0000000000000..2130065109efa --- /dev/null +++ b/ext/zend_test/zend_mm_custom_handlers.c @@ -0,0 +1,180 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Florian Engelhardt | + +----------------------------------------------------------------------+ +*/ + +#include "php.h" +#include "php_test.h" +#include "Zend/zend_alloc.h" +#include "zend_portability.h" +#include +#include + +void* observe_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +{ + void *ptr = NULL; + if (ZT_G(custom_malloc)) { + zend_mm_set_heap(ZT_G(original_heap)); + ptr = ZT_G(custom_malloc)(size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + zend_mm_set_heap(ZT_G(observed_heap)); + } else { + ptr = _zend_mm_alloc(ZT_G(original_heap), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + } + printf("Allocated %zu bytes at %p\n", size, ptr); + return ptr; +} + +void observe_free(void* ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +{ + if (ZT_G(custom_free)) + { + zend_mm_set_heap(ZT_G(original_heap)); + ZT_G(custom_free)(ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + zend_mm_set_heap(ZT_G(observed_heap)); + } else { + _zend_mm_free(ZT_G(original_heap), ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + } + printf("Freed memory at %p\n", ptr); +} + +void* observe_realloc(void* ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +{ + void * new_ptr; + if (ZT_G(custom_realloc)) + { + zend_mm_set_heap(ZT_G(original_heap)); + new_ptr = ZT_G(custom_realloc)(ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + zend_mm_set_heap(ZT_G(observed_heap)); + } else { + new_ptr = _zend_mm_realloc(ZT_G(original_heap), ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + } + printf("Realloc of %zu bytes from %p to %p\n", size, ptr, new_ptr); + return new_ptr; +} + +size_t observe_gc(void) +{ + size_t size = 0; + if (ZT_G(custom_gc)) { + zend_mm_set_heap(ZT_G(original_heap)); + size = ZT_G(custom_gc)(); + zend_mm_set_heap(ZT_G(observed_heap)); + } else { + size = zend_mm_gc(ZT_G(original_heap)); + } + printf("ZendMM GC freed %zu bytes", size); + return size; +} + +void observe_shutdown(bool full, bool silent) +{ + if (ZT_G(custom_shutdown)) { + zend_mm_set_heap(ZT_G(original_heap)); + ZT_G(custom_shutdown)(full, silent); + zend_mm_set_heap(ZT_G(observed_heap)); + } else { + zend_mm_shutdown(ZT_G(original_heap), full, silent); + } + printf("Shutdown happened: full -> %d, silent -> %d\n", full, silent); +} + +void zend_test_mm_custom_handlers_init(void) +{ + if (ZT_G(observed_heap) != NULL) { + return; + } + ZT_G(original_heap) = zend_mm_get_heap(); + if (zend_mm_is_custom_heap(ZT_G(original_heap))) { + zend_mm_get_custom_handlers_ex( + ZT_G(original_heap), + &ZT_G(custom_malloc), + &ZT_G(custom_free), + &ZT_G(custom_realloc), + &ZT_G(custom_gc), + &ZT_G(custom_shutdown) + ); + } + printf("Prev handlers at %p, %p, %p, %p, %p\n", ZT_G(custom_malloc), ZT_G(custom_free), ZT_G(custom_realloc), ZT_G(custom_gc), ZT_G(custom_shutdown)); + ZT_G(observed_heap) = zend_mm_startup(); + zend_mm_set_custom_handlers_ex( + ZT_G(observed_heap), + observe_malloc, + observe_free, + observe_realloc, + observe_gc, + observe_shutdown + ); + zend_mm_set_heap(ZT_G(observed_heap)); + printf("Heap at %p installed in ZendMM (orig at %p)\n", ZT_G(observed_heap), ZT_G(original_heap)); +} + +void zend_test_mm_custom_handlers_shutdown(void) +{ + if (ZT_G(observed_heap) == NULL) { + return; + } + zend_mm_set_heap(ZT_G(original_heap)); + zend_mm_set_custom_handlers_ex( + ZT_G(observed_heap), + NULL, + NULL, + NULL, + NULL, + NULL + ); + zend_mm_shutdown(ZT_G(observed_heap), true, true); + ZT_G(observed_heap) = NULL; + printf("Prev heap at %p restored in ZendMM\n", ZT_G(original_heap)); +} + +void zend_test_mm_custom_handlers_rshutdown(void) +{ + zend_test_mm_custom_handlers_shutdown(); +} + +void zend_test_mm_custom_handlers_rinit(void) +{ + if (ZT_G(zend_mm_custom_handlers_enabled)) { + zend_test_mm_custom_handlers_init(); + } +} + +static PHP_INI_MH(OnUpdateZendTestMMCustomHandlersEnabled) +{ + if (new_value == NULL) { + return FAILURE; + } + + int int_value = zend_ini_parse_bool(new_value); + + if (int_value == 1) { + zend_test_mm_custom_handlers_init(); + } else { + zend_test_mm_custom_handlers_shutdown(); + } + return OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); +} + +PHP_INI_BEGIN() + STD_PHP_INI_BOOLEAN("zend_test.zend_mm_custom_handlers.enabled", "0", PHP_INI_USER, OnUpdateZendTestMMCustomHandlersEnabled, zend_mm_custom_handlers_enabled, zend_zend_test_globals, zend_test_globals) +PHP_INI_END() + +void zend_test_mm_custom_handlers_minit(INIT_FUNC_ARGS) +{ + if (type != MODULE_TEMPORARY) { + REGISTER_INI_ENTRIES(); + } else { + (void)ini_entries; + } +} diff --git a/ext/zend_test/zend_mm_custom_handlers.h b/ext/zend_test/zend_mm_custom_handlers.h new file mode 100644 index 0000000000000..7e0c8021e7e22 --- /dev/null +++ b/ext/zend_test/zend_mm_custom_handlers.h @@ -0,0 +1,24 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Florian Engelhardt | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_TEST_MM_CUSTOM_HANDLERS_H +#define ZEND_TEST_MM_CUSTOM_HANDLERS_H + +void zend_test_mm_custom_handlers_minit(INIT_FUNC_ARGS); +void zend_test_mm_custom_handlers_rinit(void); +void zend_test_mm_custom_handlers_rshutdown(void); + +#endif