phpmainthread.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package frankenphp
  2. // #include "frankenphp.h"
  3. import "C"
  4. import (
  5. "fmt"
  6. "sync"
  7. )
  8. // represents the main PHP thread
  9. // the thread needs to keep running as long as all other threads are running
  10. type phpMainThread struct {
  11. state *threadState
  12. done chan struct{}
  13. numThreads int
  14. }
  15. var (
  16. phpThreads []*phpThread
  17. mainThread *phpMainThread
  18. )
  19. // start the main PHP thread
  20. // start a fixed number of inactive PHP threads
  21. // reserve a fixed number of possible PHP threads
  22. func initPHPThreads(numThreads int, numMaxThreads int) error {
  23. mainThread = &phpMainThread{
  24. state: newThreadState(),
  25. done: make(chan struct{}),
  26. numThreads: numThreads,
  27. }
  28. phpThreads = make([]*phpThread, numMaxThreads)
  29. if err := mainThread.start(); err != nil {
  30. return err
  31. }
  32. // initialize all threads as inactive
  33. for i := 0; i < numMaxThreads; i++ {
  34. phpThreads[i] = newPHPThread(i)
  35. }
  36. // start the underlying C threads
  37. ready := sync.WaitGroup{}
  38. ready.Add(numThreads)
  39. for i := 0; i < numThreads; i++ {
  40. thread := phpThreads[i]
  41. go func() {
  42. thread.boot()
  43. ready.Done()
  44. }()
  45. }
  46. ready.Wait()
  47. return nil
  48. }
  49. func ThreadDebugStatus() string {
  50. statusMessage := ""
  51. reservedThreadCount := 0
  52. for _, thread := range phpThreads {
  53. if thread.state.is(stateReserved) {
  54. reservedThreadCount++
  55. continue
  56. }
  57. statusMessage += thread.debugStatus() + "\n"
  58. }
  59. statusMessage += fmt.Sprintf("%d additional threads can be started at runtime\n", reservedThreadCount)
  60. return statusMessage
  61. }
  62. func drainPHPThreads() {
  63. doneWG := sync.WaitGroup{}
  64. doneWG.Add(len(phpThreads))
  65. close(mainThread.done)
  66. for _, thread := range phpThreads {
  67. go func(thread *phpThread) {
  68. thread.shutdown()
  69. doneWG.Done()
  70. }(thread)
  71. }
  72. doneWG.Wait()
  73. mainThread.state.set(stateShuttingDown)
  74. mainThread.state.waitFor(stateDone)
  75. phpThreads = nil
  76. }
  77. func (mainThread *phpMainThread) start() error {
  78. if C.frankenphp_new_main_thread(C.int(mainThread.numThreads)) != 0 {
  79. return MainThreadCreationError
  80. }
  81. mainThread.state.waitFor(stateReady)
  82. return nil
  83. }
  84. func getInactivePHPThread() *phpThread {
  85. thread := getPHPThreadAtState(stateInactive)
  86. if thread != nil {
  87. return thread
  88. }
  89. thread = getPHPThreadAtState(stateReserved)
  90. if thread == nil {
  91. return nil
  92. }
  93. thread.boot()
  94. return thread
  95. }
  96. func getPHPThreadAtState(state stateID) *phpThread {
  97. for _, thread := range phpThreads {
  98. if thread.state.is(state) {
  99. return thread
  100. }
  101. }
  102. return nil
  103. }
  104. //export go_frankenphp_main_thread_is_ready
  105. func go_frankenphp_main_thread_is_ready() {
  106. mainThread.state.set(stateReady)
  107. mainThread.state.waitFor(stateShuttingDown)
  108. }
  109. //export go_frankenphp_shutdown_main_thread
  110. func go_frankenphp_shutdown_main_thread() {
  111. mainThread.state.set(stateDone)
  112. }