22declare(strict_types=1);
24namespace pocketmine\thread;
26use pmmp\thread\Thread as NativeThread;
27use pmmp\thread\ThreadSafeArray;
31use
function error_get_last;
32use
function error_reporting;
34use
function register_shutdown_function;
35use
function set_exception_handler;
37trait CommonThreadPartsTrait{
42 private ?ThreadSafeArray $classLoaders =
null;
43 protected ?
string $composerAutoloaderPath =
null;
45 protected bool $isKilled =
false;
47 private ?ThreadCrashInfo $crashInfo =
null;
52 public function getClassLoaders() : ?array{
53 return $this->classLoaders !== null ? (array) $this->classLoaders : null;
59 public function setClassLoaders(?array $autoloaders =
null) : void{
60 $this->composerAutoloaderPath = \
pocketmine\COMPOSER_AUTOLOADER_PATH;
62 if($autoloaders ===
null){
63 $autoloaders = [Server::getInstance()->getLoader()];
66 if($this->classLoaders ===
null){
67 $loaders = $this->classLoaders =
new ThreadSafeArray();
69 $loaders = $this->classLoaders;
70 foreach($this->classLoaders as $k => $autoloader){
71 unset($this->classLoaders[$k]);
74 foreach($autoloaders as $autoloader){
75 $loaders[] = $autoloader;
84 public function registerClassLoaders() : void{
85 if($this->composerAutoloaderPath !== null){
86 require $this->composerAutoloaderPath;
88 $autoloaders = $this->classLoaders;
89 if($autoloaders !==
null){
90 foreach($autoloaders as $autoloader){
92 $autoloader->register(
false);
97 public function getCrashInfo() : ?ThreadCrashInfo{
105 if($this->isTerminated() && !$this->isJoined()){
108 return $this->crashInfo;
111 public function start(
int $options = NativeThread::INHERIT_NONE) : bool{
112 ThreadManager::getInstance()->add($this);
114 if($this->getClassLoaders() ===
null){
115 $this->setClassLoaders();
117 return parent::start($options);
120 final public function run() : void{
122 $this->registerClassLoaders();
124 ErrorToExceptionHandler::set();
127 set_exception_handler($this->onUncaughtException(...));
128 register_shutdown_function($this->onShutdown(...));
131 $this->isKilled =
true;
137 public function quit() : void{
138 $this->isKilled = true;
140 if(!$this->isJoined()){
145 ThreadManager::getInstance()->remove($this);
151 protected function onUncaughtException(\Throwable $e) : void{
152 $this->synchronized(function() use ($e) : void{
153 $this->crashInfo = ThreadCrashInfo::fromThrowable($e, $this->getThreadName());
154 \GlobalLogger::get()->logException($e);
162 protected function onShutdown() : void{
163 $this->synchronized(function() : void{
164 if($this->isTerminated() && $this->crashInfo === null){
165 $last = error_get_last();
166 if($last !==
null && ($last[
"type"] & CrashDump::FATAL_ERROR_MASK) !== 0){
171 $crashInfo = ThreadCrashInfo::fromThrowable(
new \RuntimeException(
"Thread crashed without an error - perhaps exit() was called?"), $this->getThreadName());
173 $this->crashInfo = $crashInfo;
177 $lines[] =
"Fatal error: " . $crashInfo->makePrettyMessage();
178 $lines[] =
"--- Stack trace ---";
179 foreach($crashInfo->
getTrace() as $frame){
180 $lines[] =
" " . $frame->getPrintableFrame();
182 $lines[] =
"--- End of fatal error information ---";
183 \GlobalLogger::get()->critical(implode(
"\n", $lines));
191 abstract protected function onRun() : void;
193 public function getThreadName() : string{
194 return (new \ReflectionClass($this))->getShortName();
static fromLastErrorInfo(array $info, string $threadName)