KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mckoi > database > LockingMechanism


1 /**
2  * com.mckoi.database.LockingMechanism 09 May 1998
3  *
4  * Mckoi SQL Database ( http://www.mckoi.com/database )
5  * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * Version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License Version 2 for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * Version 2 along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * Change Log:
21  *
22  *
23  */

24
25 package com.mckoi.database;
26
27 import com.mckoi.debug.*;
28 import java.util.HashMap JavaDoc;
29
30 /**
31  * This class represents a model for locking the tables in a database during
32  * any sequence of concurrent read/write accesses.
33  * <p>
34  * Every table in the database has an 'access_queue' that is generated the
35  * first time the table is accessed. When a read or write request happens,
36  * the thread and the type of access is put onto the top of the queue. When
37  * the read/write access to the table has completed, the access is removed
38  * from the queue.
39  * <p>
40  * An access to the table may be 'blocked' until other threads have completed
41  * their access of the table.
42  * <p>
43  * A table that has a 'read lock' can not be altered until the table object
44  * is released. A table that has a 'write lock' may not be read until the
45  * table object is released.
46  * <p>
47  * The general rules are:
48  * a) A read request can go ahead if there are no write request infront of
49  * this request in the access queue.
50  * b) A write request can go ahead if the write request is at the front of
51  * the access queue.
52  * <p>
53  * This class requires some for-sight to which tables will be read/written
54  * to. We must pass all tables being read/written in a single stage. This
55  * implies a 2 stage process, the 1st determining which tables are being
56  * accessed and the 2nd performing the actual operations.
57  * <p>
58  * Some operations such as creating and dropping and modifying the security
59  * tables may require that no threads interfere with the database state while
60  * the operation is occuring. This is handled through an 'Excluside Mode'.
61  * When an object calls the locking mechanism to switch into exclusive mode, it
62  * blocks until all access to the database are complete, then continues,
63  * blocking all other threads until the exclusive mode is cancelled.
64  * <p>
65  * The locking system, in simple terms, ensures that any multiple read
66  * operations will happen concurrently, however write operations block until
67  * all operations are complete.
68  * <p>
69  * SYNCHRONIZATION: This method implements some important concurrent models
70  * for ensuring that queries can never be corrupted.
71  * <p>
72  * @author Tobias Downer
73  */

74
75 public final class LockingMechanism {
76
77   /**
78    * Class statics. These are used in the 'setMode' method to request either
79    * shared or exclusive access to the database.
80    */

81   public final static int SHARED_MODE = 1;
82   public final static int EXCLUSIVE_MODE = 2;
83
84   /**
85    * This Hashtable is a mapping from a 'DataTable' to the 'LockingQueue'
86    * object that is available for it.
87    */

88   private HashMap JavaDoc queues_map = new HashMap JavaDoc();
89
90   /**
91    * This boolean is set as soon as a Thread requests to go into 'exclusive
92    * mode'.
93    */

94   private boolean in_exclusive_mode = false;
95
96   /**
97    * This contains the number of Threads that have requested to go into
98    * 'shared mode'. It is incremented each time 'setMode(SHARED_MODE)' is
99    * called.
100    */

101   private int shared_mode = 0;
102
103   /**
104    * The DebugLogger object that we log debug messages to.
105    */

106   private final DebugLogger debug;
107
108   /**
109    * Constructor.
110    */

111   public LockingMechanism(DebugLogger logger) {
112     this.debug = logger;
113   }
114
115   /**
116    * This is a helper function for returning the LockingQueue object for the
117    * DataTable object. If there has not previously been a queue instantiated
118    * for the table, it creates a new one and adds it to the Hashtable.
119    * <p>
120    * ISSUE: Not synchronized because we guarenteed to be called from a
121    * synchronized method right?
122    */

123   private LockingQueue getQueueFor(DataTable table) {
124     LockingQueue queue = (LockingQueue) queues_map.get(table);
125
126     // If queue not in hashtable then create a new one and put it into mapping
127
if (queue == null) {
128       queue = new LockingQueue(table);
129       queues_map.put(table, queue);
130     }
131
132     return queue;
133   }
134
135   /**
136    * Resets this object so it may be reused. This will release all internal
137    * DataTable queues that are being kept.
138    */

139   public void reset() {
140
141     synchronized (this) {
142       // Check we are in exclusive mode,
143
if (!isInExclusiveMode()) {
144         // This is currently just a warning but should be upgraded to a
145
// full error.
146
debug.writeException(new RuntimeException JavaDoc("Should not clear a " +
147                         "LockingMechanism that's not in exclusive mode."));
148       }
149       queues_map.clear();
150     }
151
152   }
153
154   /**
155    * This method locks the given tables for either reading or writing. It
156    * puts the access locks in a queue for the given tables. This 'reserves'
157    * the rights for this thread to access the table in that way. This
158    * reservation can be used by the system to decide table accessability.
159    * <p>
160    * NOTE: ** IMPORTANT ** We must ensure that a single Thread can not create
161    * multiple table locks. Otherwise it will cause situations where deadlock
162    * can result.
163    * NOTE: ** IMPORTANT ** We must ensure that once a lock has occured, it
164    * is unlocked at a later time _no matter what happens_. Otherwise there
165    * will be situations where deadlock can result.
166    * NOTE: A LockHandle should not be given to another Thread.
167    * <p>
168    * SYNCHRONIZATION: This method is synchronized to ensure multiple additions
169    * to the locking queues can happen without interference.
170    */

171   public LockHandle lockTables(DataTable[] t_write, DataTable[] t_read) {
172
173     // Set up the local constants.
174

175     final int lock_count = t_read.length + t_write.length;
176     final LockHandle handle = new LockHandle(lock_count, debug);
177
178     synchronized (this) {
179
180       Lock lock;
181       LockingQueue queue;
182       int queue_index;
183
184       // Add read and write locks to cache and to the handle.
185

186       for (int i = t_write.length - 1; i >= 0; --i) {
187         DataTable to_write_lock = t_write[i];
188         queue = getQueueFor(to_write_lock);
189         // slightly confusing: this will add lock to given table queue
190
lock = new Lock(Lock.WRITE, queue, debug);
191         handle.addLock(lock);
192
193         debug.write(Lvl.INFORMATION, this,
194           "[LockingMechanism] Locking for WRITE: " +
195           to_write_lock.getTableName());
196       }
197
198       for (int i = t_read.length - 1; i >= 0; --i) {
199         DataTable to_read_lock = t_read[i];
200         queue = getQueueFor(to_read_lock);
201         // slightly confusing: this will add lock to given table queue
202
lock = new Lock(Lock.READ, queue, debug);
203         handle.addLock(lock);
204
205         debug.write(Lvl.INFORMATION, this,
206             "[LockingMechanism] Locking for READ: " +
207             to_read_lock.getTableName());
208       }
209
210     }
211
212     debug.write(Lvl.INFORMATION, this, "Locked Tables");
213
214     return handle;
215
216   }
217
218   /**
219    * Unlocks the tables that were previously locked by the 'lockTables' method.
220    * It is required that this method is called after the table references made
221    * by a query are released (set to null or forgotten). This usually means
222    * _after_ the result set has been written to the client.
223    * SYNCHRONIZATION: This method is synchronized so concurrent unlocking
224    * can not corrupt the queues.
225    */

226   public void unlockTables(LockHandle handle) {
227     synchronized (this) {
228       handle.unlockAll();
229     }
230     debug.write(Lvl.INFORMATION, this, "UnLocked Tables");
231   }
232
233   /**
234    * Returns true if we are locked into exclusive mode.
235    */

236   public synchronized boolean isInExclusiveMode() {
237     return in_exclusive_mode;
238   }
239
240   /**
241    * This method _must_ be called before a threads initial access to a Database
242    * object. It registers whether the preceding database accesses will be in
243    * an 'exclusive mode' or a 'shared mode'. In shared mode, any number of
244    * threads are able to access the database. In exclusive, the current thread
245    * may be the only one that may access the database.
246    * On requesting exclusive mode, it blocks until exclusive mode is available.
247    * On requesting shared mode, it blocks only if currently in exclusive mode.
248    * NOTE: 'exclusive mode' should be used only in system maintenance type
249    * operations such as creating and dropping tables from the database.
250    */

251   public synchronized void setMode(int mode) {
252
253     // If currently in exclusive mode, block until not.
254

255     while (in_exclusive_mode == true) {
256       try {
257 // System.out.println("Waiting because in exclusive lock.");
258
wait();
259 // System.out.println("Finish: Waiting because in exclusive lock.");
260
}
261       catch (InterruptedException JavaDoc e) {}
262     }
263
264     if (mode == EXCLUSIVE_MODE) {
265
266       // Set this thread to exclusive mode, and wait until all shared modes
267
// have completed.
268

269       in_exclusive_mode = true;
270       while (shared_mode > 0) {
271         try {
272 // System.out.println("Waiting on exclusive lock: " + shared_mode);
273
wait();
274 // System.out.println("Finish: Waiting on exclusive lock: " + shared_mode);
275
}
276         catch (InterruptedException JavaDoc e) {}
277       }
278
279       debug.write(Lvl.INFORMATION, this, "Locked into ** EXCLUSIVE MODE **");
280
281     }
282     else if (mode == SHARED_MODE) {
283
284       // Increase the threads counter that are in shared mode.
285

286       ++shared_mode;
287
288       debug.write(Lvl.INFORMATION, this, "Locked into SHARED MODE");
289
290     }
291     else {
292       throw new Error JavaDoc("Invalid mode");
293     }
294   }
295
296   /**
297    * This must be called when the calls to a Database object have finished.
298    * It 'finishes' the mode that the locking mechanism was set into by the
299    * call to the 'setMode' method.
300    * NOTE: ** IMPORTANT ** This method __MUST__ be guarenteed to be called some
301    * time after the 'setMode' method. Otherwise deadlock.
302    */

303   public synchronized void finishMode(int mode) {
304     if (mode == EXCLUSIVE_MODE) {
305       in_exclusive_mode = false;
306       notifyAll();
307
308       debug.write(Lvl.INFORMATION, this, "UnLocked from ** EXCLUSIVE MODE **");
309
310     }
311     else if (mode == SHARED_MODE) {
312       --shared_mode;
313       if (shared_mode == 0 && in_exclusive_mode) {
314         notifyAll();
315       }
316       else if (shared_mode < 0) {
317         shared_mode = 0;
318         notifyAll();
319         throw new RuntimeException JavaDoc("Too many 'finishMode(SHARED_MODE)' calls");
320       }
321
322       debug.write(Lvl.INFORMATION, this, "UnLocked from SHARED MODE");
323
324     }
325     else {
326       throw new Error JavaDoc("Invalid mode");
327     }
328   }
329
330 }
331
Popular Tags