alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
 * Microsystems, Inc. All Rights Reserved.
 */
package org.netbeans.mdr.util;

import java.util.HashMap;
import java.util.HashSet;
import org.netbeans.api.mdr.events.TransactionEvent;
import org.netbeans.mdr.NBMDRepositoryImpl;
import org.netbeans.mdr.storagemodel.MdrStorage;
import org.netbeans.mdr.persistence.StorageException;

/** This class implements mutex that controls storage transactions as well as 
 * MDR events firing.
 *
 * @author  Martin Matula
 * @version 0.1
 */
public class MultipleReadersMutex extends TransactionMutex {

    /* -------------------------------------------------------------------- */
    /* -- Private attributes ---------------------------------------------- */
    /* -------------------------------------------------------------------- */

    /**  The single writer thread or null. */
    private Thread writer = null;
    
    /** Counter of locks in the single writer thread. */
    private volatile int counter = 0;
    
    /** Maps reader threads to the counter of read locks acquired by them, i.e.
     *  to the counter of nested read transactions currently in process in
     *  the reader thread.
     *
     *  

Used to check if a write transaction is nested within a read * transaction. */ private final HashMap readers = new HashMap(10); private final HashSet mutators = new HashSet(2); /** transaction status */ private volatile boolean fail = false; /* -------------------------------------------------------------------- */ /* -- Constructor (public) -------------------------------------------- */ /* -------------------------------------------------------------------- */ /** Creates new TransactionMutex */ public MultipleReadersMutex(Object p1, Object p2, Object p3) { super(p1, p2, p3); } /* -------------------------------------------------------------------- */ /* -- Getters (public) ------------------------------------------------ */ /* -------------------------------------------------------------------- */ public boolean willFail() { return fail; } /** * Returns true if there is a write transaction going on. */ public boolean pendingChanges() { return counter > 0; } /* -------------------------------------------------------------------- */ /* -- transaction lock methods ---------------------------------------- */ /* -------------------------------------------------------------------- */ /** * Enters a new transaction. If an existing lock hinders the transaction * from being started, the method waits on the current object until the * lock is removed. * * @param writeAccess if true, a write transaction is entered, * otherwise a read transaction * @exception DebugException if a writable lock is requested * in a read-only lock */ public void enter(boolean writeAccess) { // Logger.getDefault().notify(Logger.INFORMATIONAL, new DebugException("enter: " + writeAccess)); Thread thread = Thread.currentThread(); int size; boolean isMutator; synchronized (this) { Counter rCount = (Counter) readers.get(thread); isMutator = mutators.contains(thread); while ((counter > 0 && writer != thread) || (writeAccess && (!mutators.containsAll(readers.keySet())))) { // (writeAccess && (((size = readers.size()) > 1) || // (size == 1 && readers.get(thread) == null)))) { // reader cannot enter a write transaction as it would not be possible to // prevent deadlocks (when two readers decide to enter write, they would // lock each other) if ((rCount != null) && !isMutator) { System.err.println("Writable lock nested in read-only lock."); Thread.dumpStack(); throw new DebugException("Writable lock nested in read-only lock."); } try { this.wait(); } catch (InterruptedException e) { Logger.getDefault().notify(Logger.INFORMATIONAL, e); } } if (writeAccess || counter > 0) { if (counter == 0) { writer = thread; if (isMutator) { counter = rCount.intValue(); } readers.remove(thread); mutators.remove(thread); } counter++; } else { if (rCount == null) { readers.put(thread, new Counter()); } else { rCount.inc(); } } } if (writeAccess && (counter == 1 || isMutator)) { start(); } } public synchronized void mutateToWrite() { Thread thread = Thread.currentThread(); if (writer != thread) { if (readers.get(thread) == null) { throw new DebugException("Cannot mutate transaction - no transaction opened."); } mutators.add(Thread.currentThread()); } enter(true); } /** * Leave a transaction. If an outermost (i.e. not nested) write * transaction is left, the listeners are informed and the transaction * is committed resp. rolled back. Finally all waiting threads are * notified. * * @param fail false indicates transaction * success, true its failure. Failure is allowed * only if the outermost transaction is a write transaction. * @exception DebugException if a transaction shall be left which was * not entered or if failure was indicated in read mode */ public boolean leave(boolean fail) { // Logger.getDefault().notify(Logger.INFORMATIONAL, new DebugException("leave: " + fail)); boolean result = false; try { this.fail |= fail; if (counter == 1) { // leaving the last write lock -> commit/rollback and send events result = true; end(this.fail); } } finally { synchronized (this) { if (counter > 0) { if ((--counter) == 0) { writer = null; this.fail = false; this.notifyAll(); } } else { Thread thread = Thread.currentThread(); Counter rCount = (Counter) readers.get(thread); if (rCount == null) { throw new DebugException("Error: leave() without enter()."); } else { if (rCount.dec() == 0) { try { result = true; readers.remove(thread); } finally { this.notifyAll(); } } } if (fail) throw new DebugException("Cannot fail when in read mode."); } } } return result; } /* -------------------------------------------------------------------- */ /* -- TransactionMutex.Counter (private inner class) ------------------ */ /* -------------------------------------------------------------------- */ /** * Instances Counter are integers with {@link #dec() decrement} * and {@link #inc() increment} methods. */ private static class Counter { private int value = 1; /** * Creates a new counter, initializing it to 1. */ public Counter() { } /** * Returns the counter value. */ public int intValue() { return value; } /** * Decrements the counter by 1 * * @return the new counter value * @exception Exception if the minimal counter value, namely * 0, was already reached */ public int dec() { if (value <= 0) throw new DebugException("Couter underflow: " + value); return (--value); } /** * Increments the counter by 1. * * @return the new counter value */ public int inc() { return (++value); } } }

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.