package dmg.util.cdb ; import java.util.Hashtable; import java.util.Vector; public class CdbGLock implements CdbLockListener, CdbLockable { private static class LockEntry { private static class LockEntryDesc { private boolean _isWriteLock; private int _counter; private LockEntryDesc( boolean writeLock ){ _isWriteLock = writeLock ; _counter = 1 ; } public String toString(){ return "WriteLock="+_isWriteLock+ ";Counter="+_counter ; } } private Thread _thread; private int _position = -1 ; private LockEntryDesc [] _desc = new LockEntryDesc[2] ; private LockEntry( Thread thread , boolean writeLock ){ _thread = thread ; _desc[0] = new LockEntryDesc(writeLock); _position = 0 ; } public Thread getThread(){ return _thread ; } public boolean isWriteLocked(){ return _desc[_position]._isWriteLock ; } public void increment(){ _desc[_position]._counter++ ; } public int getCounter(){ return _desc[_position]._counter ; } public void upgrade() throws CdbLockException { if( _position > 0 ) { throw new CdbLockException("PANIC close=(_position==1)"); } // // prepare the new lock entry // _desc[++_position] = new LockEntryDesc(true); } public int degrade() throws CdbLockException { if( _position < 0 ) { throw new CdbLockException("PANIC close=(_position<0)"); } if( _desc[_position]._counter <= 0 ) { throw new CdbLockException("PANIC close=(_counter<=0)"); } _desc[_position]._counter-- ; if( _desc[_position]._counter <= 0 ){ _position-- ; return _position < 0 ? NOTHING_LEFT : WRITE_TO_READ ; }else { return NOTHING_CHANGED; } } public String toString(){ StringBuilder sb = new StringBuilder() ; sb.append(" +Thread : ").append(_thread).append('\n'); if( _position < 0 ) { sb.append("Not assigned ???\n"); } else{ for( int i = 0 ; i < 2 ; i ++ ){ if( _desc[i] == null ){ sb.append(" Desc[").append(i).append("]=null\n"); }else{ sb.append( (_position==i)?"*":" ") ; sb.append(" Desc[").append(i).append("]=") .append(_desc[i]).append('\n'); } } } return sb.toString() ; } } private static final int NOTHING_CHANGED = 0 ; private static final int WRITE_TO_READ = 1 ; private static final int NOTHING_LEFT = 2 ; private Vector<LockEntry> _list = new Vector<>(8) ; private Hashtable<Thread, LockEntry> _hash = new Hashtable<>() ; private CdbLockListener _listener; private CdbLockable _creator; public CdbGLock( CdbLockListener listener ){ _listener = listener ; } public CdbGLock(){ _listener = this ; } public CdbGLock( CdbLockable creator ){ _listener = this ; _creator = creator ; } public String toString(){ StringBuilder sb = new StringBuilder() ; for( int i = 0 ; i < _list.size() ; i++ ){ sb.append(_list.elementAt(i)) ; } return sb.toString() ; } @Override public synchronized void close( int flags ) throws CdbLockException { // System.out.println( "Asking for close : "+flags ) ; Thread ourThread = Thread.currentThread() ; LockEntry entry = _hash.get( ourThread ); if( entry == null ) { throw new CdbLockException("mutex not owned"); } // // decrement the usage count and check if we are still in use. // boolean wasWriteLocked = entry.isWriteLocked() ; switch( entry.degrade() ){ case WRITE_TO_READ : if( ( flags & CdbLockable.COMMIT ) > 0 ) { writeLockReleased(); } else { writeLockAborted(); } break ; case NOTHING_LEFT : _list.removeElement( entry ) ; _hash.remove( ourThread ) ; if( wasWriteLocked ){ if( ( flags & CdbLockable.COMMIT ) > 0 ) { writeLockReleased(); } else { writeLockAborted(); } }else{ readLockReleased() ; } break ; } notifyAll() ; if( _creator != null ) { _creator.close(CdbLockable.COMMIT); } } @Override public synchronized void open( int flags ) throws CdbLockException, InterruptedException { // System.out.println( "Asking for lock : "+flags ) ; // // make sure we are holding the container mutex // if( _creator != null ) { _creator.open(CdbLockable.READ); } // // are we already in the thread list // Thread ourThread = Thread.currentThread() ; LockEntry entry = _hash.get( ourThread ); if( entry != null ){ // // ok we got some kind of lock ( which one ? ) ; // if( ( ( flags & CdbLockable.WRITE ) > 0 ) && ! entry.isWriteLocked() ){ // // upgrade the entry // entry.upgrade() ; // // remove the entry from the read list and add to // the write waiting list. // _list.removeElement( entry ) ; _list.addElement( entry ) ; notifyAll() ; // // and now wait until we reached the bottom of the queue. // while(true){ if( _list.elementAt(0) == entry ) { break; } wait() ; } writeLockGranted() ; return ; } // // increment the lock thread counter // entry.increment() ; return ; } // // create a new thread lock entry and insert it // entry = new LockEntry(ourThread, (flags & CdbLockable.WRITE) > 0); _list.addElement( entry ); // // we need to destingueck between read and write locks // because we only allow one writer or many readers. // if( ( flags & CdbLockable.WRITE ) > 0 ){ ////////////////////////////////////////////////////////// // // // The writer // // // if( ( flags & CdbLockable.NON_BLOCKING ) > 0 ){ if( _list.elementAt(0) != entry ){ _list.removeElementAt(_list.size() - 1 ) ; throw new CdbLockException("Lock not granted") ; } } while(true){ if( _list.elementAt(0) == entry ) { break; } wait() ; } writeLockGranted() ; }else{ ////////////////////////////////////////////////////////// // // // The reader // // // if( ( flags & CdbLockable.NON_BLOCKING ) > 0 ){ int i; for( i = 0 ; ( i < _list.size() ) && ( ! (_list.elementAt(i)).isWriteLocked() ) && ( _list.elementAt(i) != entry ) ; i++ ) { } if( i == _list.size() ) { throw new CdbLockException("Panic : 1"); } if( _list.elementAt(i) != entry ){ _list.removeElementAt(_list.size() - 1 ) ; throw new CdbLockException("Lock not granted") ; } } while(true){ int i; for( i = 0 ; ( i < _list.size() ) && ( ! (_list.elementAt(i)).isWriteLocked() ) && ( _list.elementAt(i) != entry ) ; i++ ) { } if( i == _list.size() ) { throw new CdbLockException("Panic : 2"); } if( _list.elementAt(i) == entry ) { break; } wait() ; } readLockGranted() ; } _hash.put( ourThread , entry ) ; } @Override public void readLockGranted() {} @Override public void writeLockGranted(){} @Override public void readLockReleased(){} @Override public void writeLockReleased(){} @Override public void writeLockAborted() {} public static void main( String [] args ) throws Exception { CdbGLock lock = new CdbGLock() ; long start , opened , finished ; for( int i = 0 ; i < 3 ; i++ ){ start = System.currentTimeMillis() ; lock.open( CdbLockable.WRITE ) ; opened = System.currentTimeMillis() ; lock.close(CdbLockable.COMMIT) ; finished = System.currentTimeMillis() ; System.out.println( "Open : "+(opened-start) ) ; System.out.println( "Close : "+(finished-opened) ) ; } } }