key = abs(crc32(spl_object_hash($this))); $this->queue = msg_get_queue($this->key, $permissions); if (!$this->queue) { throw new SemaphoreException('Failed to create the semaphore.'); } // Fill the semaphore with locks. while (--$maxLocks >= 0) { $this->release(); } } /** * Checks if the semaphore has been freed. * * @return bool True if the semaphore has been freed, otherwise false. */ public function isFreed() { return !is_resource($this->queue) || !msg_queue_exists($this->key); } /** * {@inheritdoc} */ public function count() { $stat = msg_stat_queue($this->queue); return $stat['msg_qnum']; } /** * {@inheritdoc} */ public function acquire() { while (true) { // Attempt to acquire a lock from the semaphore. if (@msg_receive($this->queue, 0, $type, 1, $chr, false, MSG_IPC_NOWAIT, $errno)) { // A free lock was found, so resolve with a lock object that can // be used to release the lock. yield new Lock(function (Lock $lock) { $this->release(); }); return; } // Check for unusual errors. if ($errno !== MSG_ENOMSG) { throw new SemaphoreException('Failed to acquire a lock.'); } // Sleep for a while, giving a chance for other threads to release // their locks. After we finish sleeping, we can check again to see // if it is our turn to acquire a lock. yield Coroutine\sleep(self::LATENCY_TIMEOUT); } } /** * Removes the semaphore if it still exists. * * @throws SemaphoreException If the operation failed. */ public function free() { if (is_resource($this->queue) && msg_queue_exists($this->key)) { if (!msg_remove_queue($this->queue)) { throw new SemaphoreException('Failed to free the semaphore.'); } unset($this->queue); } } /** * Serializes the semaphore. * * @return string The serialized semaphore. */ public function serialize() { return serialize($this->key); } /** * Unserializes a serialized semaphore. * * @param string $serialized The serialized semaphore. */ public function unserialize($serialized) { // Get the semaphore key and attempt to re-connect to the semaphore in memory. $this->key = unserialize($serialized); if (msg_queue_exists($this->key)) { $this->queue = msg_get_queue($this->key); } } /** * Releases a lock from the semaphore. * * @throws SemaphoreException If the operation failed. */ protected function release() { // Call send in non-blocking mode. If the call fails because the queue // is full, then the number of locks configured is too large. if (!@msg_send($this->queue, 1, "\0", false, false, $errno)) { if ($errno === MSG_EAGAIN) { throw new SemaphoreException('The semaphore size is larger than the system allows.'); } throw new SemaphoreException('Failed to release the lock.'); } } }