@@ -33,12 +33,17 @@ var backgroundLookups map[string]*backgroundWorkerLookup
3333type backgroundWorkerLookup struct {
3434 byName map [string ]* backgroundWorkerRegistry
3535 catchAll * backgroundWorkerRegistry
36+ httpVars backgroundWorkerState // shared vars bucket for HTTP workers in this scope
3637}
3738
3839func newBackgroundWorkerLookup () * backgroundWorkerLookup {
39- return & backgroundWorkerLookup {
40+ l := & backgroundWorkerLookup {
4041 byName : make (map [string ]* backgroundWorkerRegistry ),
4142 }
43+ // httpVars is always "ready" — get_worker_vars(null) never blocks
44+ l .httpVars .ready = make (chan struct {})
45+ close (l .httpVars .ready )
46+ return l
4247}
4348
4449func (l * backgroundWorkerLookup ) AddNamed (name string , registry * backgroundWorkerRegistry ) {
@@ -244,6 +249,20 @@ func startBackgroundWorkerWithRegistry(registry *backgroundWorkerRegistry, bgWor
244249 return nil
245250}
246251
252+ var backgroundLookupsMu sync.Mutex
253+
254+ func getOrCreateLookup (scope string ) * backgroundWorkerLookup {
255+ if backgroundLookups == nil {
256+ backgroundLookups = make (map [string ]* backgroundWorkerLookup )
257+ }
258+ if l , ok := backgroundLookups [scope ]; ok {
259+ return l
260+ }
261+ l := newBackgroundWorkerLookup ()
262+ backgroundLookups [scope ] = l
263+ return l
264+ }
265+
247266func getLookup (thread * phpThread ) * backgroundWorkerLookup {
248267 if handler , ok := thread .handler .(* workerThread ); ok && handler .worker .backgroundLookup != nil {
249268 return handler .worker .backgroundLookup
@@ -253,16 +272,16 @@ func getLookup(thread *phpThread) *backgroundWorkerLookup {
253272 }
254273 // Non-worker requests: resolve scope from context
255274 if fc , ok := fromContext (thread .context ()); ok && fc .backgroundScope != "" {
256- if backgroundLookups != nil {
257- return backgroundLookups [fc .backgroundScope ]
258- }
275+ backgroundLookupsMu .Lock ()
276+ l := getOrCreateLookup (fc .backgroundScope )
277+ backgroundLookupsMu .Unlock ()
278+ return l
259279 }
260280 // Fall back to global scope
261- if backgroundLookups != nil {
262- return backgroundLookups ["" ]
263- }
264-
265- return nil
281+ backgroundLookupsMu .Lock ()
282+ l := getOrCreateLookup ("" )
283+ backgroundLookupsMu .Unlock ()
284+ return l
266285}
267286
268287// go_frankenphp_get_vars starts background workers if needed, waits for them
@@ -277,7 +296,7 @@ func go_frankenphp_get_vars(threadIndex C.uintptr_t, names **C.char, nameLens *C
277296 thread := phpThreads [threadIndex ]
278297 lookup := getLookup (thread )
279298 if lookup == nil {
280- return C .CString ("no background worker configured in this php_server" )
299+ return C .CString ("no worker configured in this php_server" )
281300 }
282301
283302 n := int (nameCount )
@@ -289,6 +308,12 @@ func go_frankenphp_get_vars(threadIndex C.uintptr_t, names **C.char, nameLens *C
289308 for i := 0 ; i < n ; i ++ {
290309 goNames [i ] = C .GoStringN (nameSlice [i ], C .int (nameLenSlice [i ]))
291310
311+ if goNames [i ] == "" {
312+ // Empty string = HTTP workers' shared scope (ready channel is pre-closed)
313+ sks [i ] = & lookup .httpVars
314+ continue
315+ }
316+
292317 // Start background worker if not already running
293318 if err := startBackgroundWorker (thread , goNames [i ]); err != nil {
294319 return C .CString (err .Error ())
@@ -363,23 +388,36 @@ func go_frankenphp_get_vars(threadIndex C.uintptr_t, names **C.char, nameLens *C
363388func go_frankenphp_set_vars (threadIndex C.uintptr_t , varsPtr unsafe.Pointer , oldPtr * unsafe.Pointer ) * C.char {
364389 thread := phpThreads [threadIndex ]
365390
366- bgHandler , ok := thread .handler .(* backgroundWorkerThread )
367- if ! ok || bgHandler .worker .backgroundWorker == nil {
368- return C .CString ("frankenphp_set_vars() can only be called from a background worker" )
369- }
391+ // Background worker: write to named bucket
392+ if bgHandler , ok := thread .handler .(* backgroundWorkerThread ); ok && bgHandler .worker .backgroundWorker != nil {
393+ sk := bgHandler .worker .backgroundWorker
370394
371- sk := bgHandler .worker .backgroundWorker
395+ sk .mu .Lock ()
396+ * oldPtr = sk .varsPtr
397+ sk .varsPtr = varsPtr
398+ sk .varsVersion .Add (1 )
399+ sk .mu .Unlock ()
372400
373- sk .mu .Lock ()
374- * oldPtr = sk .varsPtr
375- sk .varsPtr = varsPtr
376- sk .varsVersion .Add (1 )
377- sk .mu .Unlock ()
401+ sk .readyOnce .Do (func () {
402+ bgHandler .markBackgroundReady ()
403+ close (sk .ready )
404+ })
378405
379- sk .readyOnce .Do (func () {
380- bgHandler .markBackgroundReady ()
381- close (sk .ready )
382- })
406+ return nil
407+ }
408+
409+ // HTTP worker: write to scope's shared httpVars bucket
410+ lookup := getLookup (thread )
411+ if lookup == nil {
412+ return C .CString ("frankenphp_set_vars() requires a configured php_server block" )
413+ }
414+
415+ hv := & lookup .httpVars
416+ hv .mu .Lock ()
417+ * oldPtr = hv .varsPtr
418+ hv .varsPtr = varsPtr
419+ hv .varsVersion .Add (1 )
420+ hv .mu .Unlock ()
383421
384422 return nil
385423}
0 commit comments