Skip to content

Commit 0366f30

Browse files
add gc and shutdown callbacks
1 parent fd47cd8 commit 0366f30

10 files changed

+236
-15
lines changed

Zend/zend_alloc.c

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include "zend_multiply.h"
5959
#include "zend_bitset.h"
6060
#include "zend_mmap.h"
61+
#include "zend_portability.h"
6162
#include <signal.h>
6263

6364
#ifdef HAVE_UNISTD_H
@@ -273,11 +274,15 @@ struct _zend_mm_heap {
273274
void *(*_malloc)(size_t);
274275
void (*_free)(void*);
275276
void *(*_realloc)(void*, size_t);
277+
size_t (*_gc)(void);
278+
void (*_shutdown)(bool full, bool silent);
276279
} std;
277280
struct {
278281
void *(*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
279282
void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
280283
void *(*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
284+
size_t (*_gc)(void ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
285+
void (*_shutdown)(bool full, bool silent ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
281286
} debug;
282287
} custom_heap;
283288
HashTable *tracked_allocs;
@@ -1968,6 +1973,10 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap)
19681973

19691974
#if ZEND_MM_CUSTOM
19701975
if (heap->use_custom_heap) {
1976+
size_t (*gc)(void) = heap->custom_heap.std._gc;
1977+
if (gc) {
1978+
return gc();
1979+
}
19711980
return 0;
19721981
}
19731982
#endif
@@ -2265,7 +2274,7 @@ static void *tracked_malloc(size_t size);
22652274
static void tracked_free_all(void);
22662275
#endif
22672276

2268-
void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent)
2277+
ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent)
22692278
{
22702279
zend_mm_chunk *p;
22712280
zend_mm_huge_list *list;
@@ -2293,6 +2302,10 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent)
22932302
heap->custom_heap.std._free(heap);
22942303
}
22952304
}
2305+
void (*shutdown)(bool, bool) = heap->custom_heap.std._shutdown;
2306+
if (shutdown) {
2307+
shutdown(full, silent);
2308+
}
22962309
return;
22972310
}
22982311
#endif
@@ -2980,6 +2993,16 @@ ZEND_API zend_mm_heap *zend_mm_get_heap(void)
29802993
return AG(mm_heap);
29812994
}
29822995

2996+
ZEND_API zend_mm_heap *zend_mm_heap_create(void)
2997+
{
2998+
return zend_mm_init();
2999+
}
3000+
3001+
ZEND_API void zend_mm_heap_free(zend_mm_heap *heap)
3002+
{
3003+
zend_mm_chunk_free(heap, heap->main_chunk, ZEND_MM_CHUNK_SIZE);
3004+
}
3005+
29833006
ZEND_API bool zend_mm_is_custom_heap(zend_mm_heap *new_heap)
29843007
{
29853008
#if ZEND_MM_CUSTOM
@@ -2990,9 +3013,11 @@ ZEND_API bool zend_mm_is_custom_heap(zend_mm_heap *new_heap)
29903013
}
29913014

29923015
ZEND_API void zend_mm_set_custom_handlers(zend_mm_heap *heap,
2993-
void* (*_malloc)(size_t),
2994-
void (*_free)(void*),
2995-
void* (*_realloc)(void*, size_t))
3016+
void* (*_malloc)(size_t),
3017+
void (*_free)(void*),
3018+
void* (*_realloc)(void*, size_t),
3019+
size_t (*_gc)(void),
3020+
void (*_shutdown)(bool, bool))
29963021
{
29973022
#if ZEND_MM_CUSTOM
29983023
zend_mm_heap *_heap = (zend_mm_heap*)heap;
@@ -3004,14 +3029,18 @@ ZEND_API void zend_mm_set_custom_handlers(zend_mm_heap *heap,
30043029
_heap->custom_heap.std._malloc = _malloc;
30053030
_heap->custom_heap.std._free = _free;
30063031
_heap->custom_heap.std._realloc = _realloc;
3032+
_heap->custom_heap.std._gc = _gc;
3033+
_heap->custom_heap.std._shutdown = _shutdown;
30073034
}
30083035
#endif
30093036
}
30103037

30113038
ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap,
3012-
void* (**_malloc)(size_t),
3013-
void (**_free)(void*),
3014-
void* (**_realloc)(void*, size_t))
3039+
void* (**_malloc)(size_t),
3040+
void (**_free)(void*),
3041+
void* (**_realloc)(void*, size_t),
3042+
size_t (**_gc)(void),
3043+
void (**_shutdown)(bool, bool))
30153044
{
30163045
#if ZEND_MM_CUSTOM
30173046
zend_mm_heap *_heap = (zend_mm_heap*)heap;
@@ -3020,15 +3049,21 @@ ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap,
30203049
*_malloc = _heap->custom_heap.std._malloc;
30213050
*_free = _heap->custom_heap.std._free;
30223051
*_realloc = _heap->custom_heap.std._realloc;
3052+
*_gc = _heap->custom_heap.std._gc;
3053+
*_shutdown = _heap->custom_heap.std._shutdown;
30233054
} else {
30243055
*_malloc = NULL;
30253056
*_free = NULL;
30263057
*_realloc = NULL;
3058+
*_gc = NULL;
3059+
*_shutdown = NULL;
30273060
}
30283061
#else
30293062
*_malloc = NULL;
30303063
*_free = NULL;
30313064
*_realloc = NULL;
3065+
*_gc = NULL;
3066+
*_shutdown = NULL;
30323067
#endif
30333068
}
30343069

Zend/zend_alloc.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ ZEND_API size_t ZEND_FASTCALL _zend_mm_block_size(zend_mm_heap *heap, void *p ZE
261261
#define zend_mm_realloc2_rel(heap, p, size, copy_size) _zend_mm_realloc2((heap), (p), (size), (copy_size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
262262
#define zend_mm_block_size_rel(heap, p) _zend_mm_block_size((heap), (p) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
263263

264+
ZEND_API zend_mm_heap *zend_mm_heap_create(void);
265+
ZEND_API void zend_mm_heap_free(zend_mm_heap* heap);
264266
ZEND_API zend_mm_heap *zend_mm_set_heap(zend_mm_heap *new_heap);
265267
ZEND_API zend_mm_heap *zend_mm_get_heap(void);
266268

@@ -274,11 +276,15 @@ ZEND_API bool zend_mm_is_custom_heap(zend_mm_heap *new_heap);
274276
ZEND_API void zend_mm_set_custom_handlers(zend_mm_heap *heap,
275277
void* (*_malloc)(size_t),
276278
void (*_free)(void*),
277-
void* (*_realloc)(void*, size_t));
279+
void* (*_realloc)(void*, size_t),
280+
size_t (*gc)(void),
281+
void (*shutdown)(bool full, bool silent));
278282
ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap,
279283
void* (**_malloc)(size_t),
280284
void (**_free)(void*),
281-
void* (**_realloc)(void*, size_t));
285+
void* (**_realloc)(void*, size_t),
286+
size_t (**gc)(void),
287+
void (**shutdown)(bool full, bool silent));
282288

283289
#if ZEND_DEBUG
284290
ZEND_API void zend_mm_set_custom_debug_handlers(zend_mm_heap *heap,

ext/zend_test/config.m4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ PHP_ARG_ENABLE([zend-test],
44
[Enable zend_test extension])])
55

66
if test "$PHP_ZEND_TEST" != "no"; then
7-
PHP_NEW_EXTENSION(zend_test, test.c observer.c fiber.c iterators.c object_handlers.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
7+
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)
88
fi

ext/zend_test/config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
ARG_ENABLE("zend-test", "enable zend_test extension", "no");
44

55
if (PHP_ZEND_TEST != "no") {
6-
EXTENSION("zend_test", "test.c observer.c fiber.c iterators.c object_handlers.c", PHP_ZEND_TEST_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
6+
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");
77
ADD_FLAG("CFLAGS_ZEND_TEST", "/D PHP_ZEND_TEST_EXPORTS ");
88
}

ext/zend_test/php_test.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test)
6262
zend_long quantity_value;
6363
zend_string *str_test;
6464
zend_string *not_empty_str_test;
65+
int zend_mm_custom_handlers_enabled;
6566
ZEND_END_MODULE_GLOBALS(zend_test)
6667

6768
extern ZEND_DECLARE_MODULE_GLOBALS(zend_test)

ext/zend_test/test.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "test_arginfo.h"
3636
#include "zend_call_stack.h"
3737
#include "zend_exceptions.h"
38+
#include "zend_mm_custom_handlers.h"
3839

3940
// `php.h` sets `NDEBUG` when not `PHP_DEBUG` which will make `assert()` from
4041
// assert.h a no-op. In order to have `assert()` working on NDEBUG builds, we
@@ -682,7 +683,9 @@ static PHP_INI_MH(OnUpdateZendTestObserveOplineInZendMM)
682683
ZT_G(zend_test_heap),
683684
zend_test_custom_malloc,
684685
zend_test_custom_free,
685-
zend_test_custom_realloc
686+
zend_test_custom_realloc,
687+
NULL,
688+
NULL
686689
);
687690
ZT_G(zend_orig_heap) = zend_mm_get_heap();
688691
zend_mm_set_heap(ZT_G(zend_test_heap));
@@ -994,6 +997,7 @@ PHP_INI_BEGIN()
994997
STD_PHP_INI_ENTRY("zend_test.str_test", "", PHP_INI_ALL, OnUpdateStr, str_test, zend_zend_test_globals, zend_test_globals)
995998
STD_PHP_INI_ENTRY("zend_test.not_empty_str_test", "val", PHP_INI_ALL, OnUpdateStrNotEmpty, not_empty_str_test, zend_zend_test_globals, zend_test_globals)
996999
STD_PHP_INI_BOOLEAN("zend_test.observe_opline_in_zendmm", "0", PHP_INI_ALL, OnUpdateZendTestObserveOplineInZendMM, observe_opline_in_zendmm, zend_zend_test_globals, zend_test_globals)
1000+
STD_PHP_INI_BOOLEAN("zend_test.zend_mm_custom_handlers.enabled", "0", PHP_INI_SYSTEM, OnUpdateBool, zend_mm_custom_handlers_enabled, zend_zend_test_globals, zend_test_globals)
9971001
PHP_INI_END()
9981002

9991003
void (*old_zend_execute_ex)(zend_execute_data *execute_data);
@@ -1217,11 +1221,13 @@ PHP_RINIT_FUNCTION(zend_test)
12171221
{
12181222
zend_hash_init(&ZT_G(global_weakmap), 8, NULL, ZVAL_PTR_DTOR, 0);
12191223
ZT_G(observer_nesting_depth) = 0;
1224+
zend_mm_custom_handlers_rinit();
12201225
return SUCCESS;
12211226
}
12221227

12231228
PHP_RSHUTDOWN_FUNCTION(zend_test)
12241229
{
1230+
zend_mm_custom_handlers_rshutdown();
12251231
zend_ulong obj_key;
12261232
ZEND_HASH_FOREACH_NUM_KEY(&ZT_G(global_weakmap), obj_key) {
12271233
zend_weakrefs_hash_del(&ZT_G(global_weakmap), zend_weakref_key_to_object(obj_key));
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
ZendMM Custom Handlers: garbage collection
3+
--EXTENSIONS--
4+
zend_test
5+
--INI--
6+
zend_test.zend_mm_custom_handlers.enabled=1
7+
opcache.enable=0
8+
--FILE--
9+
<?php
10+
gc_mem_caches();
11+
?>
12+
--EXPECTREGEX--
13+
.*ZendMM GC freed \d+ bytes.*
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| Copyright (c) The PHP Group |
4+
+----------------------------------------------------------------------+
5+
| This source file is subject to version 3.01 of the PHP 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+
| https://www.php.net/license/3_01.txt |
9+
| If you did not receive a copy of the PHP license and are unable to |
10+
| obtain it through the world-wide-web, please send a note to |
11+
| [email protected] so we can mail you a copy immediately. |
12+
+----------------------------------------------------------------------+
13+
| Author: Florian Engelhardt <[email protected]> |
14+
+----------------------------------------------------------------------+
15+
*/
16+
17+
#include "php.h"
18+
#include "php_test.h"
19+
#include "ext/standard/info.h"
20+
#include "ext/standard/php_var.h"
21+
#include "Zend/zend_alloc.h"
22+
#include <stddef.h>
23+
#include <stdio.h>
24+
25+
// the previous heap that was found in ZendMM
26+
zend_mm_heap* prev_heap = NULL;
27+
28+
// the custom handlers that might have been found in the previous heap in case
29+
// there are any
30+
void* (*custom_malloc)(size_t) = NULL;
31+
void (*custom_free)(void*) = NULL;
32+
void* (*custom_realloc)(void *, size_t) = NULL;
33+
size_t (*custom_gc)(void) = NULL;
34+
void (*custom_shutdown)(bool, bool) = NULL;
35+
36+
// this is our heap that we install our custom handlers on and inject into
37+
// ZendMM
38+
zend_mm_heap* heap = NULL;
39+
40+
void* observe_malloc(size_t size)
41+
{
42+
void *ptr = NULL;
43+
if (custom_malloc) {
44+
ptr = custom_malloc(size);
45+
} else {
46+
ptr = _zend_mm_alloc(prev_heap, size ZEND_FILE_LINE_EMPTY_CC ZEND_FILE_LINE_EMPTY_CC);
47+
}
48+
printf("Allocated %zu bytes at %p\n", size, ptr);
49+
return ptr;
50+
}
51+
52+
void observe_free(void* ptr)
53+
{
54+
if (custom_free)
55+
{
56+
custom_free(ptr);
57+
} else {
58+
_zend_mm_free(prev_heap, ptr ZEND_FILE_LINE_EMPTY_CC ZEND_FILE_LINE_EMPTY_CC);
59+
}
60+
printf("Freed memory at %p\n", ptr);
61+
}
62+
63+
void* observe_realloc(void* ptr, size_t size)
64+
{
65+
void * new_ptr;
66+
if (custom_realloc)
67+
{
68+
new_ptr = custom_realloc(ptr, size);
69+
} else {
70+
new_ptr = _zend_mm_realloc(prev_heap, ptr, size ZEND_FILE_LINE_EMPTY_CC ZEND_FILE_LINE_EMPTY_CC);
71+
}
72+
printf("Realloc of %zu bytes from %p to %p\n", size, ptr, new_ptr);
73+
return new_ptr;
74+
}
75+
76+
size_t observe_gc(void)
77+
{
78+
size_t size = 0;
79+
if (custom_gc) {
80+
size = custom_gc();
81+
} else {
82+
size = zend_mm_gc(prev_heap);
83+
}
84+
printf("ZendMM GC freed %zu bytes", size);
85+
return size;
86+
}
87+
88+
void observe_shutdown(bool full, bool silent)
89+
{
90+
if (custom_shutdown) {
91+
custom_shutdown(full, silent);
92+
} else {
93+
zend_mm_shutdown(prev_heap, full, silent);
94+
}
95+
printf("Shutdown happened: full -> %d, silent -> %d\n", full, silent);
96+
}
97+
98+
void zend_mm_custom_handlers_rinit(void)
99+
{
100+
if (ZT_G(zend_mm_custom_handlers_enabled) == 0) {
101+
return;
102+
}
103+
prev_heap = zend_mm_get_heap();
104+
if (zend_mm_is_custom_heap(prev_heap)) {
105+
zend_mm_get_custom_handlers(
106+
prev_heap,
107+
&custom_malloc,
108+
&custom_free,
109+
&custom_realloc,
110+
&custom_gc,
111+
&custom_shutdown
112+
);
113+
}
114+
printf("Prev handlers at %p, %p, %p, %p, %p\n", custom_malloc, custom_free, custom_realloc, custom_gc, custom_shutdown);
115+
heap = zend_mm_heap_create();
116+
zend_mm_set_custom_handlers(
117+
heap,
118+
observe_malloc,
119+
observe_free,
120+
observe_realloc,
121+
observe_gc,
122+
observe_shutdown
123+
);
124+
zend_mm_set_heap(heap);
125+
printf("Heap at %p installed in ZendMM (orig at %p)\n", heap, prev_heap);
126+
}
127+
128+
void zend_mm_custom_handlers_rshutdown(void)
129+
{
130+
if (ZT_G(zend_mm_custom_handlers_enabled) == 0) {
131+
return;
132+
}
133+
zend_mm_set_heap(prev_heap);
134+
zend_mm_heap_free(heap);
135+
heap = NULL;
136+
printf("Prev heap at %p restored in ZendMM\n", prev_heap);
137+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| Copyright (c) The PHP Group |
4+
+----------------------------------------------------------------------+
5+
| This source file is subject to version 3.01 of the PHP 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+
| https://www.php.net/license/3_01.txt |
9+
| If you did not receive a copy of the PHP license and are unable to |
10+
| obtain it through the world-wide-web, please send a note to |
11+
| [email protected] so we can mail you a copy immediately. |
12+
+----------------------------------------------------------------------+
13+
| Author: Florian Engelhardt <[email protected]> |
14+
+----------------------------------------------------------------------+
15+
*/
16+
17+
#ifndef ZEND_TEST_MM_CUSTOM_HANDLERS_H
18+
#define ZEND_TEST_MM_CUSTOM_HANDLERS_H
19+
20+
void zend_mm_custom_handlers_rinit(void);
21+
void zend_mm_custom_handlers_rshutdown(void);
22+
23+
#endif

0 commit comments

Comments
 (0)