xref: /PHP-7.3/ext/mysqlnd/mysqlnd_block_alloc.c (revision df982da5)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2006-2018 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Andrey Hristov <andrey@php.net>                             |
16   |          Ulf Wendel <uw@php.net>                                     |
17   |          Dmitry Stogov <dmitry@php.net>                              |
18   +----------------------------------------------------------------------+
19 */
20 
21 #include "php.h"
22 #include "mysqlnd.h"
23 #include "mysqlnd_block_alloc.h"
24 #include "mysqlnd_debug.h"
25 #include "mysqlnd_priv.h"
26 
27 
28 /* {{{ mysqlnd_arena_create */
mysqlnd_arena_create(size_t size)29 static zend_always_inline zend_arena* mysqlnd_arena_create(size_t size)
30 {
31 	zend_arena *arena = (zend_arena*)mnd_emalloc(size);
32 
33 	arena->ptr = (char*) arena + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena));
34 	arena->end = (char*) arena + size;
35 	arena->prev = NULL;
36 	return arena;
37 }
38 /* }}} */
39 
40 /* {{{ mysqlnd_arena_destroy */
mysqlnd_arena_destroy(zend_arena * arena)41 static zend_always_inline void mysqlnd_arena_destroy(zend_arena *arena)
42 {
43 	do {
44 		zend_arena *prev = arena->prev;
45 		mnd_efree(arena);
46 		arena = prev;
47 	} while (arena);
48 }
49 /* }}} */
50 
51 /* {{{ mysqlnd_arena_alloc */
mysqlnd_arena_alloc(zend_arena ** arena_ptr,size_t size)52 static zend_always_inline void* mysqlnd_arena_alloc(zend_arena **arena_ptr, size_t size)
53 {
54 	zend_arena *arena = *arena_ptr;
55 	char *ptr = arena->ptr;
56 
57 	size = ZEND_MM_ALIGNED_SIZE(size);
58 
59 	if (EXPECTED(size <= (size_t)(arena->end - ptr))) {
60 		arena->ptr = ptr + size;
61 	} else {
62 		size_t arena_size =
63 			UNEXPECTED((size + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena))) > (size_t)(arena->end - (char*) arena)) ?
64 				(size + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena))) :
65 				(size_t)(arena->end - (char*) arena);
66 		zend_arena *new_arena = (zend_arena*)mnd_emalloc(arena_size);
67 
68 		ptr = (char*) new_arena + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena));
69 		new_arena->ptr = (char*) new_arena + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena)) + size;
70 		new_arena->end = (char*) new_arena + arena_size;
71 		new_arena->prev = arena;
72 		*arena_ptr = new_arena;
73 	}
74 
75 	return (void*) ptr;
76 }
77 /* }}} */
78 
mysqlnd_arena_checkpoint(zend_arena * arena)79 static zend_always_inline void* mysqlnd_arena_checkpoint(zend_arena *arena)
80 {
81 	return arena->ptr;
82 }
83 
mysqlnd_arena_release(zend_arena ** arena_ptr,void * checkpoint)84 static zend_always_inline void mysqlnd_arena_release(zend_arena **arena_ptr, void *checkpoint)
85 {
86 	zend_arena *arena = *arena_ptr;
87 
88 	while (UNEXPECTED((char*)checkpoint > arena->end) ||
89 	       UNEXPECTED((char*)checkpoint <= (char*)arena)) {
90 		zend_arena *prev = arena->prev;
91 		mnd_efree(arena);
92 		*arena_ptr = arena = prev;
93 	}
94 	ZEND_ASSERT((char*)checkpoint > (char*)arena && (char*)checkpoint <= arena->end);
95 	arena->ptr = (char*)checkpoint;
96 }
97 
98 /* {{{ mysqlnd_mempool_free_chunk */
99 static void
mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL * pool,void * ptr)100 mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL * pool, void * ptr)
101 {
102 	DBG_ENTER("mysqlnd_mempool_free_chunk");
103 	/* Try to back-off and guess if this is the last block allocated */
104 	if (ptr == pool->last) {
105 		/*
106 			This was the last allocation. Lucky us, we can free
107 			a bit of memory from the pool. Next time we will return from the same ptr.
108 		*/
109 		pool->arena->ptr = (char*)ptr;
110 		pool->last = NULL;
111 	}
112 	DBG_VOID_RETURN;
113 }
114 /* }}} */
115 
116 
117 /* {{{ mysqlnd_mempool_resize_chunk */
118 static void *
mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL * pool,void * ptr,size_t old_size,size_t size)119 mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL * pool, void * ptr, size_t old_size, size_t size)
120 {
121 	DBG_ENTER("mysqlnd_mempool_resize_chunk");
122 
123 	/* Try to back-off and guess if this is the last block allocated */
124 	if (ptr == pool->last
125 	  && (ZEND_MM_ALIGNED_SIZE(size) <= ((char*)pool->arena->end - (char*)ptr))) {
126 		/*
127 			This was the last allocation. Lucky us, we can free
128 			a bit of memory from the pool. Next time we will return from the same ptr.
129 		*/
130 		pool->arena->ptr = (char*)ptr + ZEND_MM_ALIGNED_SIZE(size);
131 	} else {
132 		void *new_ptr = mysqlnd_arena_alloc(&pool->arena, size);
133 		memcpy(new_ptr, ptr, MIN(old_size, size));
134 		pool->last = ptr = new_ptr;
135 	}
136 	DBG_RETURN(ptr);
137 }
138 /* }}} */
139 
140 
141 /* {{{ mysqlnd_mempool_get_chunk */
142 static void *
mysqlnd_mempool_get_chunk(MYSQLND_MEMORY_POOL * pool,size_t size)143 mysqlnd_mempool_get_chunk(MYSQLND_MEMORY_POOL * pool, size_t size)
144 {
145 	void *ptr = NULL;
146 	DBG_ENTER("mysqlnd_mempool_get_chunk");
147 
148 	ptr = mysqlnd_arena_alloc(&pool->arena, size);
149 	pool->last = ptr;
150 
151 	DBG_RETURN(ptr);
152 }
153 /* }}} */
154 
155 
156 /* {{{ mysqlnd_mempool_create */
157 PHPAPI MYSQLND_MEMORY_POOL *
mysqlnd_mempool_create(size_t arena_size)158 mysqlnd_mempool_create(size_t arena_size)
159 {
160 	zend_arena * arena;
161 	MYSQLND_MEMORY_POOL * ret;
162 
163 	DBG_ENTER("mysqlnd_mempool_create");
164 	arena = mysqlnd_arena_create(MAX(arena_size, ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena))));
165 	ret = mysqlnd_arena_alloc(&arena, sizeof(MYSQLND_MEMORY_POOL));
166 	ret->arena = arena;
167 	ret->last = NULL;
168 	ret->checkpoint = NULL;
169 	ret->get_chunk = mysqlnd_mempool_get_chunk;
170 	ret->free_chunk = mysqlnd_mempool_free_chunk;
171 	ret->resize_chunk = mysqlnd_mempool_resize_chunk;
172 	DBG_RETURN(ret);
173 }
174 /* }}} */
175 
176 
177 /* {{{ mysqlnd_mempool_destroy */
178 PHPAPI void
mysqlnd_mempool_destroy(MYSQLND_MEMORY_POOL * pool)179 mysqlnd_mempool_destroy(MYSQLND_MEMORY_POOL * pool)
180 {
181 	DBG_ENTER("mysqlnd_mempool_destroy");
182 	/* mnd_free will reference LOCK_access and might crash, depending on the caller...*/
183 	mysqlnd_arena_destroy(pool->arena);
184 	DBG_VOID_RETURN;
185 }
186 /* }}} */
187 
188 /* {{{ mysqlnd_mempool_save_state */
189 PHPAPI void
mysqlnd_mempool_save_state(MYSQLND_MEMORY_POOL * pool)190 mysqlnd_mempool_save_state(MYSQLND_MEMORY_POOL * pool)
191 {
192 	DBG_ENTER("mysqlnd_mempool_save_state");
193 	pool->checkpoint = mysqlnd_arena_checkpoint(pool->arena);
194 	DBG_VOID_RETURN;
195 }
196 /* }}} */
197 
198 /* {{{ mysqlnd_mempool_restore_state */
199 PHPAPI void
mysqlnd_mempool_restore_state(MYSQLND_MEMORY_POOL * pool)200 mysqlnd_mempool_restore_state(MYSQLND_MEMORY_POOL * pool)
201 {
202 	DBG_ENTER("mysqlnd_mempool_restore_state");
203 #if ZEND_DEBUG
204 	ZEND_ASSERT(pool->checkpoint);
205 #endif
206 	if (pool->checkpoint) {
207 		mysqlnd_arena_release(&pool->arena, pool->checkpoint);
208 		pool->last = NULL;
209 		pool->checkpoint = NULL;
210 	}
211 	DBG_VOID_RETURN;
212 }
213 /* }}} */
214 
215 /*
216  * Local variables:
217  * tab-width: 4
218  * c-basic-offset: 4
219  * End:
220  * vim600: noet sw=4 ts=4 fdm=marker
221  * vim<600: noet sw=4 ts=4
222  */
223