• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

brick / lock / 17475241346

04 Sep 2025 07:59PM UTC coverage: 5.5%. Remained the same
17475241346

push

github

BenMorel
Apply coding standard

11 of 200 relevant lines covered (5.5%)

3.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/MultiLock.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Brick\Lock;
6

7
use Brick\Lock\Exception\LockAcquireException;
8
use Brick\Lock\Exception\LockReleaseException;
9
use InvalidArgumentException;
10
use Override;
11

12
use function count;
13
use function max;
14
use function microtime;
15
use function sort;
16

17
final readonly class MultiLock extends AbstractLock
18
{
19
    /**
20
     * @var list<string>
21
     */
22
    private array $lockNames;
23

24
    /**
25
     * @param string[] $lockNames
26
     *
27
     * @throws InvalidArgumentException
28
     */
29
    public function __construct(
30
        private LockDriverInterface $store,
31
        array $lockNames,
32
    ) {
33
        if (count($lockNames) === 0) {
×
34
            throw new InvalidArgumentException('At least one lock name must be provided.');
×
35
        }
36

37
        // sort lock names to minimize deadlock risk
38
        sort($lockNames);
×
39
        $this->lockNames = $lockNames;
×
40
    }
41

42
    #[Override]
43
    public function acquire(): void
44
    {
45
        foreach ($this->lockNames as $lockName) {
×
46
            $this->store->acquire($lockName);
×
47
        }
48
    }
49

50
    #[Override]
51
    public function tryAcquire(): bool
52
    {
53
        $acquiredLockNames = [];
×
54

55
        foreach ($this->lockNames as $lockName) {
×
56
            if ($this->store->tryAcquire($lockName)) {
×
57
                $acquiredLockNames[] = $lockName;
×
58
            } else {
59
                try {
60
                    foreach ($acquiredLockNames as $acquiredLockName) {
×
61
                        $this->store->release($acquiredLockName);
×
62
                    }
63
                } catch (LockReleaseException $e) {
×
64
                    throw LockAcquireException::forMultiLock('Failed to release previously acquired lock', $e);
×
65
                }
66

67
                return false;
×
68
            }
69
        }
70

71
        return true;
×
72
    }
73

74
    #[Override]
75
    public function tryAcquireWithTimeout(int $seconds): bool
76
    {
77
        if ($seconds <= 0) {
×
78
            throw new InvalidArgumentException('Timeout must be a positive integer.');
×
79
        }
80

81
        $acquiredLockNames = [];
×
82
        $startTime = microtime(true);
×
83

84
        foreach ($this->lockNames as $lockName) {
×
85
            $elapsedSeconds = (int) (microtime(true) - $startTime); // rounds down
×
86
            $secondsLeft = max(0, $seconds - $elapsedSeconds); // zero or more
×
87

88
            $lockAcquired = ($secondsLeft === 0)
×
89
                ? $this->store->tryAcquire($lockName)
×
90
                : $this->store->tryAcquireWithTimeout($lockName, $secondsLeft);
×
91

92
            if ($lockAcquired) {
×
93
                $acquiredLockNames[] = $lockName;
×
94
            } else {
95
                try {
96
                    foreach ($acquiredLockNames as $acquiredLockName) {
×
97
                        $this->store->release($acquiredLockName);
×
98
                    }
99
                } catch (LockReleaseException $e) {
×
100
                    throw LockAcquireException::forMultiLock('Failed to release previously acquired lock', $e);
×
101
                }
102

103
                return false;
×
104
            }
105
        }
106

107
        return true;
×
108
    }
109

110
    #[Override]
111
    public function release(): void
112
    {
113
        foreach ($this->lockNames as $lockName) {
×
114
            $this->store->release($lockName);
×
115
        }
116
    }
117
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc