/**
 * Safe update json in Memcached storage
 * @param Memcached $Memcached
 * @param string $key
 * @param array $array
 * @throws Exception
 */
function updateJsonInMemcached(\Memcached $Memcached, $key, array $array)
{
    // Create new Lock instance
    $Lock = new MemcachedLock($Memcached, 'Lock_' . $key, MemcachedLock::FLAG_CATCH_EXCEPTIONS);
    // Acquire lock for 2 sec.
    // If lock has acquired in another thread then we will wait 3 second,
    // until another thread release the lock. Otherwise it throws a exception.
    if (!$Lock->acquire(2, 3)) {
        throw new Exception('Can\'t get a Lock');
    }
    // Get value from storage
    $json = $Memcached->get($key);
    if (!$json) {
        $jsonArray = [];
    } else {
        $jsonArray = json_decode($json, true);
    }
    // Some operations with json
    $jsonArray = array_merge($jsonArray, $array);
    $json = json_encode($jsonArray);
    // Update key in storage
    $Memcached->set($key, $json);
    // Release the lock
    // After $lock->release() another waiting thread (Lock) will be able to update json in storage
    $Lock->release();
}
 /**
  * @param int $locktime
  * @param int $waittime
  * @return MemcachedLock
  * @throws \MemcachedLock\Exception\LockHasAcquiredAlreadyException
  */
 protected function createLock($locktime = 2, $waittime = 3)
 {
     $Lock = new MemcachedLock($this->Memcached, $this->prefix . self::PREFIX_LOCK);
     $Lock->acquire($locktime, $waittime);
     return $Lock;
 }
 public function test_parallel()
 {
     $MC = $this->getMemcached();
     $MC->flush();
     $this->assertSame(true, $MC->set('testcount', '1000000'));
     unset($MC);
     $Storage = new MemcachedStorage(['servers' => [explode(':', MEMCACHED_TEST_SERVER)]]);
     $Parallel = new Parallel($Storage);
     $start = microtime(true) + 2;
     // 1st operation
     $Parallel->run('foo', function () use($start) {
         $MemcachedLock = new MemcachedLock($MC = $this->getMemcached(), 'lock_');
         while (microtime(true) < $start) {
             // wait for start
         }
         $c = 0;
         for ($i = 1; $i <= 10000; ++$i) {
             if ($MemcachedLock->acquire(2, 3)) {
                 $count = (int) $MC->get('testcount');
                 ++$count;
                 $MC->set('testcount', $count);
                 $MemcachedLock->release();
                 ++$c;
             }
         }
         return $c;
     });
     // 2st operation
     $Parallel->run('bar', function () use($start) {
         $MemcachedLock = new MemcachedLock($MC = $this->getMemcached(), 'lock_');
         while (microtime(true) < $start) {
             // wait for start
         }
         $c = 0;
         for ($i = 1; $i <= 10000; ++$i) {
             if ($MemcachedLock->acquire(2, 3)) {
                 $count = (int) $MC->get('testcount');
                 ++$count;
                 $MC->set('testcount', $count);
                 $MemcachedLock->release();
                 ++$c;
             }
         }
         return $c;
     });
     $MemcachedLock = new MemcachedLock($MC = $this->getMemcached(), 'lock_');
     while (microtime(true) < $start) {
         // wait for start
     }
     $c = 0;
     for ($i = 1; $i <= 10000; ++$i) {
         if ($MemcachedLock->acquire(2, 3)) {
             $count = (int) $MC->get('testcount');
             ++$count;
             $MC->set('testcount', $count);
             $MemcachedLock->release();
             ++$c;
         }
     }
     $result = $Parallel->wait(['foo', 'bar']);
     $this->assertSame(10000, (int) $result['foo']);
     $this->assertSame(10000, (int) $result['bar']);
     $this->assertSame(10000, $c);
     $this->assertSame(1030000, (int) $MC->get('testcount'));
     $MC->flush();
 }
 public function test_MemcachedLock_Exceptions()
 {
     $key = static::TEST_KEY;
     $MemcachedLock = new MemcachedLock(static::$Memcached, $key, MemcachedLock::FLAG_USE_SELF_EXPIRE_SYNC);
     $this->assertSame(true, $MemcachedLock->acquire(2));
     $this->assertSame(true, $MemcachedLock->isLocked());
     static::$Memcached->delete($key);
     try {
         $MemcachedLock->release();
         $this->assertFalse('Expect LostLockException');
     } catch (\Exception $Ex) {
         $this->assertInstanceOf(LostLockException::class, $Ex);
     }
     $this->assertSame(false, $MemcachedLock->isLocked());
     $this->assertSame(true, $MemcachedLock->acquire(2));
     $this->assertSame(true, $MemcachedLock->isLocked());
     static::$Memcached->delete($key);
     $this->assertSame(false, $MemcachedLock->isExists());
     try {
         $MemcachedLock->isLocked();
         $this->assertFalse('Expect LostLockException');
     } catch (\Exception $Ex) {
         $this->assertInstanceOf(LostLockException::class, $Ex);
     }
 }