KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > mdr > util > MultipleReadersMutex


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.mdr.util;
20
21 import java.util.HashMap JavaDoc;
22 import java.util.HashSet JavaDoc;
23 import org.netbeans.api.mdr.events.TransactionEvent;
24 import org.netbeans.mdr.NBMDRepositoryImpl;
25 import org.netbeans.mdr.storagemodel.MdrStorage;
26 import org.netbeans.mdr.persistence.StorageException;
27
28 /** This class implements mutex that controls storage transactions as well as
29  * MDR events firing.
30  *
31  * @author Martin Matula
32  * @version 0.1
33  */

34 public class MultipleReadersMutex extends TransactionMutex {
35
36     /* -------------------------------------------------------------------- */
37     /* -- Private attributes ---------------------------------------------- */
38     /* -------------------------------------------------------------------- */
39
40     /** The single writer thread or <code>null</code>. */
41     private Thread JavaDoc writer = null;
42     
43     /** Counter of locks in the single writer thread. */
44     private volatile int counter = 0;
45     
46     /** Maps reader threads to the counter of read locks acquired by them, i.e.
47      * to the counter of nested read transactions currently in process in
48      * the reader thread.
49      *
50      * <p>Used to check if a write transaction is nested within a read
51      * transaction.
52      */

53     private final HashMap JavaDoc readers = new HashMap JavaDoc(10);
54     
55     private final HashSet JavaDoc mutators = new HashSet JavaDoc(2);
56     
57     /** transaction status */
58     private volatile boolean fail = false;
59     
60     /* -------------------------------------------------------------------- */
61     /* -- Constructor (public) -------------------------------------------- */
62     /* -------------------------------------------------------------------- */
63
64     /** Creates new TransactionMutex */
65     public MultipleReadersMutex(Object JavaDoc p1, Object JavaDoc p2, Object JavaDoc p3) {
66         super(p1, p2, p3);
67     }
68
69     /* -------------------------------------------------------------------- */
70     /* -- Getters (public) ------------------------------------------------ */
71     /* -------------------------------------------------------------------- */
72     
73     public boolean willFail() {
74         return fail;
75     }
76     
77     /**
78      * Returns <code>true</code> if there is a write transaction going on.
79      */

80     public boolean pendingChanges() {
81         return counter > 0;
82     }
83     
84     /* -------------------------------------------------------------------- */
85     /* -- transaction lock methods ---------------------------------------- */
86     /* -------------------------------------------------------------------- */
87      
88     /**
89      * Enters a new transaction. If an existing lock hinders the transaction
90      * from being started, the method waits on the current object until the
91      * lock is removed.
92      *
93      * @param writeAccess if <code>true</code>, a write transaction is entered,
94      * otherwise a read transaction
95      * @exception DebugException if a writable lock is requested
96      * in a read-only lock
97      */

98     public void enter(boolean writeAccess) {
99 // Logger.getDefault().notify(Logger.INFORMATIONAL, new DebugException("enter: " + writeAccess));
100
Thread JavaDoc thread = Thread.currentThread();
101         boolean isMutator;
102         synchronized (this) {
103             Counter rCount = (Counter) readers.get(thread);
104             isMutator = mutators.contains(thread);
105             while ((counter > 0 && writer != thread) ||
106                    (writeAccess && (!mutators.containsAll(readers.keySet())))) {
107     // (writeAccess && (((size = readers.size()) > 1) ||
108
// (size == 1 && readers.get(thread) == null)))) {
109

110                 // reader cannot enter a write transaction as it would not be possible to
111
// prevent deadlocks (when two readers decide to enter write, they would
112
// lock each other)
113
if ((rCount != null) && !isMutator) {
114                     System.err.println("Writable lock nested in read-only lock.");
115                     Thread.dumpStack();
116                     throw new DebugException("Writable lock nested in read-only lock.");
117                 }
118                 try {
119                     this.wait();
120                 } catch (InterruptedException JavaDoc e) {
121                     Logger.getDefault().notify(Logger.INFORMATIONAL, e);
122                 }
123             }
124             if (writeAccess || counter > 0) {
125                 if (counter == 0) {
126                     writer = thread;
127                     if (isMutator) {
128                         counter = rCount.intValue();
129                     }
130                     readers.remove(thread);
131                     mutators.remove(thread);
132                 }
133                 counter++;
134             } else {
135                 if (rCount == null) {
136                     readers.put(thread, new Counter());
137                 } else {
138                     rCount.inc();
139                 }
140             }
141         }
142         
143         if (writeAccess && (counter == 1 || isMutator)) {
144             start();
145         }
146     }
147     
148     public synchronized void mutateToWrite() {
149         Thread JavaDoc thread = Thread.currentThread();
150         if (writer != thread) {
151             if (readers.get(thread) == null) {
152                 throw new DebugException("Cannot mutate transaction - no transaction opened.");
153             }
154             mutators.add(Thread.currentThread());
155         }
156         enter(true);
157     }
158     
159     /**
160      * Leave a transaction. If an outermost (i.e. not nested) write
161      * transaction is left, the listeners are informed and the transaction
162      * is committed resp. rolled back. Finally all waiting threads are
163      * notified.
164      *
165      * @param fail <code>false</code> indicates transaction
166      * success, <code>true</code> its failure. Failure is allowed
167      * only if the outermost transaction is a write transaction.
168      * @exception DebugException if a transaction shall be left which was
169      * not entered or if failure was indicated in read mode
170      */

171     public boolean leave(boolean fail) {
172 // Logger.getDefault().notify(Logger.INFORMATIONAL, new DebugException("leave: " + fail));
173
boolean result = false;
174         try {
175             this.fail |= fail;
176             if (counter == 1) {
177                 // leaving the last write lock -> commit/rollback and send events
178
result = true;
179                 end(this.fail);
180             }
181         } finally {
182             synchronized (this) {
183                 if (counter > 0) {
184                     if ((--counter) == 0) {
185                         writer = null;
186                         this.fail = false;
187                         this.notifyAll();
188                     }
189                 } else {
190                     Thread JavaDoc thread = Thread.currentThread();
191                     Counter rCount = (Counter) readers.get(thread);
192                     if (rCount == null) {
193                         throw new DebugException("Error: leave() without enter().");
194                     } else {
195                         if (rCount.dec() == 0) {
196                             try {
197                                 result = true;
198                                 readers.remove(thread);
199                             } finally {
200                                 this.notifyAll();
201                             }
202                         }
203                     }
204                     if (fail) throw new DebugException("Cannot fail when in read mode.");
205                 }
206             }
207         }
208         return result;
209     }
210     
211     /* -------------------------------------------------------------------- */
212     /* -- TransactionMutex.Counter (private inner class) ------------------ */
213     /* -------------------------------------------------------------------- */
214     
215     /**
216      * Instances <code>Counter</code> are integers with {@link #dec() decrement}
217      * and {@link #inc() increment} methods.
218      */

219     private static class Counter {
220         private int value = 1;
221         
222         /**
223          * Creates a new counter, initializing it to <code>1</code>.
224          */

225         public Counter() {
226         }
227         
228         /**
229          * Returns the counter value.
230          */

231         public int intValue() {
232             return value;
233         }
234         
235         /**
236          * Decrements the counter by <code>1</code>
237          *
238          * @return the new counter value
239          * @exception Exception if the minimal counter value, namely
240          * <code>0</code>, was already reached
241          */

242         public int dec() {
243             if (value <= 0) throw new DebugException("Couter underflow: " + value);
244             return (--value);
245         }
246         
247         /**
248          * Increments the counter by <code>1</code>.
249          *
250          * @return the new counter value
251          */

252         public int inc() {
253             return (++value);
254         }
255     }
256 }
257
Popular Tags