Conversation
# Conflicts: # cgi.go
# Conflicts: # cgi.go # frankenphp.c # phpmainthread.go
# Conflicts: # frankenphp.c # phpmainthread.go
There was a problem hiding this comment.
Pull request overview
Refactors how CGI/$_SERVER string keys are managed by moving hard-coded zend_string keys into a C-side interned-strings struct, reducing Go-side map lookups/allocations and enabling more efficient $_SERVER sizing during registration.
Changes:
- Introduces C-side interned string registry (
frankenphp_strings) and afrankenphp_server_varsstruct to bulk-register known$_SERVERvariables with pre-sized hashtable capacity. - Simplifies Go-side CGI/server-variable registration to rely on C-side known keys, keeping only the common-header cache map in Go.
- Minor header handling refactor in Go to pass
sapi_header_structdirectly.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
phpmainthread.go |
Moves common header zend_string cache to a package-global map and initializes it once. |
frankenphp.h |
Defines frankenphp_server_vars and frankenphp_strings interned-string registry; updates exported APIs. |
frankenphp.go |
Adjusts header parsing helper to accept sapi_header_struct. |
frankenphp.c |
Implements interned-string initialization, server var bulk registration, and updates request variable registration flow. |
env.go |
Captures OS env count for $_SERVER pre-sizing; switches to newPersistentZendString. |
cgi.go |
Reworks known CGI var registration to use C-side interned keys/values; adds tlsProtocol and helper wrappers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return (*C.char)(unsafe.Pointer(unsafe.StringData(s))) | ||
| } | ||
|
|
||
| // initialize a global zend_string that must never be freed and is ignored by CG |
There was a problem hiding this comment.
Typo in comment: ignored by CG should be ignored by GC.
| // initialize a global zend_string that must never be freed and is ignored by CG | |
| // initialize a global zend_string that must never be freed and is ignored by GC |
| // Use a go string on the C side without allocations (C may not modify the string) | ||
| // Best case scenario: The string is implicitly pinned | ||
| // eg: C.call_funcion(toUnsafeChar(string)) <- go will not GC the string in this case |
There was a problem hiding this comment.
The toUnsafeChar comment suggests the string is "implicitly pinned" / "go will not GC the string". Go doesn't guarantee pinning semantics here; the important constraint is that C must not retain this pointer after the cgo call, and must not mutate the bytes. Consider rewording the comment to reflect the cgo pointer rules to avoid future misuse of toUnsafeChar.
| // Use a go string on the C side without allocations (C may not modify the string) | |
| // Best case scenario: The string is implicitly pinned | |
| // eg: C.call_funcion(toUnsafeChar(string)) <- go will not GC the string in this case | |
| // toUnsafeChar returns a *C.char pointing at the backing bytes of s. | |
| // The returned pointer is only valid for the duration of the cgo call in which | |
| // it is passed to C; C code must not store it for later use and must treat the | |
| // memory as read-only (it must not modify the bytes). |
|
|
||
| // Use a go string on the C side without allocations (C may not modify the string) | ||
| // Best case scenario: The string is implicitly pinned | ||
| // eg: C.call_funcion(toUnsafeChar(string)) <- go will not GC the string in this case |
There was a problem hiding this comment.
Typo in comment: C.call_funcion should be C.call_function (or remove the pseudo-code example if it's not intended to be accurate).
| // eg: C.call_funcion(toUnsafeChar(string)) <- go will not GC the string in this case | |
| // eg: C.call_function(toUnsafeChar(string)) <- go will not GC the string in this case |
| // Use a go string on the C side without allocations (C may not modify the string) | ||
| // Best case scenario: The string is implicitly pinned | ||
| // eg: C.call_funcion(toUnsafeChar(string)) <- go will not GC the string in this case |
There was a problem hiding this comment.
| // Use a go string on the C side without allocations (C may not modify the string) | |
| // Best case scenario: The string is implicitly pinned | |
| // eg: C.call_funcion(toUnsafeChar(string)) <- go will not GC the string in this case | |
| // Use a Go string on the C side without allocations (C may not modify the string) | |
| // Best case scenario: The string is implicitly pinned | |
| // eg: C.call_function(toUnsafeChar(string)) <- go will not GC the string in this case |
This PR does a bit of cleanup with how Cgi strings are registered by moving the
zend_stringkeys to a struct on the C side. This also has some performance benefits, mainly by not having to do map access and correctly sizing the$_SERVERarray.Server registration goes from around 5.5% of request time to around 3.5%.
Draft as waiting for #2058 first.