Bug with parallel
What happened?
Seem on version 1.2.5 , parallel is crashing (with server mode)
sudo ./frankenphp-linux-x86_64 php-server index.php
❯ cat index.php
<?php
$runtime = new \parallel\Runtime();
$future = $runtime->run(function(){
for ($i = 0; $i < 500; $i++)
echo "*";
return "easy";
});
for ($i = 0; $i < 500; $i++) {
echo ".";
}
printf("\nUsing \\parallel\\Runtime is %s\n", $future->value());
===> Loading N times http://localhost
Working some times then crash :+1:
`
2024/05/15 14:30:16.513 WARN admin admin endpoint disabled
2024/05/15 14:30:16.513 WARN http.auto_https server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server {"server_name": "php", "http_port": 80}
2024/05/15 14:30:16.513 INFO tls.cache.maintenance started background certificate maintenance {"cache": "0xc000149e00"}
2024/05/15 14:30:16.514 INFO http.log server running {"name": "php", "protocols": ["h1", "h2", "h3"]}
2024/05/15 14:30:16.514 INFO FrankenPHP started 🐘 {"php_version": "8.3.7"}
2024/05/15 14:30:16.514 INFO Caddy serving PHP app on :80
2024/05/15 14:30:16.519 WARN tls storage cleaning happened too recently; skipping for now {"storage": "FileStorage:/root/.local/share/caddy", "instance": "18f83e8b-04c2-44af-a8ee-3a629c0cddcb", "try_again": "2024/05/16 14:30:16.519", "try_again_in": 86399.999999624}
2024/05/15 14:30:16.519 INFO tls finished cleaning storage units
fatal error: concurrent map read and map write
goroutine 106 [running, locked to thread]:
net/textproto.MIMEHeader.Get(0xc00013dec0, {0x7f5e63b56432?, 0xc0001f2db0?})
/usr/local/go/src/net/textproto/header.go:34 +0x3f
net/http.Header.Get(...)
/usr/local/go/src/net/http/header.go:50
github.com/caddyserver/caddy/v2/modules/caddyhttp/encode.(*responseWriter).Write(0xc00078b680, {0x7f5e110007c8, 0x1, 0x1})
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/encode/encode.go:246 +0x85
github.com/dunglas/frankenphp.go_ub_write(0xc0000c6000?, 0x7f5e110007c8, 0x1)
/go/src/app/frankenphp.go:531 +0x13f
goroutine 1 [select (no cases)]:
github.com/dunglas/frankenphp/caddy.cmdPHPServer({0x0?})
/go/src/app/caddy/php-server.go:329 +0x27d0
github.com/dunglas/frankenphp/caddy.init.2.func1.WrapCommandFuncForCobra.1(0xc000427208, {0x7f5e63b3d48d?, 0x4?, 0x7f5e63b3d295?})
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/cmd/cobra.go:137 +0x2f
github.com/spf13/cobra.(*Command).execute(0xc000427208, {0xc0004868d0, 0x1, 0x1})
/go/pkg/mod/github.com/spf13/[email protected]/command.go:983 +0xaca
github.com/spf13/cobra.(*Command).ExecuteC(0x7f5e68291080)
/go/pkg/mod/github.com/spf13/[email protected]/command.go:1115 +0x3ff
github.com/spf13/cobra.(*Command).Execute(...)
/go/pkg/mod/github.com/spf13/[email protected]/command.go:1039
github.com/caddyserver/caddy/v2/cmd.Main()
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/cmd/main.go:66 +0x5b
main.main()
/go/src/app/caddy/frankenphp/main.go:24 +0x18d
goroutine 17 [select, locked to thread]:
github.com/dunglas/frankenphp.go_fetch_request()
/go/src/app/frankenphp.go:473 +0x75
goroutine 9 [select]:
github.com/golang/glog.(*fileSink).flushDaemon(0x7f5e6837b538)
/go/pkg/mod/github.com/golang/[email protected]/glog_file.go:351 +0xb9
created by github.com/golang/glog.init.1 in goroutine 1
/go/pkg/mod/github.com/golang/[email protected]/glog_file.go:166 +0x126
goroutine 35 [chan receive]:
github.com/dunglas/frankenphp.ServeHTTP({0x7f5e6750a7d0, 0xc00078b680}, 0xc0006e5200)
/go/src/app/frankenphp.go:465 +0x20e
github.com/dunglas/frankenphp/caddy.FrankenPHPModule.ServeHTTP({{0x7f5e63b5afd2, 0x10}, {0xc000137a50, 0x1, 0x1}, 0xc00033ed40, 0x0, 0xc000505500}, {0x7f5e6750a7d0, 0xc00078b680}, ...)
/go/src/app/caddy/caddy.go:275 +0x4ac
github.com/caddyserver/caddy/v2/modules/caddyhttp.wrapMiddleware.func1.1({0x7f5e6750a7d0?, 0xc00078b680?}, 0xc000317540?)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/routes.go:331 +0x3a
github.com/caddyserver/caddy/v2/modules/caddyhttp.HandlerFunc.ServeHTTP(0x7f5e674fc800?, {0x7f5e6750a7d0?, 0xc00078b680?}, 0xc0006e4ea0?)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/caddyhttp.go:58 +0x29
github.com/caddyserver/caddy/v2/modules/caddyhttp.RouteList.Compile.wrapRoute.func1.1({0x7f5e6750a7d0, 0xc00078b680}, 0xc0006e4ea0)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/routes.go:300 +0x325
github.com/caddyserver/caddy/v2/modules/caddyhttp.HandlerFunc.ServeHTTP(0x0?, {0x7f5e6750a7d0?, 0xc00078b680?}, 0xc000149400?)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/caddyhttp.go:58 +0x29
github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite.Rewrite.ServeHTTP({{0x0, 0x0}, {0xc00079cb80, 0x1d}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0, ...}, ...}, ...)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/rewrite/rewrite.go:137 +0x3f3
github.com/caddyserver/caddy/v2/modules/caddyhttp.wrapMiddleware.func1.1({0x7f5e6750a7d0?, 0xc00078b680?}, 0xc000505300?)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/routes.go:331 +0x3a
github.com/caddyserver/caddy/v2/modules/caddyhttp.HandlerFunc.ServeHTTP(0x7f5e674fc800?, {0x7f5e6750a7d0?, 0xc00078b680?}, 0xc0006e4ea0?)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/caddyhttp.go:58 +0x29
github.com/caddyserver/caddy/v2/modules/caddyhttp.RouteList.Compile.wrapRoute.func1.1({0x7f5e6750a7d0, 0xc00078b680}, 0xc0006e4ea0)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/routes.go:300 +0x325
github.com/caddyserver/caddy/v2/modules/caddyhttp.HandlerFunc.ServeHTTP(0x7f5e6750ec60?, {0x7f5e6750a7d0?, 0xc00078b680?}, 0xe?)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/caddyhttp.go:58 +0x29
github.com/caddyserver/caddy/v2/modules/caddyhttp.RouteList.Compile.wrapRoute.func1.1({0x7f5e6750a7d0, 0xc00078b680}, 0xc0006e4ea0)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/routes.go:268 +0x244
github.com/caddyserver/caddy/v2/modules/caddyhttp.HandlerFunc.ServeHTTP(0xc000801480?, {0x7f5e6750a7d0?, 0xc00078b680?}, 0x4?)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/caddyhttp.go:58 +0x29
github.com/caddyserver/caddy/v2/modules/caddyhttp/encode.(*Encode).ServeHTTP(0xc000317400, {0x7f5e6750a0e0, 0xc00063f340}, 0xc0006e4ea0, {0x7f5e674fc800, 0xc00025f240})
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/encode/encode.go:138 +0x252
github.com/caddyserver/caddy/v2/modules/caddyhttp.wrapMiddleware.func1.1({0x7f5e6750a0e0?, 0xc00063f340?}, 0xc000317400?)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/routes.go:331 +0x3a
github.com/caddyserver/caddy/v2/modules/caddyhttp.HandlerFunc.ServeHTTP(0x7f5e674fc800?, {0x7f5e6750a0e0?, 0xc00063f340?}, 0xc0006e4ea0?)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/caddyhttp.go:58 +0x29
github.com/caddyserver/caddy/v2/modules/caddyhttp.RouteList.Compile.wrapRoute.func1.1({0x7f5e6750a0e0, 0xc00063f340}, 0xc0006e4ea0)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/routes.go:300 +0x325
github.com/caddyserver/caddy/v2/modules/caddyhttp.HandlerFunc.ServeHTTP(0xc0001bca08?, {0x7f5e6750a0e0?, 0xc00063f340?}, 0x7f5e674fc800?)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/caddyhttp.go:58 +0x29
github.com/caddyserver/caddy/v2/modules/caddyhttp.(*Subroute).ServeHTTP(0xc0006b6f00, {0x7f5e6750a0e0, 0xc00063f340}, 0xc0006e4ea0, {0x7f5e674fc800, 0x7f5e674f0378})
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/subroute.go:74 +0x67
github.com/caddyserver/caddy/v2/modules/caddyhttp.wrapMiddleware.func1.1({0x7f5e6750a0e0?, 0xc00063f340?}, 0xc0006b6f00?)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/routes.go:331 +0x3a
github.com/caddyserver/caddy/v2/modules/caddyhttp.HandlerFunc.ServeHTTP(0x7f5e674fc800?, {0x7f5e6750a0e0?, 0xc00063f340?}, 0xc0006e4ea0?)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/caddyhttp.go:58 +0x29
github.com/caddyserver/caddy/v2/modules/caddyhttp.RouteList.Compile.wrapRoute.func1.1({0x7f5e6750a0e0, 0xc00063f340}, 0xc0006e4ea0)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/routes.go:300 +0x325
github.com/caddyserver/caddy/v2/modules/caddyhttp.HandlerFunc.ServeHTTP(0x7f5e6743f260?, {0x7f5e6750a0e0?, 0xc00063f340?}, 0xc0008013c0?)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/caddyhttp.go:58 +0x29
github.com/caddyserver/caddy/v2/modules/caddyhttp.(*Server).enforcementHandler(0x10?, {0x7f5e6750a0e0?, 0xc00063f340?}, 0x0?, {0x7f5e674fc800?, 0xc0006b70e0?})
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/server.go:429 +0x24b
github.com/caddyserver/caddy/v2/modules/caddyhttp.(*App).Provision.(*Server).wrapPrimaryRoute.func1({0x7f5e6750a0e0?, 0xc00063f340?}, 0x7f5e61011b6f?)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/server.go:405 +0x35
github.com/caddyserver/caddy/v2/modules/caddyhttp.HandlerFunc.ServeHTTP(0xc000420380?, {0x7f5e6750a0e0?, 0xc00063f340?}, 0xc0006e4ea0?)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/caddyhttp.go:58 +0x29
github.com/caddyserver/caddy/v2/modules/caddyhttp.(*Server).ServeHTTP(0xc000236848, {0x7f5e6750a0e0, 0xc00063f340}, 0xc0006e4b40)
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/server.go:341 +0xc47
net/http.serverHandler.ServeHTTP({0xc0005fc1b0?}, {0x7f5e6750a0e0?, 0xc00063f340?}, 0x6?)
/usr/local/go/src/net/http/server.go:3137 +0x8e
net/http.(*conn).serve(0xc00062e000, {0x7f5e6750ec60, 0xc0005fc120})
/usr/local/go/src/net/http/server.go:2039 +0x5e8
created by net/http.(*Server).Serve in goroutine 30
/usr/local/go/src/net/http/server.go:3285 +0x4b4
goroutine 10 [sync.Cond.Wait]:
sync.runtime_notifyListWait(0xc000000318, 0x0)
/usr/local/go/src/runtime/sema.go:569 +0x15a
sync.(*Cond).Wait(0xc00036ddc0?)
/usr/local/go/src/sync/cond.go:70 +0x85
github.com/maypok86/otter/internal/queue.(*Growable[...]).Pop(0x7f5e67525960)
/go/pkg/mod/github.com/maypok86/[email protected]/internal/queue/growable.go:71 +0x74
github.com/maypok86/otter/internal/core.(*Cache[...]).process(0x7f5e67542dc0)
/go/pkg/mod/github.com/maypok86/[email protected]/internal/core/cache.go:386 +0xe8
created by github.com/maypok86/otter/internal/core.NewCache[...] in goroutine 1
/go/pkg/mod/github.com/maypok86/[email protected]/internal/core/cache.go:167 +0x6b3
goroutine 14 [select]:
github.com/caddyserver/certmagic.(*RingBufferRateLimiter).permit(0xc00078ac30)
/go/pkg/mod/github.com/caddyserver/[email protected]/ratelimiter.go:217 +0x86
github.com/caddyserver/certmagic.(*RingBufferRateLimiter).loop(0xc00078ac30)
/go/pkg/mod/github.com/caddyserver/[email protected]/ratelimiter.go:89 +0x8b
created by github.com/caddyserver/certmagic.NewRateLimiter in goroutine 1
/go/pkg/mod/github.com/caddyserver/[email protected]/ratelimiter.go:45 +0x13c
goroutine 26 [chan receive]:
github.com/caddyserver/caddy/v2.trapSignalsCrossPlatform.func1()
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/sigtrap.go:43 +0xe5
created by github.com/caddyserver/caddy/v2.trapSignalsCrossPlatform in goroutine 1
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/sigtrap.go:38 +0x1a
goroutine 27 [chan receive]:
github.com/caddyserver/caddy/v2.trapSignalsPosix.func1()
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/sigtrap_posix.go:35 +0x105
created by github.com/caddyserver/caddy/v2.trapSignalsPosix in goroutine 1
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/sigtrap_posix.go:31 +0x1a
goroutine 67 [syscall]:
os/signal.signal_recv()
/usr/local/go/src/runtime/sigqueue.go:152 +0x29
os/signal.loop()
/usr/local/go/src/os/signal/signal_unix.go:23 +0x13
created by os/signal.Notify.func1.1 in goroutine 26
/usr/local/go/src/os/signal/signal.go:151 +0x1f
goroutine 28 [select]:
github.com/caddyserver/certmagic.(*Cache).maintainAssets(0xc000149e00, 0x0)
/go/pkg/mod/github.com/caddyserver/[email protected]/maintain.go:69 +0x31f
created by github.com/caddyserver/certmagic.NewCache in goroutine 1
/go/pkg/mod/github.com/caddyserver/[email protected]/cache.go:127 +0x1f6
goroutine 29 [select]:
github.com/caddyserver/caddy/v2/modules/caddytls.(*TLS).keepStorageClean.func1()
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddytls/tls.go:540 +0x93
created by github.com/caddyserver/caddy/v2/modules/caddytls.(*TLS).keepStorageClean in goroutine 1
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddytls/tls.go:532 +0xe5
goroutine 30 [IO wait]:
internal/poll.runtime_pollWait(0x7f5e1c6806d0, 0x72)
/usr/local/go/src/runtime/netpoll.go:345 +0x85
internal/poll.(*pollDesc).wait(0x3?, 0x7f5e60f7f285?, 0x0)
/usr/local/go/src/internal/poll/fd_poll_runtime.go:84 +0x27
internal/poll.(*pollDesc).waitRead(...)
/usr/local/go/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Accept(0xc000505880)
/usr/local/go/src/internal/poll/fd_unix.go:611 +0x2ac
net.(*netFD).accept(0xc000505880)
/usr/local/go/src/net/fd_unix.go:172 +0x29
net.(*TCPListener).accept(0xc0006b7160)
/usr/local/go/src/net/tcpsock_posix.go:159 +0x1e
net.(*TCPListener).Accept(0xc0006b7160)
/usr/local/go/src/net/tcpsock.go:327 +0x30
net/http.(*Server).Serve(0xc0007a3770, {0x7f5e1c064100, 0xc0006b7180})
/usr/local/go/src/net/http/server.go:3255 +0x33e
created by github.com/caddyserver/caddy/v2/modules/caddyhttp.(*App).Start in goroutine 1
/go/pkg/mod/github.com/caddyserver/caddy/[email protected]/modules/caddyhttp/app.go:509 +0x1b95
goroutine 82 [syscall, locked to thread]:
runtime.goexit({})
/usr/local/go/src/runtime/asm_amd64.s:1695 +0x1
goroutine 83 [syscall, locked to thread]:
runtime.goexit({})
/usr/local/go/src/runtime/asm_amd64.s:1695 +0x1
goroutine 84 [syscall, locked to thread]:
runtime.goexit({})
/usr/local/go/src/runtime/asm_amd64.s:1695 +0x1
goroutine 101 [syscall, locked to thread]:
runtime.goexit({})
/usr/local/go/src/runtime/asm_amd64.s:1695 +0x1
goroutine 102 [syscall, locked to thread]:
runtime.goexit({})
/usr/local/go/src/runtime/asm_amd64.s:1695 +0x1
goroutine 103 [syscall, locked to thread]:
github.com/dunglas/frankenphp._Cfunc_frankenphp_execute_script(0x7f5e10a65710)
_cgo_gotypes.go:1089 +0x4b
github.com/dunglas/frankenphp.go_execute_script(0x4001?)
/go/src/app/frankenphp.go:511 +0x133
goroutine 104 [syscall, locked to thread]:
runtime.goexit({})
/usr/local/go/src/runtime/asm_amd64.s:1695 +0x1
goroutine 105 [syscall, locked to thread]:
runtime.goexit({})
/usr/local/go/src/runtime/asm_amd64.s:1695 +0x1
goroutine 113 [IO wait]:
internal/poll.runtime_pollWait(0x7f5e1c6805d8, 0x72)
/usr/local/go/src/runtime/netpoll.go:345 +0x85
internal/poll.(*pollDesc).wait(0xc00078c080?, 0xc0005fc1c1?, 0x0)
/usr/local/go/src/internal/poll/fd_poll_runtime.go:84 +0x27
internal/poll.(*pollDesc).waitRead(...)
/usr/local/go/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc00078c080, {0xc0005fc1c1, 0x1, 0x1})
/usr/local/go/src/internal/poll/fd_unix.go:164 +0x27a
net.(*netFD).Read(0xc00078c080, {0xc0005fc1c1?, 0x0?, 0x0?})
/usr/local/go/src/net/fd_posix.go:55 +0x25
net.(*conn).Read(0xc000380010, {0xc0005fc1c1?, 0x0?, 0x0?})
/usr/local/go/src/net/net.go:179 +0x45
net/http.(*connReader).backgroundRead(0xc0005fc1b0)
/usr/local/go/src/net/http/server.go:681 +0x37
created by net/http.(*connReader).startBackgroundRead in goroutine 35
/usr/local/go/src/net/http/server.go:677 +0xba
~/poc/franken_parallel via 🐘 v8.2.18 took 7s
`
Build Type
Docker (Debian Bookworm)
Worker Mode
Yes
Operating System
GNU/Linux
CPU Architecture
x86_64
PHP configuration
latest version 1.2.5 franken
Relevant log output
No response
Parallel extension probably isn't compatible with go stacks/memory, if I had to guess (possibly collisions with channels and other shenanigans, plus maintaining threads).
It's probably better to break out the Parallel code into a separate, regular php file.
I have been doing that for quite some time. I should probably open source my Parallel shims...
The problem has been reported upstream and parallel has been unbundled from FrankenPHP. https://github.com/krakjoe/parallel/issues/308
Hey @davidnurdin 👋
the error fatal error: concurrent map read and map write looks like a race condition to me. I am not 100% sure and might be wrong but I can fix the problem with a simple
echo "foo";
flush();
at the beginning of the file.
What might be happing is the following: As soon as output is started, PHP will start creating the HTTP-Response. In case you are using user land threads (as the example code above would), you end up having two threads that do write to the same buffers/socket. I assume that both threads do echo "at the same time" and trigger the creating and sending the HTTP-Response to the client which causes this.
ext-parallel overwrites the SAPI's ub_write with a version that uses a mutex to make sure only one thread can echo at a time, but it seems there is more going on.
I'll have a look if I can see something and get back to you
Hey @dunglas,
can you help me running FrankenPHP with go -race?
I am not an expert in Go, but this looks to me like goroutine 106 and 35 both accessing the same "map" (which I assume is the responseWriter or the FrankenPHPContext or something related to it). To me it looks like the following could happen here:
Besides goroutines 106 and 35 all seem to be idling, while 106 and 35 seem both to access the responseWriter. I do assume that 106 is the thread echoing out the .-char (the main thread), while 35 is the one echoing out the *-char. I'd further assume that while 35 is shutting down and writing it's output, 106 is still calling echo and writing to the same responseWriter causing the fatal error: concurrent map read and map write.
This is all assumptions, but this is as far as I could understand things happening.
But it boils down to the main problem being the concurrency with two (or more) threads that share the same underlying structures and streams in the SAPI without synchronisation.
I assume we'd need to guard access to whatever "concurrent map read and map write" is referring to with a mutex. What do you think @dunglas?
Hi,
Bug is still here (on FrankenPHP v1.4.2 PHP 8.4.3 Caddy v2.9.1 h1:OEYiZ7DbCzAWVb6TNEkjRcSCRGHVoZsJinoDR/n9oaY= ) It is fix or not ? Thanks :)
Considering that https://github.com/krakjoe/parallel/issues/308 is still open ... it isn't fixed.