|
59 | 59 | contextKey = contextKeyStruct{} |
60 | 60 | serverHeader = []string{"FrankenPHP"} |
61 | 61 |
|
62 | | - isRunning bool |
63 | | - isOpcacheResetting atomic.Bool |
64 | | - threadsAreRestarting atomic.Bool |
65 | | - onServerShutdown []func() |
| 62 | + isRunning bool |
| 63 | + restartCounter atomic.Int32 |
| 64 | + onServerShutdown []func() |
66 | 65 |
|
67 | 66 | // Set default values to make Shutdown() idempotent |
68 | 67 | globalMu sync.Mutex |
@@ -759,68 +758,49 @@ func go_is_context_done(threadIndex C.uintptr_t) C.bool { |
759 | 758 | } |
760 | 759 |
|
761 | 760 | //export go_schedule_opcache_reset |
762 | | -func go_schedule_opcache_reset(threadIndex C.uintptr_t) C.bool { |
763 | | - if isOpcacheResetting.CompareAndSwap(false, true) { |
764 | | - restartThreadsForOpcacheReset(phpThreads[threadIndex]) |
765 | | - return C.bool(true) |
| 761 | +func go_schedule_opcache_reset(threadIndex C.uintptr_t) { |
| 762 | + if restartCounter.CompareAndSwap(0, 1) { |
| 763 | + go restartThreadsForOpcacheReset() |
766 | 764 | } |
767 | | - |
768 | | - // always call the original opcache_reset if already restarting |
769 | | - return C.bool(phpThreads[threadIndex].state.Is(state.Restarting)) |
770 | 765 | } |
771 | 766 |
|
772 | 767 | // restart all threads for an opcache_reset |
773 | | -func restartThreadsForOpcacheReset(callingThread *phpThread) { |
774 | | - if threadsAreRestarting.Load() { |
775 | | - // ignore reloads while a restart is already ongoing |
776 | | - return |
777 | | - } |
778 | | - |
| 768 | +func restartThreadsForOpcacheReset() { |
779 | 769 | // disallow scaling threads while restarting workers |
780 | 770 | scalingMu.Lock() |
781 | 771 | defer scalingMu.Unlock() |
782 | 772 |
|
783 | | - // drain workers |
784 | | - globalLogger.Info("Restarting all PHP threads for opcache_reset") |
785 | | - threadsToRestart := drainWorkerThreads() |
786 | | - |
787 | | - // drain regular threads |
788 | | - globalLogger.Info("Draining regular PHP threads for opcache_reset") |
789 | | - wg := sync.WaitGroup{} |
790 | | - for _, thread := range regularThreads { |
791 | | - if thread.state.Is(state.Ready) { |
792 | | - threadsToRestart = append(threadsToRestart, thread) |
793 | | - thread.state.Set(state.Restarting) |
794 | | - close(thread.drainChan) |
795 | | - |
796 | | - wg.Go(func() { |
797 | | - thread.state.WaitFor(state.Yielding) |
798 | | - }) |
799 | | - } |
| 773 | + threadsToRestart := drainWorkerThreads(true) |
| 774 | + |
| 775 | + for _, thread := range threadsToRestart { |
| 776 | + thread.drainChan = make(chan struct{}) |
| 777 | + thread.state.Set(state.Ready) |
800 | 778 | } |
| 779 | +} |
801 | 780 |
|
802 | | - wg.Done() // ignore the calling thread |
803 | | - done := make(chan struct{}) |
804 | | - go func() { |
805 | | - wg.Wait() |
806 | | - close(done) |
807 | | - }() |
| 781 | +func scheduleOpcacheReset(thread *phpThread) { |
| 782 | + restartCounter.Add(-1) |
| 783 | + if restartCounter.Load() != 1 { |
| 784 | + return // only the last restarting thread will trigger an actual opcache_reset |
| 785 | + } |
| 786 | + workerThread, ok := thread.handler.(*workerThread) |
| 787 | + fc, _ := newDummyContext("/opcache_reset") |
| 788 | + if ok && workerThread.worker != nil { |
| 789 | + workerThread.dummyFrankenPHPContext = fc |
| 790 | + defer func() { workerThread.dummyFrankenPHPContext = nil }() |
| 791 | + } |
808 | 792 |
|
809 | | - select { |
810 | | - case <-done: |
811 | | - // all other threads are drained |
812 | | - case <-time.After(time.Second): |
813 | | - // probably a deadlock, continue anyway and hope for the best |
| 793 | + regularThread, ok := thread.handler.(*regularThread) |
| 794 | + if ok { |
| 795 | + regularThread.contextHolder.frankenPHPContext = fc |
| 796 | + defer func() { regularThread.contextHolder.frankenPHPContext = nil }() |
814 | 797 | } |
815 | 798 |
|
816 | | - go func() { |
817 | | - callingThread.state.WaitFor(state.Yielding) |
818 | | - for _, thread := range threadsToRestart { |
819 | | - thread.drainChan = make(chan struct{}) |
820 | | - thread.state.Set(state.Ready) |
821 | | - isOpcacheResetting.Store(false) |
822 | | - } |
823 | | - }() |
| 799 | + globalLogger.Info("resetting opcache in all threads") |
| 800 | + C.frankenphp_reset_opcache() |
| 801 | + |
| 802 | + // all threads should have restarted now |
| 803 | + restartCounter.Store(0) |
824 | 804 | } |
825 | 805 |
|
826 | 806 | // ExecuteScriptCLI executes the PHP script passed as parameter. |
|
0 commit comments