0byt3m1n1
Path:
/
data
/
applications
/
aps
/
gallery
/
2.2-08
/
htdocs
/
modules
/
core
/
classes
/
[
Home
]
File: GalleryLockSystem.class
<?php /* * Gallery - a web based photo album viewer and editor * Copyright (C) 2000-2007 Bharat Mediratta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ /** * This is an interface for all locking systems. You must extend it and * implement all of its methods in order to introduce a new locking system. * * @package GalleryCore * @subpackage Classes * @author Bharat Mediratta <bharat@menalto.com> * @author Alan Harder <alan.harder@sun.com> * @version $Revision: 15513 $ * @abstract */ class GalleryLockSystem { /** * Information about all the locks we currently hold. * array of (lockId => array('lockId' => lock id, 'type' => LOCK_READ or LOCK_WRITE, * 'ids' => array of locked object id => additional data)) * @var array * @access protected */ var $_locks; /** * List of locks that are pending release. * array of (lockId => array('lockId' => lock id, 'type' => LOCK_READ or LOCK_WRITE, * 'ids' => array of locked object id => additional data)) * @var array * @access protected */ var $_releaseQueue; function GalleryLockSystem() { $this->_locks = $this->_releaseQueue = array(); } /** * Read lock one or more objects * * @param mixed $ids array of ids to lock, or single int id * @param int $timeout (optional) how many seconds to wait for the lock before giving up * @return array object GalleryStatus a status code * mixed the lock id */ function acquireReadLock($ids, $timeout=10) { if (!is_array($ids)) { $ids = array($ids); } /* * Close out write locks pending release that cover any of these ids * (this does leave a window where another request could grab a lock) */ foreach ($this->_releaseQueue as $lockId => $lock) { if ($lock['type'] != LOCK_WRITE) { continue; } $foundIds = array_intersect(array_keys($lock['ids']), $ids); if (count($foundIds) == count($lock['ids'])) { /* Release this entire lock */ $ret = $this->_releaseLocksNow(array($lockId => $lock)); if ($ret) { return array($ret, null); } unset($this->_releaseQueue[$lockId]); } else if (!empty($foundIds)) { /* Remove ids from this lock */ $ret = $this->_removeObjectsFromLock($this->_releaseQueue[$lockId], $foundIds); if ($ret) { return array($ret, null); } } } list ($ret, $lockId) = $this->_acquireLock($ids, $timeout, LOCK_READ); if ($ret) { return array($ret, null); } return array(null, $lockId); } /** * Write lock one or more objects * * @param mixed $ids array of ids to lock, or single int id * @param int $timeout (optional) how many seconds to wait for the lock before giving up * @return array object GalleryStatus a status code * mixed the lock id */ function acquireWriteLock($ids, $timeout=10) { if (!is_array($ids)) { $ids = array($ids); } /* * Check for locks pending release that cover any of these ids. * Move write locks between lockIds to keep lock in place; close out read locks * (so read->write does leave a window where another request could grab a lock) */ $relock = array(); foreach ($this->_releaseQueue as $lockId => $lock) { $foundIds = array_intersect(array_keys($lock['ids']), $ids); if (empty($foundIds)) { continue; } if ($lock['type'] == LOCK_WRITE) { $relock[$lockId] = $foundIds; $ids = array_diff($ids, $foundIds); } else if (count($foundIds) == count($lock['ids'])) { /* Release this entire read lock */ $ret = $this->_releaseLocksNow(array($lockId => $lock)); if ($ret) { return array($ret, null); } unset($this->_releaseQueue[$lockId]); } else if (!empty($foundIds)) { /* Remove ids from this read lock */ $ret = $this->_removeObjectsFromLock($this->_releaseQueue[$lockId], $foundIds); if ($ret) { return array($ret, null); } } } if (!empty($ids)) { list ($ret, $lockId) = $this->_acquireLock($ids, $timeout, LOCK_WRITE); if ($ret) { return array($ret, null); } } else { list ($ret, $lockId) = $this->_newLockId(); if ($ret) { return array($ret, null); } $this->_locks[$lockId] = array('lockId' => $lockId, 'type' => LOCK_WRITE); } if (!empty($relock)) { $ret = $this->_moveObjectsBetweenLocks($relock, $lockId); if ($ret) { return array($ret, null); } } return array(null, $lockId); } /** * Extending class must implement this function to actually acquire a lock. * * @param array $ids of object ids * @param int $timeout how many seconds to wait for the lock before giving up * @param int $lockType LOCK_READ or LOCK_WRITE * @return array object GalleryStatus a status code * int the lock id * @access protected * @abstract */ function _acquireLock($ids, $timeout, $lockType) { return array(GalleryCoreApi::error(ERROR_UNIMPLEMENTED), null); } /** * Return true if the given id is read locked or write locked * * @param int $id an object id * @return boolean true if the object is read locked */ function isReadLocked($id) { foreach ($this->_locks as $lockId => $lock) { if (isset($lock['ids'][$id])) { return true; } } return false; } /** * Return true if the given id is write locked * * @param int $id an object id * @return boolean true if the object is write locked */ function isWriteLocked($id) { foreach ($this->_locks as $lockId => $lock) { if ($lock['type'] == LOCK_WRITE && isset($lock['ids'][$id])) { return true; } } return false; } /** * Release the given lock(s) * (queue the specified locks for later release on transactional databases or release them * immediately if transactions are not supported) * * @param mixed $lockIds array of lock ids, or a single lock id * @return object GalleryStatus a status code */ function releaseLocks($lockIds) { global $gallery; $storage =& $gallery->getStorage(); if (!is_array($lockIds)) { $lockIds = array($lockIds); } /* Remove any empty or invalid elements */ foreach ($lockIds as $key => $lockId) { if (empty($lockId) || !isset($this->_locks[$lockId])) { unset($lockIds[$key]); } } /* Queue locks for release */ foreach ($lockIds as $lockId) { $this->_releaseQueue[$lockId] = $this->_locks[$lockId]; unset($this->_locks[$lockId]); } if (!$storage->isTransactional()) { $ret = $this->releaseQueue(); if ($ret) { return $ret; } } return null; } /** * Cleanup any locks queued for release * * @return object GalleryStatus a status code */ function releaseQueue() { if (!empty($this->_releaseQueue)) { $ret = $this->_releaseLocksNow($this->_releaseQueue); if ($ret) { return $ret; } $this->_releaseQueue = array(); } return null; } /** * Release the given locks now * * @param array $locks of lockId => lock * @return object GalleryStatus a status code * @access protected * @abstract */ function _releaseLocksNow($locks) { return GalleryCoreApi::error(ERROR_UNIMPLEMENTED); } /** * Remove some object ids from the given lock. * * @param array $lock * @param array $ids ids to remove * @return object GalleryStatus a status code * @access protected * @abstract */ function _removeObjectsFromLock(&$lock, $ids) { return GalleryCoreApi::error(ERROR_UNIMPLEMENTED); } /** * Move ids from givens locks into a new lock. * Remove any locks that now have no objects remaining. * * @param array $relock of (lockId => array of ids) * @param int $newLockId move ids into this lock * @return object GalleryStatus a status code * @access protected */ function _moveObjectsBetweenLocks($relock, $newLockId) { foreach ($relock as $lockId => $ids) { foreach ($ids as $id) { $this->_locks[$newLockId]['ids'][$id] = $this->_releaseQueue[$lockId]['ids'][$id]; unset($this->_releaseQueue[$lockId]['ids'][$id]); } if (empty($this->_releaseQueue[$lockId]['ids'])) { unset($this->_releaseQueue[$lockId]); } } return null; } /** * Generate an id for a new lock. * * @return array object GalleryStatus a status code * mixed lockId * @access protected * @abstract */ function _newLockId() { return GalleryCoreApi::error(ERROR_UNIMPLEMENTED); } /** * Release any locks that we're holding * * @return object GalleryStatus a status code */ function releaseAllLocks() { $ret = $this->releaseLocks(array_keys($this->_locks)); if ($ret) { return $ret; } return null; } /** * Refresh all the locks that we hold so that they aren't accidentally considered expired * * @param int $freshUntil the new "fresh until" timestamp * @return object GalleryStatus a status code * @abstract */ function refreshLocks($freshUntil) { return GalleryCoreApi::error(ERROR_UNIMPLEMENTED); } /** * Return the ids of all the locks we hold * * @return array lock ids */ function getLockIds() { return array_keys($this->_locks); } } ?>