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