@@ -764,15 +764,19 @@ func go_is_context_done(threadIndex C.uintptr_t) C.bool {
764764func go_schedule_opcache_reset (threadIndex C.uintptr_t ) {
765765 if threadsAreRestarting .CompareAndSwap (false , true ) {
766766 go func () {
767+ defer threadsAreRestarting .Store (false )
767768 restartThreadsAndOpcacheReset (true )
768- threadsAreRestarting .Store (false )
769769 }()
770770 }
771771}
772772
773- // opcacheResetOnce ensures only one thread calls the actual opcache_reset.
774- // Multiple threads calling it concurrently can race on shared memory.
775- var opcacheResetOnce sync.Once
773+ // opcacheResetOnce ensures only one thread per restart generation calls
774+ // the actual opcache_reset; concurrent calls into opcache can corrupt SHM
775+ var opcacheResetOnce atomic.Pointer [sync.Once ]
776+
777+ func init () {
778+ opcacheResetOnce .Store (& sync.Once {})
779+ }
776780
777781// restart all threads for an opcache_reset
778782func restartThreadsAndOpcacheReset (withRegularThreads bool ) {
@@ -782,7 +786,7 @@ func restartThreadsAndOpcacheReset(withRegularThreads bool) {
782786
783787 threadsToRestart := drainThreads (withRegularThreads )
784788
785- opcacheResetOnce = sync.Once {}
789+ opcacheResetOnce . Store ( & sync.Once {})
786790 opcacheResetWg := sync.WaitGroup {}
787791 for _ , thread := range threadsToRestart {
788792 thread .state .Set (state .OpcacheResetting )
@@ -880,7 +884,7 @@ func drainThreads(withRegularThreads bool) []*phpThread {
880884}
881885
882886func scheduleOpcacheReset (thread * phpThread ) {
883- opcacheResetOnce .Do (func () {
887+ opcacheResetOnce .Load (). Do (func () {
884888 C .frankenphp_reset_opcache ()
885889 })
886890}
0 commit comments