php_threads.go 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. package frankenphp
  2. // #include "frankenphp.h"
  3. import "C"
  4. import (
  5. "fmt"
  6. "sync"
  7. )
  8. var (
  9. phpThreads []*phpThread
  10. done chan struct{}
  11. mainThreadState *threadStateHandler
  12. )
  13. // reserve a fixed number of PHP threads on the go side
  14. func initPHPThreads(numThreads int) error {
  15. done = make(chan struct{})
  16. phpThreads = make([]*phpThread, numThreads)
  17. for i := 0; i < numThreads; i++ {
  18. phpThreads[i] = &phpThread{
  19. threadIndex: i,
  20. state: &threadStateHandler{currentState: stateBooting},
  21. }
  22. }
  23. if err := startMainThread(numThreads); err != nil {
  24. return err
  25. }
  26. // initialize all threads as inactive
  27. ready := sync.WaitGroup{}
  28. ready.Add(len(phpThreads))
  29. for _, thread := range phpThreads {
  30. go func() {
  31. if !C.frankenphp_new_php_thread(C.uintptr_t(thread.threadIndex)) {
  32. panic(fmt.Sprintf("unable to create thread %d", thread.threadIndex))
  33. }
  34. thread.state.waitFor(stateInactive)
  35. ready.Done()
  36. }()
  37. }
  38. ready.Wait()
  39. return nil
  40. }
  41. func drainPHPThreads() {
  42. doneWG := sync.WaitGroup{}
  43. doneWG.Add(len(phpThreads))
  44. for _, thread := range phpThreads {
  45. thread.state.set(stateShuttingDown)
  46. }
  47. close(done)
  48. for _, thread := range phpThreads {
  49. go func(thread *phpThread) {
  50. thread.state.waitFor(stateDone)
  51. doneWG.Done()
  52. }(thread)
  53. }
  54. doneWG.Wait()
  55. mainThreadState.set(stateShuttingDown)
  56. mainThreadState.waitFor(stateDone)
  57. phpThreads = nil
  58. }
  59. func startMainThread(numThreads int) error {
  60. mainThreadState = &threadStateHandler{currentState: stateBooting}
  61. if C.frankenphp_new_main_thread(C.int(numThreads)) != 0 {
  62. return MainThreadCreationError
  63. }
  64. mainThreadState.waitFor(stateActive)
  65. return nil
  66. }
  67. func getInactivePHPThread() *phpThread {
  68. for _, thread := range phpThreads {
  69. if thread.state.is(stateInactive) {
  70. return thread
  71. }
  72. }
  73. panic("not enough threads reserved")
  74. }
  75. //export go_frankenphp_main_thread_is_ready
  76. func go_frankenphp_main_thread_is_ready() {
  77. mainThreadState.set(stateActive)
  78. mainThreadState.waitFor(stateShuttingDown)
  79. }
  80. //export go_frankenphp_shutdown_main_thread
  81. func go_frankenphp_shutdown_main_thread() {
  82. mainThreadState.set(stateDone)
  83. }