Completed
Push — master ( 49d4ca...54f4ec )
by Dmitry
02:43
created

Lock::acquireOrQueue()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 0
cts 14
cp 0
rs 9.7333
c 0
b 0
f 0
cc 4
nc 4
nop 2
crap 20
1
<?php
2
3
namespace Basis;
4
5
use Exception;
6
use Predis\Client;
7
8
class Lock
9
{
10
    private $redis;
11
    private $locks = [];
12
13
    public function __construct(Client $redis)
14
    {
15
        $this->redis = $redis;
16
        register_shutdown_function([$this, 'releaseLocks']);
17
    }
18
19
    public function acquireOrQueue($name, $ttl = 1)
20
    {
21
        if (!$this->lock($name, $ttl)) {
22
            if (!$this->lock("queue-$name")) {
23
                // somebody else waiting for next
24
                return false;
25
            }
26
            $this->waitUnlock($name);
27
            $this->unlock("queue-$name");
28
            if (!$this->lock($name, $ttl)) {
29
                // somebody else take acquire locks
30
                return false;
31
            }
32
        }
33
        return true;
34
    }
35
36
    public function acquire($name, $ttl = 1)
37
    {
38
        while (!$this->lock($name, $ttl)) {
39
            $this->waitUnlock($name);
40
        }
41
        return true;
42
    }
43
44
    public function exists($name)
45
    {
46
        return $this->redis->get("lock-$name") !== null;
47
    }
48
49
    public function lock($name, $ttl = 1)
50
    {
51
        if (in_array($name, $this->locks)) {
52
            throw new Exception("Lock $name was already registered");
53
        }
54
        $result = $this->redis->set("lock-$name", 1, 'EX', $ttl, 'NX');
55
        if ($result) {
56
            $this->locks[] = $name;
57
            return true;
58
        }
59
        return false;
60
    }
61
62
    public function releaseLocks()
63
    {
64
        foreach ($this->locks as $name) {
65
            $this->unlock($name);
66
        }
67
    }
68
69
    public function unlock($name)
70
    {
71
        if (!in_array($name, $this->locks)) {
72
            throw new Exception("Lock $name was already registered");
73
        }
74
        
75
        foreach ($this->locks as $i => $candidate) {
76
            if ($candidate == $name) {
77
                unset($this->locks[$i]);
78
                $this->redis->del("lock-$name");
79
                break;
80
            }
81
        }
82
83
        $this->locks = array_values($this->locks);
84
    }
85
86
    public function waitUnlock($name)
87
    {
88
        while ($this->exists($name)) {
89
            // 100ms
90
            usleep(100000);
91
        }
92
        return true;
93
    }
94
}
95