| 78 | } |
| 79 | |
| 80 | func drainPHPThreads() { |
| 81 | if mainThread == nil { |
| 82 | return // mainThread was never initialized |
| 83 | } |
| 84 | // Idempotent: post-drain state is Reserved; a re-entry (e.g. a |
| 85 | // failed-Init cleanup) must not double-close mainThread.done. |
| 86 | if mainThread.state.Is(state.Reserved) { |
| 87 | return |
| 88 | } |
| 89 | doneWG := sync.WaitGroup{} |
| 90 | doneWG.Add(len(phpThreads)) |
| 91 | mainThread.state.Set(state.ShuttingDown) |
| 92 | close(mainThread.done) |
| 93 | for _, thread := range phpThreads { |
| 94 | // shut down all reserved threads |
| 95 | if thread.state.CompareAndSwap(state.Reserved, state.Done) { |
| 96 | doneWG.Done() |
| 97 | continue |
| 98 | } |
| 99 | // shut down all active threads |
| 100 | go func(thread *phpThread) { |
| 101 | thread.shutdown() |
| 102 | doneWG.Done() |
| 103 | }(thread) |
| 104 | } |
| 105 | |
| 106 | doneWG.Wait() |
| 107 | mainThread.state.Set(state.Done) |
| 108 | mainThread.state.WaitFor(state.Reserved) |
| 109 | C.frankenphp_destroy_thread_metrics() |
| 110 | phpThreads = nil |
| 111 | } |
| 112 | |
| 113 | func (mainThread *phpMainThread) start() error { |
| 114 | if C.frankenphp_new_main_thread(C.int(mainThread.numThreads)) != 0 { |