phpthread.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. package frankenphp
  2. // #include "frankenphp.h"
  3. import "C"
  4. import (
  5. "net/http"
  6. "runtime"
  7. "sync"
  8. "unsafe"
  9. )
  10. // representation of the actual underlying PHP thread
  11. // identified by the index in the phpThreads slice
  12. type phpThread struct {
  13. runtime.Pinner
  14. threadIndex int
  15. requestChan chan *http.Request
  16. drainChan chan struct{}
  17. handlerMu *sync.Mutex
  18. handler threadHandler
  19. state *threadState
  20. }
  21. // interface that defines how the callbacks from the C thread should be handled
  22. type threadHandler interface {
  23. beforeScriptExecution() string
  24. afterScriptExecution(exitStatus int)
  25. getActiveRequest() *http.Request
  26. }
  27. func newPHPThread(threadIndex int) *phpThread {
  28. return &phpThread{
  29. threadIndex: threadIndex,
  30. drainChan: make(chan struct{}),
  31. requestChan: make(chan *http.Request),
  32. handlerMu: &sync.Mutex{},
  33. state: newThreadState(),
  34. }
  35. }
  36. // change the thread handler safely
  37. // must be called from outside the PHP thread
  38. func (thread *phpThread) setHandler(handler threadHandler) {
  39. logger.Debug("setHandler")
  40. thread.handlerMu.Lock()
  41. defer thread.handlerMu.Unlock()
  42. if !thread.state.requestSafeStateChange(stateTransitionRequested) {
  43. // no state change allowed == shutdown
  44. return
  45. }
  46. close(thread.drainChan)
  47. thread.state.waitFor(stateTransitionInProgress)
  48. thread.handler = handler
  49. thread.drainChan = make(chan struct{})
  50. thread.state.set(stateTransitionComplete)
  51. }
  52. // transition to a new handler safely
  53. // is triggered by setHandler and executed on the PHP thread
  54. func (thread *phpThread) transitionToNewHandler() string {
  55. thread.state.set(stateTransitionInProgress)
  56. thread.state.waitFor(stateTransitionComplete)
  57. // execute beforeScriptExecution of the new handler
  58. return thread.handler.beforeScriptExecution()
  59. }
  60. func (thread *phpThread) getActiveRequest() *http.Request {
  61. return thread.handler.getActiveRequest()
  62. }
  63. // Pin a string that is not null-terminated
  64. // PHP's zend_string may contain null-bytes
  65. func (thread *phpThread) pinString(s string) *C.char {
  66. sData := unsafe.StringData(s)
  67. if sData == nil {
  68. return nil
  69. }
  70. thread.Pin(sData)
  71. return (*C.char)(unsafe.Pointer(sData))
  72. }
  73. // C strings must be null-terminated
  74. func (thread *phpThread) pinCString(s string) *C.char {
  75. return thread.pinString(s + "\x00")
  76. }
  77. //export go_frankenphp_before_script_execution
  78. func go_frankenphp_before_script_execution(threadIndex C.uintptr_t) *C.char {
  79. thread := phpThreads[threadIndex]
  80. scriptName := thread.handler.beforeScriptExecution()
  81. // if no scriptName is passed, shut down
  82. if scriptName == "" {
  83. return nil
  84. }
  85. // return the name of the PHP script that should be executed
  86. return thread.pinCString(scriptName)
  87. }
  88. //export go_frankenphp_after_script_execution
  89. func go_frankenphp_after_script_execution(threadIndex C.uintptr_t, exitStatus C.int) {
  90. thread := phpThreads[threadIndex]
  91. if exitStatus < 0 {
  92. panic(ScriptExecutionError)
  93. }
  94. thread.handler.afterScriptExecution(int(exitStatus))
  95. // unpin all memory used during script execution
  96. thread.Unpin()
  97. }
  98. //export go_frankenphp_on_thread_shutdown
  99. func go_frankenphp_on_thread_shutdown(threadIndex C.uintptr_t) {
  100. thread := phpThreads[threadIndex]
  101. thread.Unpin()
  102. thread.state.set(stateDone)
  103. }