threadregular.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. package frankenphp
  2. import (
  3. "net/http"
  4. "sync"
  5. )
  6. // representation of a non-worker PHP thread
  7. // executes PHP scripts in a web context
  8. // implements the threadHandler interface
  9. type regularThread struct {
  10. state *threadState
  11. thread *phpThread
  12. activeRequest *http.Request
  13. }
  14. var (
  15. regularThreads []*phpThread
  16. regularThreadMu = &sync.RWMutex{}
  17. regularRequestChan chan *http.Request
  18. )
  19. func convertToRegularThread(thread *phpThread) {
  20. thread.setHandler(&regularThread{
  21. thread: thread,
  22. state: thread.state,
  23. })
  24. attachRegularThread(thread)
  25. }
  26. // beforeScriptExecution returns the name of the script or an empty string on shutdown
  27. func (handler *regularThread) beforeScriptExecution() string {
  28. switch handler.state.get() {
  29. case stateTransitionRequested:
  30. detachRegularThread(handler.thread)
  31. return handler.thread.transitionToNewHandler()
  32. case stateTransitionComplete:
  33. handler.state.set(stateReady)
  34. return handler.waitForRequest()
  35. case stateReady:
  36. return handler.waitForRequest()
  37. case stateShuttingDown:
  38. detachRegularThread(handler.thread)
  39. // signal to stop
  40. return ""
  41. }
  42. panic("unexpected state: " + handler.state.name())
  43. }
  44. // return true if the worker should continue to run
  45. func (handler *regularThread) afterScriptExecution(exitStatus int) {
  46. handler.afterRequest(exitStatus)
  47. }
  48. func (handler *regularThread) getActiveRequest() *http.Request {
  49. return handler.activeRequest
  50. }
  51. func (handler *regularThread) name() string {
  52. return "Regular PHP Thread"
  53. }
  54. func (handler *regularThread) waitForRequest() string {
  55. // clear any previously sandboxed env
  56. clearSandboxedEnv(handler.thread)
  57. handler.state.markAsWaiting(true)
  58. var r *http.Request
  59. select {
  60. case <-handler.thread.drainChan:
  61. // go back to beforeScriptExecution
  62. return handler.beforeScriptExecution()
  63. case r = <-regularRequestChan:
  64. }
  65. handler.activeRequest = r
  66. handler.state.markAsWaiting(false)
  67. fc := r.Context().Value(contextKey).(*FrankenPHPContext)
  68. if err := updateServerContext(handler.thread, r, true, false); err != nil {
  69. rejectRequest(fc.responseWriter, err.Error())
  70. handler.afterRequest(0)
  71. handler.thread.Unpin()
  72. // go back to beforeScriptExecution
  73. return handler.beforeScriptExecution()
  74. }
  75. // set the scriptFilename that should be executed
  76. return fc.scriptFilename
  77. }
  78. func (handler *regularThread) afterRequest(exitStatus int) {
  79. fc := handler.activeRequest.Context().Value(contextKey).(*FrankenPHPContext)
  80. fc.exitStatus = exitStatus
  81. maybeCloseContext(fc)
  82. handler.activeRequest = nil
  83. }
  84. func handleRequestWithRegularPHPThreads(r *http.Request, fc *FrankenPHPContext) {
  85. metrics.StartRequest()
  86. select {
  87. case regularRequestChan <- r:
  88. // a thread was available to handle the request immediately
  89. <-fc.done
  90. metrics.StopRequest()
  91. return
  92. default:
  93. // no thread was available
  94. }
  95. // if no thread was available, mark the request as queued and fan it out to all threads
  96. metrics.QueuedRequest()
  97. for {
  98. select {
  99. case regularRequestChan <- r:
  100. metrics.DequeuedRequest()
  101. <-fc.done
  102. metrics.StopRequest()
  103. return
  104. case scaleChan <- fc:
  105. // the request has triggered scaling, continue to wait for a thread
  106. }
  107. }
  108. }
  109. func attachRegularThread(thread *phpThread) {
  110. regularThreadMu.Lock()
  111. regularThreads = append(regularThreads, thread)
  112. regularThreadMu.Unlock()
  113. }
  114. func detachRegularThread(thread *phpThread) {
  115. regularThreadMu.Lock()
  116. for i, t := range regularThreads {
  117. if t == thread {
  118. regularThreads = append(regularThreads[:i], regularThreads[i+1:]...)
  119. break
  120. }
  121. }
  122. regularThreadMu.Unlock()
  123. }