-
Notifications
You must be signed in to change notification settings - Fork 6
/
FileLock.php
130 lines (106 loc) · 3.36 KB
/
FileLock.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
<?php
namespace TH\Lock;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
class FileLock implements Lock
{
const EXCLUSIVE = true;
const SHARED = false;
const BLOCKING = true;
const NON_BLOCKING = false;
private $lock_file;
private $exclusive;
private $blocking;
private $fh;
private $remove_on_release;
private $logger;
/**
* @param string $lock_file path to file
* @param boolean $exclusive true for an exclusive lock, false for shared one
* @param boolean $blocking true to wait for lock to be available,
* false to throw exception instead of waiting
* @param boolean $remove_on_release remove file on release if no other lock remains
* @param LoggerInterface $logger
*/
public function __construct(
$lock_file,
$exclusive = FileLock::EXCLUSIVE,
$blocking = FileLock::NON_BLOCKING,
$remove_on_release = false,
LoggerInterface $logger = null
) {
$this->lock_file = $lock_file;
$this->exclusive = $exclusive;
$this->blocking = $blocking;
$this->remove_on_release = $remove_on_release;
$this->logger = $logger ?: new NullLogger;
}
/**
* @inherit
*/
public function acquire()
{
if ($this->exclusive === FileLock::EXCLUSIVE) {
$lock_type = "exclusive";
$operation = LOCK_EX;
} else {
$lock_type = "shared";
$operation = LOCK_SH;
}
if ($this->blocking === FileLock::NON_BLOCKING) {
$operation |= LOCK_NB;
}
$this->tryAcquire($operation, $lock_type);
}
/**
* try to acquire lock on file, throw in case of faillure
* @param int $operation
* @param string $lock_type lock type description
* @return void
* @see https://php.net/flock
*/
private function tryAcquire($operation, $lock_type)
{
$log_data = [
"lock_file" => $this->lock_file,
"lock_type" => $lock_type
];
if (!$this->flock($operation)) {
$this->logger->debug("could not acquire {lock_type} lock on {lock_file}", $log_data);
throw new RuntimeException(
"Could not acquire $lock_type lock on {$this->lock_file}"
);
}
$this->logger->debug("{lock_type} lock acquired on {lock_file}", $log_data);
}
public function release()
{
if ($this->fh === null) {
return;
}
if ($this->remove_on_release && $this->flock(LOCK_EX | LOCK_NB)) {
unlink($this->lock_file);
}
$this->flock(LOCK_UN);
fclose($this->fh);
$this->fh = null;
$this->logger->debug("{lock_type} lock released on {lock_file}", ["lock_file" => $this->lock_file]);
}
public function __destruct()
{
$this->release();
}
/**
* @return boolean
*/
private function flock($operation)
{
if ($this->fh === null) {
$this->fh = fopen($this->lock_file, "c");
}
if (!is_resource($this->fh)) {
throw new RuntimeException("Could not open lock file {$this->lock_file}");
}
return flock($this->fh, $operation);
}
}