Passed
Push — master ( 811217...98b68e )
by
unknown
12:07
created

ResourceMutex::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Core\Locking;
17
18
use TYPO3\CMS\Core\Locking\Exception\LockAcquireException;
19
use TYPO3\CMS\Core\Locking\Exception\LockAcquireWouldBlockException;
20
use TYPO3\CMS\Core\Locking\Exception\LockCreateException;
21
22
/**
23
 * Wrapper for locking API that uses two locks
24
 * to not exhaust locking resources and still block properly
25
 */
26
class ResourceMutex
27
{
28
    /**
29
     * @var LockFactory
30
     */
31
    private $lockFactory;
32
33
    /**
34
     * Access lock
35
     *
36
     * @var LockingStrategyInterface[]
37
     */
38
    private $accessLocks = [];
39
40
    /**
41
     * Image processing lock
42
     *
43
     * @var LockingStrategyInterface[]
44
     */
45
    private $workerLocks = [];
46
47
    public function __construct(LockFactory $lockFactory)
48
    {
49
        $this->lockFactory = $lockFactory;
50
    }
51
52
    /**
53
     * Acquire a specific lock for the given scope
54
     * @see \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::acquireLock
55
     *
56
     * @param string $scope
57
     * @param string $key
58
     * @throws LockAcquireException
59
     * @throws LockCreateException
60
     */
61
    public function acquireLock(string $scope, string $key): void
62
    {
63
        $this->accessLocks[$scope] = $this->lockFactory->createLocker($scope);
64
65
        $this->workerLocks[$scope] = $this->lockFactory->createLocker(
66
            $key,
67
            LockingStrategyInterface::LOCK_CAPABILITY_EXCLUSIVE | LockingStrategyInterface::LOCK_CAPABILITY_NOBLOCK
68
        );
69
70
        do {
71
            if (!$this->accessLocks[$scope]->acquire()) {
72
                throw new \RuntimeException('Could not acquire access lock for "' . $scope . '"".', 1601923209);
73
            }
74
75
            try {
76
                $locked = $this->workerLocks[$scope]->acquire(
77
                    LockingStrategyInterface::LOCK_CAPABILITY_EXCLUSIVE | LockingStrategyInterface::LOCK_CAPABILITY_NOBLOCK
78
                );
79
            } catch (LockAcquireWouldBlockException $e) {
80
                // somebody else has the lock, we keep waiting
81
82
                // first release the access lock
83
                $this->accessLocks[$scope]->release();
84
                // now lets make a short break (100ms) until we try again, since
85
                // the page generation by the lock owner will take a while anyways
86
                usleep(100000);
87
                continue;
88
            }
89
            $this->accessLocks[$scope]->release();
90
            if ($locked) {
91
                break;
92
            }
93
            throw new \RuntimeException('Could not acquire image process lock for ' . $key . '.', 1601923215);
94
        } while (true);
95
    }
96
97
    /**
98
     * Release a worker specific lock
99
     *
100
     * @param string $scope
101
     * @throws LockAcquireException
102
     * @throws LockAcquireWouldBlockException
103
     */
104
    public function releaseLock(string $scope): void
105
    {
106
        if ($this->accessLocks[$scope] ?? null) {
107
            if (!$this->accessLocks[$scope]->acquire()) {
108
                throw new \RuntimeException('Could not acquire access lock for "' . $scope . '"".', 1601923319);
109
            }
110
111
            $this->workerLocks[$scope]->release();
112
            $this->workerLocks[$scope]->destroy();
113
            $this->workerLocks[$scope] = null;
114
115
            $this->accessLocks[$scope]->release();
116
            $this->accessLocks[$scope] = null;
117
        }
118
    }
119
}
120