Summary
Follow-up from the #242 investigation (PR #262). After a function copy-out + redefine(), a caller that already executed the call site keeps the OLD zend_function* memoized in its run-time cache slot — the bucket repoint redirects resolution only, mirroring the documented class-side model. Under opcache this bites harder because every cached caller shares immutable op_arrays with map-ptr run-time caches. PR #262 documents this as a copy-out caveat ("redefine at bootstrap, before warm-up"); this issue tracks actually lifting it, as an explicit opt-in.
Proposed shape (recommendation from the investigation)
An opt-in, targeted old→new pointer repatch over enumerable op_arrays — NOT a blanket cache flush, and NOT silent behavior inside redefine():
- Walk the enumerable holders: function-table entries, every class's method table, closures reachable via the object store; for each op_array with a materialized run-time cache, scan the cache slots for the old
zend_function* and replace with the new one.
- Expose it as an explicit API (e.g.
ReflectionFunction::repatchWarmCallers() or a redefine() flag) so the cost and semantics are chosen by the consumer.
- Bounded honesty in docs: closures held only in userland variables (not reachable via any table) cannot be enumerated; their warm slots stay stale — the API contract must say so.
Open design points: slot identification safety (run-time cache layouts vary by opcode; scanning must not repatch unrelated pointers that happen to collide — likely needs opcode-aware slot typing rather than a blind pointer scan), ZTS map-ptr areas, and interaction with HEAP_RT_CACHE bodies minted by swaps.
Part of the opcache meta-epic #245. Related: the optimizer-inlined call sites pinned by PR #262's inlined-call-site-limitation matrix leg are NOT addressable by any repatch (the call site no longer exists) and stay a documented engine reality.
Summary
Follow-up from the #242 investigation (PR #262). After a function copy-out +
redefine(), a caller that already executed the call site keeps the OLDzend_function*memoized in its run-time cache slot — the bucket repoint redirects resolution only, mirroring the documented class-side model. Under opcache this bites harder because every cached caller shares immutable op_arrays with map-ptr run-time caches. PR #262 documents this as a copy-out caveat ("redefine at bootstrap, before warm-up"); this issue tracks actually lifting it, as an explicit opt-in.Proposed shape (recommendation from the investigation)
An opt-in, targeted old→new pointer repatch over enumerable op_arrays — NOT a blanket cache flush, and NOT silent behavior inside
redefine():zend_function*and replace with the new one.ReflectionFunction::repatchWarmCallers()or aredefine()flag) so the cost and semantics are chosen by the consumer.Open design points: slot identification safety (run-time cache layouts vary by opcode; scanning must not repatch unrelated pointers that happen to collide — likely needs opcode-aware slot typing rather than a blind pointer scan), ZTS map-ptr areas, and interaction with
HEAP_RT_CACHEbodies minted by swaps.Part of the opcache meta-epic #245. Related: the optimizer-inlined call sites pinned by PR #262's
inlined-call-site-limitationmatrix leg are NOT addressable by any repatch (the call site no longer exists) and stay a documented engine reality.