Skip to content

Commit 5b99401

Browse files
committed
Shift pointers to avoid hash collision in zend_weakmap->ht
This is the same optimization done in the previous commits for EG(weakrefs)
1 parent cf752d8 commit 5b99401

File tree

1 file changed

+27
-19
lines changed

1 file changed

+27
-19
lines changed

Zend/zend_weakrefs.c

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ typedef struct _zend_weakmap_iterator {
6363
*/
6464
static zend_always_inline zend_ulong zend_object_ptr_to_weakref_key(const zend_object *object)
6565
{
66+
ZEND_ASSERT(((uintptr_t)object) % ZEND_MM_ALIGNMENT == 0);
6667
return ((uintptr_t) object) >> ZEND_MM_ALIGNMENT_LOG2;
6768
}
6869

@@ -86,11 +87,13 @@ static inline void zend_weakref_unref_single(
8687
void *ptr, uintptr_t tag, zend_object *object)
8788
{
8889
if (tag == ZEND_WEAKREF_TAG_REF) {
90+
/* Unreferencing WeakReference (at ptr) singleton that pointed to object. */
8991
zend_weakref *wr = ptr;
9092
wr->referent = NULL;
9193
} else {
94+
/* unreferencing WeakMap entry (at ptr) with a key of object. */
9295
ZEND_ASSERT(tag == ZEND_WEAKREF_TAG_MAP);
93-
zend_hash_index_del((HashTable *) ptr, (zend_ulong) (uintptr_t) object);
96+
zend_hash_index_del((HashTable *) ptr, zend_object_ptr_to_weakref_key(object));
9497
}
9598
}
9699

@@ -132,6 +135,7 @@ static void zend_weakref_register(zend_object *object, void *payload) {
132135
zend_hash_init(ht, 0, NULL, NULL, 0);
133136
zend_hash_index_add_new_ptr(ht, (zend_ulong) tagged_ptr, tagged_ptr);
134137
zend_hash_index_add_new_ptr(ht, (zend_ulong) payload, payload);
138+
/* Replace the single WeakMap or WeakReference entry in EG(weakrefs) with a HashTable with 2 entries in place. */
135139
ZVAL_PTR(zv, ZEND_WEAKREF_ENCODE(ht, ZEND_WEAKREF_TAG_HT));
136140
}
137141

@@ -151,7 +155,7 @@ static void zend_weakref_unregister(zend_object *object, void *payload, bool wea
151155
if (weakref_free) {
152156
zend_weakref_unref_single(ptr, tag, object);
153157
} else {
154-
/* This optimization is only used in the destructor of WeakMap */
158+
/* The optimization of skipping unref is only used in the destructor of WeakMap */
155159
ZEND_ASSERT(ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_MAP);
156160
}
157161
return;
@@ -176,20 +180,21 @@ static void zend_weakref_unregister(zend_object *object, void *payload, bool wea
176180
zend_weakref_unref_single(
177181
ZEND_WEAKREF_GET_PTR(payload), ZEND_WEAKREF_GET_TAG(payload), object);
178182
} else {
183+
/* The optimization of skipping unref is only used in the destructor of WeakMap */
179184
ZEND_ASSERT(ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_MAP);
180185
}
181186
}
182187

183188
ZEND_API zval *zend_weakrefs_hash_add(HashTable *ht, zend_object *key, zval *pData) {
184-
zval *zv = zend_hash_index_add(ht, (zend_ulong) key, pData);
189+
zval *zv = zend_hash_index_add(ht, zend_object_ptr_to_weakref_key(key), pData);
185190
if (zv) {
186191
zend_weakref_register(key, ZEND_WEAKREF_ENCODE(ht, ZEND_WEAKREF_TAG_MAP));
187192
}
188193
return zv;
189194
}
190195

191196
ZEND_API zend_result zend_weakrefs_hash_del(HashTable *ht, zend_object *key) {
192-
zval *zv = zend_hash_index_find(ht, (zend_ulong) key);
197+
zval *zv = zend_hash_index_find(ht, zend_object_ptr_to_weakref_key(key));
193198
if (zv) {
194199
zend_weakref_unregister(key, ZEND_WEAKREF_ENCODE(ht, ZEND_WEAKREF_TAG_MAP), 1);
195200
return SUCCESS;
@@ -201,6 +206,8 @@ void zend_weakrefs_init(void) {
201206
zend_hash_init(&EG(weakrefs), 8, NULL, NULL, 0);
202207
}
203208

209+
/* This is called when the object is garbage collected
210+
* to remove all WeakReference and WeakMap entries weakly referencing that object. */
204211
void zend_weakrefs_notify(zend_object *object) {
205212
/* Annoyingly we can't use the HT destructor here, because we need access to the key (which
206213
* is the object address), which is not provided to the dtor. */
@@ -326,13 +333,13 @@ static zend_object *zend_weakmap_create_object(zend_class_entry *ce)
326333
static void zend_weakmap_free_obj(zend_object *object)
327334
{
328335
zend_weakmap *wm = zend_weakmap_from(object);
329-
zend_ulong obj_addr;
330-
ZEND_HASH_MAP_FOREACH_NUM_KEY(&wm->ht, obj_addr) {
336+
zend_ulong obj_key;
337+
ZEND_HASH_MAP_FOREACH_NUM_KEY(&wm->ht, obj_key) {
331338
/* Optimization: Don't call zend_weakref_unref_single to free individual entries from wm->ht when unregistering (which would do a hash table lookup, call zend_hash_index_del, and skip over any bucket collisions).
332339
* Let freeing the corresponding values for WeakMap entries be done in zend_hash_destroy, freeing objects sequentially.
333340
* The performance difference is notable for larger WeakMaps with worse cache locality. */
334341
zend_weakref_unregister(
335-
(zend_object *) obj_addr, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP), 0);
342+
zend_weakref_key_to_zend_object_ptr(obj_key), ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP), 0);
336343
} ZEND_HASH_FOREACH_END();
337344
zend_hash_destroy(&wm->ht);
338345
zend_object_std_dtor(&wm->std);
@@ -352,7 +359,7 @@ static zval *zend_weakmap_read_dimension(zend_object *object, zval *offset, int
352359

353360
zend_weakmap *wm = zend_weakmap_from(object);
354361
zend_object *obj_addr = Z_OBJ_P(offset);
355-
zval *zv = zend_hash_index_find(&wm->ht, (zend_ulong) obj_addr);
362+
zval *zv = zend_hash_index_find(&wm->ht, zend_object_ptr_to_weakref_key(obj_addr));
356363
if (zv == NULL) {
357364
if (type != BP_VAR_IS) {
358365
zend_throw_error(NULL,
@@ -382,9 +389,10 @@ static void zend_weakmap_write_dimension(zend_object *object, zval *offset, zval
382389

383390
zend_weakmap *wm = zend_weakmap_from(object);
384391
zend_object *obj_addr = Z_OBJ_P(offset);
392+
zend_ulong obj_key = zend_object_ptr_to_weakref_key(obj_addr);
385393
Z_TRY_ADDREF_P(value);
386394

387-
zval *zv = zend_hash_index_find(&wm->ht, (zend_ulong) obj_addr);
395+
zval *zv = zend_hash_index_find(&wm->ht, obj_key);
388396
if (zv) {
389397
/* Because the destructors can have side effects such as resizing or rehashing the WeakMap storage,
390398
* free the zval only after overwriting the original value. */
@@ -396,7 +404,7 @@ static void zend_weakmap_write_dimension(zend_object *object, zval *offset, zval
396404
}
397405

398406
zend_weakref_register(obj_addr, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP));
399-
zend_hash_index_add_new(&wm->ht, (zend_ulong) obj_addr, value);
407+
zend_hash_index_add_new(&wm->ht, obj_key, value);
400408
}
401409

402410
/* int return and check_empty due to Object Handler API */
@@ -408,7 +416,7 @@ static int zend_weakmap_has_dimension(zend_object *object, zval *offset, int che
408416
}
409417

410418
zend_weakmap *wm = zend_weakmap_from(object);
411-
zval *zv = zend_hash_index_find(&wm->ht, (zend_ulong) Z_OBJ_P(offset));
419+
zval *zv = zend_hash_index_find(&wm->ht, zend_object_ptr_to_weakref_key(Z_OBJ_P(offset)));
412420
if (!zv) {
413421
return 0;
414422
}
@@ -428,7 +436,7 @@ static void zend_weakmap_unset_dimension(zend_object *object, zval *offset)
428436

429437
zend_weakmap *wm = zend_weakmap_from(object);
430438
zend_object *obj_addr = Z_OBJ_P(offset);
431-
if (!zend_hash_index_exists(&wm->ht, (zend_ulong) Z_OBJ_P(offset))) {
439+
if (!zend_hash_index_exists(&wm->ht, zend_object_ptr_to_weakref_key(obj_addr))) {
432440
/* Object not in WeakMap, do nothing. */
433441
return;
434442
}
@@ -454,10 +462,10 @@ static HashTable *zend_weakmap_get_properties_for(zend_object *object, zend_prop
454462
ALLOC_HASHTABLE(ht);
455463
zend_hash_init(ht, zend_hash_num_elements(&wm->ht), NULL, ZVAL_PTR_DTOR, 0);
456464

457-
zend_ulong obj_addr;
465+
zend_ulong obj_key;
458466
zval *val;
459-
ZEND_HASH_MAP_FOREACH_NUM_KEY_VAL(&wm->ht, obj_addr, val) {
460-
zend_object *obj = (zend_object*)obj_addr;
467+
ZEND_HASH_MAP_FOREACH_NUM_KEY_VAL(&wm->ht, obj_key, val) {
468+
zend_object *obj = zend_weakref_key_to_zend_object_ptr(obj_key);
461469
zval pair;
462470
array_init(&pair);
463471

@@ -491,11 +499,11 @@ static zend_object *zend_weakmap_clone_obj(zend_object *old_object)
491499
zend_weakmap *new_wm = zend_weakmap_from(new_object);
492500
zend_hash_copy(&new_wm->ht, &old_wm->ht, NULL);
493501

494-
zend_ulong obj_addr;
502+
zend_ulong obj_key;
495503
zval *val;
496-
ZEND_HASH_MAP_FOREACH_NUM_KEY_VAL(&new_wm->ht, obj_addr, val) {
504+
ZEND_HASH_MAP_FOREACH_NUM_KEY_VAL(&new_wm->ht, obj_key, val) {
497505
zend_weakref_register(
498-
(zend_object *) obj_addr, ZEND_WEAKREF_ENCODE(new_wm, ZEND_WEAKREF_TAG_MAP));
506+
zend_weakref_key_to_zend_object_ptr(obj_key), ZEND_WEAKREF_ENCODE(new_wm, ZEND_WEAKREF_TAG_MAP));
499507
zval_add_ref(val);
500508
} ZEND_HASH_FOREACH_END();
501509
return new_object;
@@ -542,7 +550,7 @@ static void zend_weakmap_iterator_get_current_key(zend_object_iterator *obj_iter
542550
ZEND_ASSERT(0 && "Must have integer key");
543551
}
544552

545-
ZVAL_OBJ_COPY(key, (zend_object *) num_key);
553+
ZVAL_OBJ_COPY(key, zend_weakref_key_to_zend_object_ptr(num_key));
546554
}
547555

548556
static void zend_weakmap_iterator_move_forward(zend_object_iterator *obj_iter)

0 commit comments

Comments
 (0)