f170465ea2dde500e479644aef8a893acbddfc22
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Locking / SemaphoreLockStrategy.php
1 <?php
2 namespace TYPO3\CMS\Core\Locking;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Locking\Exception\LockAcquireException;
18 use TYPO3\CMS\Core\Locking\Exception\LockCreateException;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20
21 /**
22 * Semaphore locking
23 */
24 class SemaphoreLockStrategy implements LockingStrategyInterface
25 {
26 const FILE_LOCK_FOLDER = 'typo3temp/var/locks/';
27
28 /**
29 * @var mixed Identifier used for this lock
30 */
31 protected $id;
32
33 /**
34 * @var resource Semaphore Resource used for this lock
35 */
36 protected $resource;
37
38 /**
39 * @var string
40 */
41 protected $filePath = '';
42
43 /**
44 * @var bool TRUE if lock is acquired
45 */
46 protected $isAcquired = false;
47
48 /**
49 * @param string $subject ID to identify this lock in the system
50 * @throws LockCreateException
51 */
52 public function __construct($subject)
53 {
54 $path = PATH_site . self::FILE_LOCK_FOLDER;
55 if (!is_dir($path)) {
56 // Not using mkdir_deep on purpose here, if typo3temp/var itself
57 // does not exist, this issue should be solved on a different
58 // level of the application.
59 if (!GeneralUtility::mkdir($path)) {
60 throw new LockCreateException('Cannot create directory ' . $path, 1460976250);
61 }
62 }
63 if (!is_writable($path)) {
64 throw new LockCreateException('Cannot write to directory ' . $path, 1460976320);
65 }
66 $this->filePath = $path . 'sem_' . md5((string)$subject);
67 touch($this->filePath);
68 $this->id = ftok($this->filePath, 'A');
69 if ($this->id === false) {
70 throw new LockCreateException('Cannot create key for semaphore using path ' . $this->filePath, 1396278734);
71 }
72 }
73
74 /**
75 * Destructor
76 */
77 public function __destruct()
78 {
79 $this->release();
80 // We do not call sem_remove() since this would remove the resource for other processes,
81 // we leave that to the system. This is not clean, but there's no other way to determine when
82 // a semaphore is no longer needed as a website is generally running endlessly
83 // and we have no way to detect if there is a process currently waiting on that lock
84 // or if the server is shutdown
85 }
86
87 /**
88 * Release the lock
89 *
90 * @return bool Returns TRUE on success or FALSE on failure
91 */
92 public function release()
93 {
94 if (!$this->isAcquired) {
95 return true;
96 }
97 $this->isAcquired = false;
98 return (bool)@sem_release($this->resource);
99 }
100
101 /**
102 * Get status of this lock
103 *
104 * @return bool Returns TRUE if lock is acquired by this locker, FALSE otherwise
105 */
106 public function isAcquired()
107 {
108 return $this->isAcquired;
109 }
110
111 /**
112 * @return int LOCK_CAPABILITY_* elements combined with bit-wise OR
113 */
114 public static function getCapabilities()
115 {
116 if (function_exists('sem_get')) {
117 return self::LOCK_CAPABILITY_EXCLUSIVE;
118 }
119 return 0;
120 }
121
122 /**
123 * Try to acquire a lock
124 *
125 * @param int $mode LOCK_CAPABILITY_EXCLUSIVE
126 * @return bool Returns TRUE if the lock was acquired successfully
127 * @throws LockAcquireException if a semaphore could not be retrieved
128 */
129 public function acquire($mode = self::LOCK_CAPABILITY_EXCLUSIVE)
130 {
131 if ($this->isAcquired) {
132 return true;
133 }
134
135 $this->resource = sem_get($this->id, 1);
136 if ($this->resource === false) {
137 throw new LockAcquireException('Unable to get semaphore with id ' . $this->id, 1313828196);
138 }
139
140 $this->isAcquired = (bool)sem_acquire($this->resource);
141 return $this->isAcquired;
142 }
143
144 /**
145 * @return int Returns a priority for the method. 0 to 100, 100 is highest
146 */
147 public static function getPriority()
148 {
149 return 25;
150 }
151
152 /**
153 * Destroys the resource associated with the lock
154 *
155 * @return void
156 */
157 public function destroy()
158 {
159 if ($this->resource) {
160 sem_remove($this->resource);
161 @unlink($this->filePath);
162 }
163 }
164 }