Skip to content

refactor: cgi strings#2188

Open
AlliBalliBaba wants to merge 35 commits intomainfrom
perf/cgi-strings
Open

refactor: cgi strings#2188
AlliBalliBaba wants to merge 35 commits intomainfrom
perf/cgi-strings

Conversation

@AlliBalliBaba
Copy link
Contributor

This PR does a bit of cleanup with how Cgi strings are registered by moving the zend_string keys 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 $_SERVER array.

Server registration goes from around 5.5% of request time to around 3.5%.

Draft as waiting for #2058 first.

@AlliBalliBaba AlliBalliBaba marked this pull request as ready for review March 1, 2026 22:05
@dunglas dunglas requested a review from Copilot March 2, 2026 17:10
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 a frankenphp_server_vars struct to bulk-register known $_SERVER variables 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_struct directly.

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
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in comment: ignored by CG should be ignored by GC.

Suggested change
// 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

Copilot uses AI. Check for mistakes.
Comment on lines +368 to +370
// 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
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
// 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).

Copilot uses AI. Check for mistakes.

// 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
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
// 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

Copilot uses AI. Check for mistakes.
Comment on lines +368 to +370
// 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants