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