public function startCodeReview($id) { $this->view = null; /** * Avoid Code Review races issues by opening a shared block * memory globally exclusive by using the job id as key. */ do { // inmediatly after first parallel reviewer has run this shmop_open line $sem_id = shmop_open($workitem_id, "n", 0644, 1); } while ($sem_id === false); // parallel attempts need to hang until shmop_delete is called try { $workitem = new WorkItem($id); $user = User::find(Session::uid()); if (!$user->isEligible() || $userId == $workitem->getMechanicId()) { throw new Exception('Action not allowed'); } $status = $workitem->startCodeReview($user->getId()); if ($status === null) { throw new Exception('Code Review not available right now'); } else { if ($status === true || (int) $status == 0) { $journal_message = '@' . $user->getNickname() . ' has started a code review for #' . $id . ' '; Utils::systemNotification($journal_message); Notification::workitemNotifyHipchat(array('type' => 'code-review-started', 'workitem' => $workitem), array('nick' => $user->getNickname())); echo json_encode(array('success' => true, 'message' => $journal_message, 'codeReviewStarted' => true, 'codeReviewFeeAmount' => $this->getCRFee($workitem))); } } } catch (Exception $e) { echo json_encode(array('success' => false, 'message' => $e->getMessage())); } // running this line means that parallel request is freed from the shmop_open while shmop_delete($sem_id); }