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{ return $this->crashInfo; }
99 public function start(
int $options = NativeThread::INHERIT_NONE) : bool{
100 ThreadManager::getInstance()->add($this);
102 if($this->getClassLoaders() ===
null){
103 $this->setClassLoaders();
105 return parent::start($options);
108 final public function run() : void{
110 $this->registerClassLoaders();
112 ErrorToExceptionHandler::set();
115 set_exception_handler($this->onUncaughtException(...));
116 register_shutdown_function($this->onShutdown(...));
119 $this->isKilled =
true;
125 public function quit() : void{
126 $this->isKilled = true;
128 if(!$this->isJoined()){
133 ThreadManager::getInstance()->remove($this);
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);
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){
159 $crashInfo = ThreadCrashInfo::fromThrowable(
new \RuntimeException(
"Thread crashed without an error - perhaps exit() was called?"), $this->getThreadName());
161 $this->crashInfo = $crashInfo;
165 $lines[] =
"Fatal error: " . $crashInfo->makePrettyMessage();
166 $lines[] =
"--- Stack trace ---";
167 foreach($crashInfo->
getTrace() as $frame){
168 $lines[] =
" " . $frame->getPrintableFrame();
170 $lines[] =
"--- End of fatal error information ---";
171 \GlobalLogger::get()->critical(implode(
"\n", $lines));
179 abstract protected function onRun() : void;
181 public function getThreadName() : string{
182 return (new \ReflectionClass($this))->getShortName();
static fromLastErrorInfo(array $info, string $threadName)