phpthread.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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 of the PHP thread
  39. func (thread *phpThread) setHandler(handler threadHandler) {
  40. thread.handlerMu.Lock()
  41. defer thread.handlerMu.Unlock()
  42. if thread.state.is(stateShuttingDown) {
  43. return
  44. }
  45. thread.state.set(stateTransitionRequested)
  46. close(thread.drainChan)
  47. thread.state.waitFor(stateTransitionInProgress, stateShuttingDown)
  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, stateShuttingDown)
  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. thread.Pin(sData)
  68. return (*C.char)(unsafe.Pointer(sData))
  69. }
  70. // C strings must be null-terminated
  71. func (thread *phpThread) pinCString(s string) *C.char {
  72. return thread.pinString(s + "\x00")
  73. }
  74. //export go_frankenphp_before_script_execution
  75. func go_frankenphp_before_script_execution(threadIndex C.uintptr_t) *C.char {
  76. thread := phpThreads[threadIndex]
  77. scriptName := thread.handler.beforeScriptExecution()
  78. // if no scriptName is passed, shut down
  79. if scriptName == "" {
  80. return nil
  81. }
  82. // return the name of the PHP script that should be executed
  83. return thread.pinCString(scriptName)
  84. }
  85. //export go_frankenphp_after_script_execution
  86. func go_frankenphp_after_script_execution(threadIndex C.uintptr_t, exitStatus C.int) {
  87. thread := phpThreads[threadIndex]
  88. if exitStatus < 0 {
  89. panic(ScriptExecutionError)
  90. }
  91. thread.handler.afterScriptExecution(int(exitStatus))
  92. // unpin all memory used during script execution
  93. thread.Unpin()
  94. }
  95. //export go_frankenphp_on_thread_shutdown
  96. func go_frankenphp_on_thread_shutdown(threadIndex C.uintptr_t) {
  97. phpThreads[threadIndex].Unpin()
  98. phpThreads[threadIndex].state.set(stateDone)
  99. }