/** * Dispatcher responsible for the thread intercommunication and communication with their parent process. * @param bool $useBlocking On true blocks the internal execution until communication data is available for the current dispatched thread otherwise it skips it. * @return void */ public static function dispatch($useBlocking = false) { // {{{ $NULL = null; // prevent any threads to run their own dispatchers if (self::$instancesCreatedEverAArr === null || count(self::$instancesCreatedEverAArr) == 0) { return; } // for checking SIGCHLD child signals informing that a particular "thread" was paused $sigSet = array(SIGCHLD); $sigInfo = array(); // begin the dispatching process foreach (self::$instancesCreatedEverAArr as $instId => &$inst) { // loop through ALL active instances of GPhpCriticalSection foreach ($inst->mastersThreadSpecificData as $threadId => &$specificDataAArr) { // loop though the threads per each instance in GPhpCriticalSection // checking for child signals informing that a thread has exited or was paused while (pcntl_sigtimedwait($sigSet, $sigInfo) == SIGCHLD) { if ($sigInfo['code'] >= 0 && $sigInfo['code'] <= 3) { // child has exited self::$threadsForRemovalAArr[$sigInfo['pid']] = $sigInfo['pid']; } else { if ($sigInfo['code'] == 5) { // stopped (paused) child $specificDataAArr['dispatchPriority'] = 0; // make the dispatch priority lowest } else { if ($sigInfo['code'] == 6) { // resume stopped (paused) child $specificDataAArr['dispatchPriority'] = 1; // increase little bit the priority since we expect interaction with the critical section } } } } $inst->intercomInterlocutorPid =& $specificDataAArr['intercomInterlocutorPid']; if (isset(self::$threadsForRemovalAArr[$inst->intercomInterlocutorPid])) { unset($inst->mastersThreadSpecificData[$threadId]); continue; } $inst->intercomRead =& $specificDataAArr['intercomRead']; $inst->intercomWrite =& $specificDataAArr['intercomWrite']; $inst->dispatchPriority =& $specificDataAArr['dispatchPriority']; if (!$useBlocking && !$inst->intercomRead->isReceivingDataAvailable()) { $inst->dispatchPriority = 0; if ($inst->isIntercomBroken()) { unset($inst->mastersThreadSpecificData[$threadId]); // remove the thread from the dispatching list as soon as we can } continue; } self::dataDispatch($inst, $threadId); $mostPrioritizedThreadId = NULL; if ($inst->dispatchPriority !== 2) { foreach ($inst->mastersThreadSpecificData as $threadId2 => &$specificDataAArr2) { if ($specificDataAArr2['dispatchPriority'] === 2) { $mostPrioritizedThreadId = $threadId2; } } } else { $mostPrioritizedThreadId = $threadId; } if ($mostPrioritizedThreadId !== NULL && $mostPrioritizedThreadId !== $threadId) { $inst->intercomInterlocutorPid =& $inst->mastersThreadSpecificData[$mostPrioritizedThreadId]['intercomInterlocutorPid']; $inst->intercomRead =& $inst->mastersThreadSpecificData[$mostPrioritizedThreadId]['intercomRead']; $inst->intercomWrite =& $inst->mastersThreadSpecificData[$mostPrioritizedThreadId]['intercomWrite']; $inst->dispatchPriority =& $inst->mastersThreadSpecificData[$mostPrioritizedThreadId]['dispatchPriority']; if (!$useBlocking && !$inst->intercomRead->isReceivingDataAvailable()) { $inst->dispatchPriority = 0; if ($inst->isIntercomBroken()) { unset($inst->mastersThreadSpecificData[$mostPrioritizedThreadId]); // remove the thread from the dispatching list as soon as we can } continue; } self::dataDispatch($inst, $threadId); } } // rearrange the threads in the current critical section // instance using their new dispatch priority number // if a lock has already occurred that thread will have the // highest priority uksort($inst->mastersThreadSpecificData, function ($a, $b) use($inst) { if ($inst->mastersThreadSpecificData[$a]['intercomInterlocutorPid'] == $inst->ownerPid) { return -1; } if ($inst->mastersThreadSpecificData[$b]['intercomInterlocutorPid'] == $inst->ownerPid) { return 1; } return $inst->mastersThreadSpecificData[$a]['dispatchPriority'] < $inst->mastersThreadSpecificData[$b]['dispatchPriority']; }); $inst->intercomInterlocutorPid =& $NULL; $inst->intercomRead =& $NULL; $inst->intercomWrite =& $NULL; $inst->dispatchPriority =& $NULL; } // make sure that no terminated threads are left in the internal thread // dispatching list that all instances of GPhpThreadCriticalSection have foreach (self::$instancesCreatedEverAArr as $instId => &$inst) { foreach ($inst->mastersThreadSpecificData as $threadId => &$specificDataAArr) { $inst->intercomInterlocutorPid =& $specificDataAArr['intercomInterlocutorPid']; if (isset(self::$threadsForRemovalAArr[$inst->intercomInterlocutorPid])) { unset($inst->mastersThreadSpecificData[$threadId]); } } $inst->intercomInterlocutorPid =& $NULL; } self::$threadsForRemovalAArr = array(); // rearrange the active instances of GPhpThreadCriticalSection in the // following priority order (the higher the number the bigger the priority): // 2. the instance with the thread that has currently locked the critical section // 1. instances with threads with the highest dispatch priority // 0. instances with the most threads inside $instCrtdEver =& self::$instancesCreatedEverAArr; uksort($instCrtdEver, function ($a, $b) use($instCrtdEver) { // the locker thread is with highest priority if ($instCrtdEver[$a]->mastersThreadSpecificData['intercomInterlocutorPid'] == $instCrtdEver[$a]->ownerPid) { return -1; } if ($instCrtdEver[$b]->mastersThreadSpecificData['intercomInterlocutorPid'] == $instCrtdEver[$b]->ownerPid) { return 1; } // deal with the case of critical sections with no threads if (!empty($instCrtdEver[$a]->mastersThreadSpecificData) && empty($instCrtdEver[$b]->mastersThreadSpecificData)) { return -1; } else { if (empty($instCrtdEver[$a]->mastersThreadSpecificData) && !empty($instCrtdEver[$b]->mastersThreadSpecificData)) { return 1; } else { if (empty($instCrtdEver[$b]->mastersThreadSpecificData) && empty($instCrtdEver[$b]->mastersThreadSpecificData)) { return 0; } } } // a // gather the thread dispatch priorities for the compared critical sections $dispPriorTableA = array(); // priority value => occurrences count $dispPriorTableB = array(); // priority value => occurrences count foreach ($instCrtdEver[$a]->mastersThreadSpecificData as $thrdSpecificData) { @($dispPriorTableA[$thrdSpecificData['dispatchPriority']] += 1); } foreach ($instCrtdEver[$b]->mastersThreadSpecificData as $thrdSpecificData) { @($dispPriorTableB[$thrdSpecificData['dispatchPriority']] += 1); } // both critical sections have threads // make the tables to have the same amount of keys (rows) foreach ($dispPriorTableA as $key => $value) { @($dispPriorTableB[$key] = $dispPriorTableB[$key]); } // this is done on purpose foreach ($dispPriorTableB as $key => $value) { @($dispPriorTableA[$key] = $dispPriorTableA[$key]); } ksort($dispPriorTableA); ksort($dispPriorTableB); // compare the tables while taking into account the priority // and the thread count that have it per critical section foreach ($dispPriorTableA as $key => $value) { if ($value < $dispPriorTableB[$key]) { return 1; } else { if ($value > $dispPriorTableB[$key]) { return -1; } } // a } return 0; // a }); }