Proyectos de Subversion Moodle

Rev

Rev 1 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 1 Rev 11
Línea 23... Línea 23...
23
 *
23
 *
24
 * Postgres locking implementation using advisory locks. Some important points. Postgres has
24
 * Postgres locking implementation using advisory locks. Some important points. Postgres has
25
 * 2 different forms of lock functions, some accepting a single int, and some accepting 2 ints. This implementation
25
 * 2 different forms of lock functions, some accepting a single int, and some accepting 2 ints. This implementation
26
 * uses the 2 int version so that it uses a separate namespace from the session locking. The second note,
26
 * uses the 2 int version so that it uses a separate namespace from the session locking. The second note,
27
 * is because postgres uses integer keys for locks, we first need to map strings to a unique integer. This is done
27
 * is because postgres uses integer keys for locks, we first need to map strings to a unique integer. This is done
28
 * using a prefix of a sha1 hash converted to an integer.
28
 * using a prefix of a sha1 hash converted to an integer. There is a realistic chance of collisions by using this
-
 
29
 * prefix when locking multiple resources at the same time (multiple resource identifiers leading to the
-
 
30
 * same token/prefix). We need to deal with that.
29
 *
31
 *
30
 * @package   core
32
 * @package   core
31
 * @category  lock
33
 * @category  lock
32
 * @copyright Damyon Wiese 2013
34
 * @copyright Damyon Wiese 2013
33
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
Línea 44... Línea 46...
44
    protected $db;
46
    protected $db;
Línea 45... Línea 47...
45
 
47
 
46
    /** @var string $type Used to prefix lock keys */
48
    /** @var string $type Used to prefix lock keys */
Línea 47... Línea 49...
47
    protected $type;
49
    protected $type;
48
 
50
 
-
 
51
    /** @var int[] $resourcetokens Mapping of held locks (resource identifier => internal token) */
-
 
52
    protected $resourcetokens = [];
-
 
53
 
Línea 49... Línea 54...
49
    /** @var array $openlocks - List of held locks - used by auto-release */
54
    /** @var int[] $locksbytoken Mapping of held locks (db connection => internal token => number of locks held) */
50
    protected $openlocks = array();
55
    static protected $locksbytoken = [];
51
 
56
 
52
    /**
57
    /**
Línea 137... Línea 142...
137
     * Create and get a lock
142
     * Create and get a lock
138
     *
143
     *
139
     * @param string $resource - The identifier for the lock. Should use frankenstyle prefix.
144
     * @param string $resource - The identifier for the lock. Should use frankenstyle prefix.
140
     * @param int $timeout - The number of seconds to wait for a lock before giving up.
145
     * @param int $timeout - The number of seconds to wait for a lock before giving up.
141
     * @param int $maxlifetime - Unused by this lock type.
146
     * @param int $maxlifetime - Unused by this lock type.
142
     * @return boolean - true if a lock was obtained.
147
     * @return \core\lock\lock|boolean - An instance of \core\lock\lock if the lock was obtained, or false.
143
     */
148
     */
144
    public function get_lock($resource, $timeout, $maxlifetime = 86400) {
149
    public function get_lock($resource, $timeout, $maxlifetime = 86400) {
-
 
150
        $dbid = spl_object_id($this->db);
145
        $giveuptime = time() + $timeout;
151
        $giveuptime = time() + $timeout;
-
 
152
        $resourcekey = $this->type . '_' . $resource;
-
 
153
        $token = $this->get_index_from_key($resourcekey);
Línea 146... Línea -...
146
 
-
 
147
        $token = $this->get_index_from_key($this->type . '_' . $resource);
-
 
148
 
154
 
149
        if (isset($this->openlocks[$token])) {
155
        if (isset($this->resourcetokens[$resourcekey])) {
150
            return false;
156
            return false;
Línea -... Línea 157...
-
 
157
        }
-
 
158
 
-
 
159
        if (isset(self::$locksbytoken[$dbid][$token])) {
-
 
160
            // There is a hash collision, another resource identifier leads to the same token.
-
 
161
            // As we already hold an advisory lock for this token, we just increase the counter.
-
 
162
            self::$locksbytoken[$dbid][$token]++;
-
 
163
            $this->resourcetokens[$resourcekey] = $token;
-
 
164
            return new lock($resourcekey, $this);
151
        }
165
        }
152
 
166
 
153
        $params = [
167
        $params = [
154
            'locktype' => $this->dblockid,
168
            'locktype' => $this->dblockid,
Línea 165... Línea 179...
165
            }
179
            }
166
            // Try until the giveup time.
180
            // Try until the giveup time.
167
        } while (!$locked && time() < $giveuptime);
181
        } while (!$locked && time() < $giveuptime);
Línea 168... Línea 182...
168
 
182
 
169
        if ($locked) {
183
        if ($locked) {
-
 
184
            self::$locksbytoken[$dbid][$token] = 1;
170
            $this->openlocks[$token] = 1;
185
            $this->resourcetokens[$resourcekey] = $token;
171
            return new lock($token, $this);
186
            return new lock($resourcekey, $this);
172
        }
187
        }
173
        return false;
188
        return false;
Línea 174... Línea 189...
174
    }
189
    }
175
 
190
 
176
    /**
191
    /**
177
     * Release a lock that was previously obtained with @lock.
192
     * Release a lock that was previously obtained with @lock.
178
     * @param lock $lock - a lock obtained from this factory.
193
     * @param lock $lock - a lock obtained from this factory.
179
     * @return boolean - true if the lock is no longer held (including if it was never held).
194
     * @return boolean - true if the lock is no longer held (including if it was never held).
-
 
195
     */
-
 
196
    public function release_lock(lock $lock) {
-
 
197
        $dbid = spl_object_id($this->db);
-
 
198
        $resourcekey = $lock->get_key();
-
 
199
 
-
 
200
        if (isset($this->resourcetokens[$resourcekey])) {
-
 
201
            $token = $this->resourcetokens[$resourcekey];
-
 
202
        } else {
-
 
203
            return true;
-
 
204
        }
-
 
205
 
-
 
206
        if (self::$locksbytoken[$dbid][$token] > 1) {
-
 
207
            // There are multiple resource identifiers that lead to the same token.
-
 
208
            // We will unlock the token later when the last resource is released.
-
 
209
            unset($this->resourcetokens[$resourcekey]);
-
 
210
            self::$locksbytoken[$dbid][$token]--;
-
 
211
            return true;
-
 
212
        }
180
     */
213
 
181
    public function release_lock(lock $lock) {
214
        $params = [
-
 
215
            'locktype' => $this->dblockid,
182
        $params = array('locktype' => $this->dblockid,
216
            'token' => $token,
183
                        'token' => $lock->get_key());
217
        ];
184
        $result = $this->db->get_record_sql('SELECT pg_advisory_unlock(:locktype, :token) AS unlocked', $params);
218
        $result = $this->db->get_record_sql('SELECT pg_advisory_unlock(:locktype, :token) AS unlocked', $params);
185
        $result = $result->unlocked === 't';
219
        $result = $result->unlocked === 't';
-
 
220
        if ($result) {
186
        if ($result) {
221
            unset($this->resourcetokens[$resourcekey]);
187
            unset($this->openlocks[$lock->get_key()]);
222
            unset(self::$locksbytoken[$dbid][$token]);
188
        }
223
        }
Línea 189... Línea 224...
189
        return $result;
224
        return $result;
Línea 200... Línea 235...
200
     * Auto release any open locks on shutdown.
235
     * Auto release any open locks on shutdown.
201
     * This is required, because we may be using persistent DB connections.
236
     * This is required, because we may be using persistent DB connections.
202
     */
237
     */
203
    public function auto_release() {
238
    public function auto_release() {
204
        // Called from the shutdown handler. Must release all open locks.
239
        // Called from the shutdown handler. Must release all open locks.
205
        foreach ($this->openlocks as $key => $unused) {
240
        foreach ($this->resourcetokens as $resourcekey => $unused) {
206
            $lock = new lock($key, $this);
241
            $lock = new lock($resourcekey, $this);
207
            $lock->release();
242
            $lock->release();
208
        }
243
        }
209
    }
244
    }
Línea 210... Línea 245...
210
 
245