@@ -226,7 +226,6 @@ typedef zend_mm_bitset zend_mm_page_map[ZEND_MM_PAGE_MAP_LEN]; /* 64B */
226226typedef struct _zend_mm_page zend_mm_page ;
227227typedef struct _zend_mm_bin zend_mm_bin ;
228228typedef struct _zend_mm_free_slot zend_mm_free_slot ;
229- typedef struct _zend_mm_chunk zend_mm_chunk ;
230229typedef struct _zend_mm_huge_list zend_mm_huge_list ;
231230
232231static bool zend_mm_use_huge_pages = false;
@@ -322,6 +321,9 @@ struct _zend_mm_chunk {
322321 zend_mm_heap heap_slot ; /* used only in main chunk */
323322 zend_mm_page_map free_map ; /* 512 bits or 64 bytes */
324323 zend_mm_page_info map [ZEND_MM_PAGES ]; /* 2 KB = 512 * 4 */
324+ bool preserve ; /* Never free this chunk.
325+ Ensures the address space can
326+ not be re-used. */
325327};
326328
327329struct _zend_mm_page {
@@ -804,7 +806,7 @@ static void *zend_mm_chunk_alloc_int(size_t size, size_t alignment)
804806 }
805807}
806808
807- static void * zend_mm_chunk_alloc (zend_mm_heap * heap , size_t size , size_t alignment )
809+ ZEND_API void * zend_mm_chunk_alloc (zend_mm_heap * heap , size_t size , size_t alignment )
808810{
809811#if ZEND_MM_STORAGE
810812 if (UNEXPECTED (heap -> storage )) {
@@ -1172,11 +1174,15 @@ static zend_always_inline void zend_mm_delete_chunk(zend_mm_heap *heap, zend_mm_
11721174 }
11731175 }
11741176 if (!heap -> cached_chunks || chunk -> num > heap -> cached_chunks -> num ) {
1175- zend_mm_chunk_free (heap , chunk , ZEND_MM_CHUNK_SIZE );
1177+ if (!chunk -> preserve ) {
1178+ zend_mm_chunk_free (heap , chunk , ZEND_MM_CHUNK_SIZE );
1179+ }
11761180 } else {
11771181//TODO: select the best chunk to delete???
11781182 chunk -> next = heap -> cached_chunks -> next ;
1179- zend_mm_chunk_free (heap , heap -> cached_chunks , ZEND_MM_CHUNK_SIZE );
1183+ if (!heap -> cached_chunks -> preserve ) {
1184+ zend_mm_chunk_free (heap , heap -> cached_chunks , ZEND_MM_CHUNK_SIZE );
1185+ }
11801186 heap -> cached_chunks = chunk ;
11811187 }
11821188 }
@@ -2195,6 +2201,53 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap)
21952201 return collected * ZEND_MM_PAGE_SIZE ;
21962202}
21972203
2204+ ZEND_API zend_mm_chunk * zend_mm_get_chunk_list (zend_mm_heap * heap )
2205+ {
2206+ return heap -> main_chunk ;
2207+ }
2208+
2209+ ZEND_API int zend_mm_get_chunks_count (zend_mm_heap * heap )
2210+ {
2211+ return heap -> chunks_count ;
2212+ }
2213+
2214+ ZEND_API void * zend_mm_get_huge_list (zend_mm_heap * heap )
2215+ {
2216+ return heap -> huge_list ;
2217+ }
2218+
2219+ ZEND_API zend_mm_chunk * zend_mm_get_next_chunk (zend_mm_heap * heap , zend_mm_chunk * chunk )
2220+ {
2221+ ZEND_ASSERT (chunk -> heap == heap );
2222+ zend_mm_chunk * next = chunk -> next ;
2223+ if (next == heap -> main_chunk ) {
2224+ return NULL ;
2225+ }
2226+ return next ;
2227+ }
2228+
2229+ /* Adds the given chunk to the heap. The chunk is not initialized, and can have
2230+ * allocated slots and pages. */
2231+ ZEND_API void zend_mm_adopt_chunk (zend_mm_heap * heap , zend_mm_chunk * chunk )
2232+ {
2233+ /* Do not import free lists, as the chunk may have been created with a
2234+ * different key. However, free pages can be allocated. */
2235+ chunk -> heap = heap ;
2236+ chunk -> next = heap -> main_chunk ;
2237+ chunk -> prev = heap -> main_chunk -> prev ;
2238+ chunk -> prev -> next = chunk ;
2239+ chunk -> next -> prev = chunk ;
2240+ chunk -> num = chunk -> prev -> num + 1 ;
2241+ heap -> chunks_count ++ ;
2242+ heap -> peak_chunks_count ++ ;
2243+ }
2244+
2245+ ZEND_API void zend_mm_preserve_chunk (zend_mm_heap * heap , zend_mm_chunk * chunk )
2246+ {
2247+ ZEND_ASSERT (chunk -> heap == heap );
2248+ chunk -> preserve = true;
2249+ }
2250+
21982251#if ZEND_DEBUG
21992252/******************/
22002253/* Leak detection */
@@ -2460,23 +2513,44 @@ ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent)
24602513 while (heap -> cached_chunks ) {
24612514 p = heap -> cached_chunks ;
24622515 heap -> cached_chunks = p -> next ;
2463- zend_mm_chunk_free (heap , p , ZEND_MM_CHUNK_SIZE );
2516+ if (!p -> preserve ) {
2517+ zend_mm_chunk_free (heap , p , ZEND_MM_CHUNK_SIZE );
2518+ }
24642519 }
24652520 /* free the first chunk */
2466- zend_mm_chunk_free (heap , heap -> main_chunk , ZEND_MM_CHUNK_SIZE );
2521+ if (!heap -> main_chunk -> preserve ) {
2522+ zend_mm_chunk_free (heap , heap -> main_chunk , ZEND_MM_CHUNK_SIZE );
2523+ }
24672524 } else {
2525+ /* unlink preserved chunks from the cache */
2526+ p = heap -> cached_chunks ;
2527+ while (p ) {
2528+ zend_mm_chunk * q = p -> next ;
2529+ while (q && q -> preserve ) {
2530+ p -> next = q = q -> next ;
2531+ heap -> cached_chunks_count -- ;
2532+ }
2533+ p = q ;
2534+ }
2535+ if (heap -> cached_chunks && heap -> cached_chunks -> preserve ) {
2536+ heap -> cached_chunks_count -- ;
2537+ heap -> cached_chunks = heap -> cached_chunks -> next ;
2538+ }
2539+
24682540 /* free some cached chunks to keep average count */
24692541 heap -> avg_chunks_count = (heap -> avg_chunks_count + (double )heap -> peak_chunks_count ) / 2.0 ;
24702542 while ((double )heap -> cached_chunks_count + 0.9 > heap -> avg_chunks_count &&
24712543 heap -> cached_chunks ) {
24722544 p = heap -> cached_chunks ;
24732545 heap -> cached_chunks = p -> next ;
2546+ ZEND_ASSERT (!p -> preserve );
24742547 zend_mm_chunk_free (heap , p , ZEND_MM_CHUNK_SIZE );
24752548 heap -> cached_chunks_count -- ;
24762549 }
24772550 /* clear cached chunks */
24782551 p = heap -> cached_chunks ;
24792552 while (p != NULL ) {
2553+ ZEND_ASSERT (!p -> preserve );
24802554 zend_mm_chunk * q = p -> next ;
24812555 memset (p , 0 , sizeof (zend_mm_chunk ));
24822556 p -> next = q ;
@@ -2864,7 +2938,9 @@ ZEND_API zend_result zend_set_memory_limit(size_t memory_limit)
28642938 do {
28652939 zend_mm_chunk * p = heap -> cached_chunks ;
28662940 heap -> cached_chunks = p -> next ;
2867- zend_mm_chunk_free (heap , p , ZEND_MM_CHUNK_SIZE );
2941+ if (!p -> preserve ) {
2942+ zend_mm_chunk_free (heap , p , ZEND_MM_CHUNK_SIZE );
2943+ }
28682944 heap -> cached_chunks_count -- ;
28692945 heap -> real_size -= ZEND_MM_CHUNK_SIZE ;
28702946 } while (memory_limit < heap -> real_size );
@@ -2919,8 +2995,22 @@ ZEND_API void zend_memory_reset_peak_usage(void)
29192995#endif
29202996}
29212997
2998+ static void alloc_globals_ctor (zend_alloc_globals * alloc_globals );
2999+
29223000ZEND_API void shutdown_memory_manager (bool silent , bool full_shutdown )
29233001{
3002+ if (!full_shutdown ) {
3003+ zend_mm_heap * heap = AG (mm_heap );
3004+ if (heap -> main_chunk -> preserve ) {
3005+ /* The main chunk is preserved, so we can not re-use it in the next
3006+ * request: We have to full shutdown and start a new heap.
3007+ * This happens when snapshot_state() was called during the request.
3008+ */
3009+ zend_mm_shutdown (AG (mm_heap ), 1 , silent );
3010+ alloc_globals_ctor (& alloc_globals );
3011+ return ;
3012+ }
3013+ }
29243014 zend_mm_shutdown (AG (mm_heap ), full_shutdown , silent );
29253015}
29263016
0 commit comments