Skip to content

Commit 1eeeca2

Browse files
committed
Merge branch 'fix/opcache-safe-reset' of https://github.com/dunglas/frankenphp into fix/opcache-safe-reset
2 parents c5ef9fc + 8e76bd7 commit 1eeeca2

3 files changed

Lines changed: 25 additions & 13 deletions

File tree

caddy/caddy_test.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package caddy_test
33
import (
44
"bytes"
55
"fmt"
6-
"math/rand/v2"
76
"net/http"
87
"os"
98
"path/filepath"
@@ -1767,9 +1766,9 @@ func TestOpcacheReset(t *testing.T) {
17671766
wg := sync.WaitGroup{}
17681767
numRequests := 500 // increase if test is flaky to get consistent failures
17691768
wg.Add(numRequests)
1769+
// pseudo random sleep simulator
17701770
for i := 0; i < numRequests; i++ {
1771-
// introduce some random delay
1772-
if rand.IntN(10) > 8 {
1771+
if i%10 == 0 {
17731772
time.Sleep(time.Millisecond * 10)
17741773
}
17751774

@@ -1785,8 +1784,8 @@ func TestOpcacheReset(t *testing.T) {
17851784
return
17861785
}
17871786

1788-
// otherwise call sleep.php with random sleep and work values
1789-
sleep := i % 100
1787+
// otherwise call sleep.php with sleep and work values
1788+
sleep := i % 50
17901789
work := i % 100
17911790
tester.AssertGetResponse(
17921791
fmt.Sprintf("http://localhost:%s/sleep.php?sleep=%d&work=%d", testPort, sleep, work),

frankenphp.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,11 +239,16 @@ static void frankenphp_update_request_context() {
239239
#if PHP_VERSION_ID < 80300
240240
zend_function *func = zend_hash_str_find_ptr(
241241
CG(function_table), "opcache_reset", sizeof("opcache_reset") - 1);
242-
if (func != NULL && func->type == ZEND_INTERNAL_FUNCTION) {
242+
if (func != NULL && func->type == ZEND_INTERNAL_FUNCTION &&
243+
((zend_internal_function *)func)->handler !=
244+
ZEND_FN(frankenphp_opcache_reset)) {
243245
pthread_mutex_lock(&opcache_reset_mutex_php_82);
244-
orig_opcache_reset = ((zend_internal_function *)func)->handler;
245-
((zend_internal_function *)func)->handler =
246-
ZEND_FN(frankenphp_opcache_reset);
246+
if (((zend_internal_function *)func)->handler !=
247+
ZEND_FN(frankenphp_opcache_reset)) {
248+
orig_opcache_reset = ((zend_internal_function *)func)->handler;
249+
((zend_internal_function *)func)->handler =
250+
ZEND_FN(frankenphp_opcache_reset);
251+
}
247252
pthread_mutex_unlock(&opcache_reset_mutex_php_82);
248253
}
249254
#endif

frankenphp.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -765,21 +765,29 @@ func go_is_context_done(threadIndex C.uintptr_t) C.bool {
765765
func go_schedule_opcache_reset(threadIndex C.uintptr_t) {
766766
if threadsAreRestarting.CompareAndSwap(false, true) {
767767
go func() {
768+
defer threadsAreRestarting.Store(false)
768769
restartThreadsAndOpcacheReset(true)
769-
threadsAreRestarting.Store(false)
770770
}()
771771
}
772772
}
773773

774-
// restart all threads for a safe opcache_reset
774+
// opcacheResetOnce ensures only one thread per restart generation calls
775+
// the actual opcache_reset; concurrent calls into opcache can corrupt SHM
776+
var opcacheResetOnce atomic.Pointer[sync.Once]
777+
778+
func init() {
779+
opcacheResetOnce.Store(&sync.Once{})
780+
}
781+
782+
// restart all threads for an opcache_reset
775783
func restartThreadsAndOpcacheReset(withRegularThreads bool) {
776784
// disallow scaling threads while restarting workers
777785
scalingMu.Lock()
778786
defer scalingMu.Unlock()
779787

780788
threadsToRestart := drainThreads(withRegularThreads)
781789

782-
opcacheResetOnce = sync.Once{}
790+
opcacheResetOnce.Store(&sync.Once{})
783791
opcacheResetWg := sync.WaitGroup{}
784792
for _, thread := range threadsToRestart {
785793
thread.state.Set(state.OpcacheResetting)
@@ -877,7 +885,7 @@ func drainThreads(withRegularThreads bool) []*phpThread {
877885
}
878886

879887
func scheduleOpcacheReset(thread *phpThread) {
880-
opcacheResetOnce.Do(func() {
888+
opcacheResetOnce.Load().Do(func() {
881889
C.frankenphp_reset_opcache()
882890
})
883891
}

0 commit comments

Comments
 (0)