RakLibServer: fixed deadlock on thread crash

synchronized block -> getCrashInfo -> join -> synchronized on the same
context on the child thread -> deadlock

instead we check for isTerminated and then get the crash info outside
of the synchronized block.
This commit is contained in:
Dylan K. Taylor 2025-08-06 15:48:29 +01:00
parent a83c62a4a2
commit 89d18f929f
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
2 changed files with 10 additions and 8 deletions

View File

@ -68,17 +68,17 @@ class RakLibServer extends Thread{
public function startAndWait(int $options = NativeThread::INHERIT_NONE) : void{
$this->start($options);
$this->synchronized(function() : void{
while(!$this->ready && $this->getCrashInfo() === null){
while(!$this->ready && !$this->isTerminated()){
$this->wait();
}
$crashInfo = $this->getCrashInfo();
if($crashInfo !== null){
if($crashInfo->getType() === SocketException::class){
throw new SocketException($crashInfo->getMessage());
}
throw new ThreadCrashException("RakLib failed to start", $crashInfo);
}
});
$crashInfo = $this->getCrashInfo();
if($crashInfo !== null){
if($crashInfo->getType() === SocketException::class){
throw new SocketException($crashInfo->getMessage());
}
throw new ThreadCrashException("RakLib failed to start", $crashInfo);
}
}
protected function onRun() : void{

View File

@ -100,6 +100,8 @@ trait CommonThreadPartsTrait{
//*before* the shutdown handler is invoked, so we might land here before the crash info has been set.
//In the future this should probably be fixed by running the shutdown handlers before setting isTerminated,
//but this workaround should be good enough for now.
//WARNING: Do not call this inside a synchronized block on this thread's context. Because the shutdown handler
//runs in a synchronized block, this will result in a deadlock.
if($this->isTerminated() && !$this->isJoined()){
$this->join();
}