shutdown the underlying PHP thread
()
| 98 | |
| 99 | // shutdown the underlying PHP thread |
| 100 | func (thread *phpThread) shutdown() { |
| 101 | if !thread.state.RequestSafeStateChange(state.ShuttingDown) { |
| 102 | // already shutting down or done, wait for the C thread to finish |
| 103 | thread.state.WaitFor(state.Done, state.Reserved) |
| 104 | |
| 105 | return |
| 106 | } |
| 107 | |
| 108 | close(thread.drainChan) |
| 109 | |
| 110 | // Arm force-kill after the grace period to wake any thread stuck in |
| 111 | // a blocking syscall (sleep, blocking I/O). The wait remains |
| 112 | // unbounded - on platforms where force-kill cannot interrupt the |
| 113 | // syscall (macOS, Windows non-alertable Sleep) the thread will exit |
| 114 | // when the syscall completes naturally; the operator's orchestrator |
| 115 | // is responsible for any harder timeout. |
| 116 | done := make(chan struct{}) |
| 117 | go func() { |
| 118 | thread.state.WaitFor(state.Done) |
| 119 | close(done) |
| 120 | }() |
| 121 | select { |
| 122 | case <-done: |
| 123 | case <-time.After(drainGracePeriod): |
| 124 | thread.forceKillMu.RLock() |
| 125 | C.frankenphp_force_kill_thread(thread.forceKill) |
| 126 | thread.forceKillMu.RUnlock() |
| 127 | <-done |
| 128 | } |
| 129 | |
| 130 | thread.drainChan = make(chan struct{}) |
| 131 | |
| 132 | // threads go back to the reserved state from which they can be booted again |
| 133 | if mainThread.state.Is(state.Ready) { |
| 134 | thread.state.Set(state.Reserved) |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | // setHandler changes the thread handler safely |
| 139 | // must be called from outside the PHP thread |