Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion context.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func newContextFromRequest(request *http.Request, responseWriter http.ResponseWr
startedAt: time.Now(),
server: s,
splitPath: s.splitPath,
logger: s.logger,
logger: s.logger.Load(),
request: request,
documentRoot: s.root,
responseWriter: responseWriter,
Expand Down
9 changes: 4 additions & 5 deletions frankenphp.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ var (
contextKey = contextKeyStruct{}
serverHeader = []string{"FrankenPHP"}

isRunning bool
isRunning atomic.Bool
onServerShutdown []func()

// Set default values to make Shutdown() idempotent
Expand Down Expand Up @@ -240,10 +240,9 @@ func calculateMaxThreads(opt *opt) (numWorkers int, _ error) {

// Init starts the PHP runtime and the configured workers.
func Init(options ...Option) error {
if isRunning {
if !isRunning.CompareAndSwap(false, true) {
return ErrAlreadyStarted
}
isRunning = true

// Ignore all SIGPIPE signals to prevent weird issues with systemd: https://github.com/php/frankenphp/issues/1020
// Docker/Moby has a similar hack: https://github.com/moby/moby/blob/d828b032a87606ae34267e349bf7f7ccb1f6495a/cmd/dockerd/docker.go#L87-L90
Expand Down Expand Up @@ -367,7 +366,7 @@ func Init(options ...Option) error {

// Shutdown stops the workers and the PHP runtime.
func Shutdown() {
if !isRunning {
if !isRunning.Load() {
return
}

Expand All @@ -387,7 +386,7 @@ func Shutdown() {
_ = os.RemoveAll(EmbeddedAppPath)
}

isRunning = false
isRunning.Store(false)
if globalLogger.Enabled(globalCtx, slog.LevelDebug) {
globalLogger.LogAttrs(globalCtx, slog.LevelDebug, "FrankenPHP shut down")
}
Expand Down
33 changes: 20 additions & 13 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"log/slog"
"net/http"
"sync/atomic"

"github.com/dunglas/frankenphp/internal/fastabs"
)
Expand All @@ -15,12 +16,18 @@ type Server struct {
root string
splitPath []string
env PreparedEnv
logger *slog.Logger
workers []*worker
workersByPath map[string]*worker
workersWithRequestMatcher []*worker
workerOpts []workerOpt
isRegistered bool

// registered while FrankenPHP runs with this server; read by concurrent
// ServeHTTP calls while Init()/Shutdown() flip it, hence atomic
isRegistered atomic.Bool

// atomic for the same reason: the fallback server's logger is replaced
// at registration time while in-flight requests may read it
logger atomic.Pointer[slog.Logger]
}

var (
Expand All @@ -34,18 +41,18 @@ var (

func registerServers(newServers []*Server) {
servers = newServers
fallbackServer.isRegistered = true
fallbackServer.logger = globalLogger
fallbackServer.logger.Store(globalLogger)
fallbackServer.isRegistered.Store(true)
for i, s := range servers {
s.isRegistered = true
s.isRegistered.Store(true)
s.idx = i
}
}

func unregisterServers() {
fallbackServer.isRegistered = false
fallbackServer.isRegistered.Store(false)
for _, server := range servers {
server.isRegistered = false
server.isRegistered.Store(false)
}
}

Expand All @@ -63,11 +70,15 @@ func NewServer(root string, splitPath []string, env map[string]string, logger *s
root: root,
splitPath: splitPath,
env: PrepareEnv(env),
logger: logger,
workersByPath: make(map[string]*worker),
workerOpts: make([]workerOpt, 0),
}

if logger == nil {
logger = globalLogger
}
s.logger.Store(logger)

if len(s.splitPath) == 0 {
s.splitPath = []string{".php"}
}
Expand All @@ -76,10 +87,6 @@ func NewServer(root string, splitPath []string, env map[string]string, logger *s
s.env = PrepareEnv(nil)
}

if s.logger == nil {
s.logger = globalLogger
}

return s, nil
}

Expand All @@ -102,7 +109,7 @@ func (s *Server) addWorker(w *worker) error {
// The request will be scoped to the server instance that was registered via WithServer().
// Otherwise, it is equivalent to calling ServeHTTP.
func (s *Server) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request, opts ...RequestOption) error {
if !s.isRegistered {
if !s.isRegistered.Load() {
return ErrNotRunning
}

Expand Down