aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/includes/lock/db.php
blob: e9885285d94f0f4b97e13339891fede5d814917d (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
154
155
156
157
158
<?php
/**
*
* @package phpBB3
* @copyright (c) 2010 phpBB Group
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/

/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
	exit;
}

/**
* Database locking class
* @package phpBB3
*/
class phpbb_lock_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 array
	*/
	private $config;

	/**
	* A database connection
	* @var dbal
	*/
	private $db;

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

	/**
	* Tries to acquire the cron lock by updating
	* the 'cron_lock' 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 lock()
	{
		if ($this->locked)
		{
			return true;
		}

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

		// make sure cron doesn't run multiple times in parallel
		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();

		$sql = 'UPDATE ' . CONFIG_TABLE . "
			SET config_value = '" . $this->db->sql_escape($this->unique_id) . "'
			WHERE config_name = '" . $this->db->sql_escape($this->config_name) . "'
				AND config_value = '" . $this->db->sql_escape($lock_value) . "'";
		$this->db->sql_query($sql);

		if ($this->db->sql_affectedrows())
		{
			$this->locked = true;
		}
		else
		{
			// another cron process altered the table between script start and
			// UPDATE query so return false
			$this->locked = false;
		}

		return $this->locked;
	}

	/**
	* Releases the cron lock.
	*
	* The lock must have been previously obtained, that is, lock() call
	* was issued and returned true.
	*
	* Note: Attempting to release a cron lock that is already released,
	* that is, calling unlock() multiple times, is harmless.
	*
	* @return void
	*/
	public function unlock()
	{
		if ($this->locked)
		{
			$sql = 'UPDATE ' . CONFIG_TABLE . "
				SET config_value = '0'
				WHERE config_name = '" . $this->db->sql_escape($this->config_name) . "'
					AND config_value = '" . $this->db->sql_escape($this->unique_id) . "'";
			$this->db->sql_query($sql);

			$this->locked = false;
		}
	}
}