aboutsummaryrefslogtreecommitdiff
blob: eea919f8f79c17c93f2f3c5b17b0aea7b6aa5e71 (plain)
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
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/

namespace phpbb\lock;

/**
* Database locking class
*/
class db
{
	/**
	* Name of the config variable this lock uses
	* @var string
	*/
	private $config_name;

	/**
	* Unique identifier for this lock.
	*
	* @var string
	*/
	private $unique_id;

	/**
	* Stores the state of this lock
	* @var bool
	*/
	private $locked;

	/**
	* The phpBB configuration
	* @var \phpbb\config\config
	*/
	private $config;

	/**
	* A database connection
	* @var \phpbb\db\driver\driver_interface
	*/
	private $db;

	/**
	* Creates a named released instance of the lock.
	*
	* You have to call acquire() to actually create the lock.
	*
	* @param	string								$config_name	A config variable to be used for locking
	* @param	\phpbb\config\config				$config			The phpBB configuration
	* @param	\phpbb\db\driver\driver_interface	$db				A database connection
	*/
	public function __construct($config_name, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db)
	{
		$this->config_name = $config_name;
		$this->config = $config;
		$this->db = $db;
	}

	/**
	* Tries to acquire the lock by updating
	* the configuration variable in the database.
	*
	* As a lock may only be held by one process at a time, lock
	* acquisition may fail if another process is holding the lock
	* or if another process obtained the lock but never released it.
	* Locks are forcibly released after a timeout of 1 hour.
	*
	* @return	bool			true if lock was acquired
	*							false otherwise
	*/
	public function acquire()
	{
		if ($this->locked)
		{
			return false;
		}

		if (!isset($this->config[$this->config_name]))
		{
			$this->config->set($this->config_name, '0', false);
		}
		$lock_value = $this->config[$this->config_name];

		// make sure lock cannot be acquired by multiple processes
		if ($lock_value)
		{
			// if the other process is running more than an hour already we have to assume it
			// aborted without cleaning the lock
			$time = explode(' ', $lock_value);
			$time = $time[0];

			if ($time + 3600 >= time())
			{
				return false;
			}
		}

		$this->unique_id = time() . ' ' . unique_id();

		// try to update the config value, if it was already modified by another
		// process we failed to acquire the lock.
		$this->locked = $this->config->set_atomic($this->config_name, $lock_value, $this->unique_id, false);

		if ($this->locked == true)
		{
			if ($this->config->ensure_lock($this->config_name, $this->unique_id))
			{
				return true;
			}
		}
		return $this->locked;
	}

	/**
	* Does this process own the lock?
	*
	* @return	bool			true if lock is owned
	*							false otherwise
	*/
	public function owns_lock()
	{
		return (bool) $this->locked;
	}

	/**
	* Releases the lock.
	*
	* The lock must have been previously obtained, that is, acquire() call
	* was issued and returned true.
	*
	* Note: Attempting to release a lock that is already released,
	* that is, calling release() multiple times, is harmless.
	*
	* @return null
	*/
	public function release()
	{
		if ($this->locked)
		{
			$this->config->set_atomic($this->config_name, $this->unique_id, '0', false);
			$this->locked = false;
		}
	}
}