Skip to content

Commit 38b9c33

Browse files
tstarlingarnaud-lb
authored andcommitted
Add some comments to zend_gc.c
Closes phpGH-19412
1 parent cf33fd7 commit 38b9c33

File tree

1 file changed

+68
-7
lines changed

1 file changed

+68
-7
lines changed

Zend/zend_gc.c

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,9 @@
172172
#define GC_IDX2PTR(idx) (GC_G(buf) + (idx))
173173
#define GC_PTR2IDX(ptr) ((ptr) - GC_G(buf))
174174

175+
/* Get the value to be placed in an unused buffer entry with the specified next unused list index */
175176
#define GC_IDX2LIST(idx) ((void*)(uintptr_t)(((idx) * sizeof(void*)) | GC_UNUSED))
177+
/* Get the index of the next item in the unused list from the given root buffer entry. */
176178
#define GC_LIST2IDX(list) (((uint32_t)(uintptr_t)(list)) / sizeof(void*))
177179

178180
/* GC buffers */
@@ -228,10 +230,16 @@
228230
} while (0)
229231

230232
/* unused buffers */
233+
234+
/* Are there any unused root buffer entries? */
231235
#define GC_HAS_UNUSED() \
232236
(GC_G(unused) != GC_INVALID)
237+
238+
/* Get the next unused entry and remove it from the list */
233239
#define GC_FETCH_UNUSED() \
234240
gc_fetch_unused()
241+
242+
/* Add a root buffer entry to the unused list */
235243
#define GC_LINK_UNUSED(root) \
236244
gc_link_unused(root)
237245

@@ -244,12 +252,23 @@
244252

245253
ZEND_API int (*gc_collect_cycles)(void);
246254

255+
/* The type of a root buffer entry.
256+
*
257+
* The lower two bits are used for flags and need to be masked out to
258+
* reconstruct a pointer.
259+
*
260+
* When a node in the root buffer is removed, the non-flag bits of the
261+
* unused entry are used to store the index of the next entry in the unused
262+
* list. */
247263
typedef struct _gc_root_buffer {
248264
zend_refcounted *ref;
249265
} gc_root_buffer;
250266

251267
typedef struct _zend_gc_globals {
252-
gc_root_buffer *buf; /* preallocated arrays of buffers */
268+
/* The root buffer, which stores possible roots of reference cycles. It is
269+
* also used to store garbage to be collected at the end of a run.
270+
* A single array which is reallocated as necessary. */
271+
gc_root_buffer *buf;
253272

254273
bool gc_enabled;
255274
bool gc_active; /* GC currently running, forbid nested GC */
@@ -262,13 +281,13 @@ typedef struct _zend_gc_globals {
262281
uint32_t buf_size; /* size of the GC buffer */
263282
uint32_t num_roots; /* number of roots in GC buffer */
264283

265-
uint32_t gc_runs;
266-
uint32_t collected;
284+
uint32_t gc_runs; /* number of GC runs since reset */
285+
uint32_t collected; /* number of collected nodes since reset */
267286

268-
zend_hrtime_t activated_at;
269-
zend_hrtime_t collector_time;
270-
zend_hrtime_t dtor_time;
271-
zend_hrtime_t free_time;
287+
zend_hrtime_t activated_at; /* the timestamp of the last reset */
288+
zend_hrtime_t collector_time; /* time spent running GC (ns) */
289+
zend_hrtime_t dtor_time; /* time spent calling destructors (ns) */
290+
zend_hrtime_t free_time; /* time spent destroying nodes and freeing memory (ns) */
272291

273292
uint32_t dtor_idx; /* root buffer index */
274293
uint32_t dtor_end;
@@ -313,6 +332,7 @@ static zend_gc_globals gc_globals;
313332

314333
typedef struct _gc_stack gc_stack;
315334

335+
/* The stack used for graph traversal is stored as a linked list of segments */
316336
struct _gc_stack {
317337
gc_stack *prev;
318338
gc_stack *next;
@@ -375,6 +395,11 @@ static void gc_stack_free(gc_stack *stack)
375395
}
376396
}
377397

398+
/* Map a full index to a compressed index.
399+
*
400+
* The root buffer can have up to 2^30 entries, but we only have 20 bits to
401+
* store the index. So we use the 1<<19 bit as a compression flag and use the
402+
* other 19 bits to store the index modulo 2^19. */
378403
static zend_always_inline uint32_t gc_compress(uint32_t idx)
379404
{
380405
if (EXPECTED(idx < GC_MAX_UNCOMPRESSED)) {
@@ -383,6 +408,9 @@ static zend_always_inline uint32_t gc_compress(uint32_t idx)
383408
return (idx % GC_MAX_UNCOMPRESSED) | GC_MAX_UNCOMPRESSED;
384409
}
385410

411+
/* Find the root buffer entry given a pointer and a compressed index.
412+
* Iterate through the root buffer in steps of 2^19 until the pointer
413+
* matches. */
386414
static zend_always_inline gc_root_buffer* gc_decompress(zend_refcounted *ref, uint32_t idx)
387415
{
388416
gc_root_buffer *root = GC_IDX2PTR(idx);
@@ -401,6 +429,8 @@ static zend_always_inline gc_root_buffer* gc_decompress(zend_refcounted *ref, ui
401429
}
402430
}
403431

432+
/* Get the index of the next unused root buffer entry, and remove it from the
433+
* unused list. GC_HAS_UNUSED() must be true before calling this. */
404434
static zend_always_inline uint32_t gc_fetch_unused(void)
405435
{
406436
uint32_t idx;
@@ -414,6 +444,7 @@ static zend_always_inline uint32_t gc_fetch_unused(void)
414444
return idx;
415445
}
416446

447+
/* Add a root buffer entry to the unused list */
417448
static zend_always_inline void gc_link_unused(gc_root_buffer *root)
418449
{
419450
root->ref = GC_IDX2LIST(GC_G(unused));
@@ -463,6 +494,7 @@ static void gc_trace_ref(zend_refcounted *ref) {
463494
}
464495
#endif
465496

497+
/* Mark a root buffer entry unused */
466498
static zend_always_inline void gc_remove_from_roots(gc_root_buffer *root)
467499
{
468500
GC_LINK_UNUSED(root);
@@ -565,6 +597,8 @@ void gc_reset(void)
565597
GC_G(activated_at) = zend_hrtime();
566598
}
567599

600+
/* Enable/disable the garbage collector.
601+
* Initialize globals if necessary. */
568602
ZEND_API bool gc_enable(bool enable)
569603
{
570604
bool old_enabled = GC_G(gc_enabled);
@@ -584,6 +618,7 @@ ZEND_API bool gc_enabled(void)
584618
return GC_G(gc_enabled);
585619
}
586620

621+
/* Protect the GC root buffer (prevent additions) */
587622
ZEND_API bool gc_protect(bool protect)
588623
{
589624
bool old_protected = GC_G(gc_protected);
@@ -621,6 +656,7 @@ static void gc_grow_root_buffer(void)
621656
GC_G(buf_size) = new_size;
622657
}
623658

659+
/* Adjust the GC activation threshold given the number of nodes collected by the last run */
624660
static void gc_adjust_threshold(int count)
625661
{
626662
uint32_t new_threshold;
@@ -651,6 +687,7 @@ static void gc_adjust_threshold(int count)
651687
}
652688
}
653689

690+
/* Perform a GC run and then add a node as a possible root. */
654691
static zend_never_inline void ZEND_FASTCALL gc_possible_root_when_full(zend_refcounted *ref)
655692
{
656693
uint32_t idx;
@@ -695,6 +732,8 @@ static zend_never_inline void ZEND_FASTCALL gc_possible_root_when_full(zend_refc
695732
GC_BENCH_PEAK(root_buf_peak, root_buf_length);
696733
}
697734

735+
/* Add a possible root node to the buffer.
736+
* Maybe perform a GC run. */
698737
ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref)
699738
{
700739
uint32_t idx;
@@ -731,6 +770,7 @@ ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref)
731770
GC_BENCH_PEAK(root_buf_peak, root_buf_length);
732771
}
733772

773+
/* Add an extra root during a GC run */
734774
static void ZEND_FASTCALL gc_extra_root(zend_refcounted *ref)
735775
{
736776
uint32_t idx;
@@ -764,6 +804,7 @@ static void ZEND_FASTCALL gc_extra_root(zend_refcounted *ref)
764804
GC_BENCH_PEAK(root_buf_peak, root_buf_length);
765805
}
766806

807+
/* Remove a node from the root buffer given its compressed index */
767808
static zend_never_inline void ZEND_FASTCALL gc_remove_compressed(zend_refcounted *ref, uint32_t idx)
768809
{
769810
gc_root_buffer *root = gc_decompress(ref, idx);
@@ -793,6 +834,10 @@ ZEND_API void ZEND_FASTCALL gc_remove_from_buffer(zend_refcounted *ref)
793834
gc_remove_from_roots(root);
794835
}
795836

837+
/* Mark all nodes reachable from ref as black (live). Restore the reference
838+
* counts decremented by gc_mark_grey(). See ScanBlack() in Bacon & Rajan.
839+
* To implement a depth-first search, discovered nodes are added to a stack
840+
* which is processed iteratively. */
796841
static void gc_scan_black(zend_refcounted *ref, gc_stack *stack)
797842
{
798843
HashTable *ht;
@@ -992,6 +1037,8 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack)
9921037
}
9931038
}
9941039

1040+
/* Traverse the graph of nodes referred to by ref. Decrement the reference
1041+
* counts and mark visited nodes grey. See MarkGray() in Bacon & Rajan. */
9951042
static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack)
9961043
{
9971044
HashTable *ht;
@@ -1204,6 +1251,9 @@ static void gc_compact(void)
12041251
}
12051252
}
12061253

1254+
/* For all roots marked purple, traverse the graph, decrementing the reference
1255+
* count of their child nodes. Mark visited nodes grey so that they are not
1256+
* visited again. See MarkRoots() in Bacon & Rajan. */
12071257
static void gc_mark_roots(gc_stack *stack)
12081258
{
12091259
gc_root_buffer *current, *last;
@@ -1223,6 +1273,10 @@ static void gc_mark_roots(gc_stack *stack)
12231273
}
12241274
}
12251275

1276+
/* Traverse the reference graph of ref. Evaluate grey nodes and mark them
1277+
* black (to keep) or white (to free). Note that nodes initially marked white
1278+
* may later become black if they are visited from a live node.
1279+
* See Scan() in Bacon & Rajan. */
12261280
static void gc_scan(zend_refcounted *ref, gc_stack *stack)
12271281
{
12281282
HashTable *ht;
@@ -1376,6 +1430,7 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack)
13761430
}
13771431
}
13781432

1433+
/* Scan all roots, coloring grey nodes black or white */
13791434
static void gc_scan_roots(gc_stack *stack)
13801435
{
13811436
uint32_t idx, end;
@@ -1409,6 +1464,8 @@ static void gc_scan_roots(gc_stack *stack)
14091464
}
14101465
}
14111466

1467+
/* Add a node to the buffer with the garbage flag, so that it will be
1468+
* destroyed and freed when the scan is complete. */
14121469
static void gc_add_garbage(zend_refcounted *ref)
14131470
{
14141471
uint32_t idx;
@@ -1434,6 +1491,7 @@ static void gc_add_garbage(zend_refcounted *ref)
14341491
GC_G(num_roots)++;
14351492
}
14361493

1494+
/* Traverse the reference graph from ref, marking any white nodes as garbage. */
14371495
static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *stack)
14381496
{
14391497
int count = 0;
@@ -1622,6 +1680,7 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta
16221680
return count;
16231681
}
16241682

1683+
/* Traverse the reference graph from all roots, marking white nodes as garbage. */
16251684
static int gc_collect_roots(uint32_t *flags, gc_stack *stack)
16261685
{
16271686
uint32_t idx, end;
@@ -1808,6 +1867,7 @@ static ZEND_COLD ZEND_NORETURN void gc_start_destructor_fiber_error(void)
18081867
zend_error_noreturn(E_ERROR, "Unable to start destructor fiber");
18091868
}
18101869

1870+
/* Call destructors for garbage in the buffer. */
18111871
static zend_always_inline zend_result gc_call_destructors(uint32_t idx, uint32_t end, zend_fiber *fiber)
18121872
{
18131873
gc_root_buffer *current;
@@ -1910,6 +1970,7 @@ static zend_never_inline void gc_call_destructors_in_fiber(uint32_t end)
19101970
}
19111971
}
19121972

1973+
/* Perform a garbage collection run. The default implementation of gc_collect_cycles. */
19131974
ZEND_API int zend_gc_collect_cycles(void)
19141975
{
19151976
int total_count = 0;

0 commit comments

Comments
 (0)