/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sun.jini.thread; /** * An Object to control the concurrent state. Allows multiple readers or * a single writer. Waiting writers have priority over new readers. * Waiting priority writers have priority over waiting regular writers. * A single thread cannot hold a lock more than once. * * @author Sun Microsystems, Inc. * */ public class ReadersWriter { /** The number of active readers */ private int activeReaders = 0; /** The number of waiting writers (both regular and priority) */ private int waitingWriters = 0; /** The number of waiting priority writers */ private int waitingPriorityWriters = 0; /** True if there is an active writer */ private boolean activeWriter = false; public ReadersWriter() {} /** Obtain a read lock. Multiple concurrent readers allowed. */ public synchronized void readLock() { while (activeWriter || waitingWriters != 0) { try { wait(); } catch (InterruptedException e) { throw new ConcurrentLockException( "read lock interrupted in thread"); } } activeReaders++; } /** Release a read lock. */ public synchronized void readUnlock() { activeReaders--; if (activeReaders == 0) notifyAll(); } /** Obtain a regular write lock. Only a single writer allowed at once. */ public synchronized void writeLock() { while (activeWriter || activeReaders != 0 || waitingPriorityWriters != 0) { try { waitingWriters++; try { wait(); } finally { waitingWriters--; } } catch (InterruptedException e) { throw new ConcurrentLockException( "write lock interrupted in thread"); } } activeWriter = true; } /** Obtain a priority write lock. Only a single writer allowed at once. */ public synchronized void priorityWriteLock() { while (activeWriter || activeReaders != 0) { try { waitingWriters++; waitingPriorityWriters++; try { wait(); } finally { waitingWriters--; waitingPriorityWriters--; } } catch (InterruptedException e) { throw new ConcurrentLockException( "write lock interrupted in thread"); } } activeWriter = true; } /** Release a (regular or priority) write lock. */ public synchronized void writeUnlock() { activeWriter = false; notifyAll(); } /** * Release a read lock, wait the given period of time or until * notified by notifier, then obtain a read lock again. * Throws ConcurrentLockException if the thread gets interrupted; * in that case, the read lock is still held. */ public void readerWait(Object notifier, long time) { try { synchronized (notifier) { readUnlock(); notifier.wait(time); } } catch (InterruptedException e) { throw new ConcurrentLockException( "read wait interrupted in thread"); } finally { readLock(); } } /** * Release a write lock, wait the given period of time or until * notified by notifier, then obtain a regular write lock again. * Throws ConcurrentLockException if the thread gets interrupted; * in that case, the write lock is still held. */ public void writerWait(Object notifier, long time) { try { synchronized (notifier) { writeUnlock(); notifier.wait(time); } } catch (InterruptedException e) { throw new ConcurrentLockException( "write wait interrupted in thread"); } finally { writeLock(); } } /** * Wake up any threads waiting on this notifier. In general, because * there is no wakeup-waiting flag, this method must be called from a * thread that holds a lock that conflicts with the lock that the waiter * was holding. If the waiter calls writerWait, then waiterNotify * can be called either under a readLock or a writeLock, but if the * waiter calls readerWait, then waiterNotify should only be called * under a writeLock. */ public void waiterNotify(Object notifier) { synchronized (notifier) { notifier.notifyAll(); } } /** InterruptedException transformed to a runtime exception. */ public static class ConcurrentLockException extends RuntimeException { private static final long serialVersionUID = 7027246653257040584L; public ConcurrentLockException() { super(); } public ConcurrentLockException(String s) { super(s); } } }