MDL-69521 core: Move all comments in code from 4.0 to 3.10
[moodle.git] / lib / classes / lock / file_lock_factory.php
CommitLineData
9843e5ec
DW
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Flock based file locking factory.
19 *
20 * The file lock factory returns file locks locked with the flock function. Works OK, except on some
21 * NFS, exotic shared storage and exotic server OSes (like windows). On windows, a second attempt to get a
22 * lock will block indefinitely instead of timing out.
23 *
24 * @package core
25 * @category lock
26 * @copyright Damyon Wiese 2013
27 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28 */
29
30namespace core\lock;
31
32defined('MOODLE_INTERNAL') || die();
33
34/**
35 * Flock based file locking factory.
36 *
37 * The file lock factory returns file locks locked with the flock function. Works OK, except on some
38 * NFS, exotic shared storage and exotic server OSes (like windows). On windows, a second attempt to get a
39 * lock will block indefinitely instead of timing out.
40 *
41 * @package core
42 * @category lock
43 * @copyright Damyon Wiese 2013
44 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
45 */
46class file_lock_factory implements lock_factory {
47
48 /** @var string $type - The type of lock, e.g. cache, cron, session. */
49 protected $type;
50
51 /** @var string $lockdirectory - Full system path to the directory used to store file locks. */
52 protected $lockdirectory;
53
54 /** @var boolean $verbose - If true, debugging info about the owner of the lock will be written to the lock file. */
55 protected $verbose;
56
57 /**
58 * Create this lock factory.
59 *
60 * @param string $type - The type, e.g. cron, cache, session
61 */
62 public function __construct($type) {
63 global $CFG;
64
65 $this->type = $type;
66 if (!isset($CFG->file_lock_root)) {
67 $this->lockdirectory = $CFG->dataroot . '/lock';
68 } else {
69 $this->lockdirectory = $CFG->file_lock_root;
70 }
71 $this->verbose = false;
72 if ($CFG->debugdeveloper) {
73 $this->verbose = true;
74 }
75 }
76
77 /**
78 * Return information about the blocking behaviour of the lock type on this platform.
79 * @return boolean - False if attempting to get a lock will block indefinitely.
80 */
81 public function supports_timeout() {
82 global $CFG;
83
84 return $CFG->ostype !== 'WINDOWS';
85 }
86
87 /**
88 * This lock type will be automatically released when a process ends.
89 * @return boolean - True
90 */
91 public function supports_auto_release() {
92 return true;
93 }
94
95 /**
96 * Is available.
d5511125 97 * @return boolean - True if preventfilelocking is not set - or the file_lock_root is not in dataroot.
9843e5ec
DW
98 */
99 public function is_available() {
34027f15 100 global $CFG;
d5511125
DW
101 $preventfilelocking = !empty($CFG->preventfilelocking);
102 $lockdirisdataroot = true;
103 if (!empty($CFG->file_lock_root) && strpos($CFG->file_lock_root, $CFG->dataroot) !== 0) {
104 $lockdirisdataroot = false;
105 }
106 return !$preventfilelocking || !$lockdirisdataroot;
9843e5ec
DW
107 }
108
109 /**
110 * Multiple locks for the same resource cannot be held from a single process.
4b71cdcd 111 *
709b46db 112 * @deprecated since Moodle 3.10.
9843e5ec
DW
113 * @return boolean - False
114 */
115 public function supports_recursion() {
4b71cdcd
MG
116 debugging('The function supports_recursion() is deprecated, please do not use it anymore.',
117 DEBUG_DEVELOPER);
9843e5ec
DW
118 return false;
119 }
120
121 /**
122 * Get some info that might be useful for debugging.
123 * @return boolean - string
124 */
125 protected function get_debug_info() {
126 return 'host:' . php_uname('n') . ', pid:' . getmypid() . ', time:' . time();
127 }
128
129 /**
130 * Get a lock within the specified timeout or return false.
131 * @param string $resource - The identifier for the lock. Should use frankenstyle prefix.
132 * @param int $timeout - The number of seconds to wait for a lock before giving up.
133 * @param int $maxlifetime - Unused by this lock type.
134 * @return boolean - true if a lock was obtained.
135 */
136 public function get_lock($resource, $timeout, $maxlifetime = 86400) {
9843e5ec
DW
137 $giveuptime = time() + $timeout;
138
139 $hash = md5($this->type . '_' . $resource);
140 $lockdir = $this->lockdirectory . '/' . substr($hash, 0, 2);
141
142 if (!check_dir_exists($lockdir, true, true)) {
143 return false;
144 }
145
146 $lockfilename = $lockdir . '/' . $hash;
147
148 $filehandle = fopen($lockfilename, "wb");
149
150 // Could not open the lock file.
151 if (!$filehandle) {
152 return false;
153 }
154
155 do {
156 // Will block on windows. So sad.
157 $wouldblock = false;
158 $locked = flock($filehandle, LOCK_EX | LOCK_NB, $wouldblock);
ecbe9206 159 if (!$locked && $wouldblock && $timeout > 0) {
9843e5ec
DW
160 usleep(rand(10000, 250000)); // Sleep between 10 and 250 milliseconds.
161 }
162 // Try until the giveup time.
163 } while (!$locked && $wouldblock && time() < $giveuptime);
164
165 if (!$locked) {
166 fclose($filehandle);
167 return false;
168 }
169 if ($this->verbose) {
170 fwrite($filehandle, $this->get_debug_info());
171 }
172 return new lock($filehandle, $this);
173 }
174
175 /**
176 * Release a lock that was previously obtained with @lock.
177 * @param lock $lock - A lock obtained from this factory.
178 * @return boolean - true if the lock is no longer held (including if it was never held).
179 */
180 public function release_lock(lock $lock) {
181 $handle = $lock->get_key();
182
183 if (!$handle) {
184 // We didn't have a lock.
185 return false;
186 }
187
188 $result = flock($handle, LOCK_UN);
189 fclose($handle);
190 return $result;
191 }
192
193 /**
194 * Extend a lock that was previously obtained with @lock.
80736a93 195 *
709b46db 196 * @deprecated since Moodle 3.10.
9843e5ec
DW
197 * @param lock $lock - not used
198 * @param int $maxlifetime - not used
199 * @return boolean - true if the lock was extended.
200 */
201 public function extend_lock(lock $lock, $maxlifetime = 86400) {
80736a93
MG
202 debugging('The function extend_lock() is deprecated, please do not use it anymore.',
203 DEBUG_DEVELOPER);
9843e5ec
DW
204 // Not supported by this factory.
205 return false;
206 }
207
208}