PocketMine-MP 5.21.2 git-a6534ecbbbcf369264567d27e5ed70f7f5be9816
Loading...
Searching...
No Matches
CommonThreadPartsTrait.php
1<?php
2
3/*
4 *
5 * ____ _ _ __ __ _ __ __ ____
6 * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8 * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9 * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * @author PocketMine Team
17 * @link http://www.pocketmine.net/
18 *
19 *
20 */
21
22declare(strict_types=1);
23
24namespace pocketmine\thread;
25
26use pmmp\thread\Thread as NativeThread;
27use pmmp\thread\ThreadSafeArray;
31use function error_get_last;
32use function error_reporting;
33use function implode;
34use function register_shutdown_function;
35use function set_exception_handler;
36
37trait CommonThreadPartsTrait{
42 private ?ThreadSafeArray $classLoaders = null;
43 protected ?string $composerAutoloaderPath = null;
44
45 protected bool $isKilled = false;
46
47 private ?ThreadCrashInfo $crashInfo = null;
48
52 public function getClassLoaders() : ?array{
53 return $this->classLoaders !== null ? (array) $this->classLoaders : null;
54 }
55
59 public function setClassLoaders(?array $autoloaders = null) : void{
60 $this->composerAutoloaderPath = \pocketmine\COMPOSER_AUTOLOADER_PATH;
61
62 if($autoloaders === null){
63 $autoloaders = [Server::getInstance()->getLoader()];
64 }
65
66 if($this->classLoaders === null){
67 $loaders = $this->classLoaders = new ThreadSafeArray();
68 }else{
69 $loaders = $this->classLoaders;
70 foreach($this->classLoaders as $k => $autoloader){
71 unset($this->classLoaders[$k]);
72 }
73 }
74 foreach($autoloaders as $autoloader){
75 $loaders[] = $autoloader;
76 }
77 }
78
84 public function registerClassLoaders() : void{
85 if($this->composerAutoloaderPath !== null){
86 require $this->composerAutoloaderPath;
87 }
88 $autoloaders = $this->classLoaders;
89 if($autoloaders !== null){
90 foreach($autoloaders as $autoloader){
92 $autoloader->register(false);
93 }
94 }
95 }
96
97 public function getCrashInfo() : ?ThreadCrashInfo{ return $this->crashInfo; }
98
99 public function start(int $options = NativeThread::INHERIT_NONE) : bool{
100 ThreadManager::getInstance()->add($this);
101
102 if($this->getClassLoaders() === null){
103 $this->setClassLoaders();
104 }
105 return parent::start($options);
106 }
107
108 final public function run() : void{
109 error_reporting(-1);
110 $this->registerClassLoaders();
111 //set this after the autoloader is registered
112 ErrorToExceptionHandler::set();
113
114 //this permits adding extra functionality to the exception and shutdown handlers via overriding
115 set_exception_handler($this->onUncaughtException(...));
116 register_shutdown_function($this->onShutdown(...));
117
118 $this->onRun();
119 $this->isKilled = true;
120 }
121
125 public function quit() : void{
126 $this->isKilled = true;
127
128 if(!$this->isJoined()){
129 $this->notify();
130 $this->join();
131 }
132
133 ThreadManager::getInstance()->remove($this);
134 }
135
139 protected function onUncaughtException(\Throwable $e) : void{
140 $this->synchronized(function() use ($e) : void{
141 $this->crashInfo = ThreadCrashInfo::fromThrowable($e, $this->getThreadName());
142 \GlobalLogger::get()->logException($e);
143 });
144 }
145
150 protected function onShutdown() : void{
151 $this->synchronized(function() : void{
152 if($this->isTerminated() && $this->crashInfo === null){
153 $last = error_get_last();
154 if($last !== null && ($last["type"] & CrashDump::FATAL_ERROR_MASK) !== 0){
155 //fatal error
156 $crashInfo = ThreadCrashInfo::fromLastErrorInfo($last, $this->getThreadName());
157 }else{
158 //probably misused exit()
159 $crashInfo = ThreadCrashInfo::fromThrowable(new \RuntimeException("Thread crashed without an error - perhaps exit() was called?"), $this->getThreadName());
160 }
161 $this->crashInfo = $crashInfo;
162
163 $lines = [];
164 //mimic exception printed format
165 $lines[] = "Fatal error: " . $crashInfo->makePrettyMessage();
166 $lines[] = "--- Stack trace ---";
167 foreach($crashInfo->getTrace() as $frame){
168 $lines[] = " " . $frame->getPrintableFrame();
169 }
170 $lines[] = "--- End of fatal error information ---";
171 \GlobalLogger::get()->critical(implode("\n", $lines));
172 }
173 });
174 }
175
179 abstract protected function onRun() : void;
180
181 public function getThreadName() : string{
182 return (new \ReflectionClass($this))->getShortName();
183 }
184}
static fromLastErrorInfo(array $info, string $threadName)