/*
GASH 2
DBSessionLockManager.java
The GANYMEDE object storage system.
Created: 15 March 2002
Module By: Jonathan Abbey, jonabbey@arlut.utexas.edu
-----------------------------------------------------------------------
Ganymede Directory Management System
Copyright (C) 1996-2014
The University of Texas at Austin
Ganymede is a registered trademark of The University of Texas at Austin
Contact information
Web site: http://www.arlut.utexas.edu/gash2
Author Email: ganymede_author@arlut.utexas.edu
Email mailing list: ganymede@arlut.utexas.edu
US Mail:
Computer Science Division
Applied Research Laboratories
The University of Texas at Austin
PO Box 8029, Austin TX 78713-8029
Telephone: (512) 835-3200
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, see <http://www.gnu.org/licenses/>.
*/
package arlut.csd.ganymede.server;
import java.util.HashSet;
import java.util.List;
/*------------------------------------------------------------------------------
class
DBSessionLockManager
------------------------------------------------------------------------------*/
/**
* <p>This class coordinates lock activity for a server-side {@link
* arlut.csd.ganymede.server.DBSession DBSession} object. This class
* handles the logic to make sure that a session does not grant a new
* lock that would conflict with a lock already held by the same
* session.</p>
*/
public final class DBSessionLockManager {
private HashSet<DBLock> lockSet = new HashSet<DBLock>(31);
private DBSession session;
/* -- */
public DBSessionLockManager(DBSession session)
{
this.session = session;
}
/**
* <p>Returns true if the session's lock is currently locked, false
* otherwise.</p>
*/
public synchronized boolean isLocked(DBLock lock)
{
if (lock == null)
{
throw new IllegalArgumentException("bad param to isLocked()");
}
if (!lockSet.contains(lock))
{
return false;
}
else
{
return lock.isLocked();
}
}
/**
* <p>Establishes a read lock for the {@link
* arlut.csd.ganymede.server.DBObjectBase DBObjectBases} in
* bases.</p>
*
* <p>The thread calling this method will block until the read lock
* can be established. If any of the {@link
* arlut.csd.ganymede.server.DBObjectBase DBObjectBases} in the
* bases vector have transactions currently committing, the
* establishment of the read lock will be suspended until all such
* transactions are committed.</p>
*
* <p>All viewDBObject calls done within the context of an open read
* lock will be transaction consistent. Other sessions may pull
* objects out for editing during the course of the session's read
* lock, but no visible changes will be made to those ObjectBases
* until the read lock is released.</p>
*/
public synchronized DBReadLock openReadLock(List<DBObjectBase> bases) throws InterruptedException
{
// we'll never be able to establish a read lock if we have to
// wait for this thread to release an existing write lock..
for (DBLock oldLock: lockSet)
{
if (oldLock instanceof DBWriteLock)
{
if (oldLock.overlaps(bases))
{
throw new InterruptedException("Can't establish read lock, session " + session.getID() +
" already has overlapping write lock:\n" +
oldLock.toString());
}
}
}
DBReadLock lock = new DBReadLock(session.getStore(), bases);
lockSet.add(lock);
lock.establish(session.getKey()); // block
return lock;
}
/**
* <p>openReadLock establishes a read lock for the entire
* {@link arlut.csd.ganymede.server.DBStore DBStore}.</p>
*
* <p>The thread calling this method will block until the read lock
* can be established. If transactions on the database are
* currently committing, the establishment of the read lock will be
* suspended until all such transactions are committed.</p>
*
* <p>All viewDBObject calls done within the context of an open read
* lock will be transaction consistent. Other sessions may pull
* objects out for editing during the course of the session's read
* lock, but no visible changes will be made to those ObjectBases
* until the read lock is released.</p>
*/
public synchronized DBReadLock openReadLock() throws InterruptedException
{
// we'll never be able to establish a read lock if we have to
// wait for this thread to release an existing write lock..
for (DBLock oldLock: lockSet)
{
if (oldLock instanceof DBWriteLock)
{
throw new InterruptedException("Can't establish global read lock, session " + session.getID() +
" already has write lock:\n" +
oldLock.toString());
}
}
DBReadLock lock = new DBReadLock(session.getStore());
lockSet.add(lock);
lock.establish(session.getKey());
return lock;
}
/**
* <p>Establishes a write lock for the {@link
* arlut.csd.ganymede.server.DBObjectBase DBObjectBases} in
* bases.</p>
*
* <p>The thread calling this method will block until the write lock
* can be established. If this DBSession already possesses a write lock,
* read lock, or dump lock, the openWriteLock() call will fail with
* an InterruptedException.</p>
*
* <p>If one or more different DBSessions (besides this) have locks in
* place that would block acquisition of the write lock, this method
* will block until the lock can be acquired.</p>
*/
public synchronized DBWriteLock openWriteLock(List<DBObjectBase> bases) throws InterruptedException
{
// we'll never be able to establish a write lock if we have to
// wait for this thread to release read, write, or dump locks..
// and we must not have pre-existing locks on bases not
// overlapping with our bases parameter either, or else we risk
// dead-lock later on..
if (lockSet.size() != 0)
{
StringBuilder resultBuffer = new StringBuilder();
for (DBLock lock: lockSet)
{
resultBuffer.append(lock.toString());
resultBuffer.append("\n");
}
throw new InterruptedException("Can't establish write lock, session " + session.getID() +
" already has locks:\n" +
resultBuffer.toString());
}
DBWriteLock lock = new DBWriteLock(session.getStore(), bases);
lockSet.add(lock);
lock.establish(session.getKey());
return lock;
}
/**
* <p>This method establishes a dump lock on all object bases in
* this Ganymede server.</p>
*/
public synchronized DBDumpLock openDumpLock() throws InterruptedException
{
// we'll never be able to establish a dump lock if we have to
// wait for this thread to release an existing write lock..
for (DBLock oldLock: lockSet)
{
if (oldLock instanceof DBWriteLock)
{
throw new InterruptedException("Can't establish global dump lock, session " + session.getID() +
" already has write lock:\n" +
oldLock.toString());
}
}
DBDumpLock lock = new DBDumpLock(session.getStore());
lockSet.add(lock);
lock.establish(session.getKey());
return lock;
}
/**
* <p>releaseLock releases a particular lock held by this session.
* This method will not force a lock being held by another thread to
* drop out of its establish method.. it is intended to be called by
* the same thread that established the lock.</p>
*/
public synchronized void releaseLock(DBLock lock)
{
if (!lockSet.contains(lock))
{
throw new IllegalArgumentException("lock " + lock.toString() + " not held by this session");
}
lock.release();
lockSet.remove(lock);
}
/**
* <p>releaseAllLocks() releases all locks held by this
* session.</p>
*/
public synchronized void releaseAllLocks()
{
for (DBLock lock: lockSet)
{
lock.abort(); // blocks until the lock can be cleared
}
lockSet.clear();
}
}