forked from EverexIO/JSON-RPC
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProcessLock.php
More file actions
173 lines (163 loc) · 4.65 KB
/
ProcessLock.php
File metadata and controls
173 lines (163 loc) · 4.65 KB
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
<?php
/**
* @package AmiLabs/JSONRPC/Core
*/
namespace AmiLabs\JSONRPC\Core;
use RuntimeException;
/**
* Process lock class.
*
* <code>
* use RuntimeException;
* use AmiLabs\JSONRPC\Core\ProcessLock;
*
* try {
* $lock = new ProcessLock(
* 'path/to/lock',
* 60 * 5, // 5 minutes
* TRUE
* );
* } catch (RuntimeException $exception) {
* switch ($exception->getCode()) {
* case ProcessLock::PREV_LOCK_VALID:
* // Previous lock is valid, interrupt process.
* die;
* default:
* throw $exception;
* }
* }
*
* // Some long time loop
* while (TRUE) {
* // ...
* $lock->update();
* }
*
* unset($lock);
* </code>
*
* @author deepeloper ({@see https://github.com/deepeloper})
* @todo Implement support of storage layers (not only files)
*/
class ProcessLock
{
const PREV_LOCK_VALID = 1;
const CANNOT_DESTROY_PREV_LOCK = 2;
const LOCK_EXISTS = 3;
const CANNOT_CREATE_LOCK = 4;
const CANNOT_DELETE_LOCK = 5;
const LOCK_DESTROYED = 6;
const LOCK_WRONG_PID = 7;
const CANNOT_UPDATE_LOCK = 8;
/**
* Path to lock
*
* @var string
*/
protected $path;
/**
* Process Id
*
* @var string
*/
protected $pid;
/**
* @param string $path Path to lock
* @param string $ttl Time to live for previous lock (in seconds)
* @param bool $destroyPrev Flag specifying to destroy previous lock if expired
* @param string $pid Process Id
* @throws RuntimeException With codes
* self::PREV_LOCK_VALID,
* self::CANNOT_DESTROY_PREV_LOCK,
* self::LOCK_EXISTS,
* self::CANNOT_CREATE_LOCK
*/
public function __construct($path, $ttl, $destroyPrev = FALSE, $pid = '')
{
$this->path = (string)$path;
$this->pid =
'' === $pid
? mt_rand() . '.' . microtime(TRUE)
: (string)$pid;
if (file_exists($this->path)) {
if ((time() - filemtime($this->path)) < $ttl) {
throw new RuntimeException(
sprintf("Previous lock '%s' is still valid", $this->path),
self::PREV_LOCK_VALID
);
}
if ($destroyPrev) {
if (!@unlink($this->path)) {
throw new RuntimeException(
sprintf("Cannot destroy previous lock '%s'", $this->path),
self::CANNOT_DESTROY_PREV_LOCK
);
}
} else {
throw new RuntimeException(
sprintf("Lock '%s' already exists", $this->path),
self::LOCK_EXISTS
);
}
}
if (!@file_put_contents($this->path, $this->pid)) {
throw new RuntimeException(
sprintf("Cannot create lock '%s'", $this->path),
self::CANNOT_CREATE_LOCK
);
}
@chmod($this->path, 0666);
}
/**
* @throws RuntimeException With code self::CANNOT_DELETE_LOCK
*/
public function __destruct()
{
$this->validate();
if (!@unlink($this->path)) {
throw new RuntimeException(
sprintf("Cannot delete lock '%s'", $this->path),
self::CANNOT_DELETE_LOCK
);
}
}
/**
* Validates lock presense and pid.
*
* @return void
* @throws RuntimeException With codes
* self::LOCK_DESTROYED,
* self::LOCK_WRONG_PID
*/
public function validate()
{
if (!file_exists($this->path)) {
throw new RuntimeException(
sprintf("Lock '%s' destroyed", $this->path),
self::LOCK_DESTROYED
);
}
if (@file_get_contents($this->path) !== $this->pid) {
throw new RuntimeException(
sprintf("Lock '%s' contains other pid"),
self::LOCK_WRONG_PID
);
}
}
/**
* Update lock modification time.
*
* @return void
* @throws RuntimeException With code self::CANNOT_UPDATE_LOCK
*/
public function update()
{
$this->validate();
if (!@touch($this->path)) {
throw new RuntimeException(
sprintf("Cannot update lock '%s' time"),
self::CANNOT_UPDATE_LOCK
);
}
}
}