Exemplo n.º 1
0
 /**
  * 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
     });
 }