Skip to content

Commit 1270784

Browse files
AlliBalliBabadunglas
authored andcommitted
suggestion: external worker api (#1928)
* Cleaner request apis.
1 parent 9b8d215 commit 1270784

File tree

7 files changed

+217
-202
lines changed

7 files changed

+217
-202
lines changed

frankenphp.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ var (
5252
ErrScriptExecution = errors.New("error during PHP script execution")
5353
ErrNotRunning = errors.New("FrankenPHP is not running. For proper configuration visit: https://frankenphp.dev/docs/config/#caddyfile-config")
5454

55-
isRunning bool
55+
isRunning bool
56+
onServerShutdown []func()
5657

5758
loggerMu sync.RWMutex
5859
logger *slog.Logger
@@ -216,7 +217,7 @@ func Init(options ...Option) error {
216217

217218
// add registered external workers
218219
for _, ew := range extensionWorkers {
219-
options = append(options, WithWorkers(ew.Name(), ew.FileName(), ew.MinThreads(), WithWorkerEnv(ew.Env())))
220+
options = append(options, WithWorkers(ew.name, ew.fileName, ew.num, ew.options...))
220221
}
221222

222223
opt := &opt{}
@@ -291,6 +292,17 @@ func Init(options ...Option) error {
291292
logger.LogAttrs(ctx, slog.LevelInfo, "embedded PHP app 📦", slog.String("path", EmbeddedAppPath))
292293
}
293294

295+
// register the startup/shutdown hooks (mainly useful for extensions)
296+
onServerShutdown = nil
297+
for _, w := range opt.workers {
298+
if w.onServerStartup != nil {
299+
w.onServerStartup()
300+
}
301+
if w.onServerShutdown != nil {
302+
onServerShutdown = append(onServerShutdown, w.onServerShutdown)
303+
}
304+
}
305+
294306
return nil
295307
}
296308

@@ -300,6 +312,11 @@ func Shutdown() {
300312
return
301313
}
302314

315+
// call the shutdown hooks (mainly useful for extensions)
316+
for _, fn := range onServerShutdown {
317+
fn()
318+
}
319+
303320
drainWatcher()
304321
drainAutoScaling()
305322
drainPHPThreads()

options.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ type workerOpt struct {
3535
env PreparedEnv
3636
watch []string
3737
maxConsecutiveFailures int
38+
onThreadReady func(int)
39+
onThreadShutdown func(int)
40+
onServerStartup func()
41+
onServerShutdown func()
3842
}
3943

4044
// WithNumThreads configures the number of PHP threads to start.
@@ -116,6 +120,40 @@ func WithWorkerMaxFailures(maxFailures int) WorkerOption {
116120
}
117121
}
118122

123+
func WithWorkerOnReady(f func(int)) WorkerOption {
124+
return func(w *workerOpt) error {
125+
w.onThreadReady = f
126+
127+
return nil
128+
}
129+
}
130+
131+
func WithWorkerOnShutdown(f func(int)) WorkerOption {
132+
return func(w *workerOpt) error {
133+
w.onThreadShutdown = f
134+
135+
return nil
136+
}
137+
}
138+
139+
// WithWorkerOnServerStartup adds a function to be called right after server startup. Useful for extensions.
140+
func WithWorkerOnServerStartup(f func()) WorkerOption {
141+
return func(w *workerOpt) error {
142+
w.onServerStartup = f
143+
144+
return nil
145+
}
146+
}
147+
148+
// WithWorkerOnServerShutdown adds a function to be called right before server shutdown. Useful for extensions.
149+
func WithWorkerOnServerShutdown(f func()) WorkerOption {
150+
return func(w *workerOpt) error {
151+
w.onServerShutdown = f
152+
153+
return nil
154+
}
155+
}
156+
119157
// WithLogger configures the global logger to use.
120158
func WithLogger(l *slog.Logger) Option {
121159
return func(o *opt) error {

testdata/message-worker.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
while (frankenphp_handle_request(function ($message) {
4+
echo $message;
5+
return "received message: $message";
6+
})) {
7+
// continue handling requests
8+
}

threadworker.go

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,10 @@ type workerThread struct {
2121
dummyContext *frankenPHPContext
2222
workerContext *frankenPHPContext
2323
backoff *exponentialBackoff
24-
externalWorker Worker
2524
isBootingScript bool // true if the worker has not reached frankenphp_handle_request yet
2625
}
2726

2827
func convertToWorkerThread(thread *phpThread, worker *worker) {
29-
externalWorker := extensionWorkers[worker.name]
30-
3128
thread.setHandler(&workerThread{
3229
state: thread.state,
3330
thread: thread,
@@ -37,7 +34,6 @@ func convertToWorkerThread(thread *phpThread, worker *worker) {
3734
minBackoff: 100 * time.Millisecond,
3835
maxConsecutiveFailures: worker.maxConsecutiveFailures,
3936
},
40-
externalWorker: externalWorker,
4137
})
4238
worker.attachThread(thread)
4339
}
@@ -46,27 +42,27 @@ func convertToWorkerThread(thread *phpThread, worker *worker) {
4642
func (handler *workerThread) beforeScriptExecution() string {
4743
switch handler.state.get() {
4844
case stateTransitionRequested:
49-
if handler.externalWorker != nil {
50-
handler.externalWorker.OnServerShutdown(handler.thread.threadIndex)
45+
if handler.worker.onThreadShutdown != nil {
46+
handler.worker.onThreadShutdown(handler.thread.threadIndex)
5147
}
5248
handler.worker.detachThread(handler.thread)
5349
return handler.thread.transitionToNewHandler()
5450
case stateRestarting:
55-
if handler.externalWorker != nil {
56-
handler.externalWorker.OnShutdown(handler.thread.threadIndex)
51+
if handler.worker.onThreadShutdown != nil {
52+
handler.worker.onThreadShutdown(handler.thread.threadIndex)
5753
}
5854
handler.state.set(stateYielding)
5955
handler.state.waitFor(stateReady, stateShuttingDown)
6056
return handler.beforeScriptExecution()
6157
case stateReady, stateTransitionComplete:
62-
if handler.externalWorker != nil {
63-
handler.externalWorker.OnReady(handler.thread.threadIndex)
58+
if handler.worker.onThreadReady != nil {
59+
handler.worker.onThreadReady(handler.thread.threadIndex)
6460
}
6561
setupWorkerScript(handler, handler.worker)
6662
return handler.worker.fileName
6763
case stateShuttingDown:
68-
if handler.externalWorker != nil {
69-
handler.externalWorker.OnServerShutdown(handler.thread.threadIndex)
64+
if handler.worker.onThreadShutdown != nil {
65+
handler.worker.onThreadShutdown(handler.thread.threadIndex)
7066
}
7167
handler.worker.detachThread(handler.thread)
7268
// signal to stop

worker.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ type worker struct {
2323
threadMutex sync.RWMutex
2424
allowPathMatching bool
2525
maxConsecutiveFailures int
26+
onThreadReady func(int)
27+
onThreadShutdown func(int)
2628
}
2729

2830
var (
@@ -51,12 +53,6 @@ func initWorkers(opt []workerOpt) error {
5153
convertToWorkerThread(thread, w)
5254
go func() {
5355
thread.state.waitFor(stateReady)
54-
55-
// create a pipe from the external worker to the main worker
56-
// note: this is locked to the initial thread size the external worker requested
57-
if workerThread, ok := thread.handler.(*workerThread); ok && workerThread.externalWorker != nil {
58-
go startWorker(w, workerThread.externalWorker, thread)
59-
}
6056
workersReady.Done()
6157
}()
6258
}
@@ -131,6 +127,8 @@ func newWorker(o workerOpt) (*worker, error) {
131127
threads: make([]*phpThread, 0, o.num),
132128
allowPathMatching: allowPathMatching,
133129
maxConsecutiveFailures: o.maxConsecutiveFailures,
130+
onThreadReady: o.onThreadReady,
131+
onThreadShutdown: o.onThreadShutdown,
134132
}
135133

136134
return w, nil

0 commit comments

Comments
 (0)