@@ -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 ) {
@@ -250,6 +255,20 @@ func startBackgroundWorkerWithRegistry(registry *backgroundWorkerRegistry, bgWor
250255 return nil
251256}
252257
258+ var backgroundLookupsMu sync.Mutex
259+
260+ func getOrCreateLookup (scope string ) * backgroundWorkerLookup {
261+ if backgroundLookups == nil {
262+ backgroundLookups = make (map [string ]* backgroundWorkerLookup )
263+ }
264+ if l , ok := backgroundLookups [scope ]; ok {
265+ return l
266+ }
267+ l := newBackgroundWorkerLookup ()
268+ backgroundLookups [scope ] = l
269+ return l
270+ }
271+
253272func getLookup (thread * phpThread ) * backgroundWorkerLookup {
254273 if handler , ok := thread .handler .(* workerThread ); ok && handler .worker .backgroundLookup != nil {
255274 return handler .worker .backgroundLookup
@@ -259,16 +278,16 @@ func getLookup(thread *phpThread) *backgroundWorkerLookup {
259278 }
260279 // Non-worker requests: resolve scope from context
261280 if fc , ok := fromContext (thread .context ()); ok && fc .backgroundScope != "" {
262- if backgroundLookups != nil {
263- return backgroundLookups [fc .backgroundScope ]
264- }
281+ backgroundLookupsMu .Lock ()
282+ l := getOrCreateLookup (fc .backgroundScope )
283+ backgroundLookupsMu .Unlock ()
284+ return l
265285 }
266286 // Fall back to global scope
267- if backgroundLookups != nil {
268- return backgroundLookups ["" ]
269- }
270-
271- return nil
287+ backgroundLookupsMu .Lock ()
288+ l := getOrCreateLookup ("" )
289+ backgroundLookupsMu .Unlock ()
290+ return l
272291}
273292
274293// go_frankenphp_get_vars starts background workers if needed, waits for them
@@ -283,7 +302,7 @@ func go_frankenphp_get_vars(threadIndex C.uintptr_t, names **C.char, nameLens *C
283302 thread := phpThreads [threadIndex ]
284303 lookup := getLookup (thread )
285304 if lookup == nil {
286- return C .CString ("no background worker configured in this php_server" )
305+ return C .CString ("no worker configured in this php_server" )
287306 }
288307
289308 n := int (nameCount )
@@ -295,6 +314,12 @@ func go_frankenphp_get_vars(threadIndex C.uintptr_t, names **C.char, nameLens *C
295314 for i := 0 ; i < n ; i ++ {
296315 goNames [i ] = C .GoStringN (nameSlice [i ], C .int (nameLenSlice [i ]))
297316
317+ if goNames [i ] == "" {
318+ // Empty string = HTTP workers' shared scope (ready channel is pre-closed)
319+ sks [i ] = & lookup .httpVars
320+ continue
321+ }
322+
298323 // Start background worker if not already running
299324 if err := startBackgroundWorker (thread , goNames [i ]); err != nil {
300325 return C .CString (err .Error ())
@@ -369,23 +394,36 @@ func go_frankenphp_get_vars(threadIndex C.uintptr_t, names **C.char, nameLens *C
369394func go_frankenphp_set_vars (threadIndex C.uintptr_t , varsPtr unsafe.Pointer , oldPtr * unsafe.Pointer ) * C.char {
370395 thread := phpThreads [threadIndex ]
371396
372- bgHandler , ok := thread .handler .(* backgroundWorkerThread )
373- if ! ok || bgHandler .worker .backgroundWorker == nil {
374- return C .CString ("frankenphp_set_vars() can only be called from a background worker" )
375- }
397+ // Background worker: write to named bucket
398+ if bgHandler , ok := thread .handler .(* backgroundWorkerThread ); ok && bgHandler .worker .backgroundWorker != nil {
399+ sk := bgHandler .worker .backgroundWorker
376400
377- sk := bgHandler .worker .backgroundWorker
401+ sk .mu .Lock ()
402+ * oldPtr = sk .varsPtr
403+ sk .varsPtr = varsPtr
404+ sk .varsVersion .Add (1 )
405+ sk .mu .Unlock ()
378406
379- sk .mu .Lock ()
380- * oldPtr = sk .varsPtr
381- sk .varsPtr = varsPtr
382- sk .varsVersion .Add (1 )
383- sk .mu .Unlock ()
407+ sk .readyOnce .Do (func () {
408+ bgHandler .markBackgroundReady ()
409+ close (sk .ready )
410+ })
384411
385- sk .readyOnce .Do (func () {
386- bgHandler .markBackgroundReady ()
387- close (sk .ready )
388- })
412+ return nil
413+ }
414+
415+ // HTTP worker: write to scope's shared httpVars bucket
416+ lookup := getLookup (thread )
417+ if lookup == nil {
418+ return C .CString ("frankenphp_set_vars() requires a configured php_server block" )
419+ }
420+
421+ hv := & lookup .httpVars
422+ hv .mu .Lock ()
423+ * oldPtr = hv .varsPtr
424+ hv .varsPtr = varsPtr
425+ hv .varsVersion .Add (1 )
426+ hv .mu .Unlock ()
389427
390428 return nil
391429}
0 commit comments