KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > services > locks > LockSet


1 /*
2
3    Derby - Class org.apache.derby.impl.services.locks.LockSet
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.impl.services.locks;
23
24 import org.apache.derby.iapi.services.locks.Latch;
25 import org.apache.derby.iapi.services.locks.Lockable;
26 import org.apache.derby.iapi.services.locks.C_LockFactory;
27 import org.apache.derby.iapi.services.monitor.Monitor;
28
29 import org.apache.derby.iapi.error.StandardException;
30
31 import org.apache.derby.iapi.services.sanity.SanityManager;
32 import org.apache.derby.iapi.services.diag.DiagnosticUtil;
33
34 import org.apache.derby.iapi.reference.Property;
35 import org.apache.derby.iapi.reference.SQLState;
36
37 import java.util.Hashtable JavaDoc;
38 import java.util.Enumeration JavaDoc;
39
40
41 /**
42     A LockSet is a complete lock table. A lock table is a hash table
43     keyed by a Lockable and with a LockControl as the data element.
44
45     <P>
46     A LockControl contains information about the locks held on a Lockable.
47
48     <BR>
49     MT - Mutable - Container Object : Thread Safe
50
51     <BR>
52     The Hashtable we extend is synchronized on this, all addition, searching of
53     the hashtable is performed using java synchroization(this).
54     <BR>
55     The class creates ActiveLock and LockControl objects.
56     
57     LockControl objects are never passed out of this class, All the methods of
58     LockControl are called while being synchronized on this, thus providing the
59     single threading that LockControl required.
60
61     Methods of Lockables are only called by this class or LockControl, and
62     always while being synchronized on this, thus providing the single
63     threading that Lockable requires.
64     
65     @see LockControl
66 */

67
68 public final class LockSet extends Hashtable JavaDoc
69 {
70     /*
71     ** Fields
72     */

73     private final SinglePool factory;
74
75     /**
76         Timeout for deadlocks, in ms.
77         <BR>
78         MT - immutable
79     */

80     protected int deadlockTimeout = Property.DEADLOCK_TIMEOUT_DEFAULT * 1000;
81     protected int waitTimeout = Property.WAIT_TIMEOUT_DEFAULT * 1000;
82
83 //EXCLUDE-START-lockdiag-
84

85     // this varible is set and get without synchronization.
86
// Only one thread should be setting it at one time.
87
private boolean deadlockTrace;
88
89     private Hashtable JavaDoc lockTraces; // rather than burden each lock with
90
// its stack trace, keep a look aside table
91
// that maps a lock to a stack trace
92
//EXCLUDE-END-lockdiag-
93

94     // The number of waiters for locks
95
protected int blockCount;
96
97     /*
98     ** Constructor
99     */

100
101     protected LockSet(SinglePool factory) {
102         super();
103         this.factory = factory;
104     }
105
106
107     /*
108     ** Public Methods
109     */

110
111     /**
112      * Lock an object within a specific compatibility space.
113      *
114      * @param compatabilitySpace Compatibility space.
115      * @param ref Lockable reference.
116      * @param qualifier Qualifier.
117      * @param timeout Timeout in milli-seconds
118      *
119      * @return Object that represents the lock.
120      *
121      * @exception StandardException Standard Cloudscape policy.
122
123     */

124     public Lock lockObject(Object JavaDoc compatabilitySpace, Lockable ref, Object JavaDoc qualifier, int timeout, Latch latch)
125         throws StandardException
126     {
127         if (SanityManager.DEBUG) {
128
129             if (SanityManager.DEBUG_ON("memoryLeakTrace")) {
130
131                 if (size() > 1000)
132                     System.out.println("memoryLeakTrace:LockSet: " + size());
133             }
134         }
135
136         Control gc;
137         LockControl control;
138         Lock lockItem;
139         String JavaDoc lockDebug = null;
140
141         synchronized (this) {
142
143             gc = getControl(ref);
144
145             if (gc == null) {
146
147                 // object is not locked, can be granted
148
Lock gl = new Lock(compatabilitySpace, ref, qualifier);
149
150                 gl.grant();
151
152                 put(ref, gl);
153
154                 return gl;
155             }
156
157             control = gc.getLockControl();
158             if (control != gc) {
159                 put(ref, control);
160             }
161             
162
163             if (SanityManager.DEBUG) {
164                 SanityManager.ASSERT(ref.equals(control.getLockable()));
165
166                 // ASSERT item is in the list
167
if (getControl(control.getLockable()) != control)
168                 {
169                     SanityManager.THROWASSERT(
170                         "lockObject mismatched lock items " +
171                         getControl(control.getLockable()) + " " + control);
172                 }
173             }
174
175             lockItem = control.addLock(this, compatabilitySpace, qualifier);
176
177             if (lockItem.getCount() != 0) {
178                 return lockItem;
179             }
180
181             if (timeout == C_LockFactory.NO_WAIT) {
182
183                 // remove all trace of lock
184
control.giveUpWait(lockItem, this);
185
186                 if (SanityManager.DEBUG)
187                 {
188                     if (SanityManager.DEBUG_ON("DeadlockTrace"))
189                     {
190
191                         SanityManager.showTrace(new Throwable JavaDoc());
192
193                         // The following dumps the lock table as it
194
// exists at the time a timeout is about to
195
// cause a deadlock exception to be thrown.
196

197                         lockDebug =
198                             DiagnosticUtil.toDiagString(lockItem) +
199                             "\nCould not grant lock with zero timeout, here's the table" +
200                             this.toDebugString();
201                     }
202                 }
203
204                 return null;
205             }
206
207             // this is where we need to release the latch
208
if (latch != null)
209                 unlock(latch, 1);
210
211
212         } // synchronized block
213

214         boolean deadlockWait = false;
215         int actualTimeout;
216
217         if (timeout == C_LockFactory.WAIT_FOREVER)
218         {
219             // always check for deadlocks as there should not be any
220
deadlockWait = true;
221             if ((actualTimeout = deadlockTimeout) == C_LockFactory.WAIT_FOREVER)
222                 actualTimeout = Property.DEADLOCK_TIMEOUT_DEFAULT * 1000;
223         }
224         else
225         {
226
227             if (timeout == C_LockFactory.TIMED_WAIT)
228                 timeout = actualTimeout = waitTimeout;
229             else
230                 actualTimeout = timeout;
231
232
233             // five posible cases
234
// i) timeout -1, deadlock -1 ->
235
// just wait forever, no deadlock check
236
// ii) timeout >= 0, deadlock -1 ->
237
// just wait for timeout, no deadlock check
238
// iii) timeout -1, deadlock >= 0 ->
239
// wait for deadlock, then deadlock check,
240
// then infinite timeout
241
// iv) timeout >=0, deadlock < timeout ->
242
// wait for deadlock, then deadlock check,
243
// then wait for (timeout - deadlock)
244
// v) timeout >=0, deadlock >= timeout ->
245
// just wait for timeout, no deadlock check
246

247
248             if (deadlockTimeout >= 0) {
249
250                 if (actualTimeout < 0) {
251                     // infinite wait but perform a deadlock check first
252
deadlockWait = true;
253                     actualTimeout = deadlockTimeout;
254                 } else if (deadlockTimeout < actualTimeout) {
255
256                     // deadlock wait followed by a timeout wait
257

258                     deadlockWait = true;
259                     actualTimeout = deadlockTimeout;
260
261                     // leave timeout as the remaining time
262
timeout -= deadlockTimeout;
263                 }
264             }
265         }
266
267
268         ActiveLock waitingLock = (ActiveLock) lockItem;
269         lockItem = null;
270
271         if (deadlockTrace)
272         {
273             // we want to keep a stack trace of this thread just before it goes
274
// into wait state, no need to synchronized because Hashtable.put
275
// is synchronized and the new throwable is local to this thread.
276
lockTraces.put(waitingLock, new Throwable JavaDoc());
277         }
278
279         int earlyWakeupCount = 0;
280         long startWaitTime = 0;
281
282         try {
283 forever: for (;;) {
284
285                 byte wakeupReason = waitingLock.waitForGrant(actualTimeout);
286                 
287                 ActiveLock nextWaitingLock = null;
288                 Object JavaDoc[] deadlockData = null;
289
290                 try {
291                     boolean willQuitWait;
292                     Enumeration JavaDoc timeoutLockTable = null;
293                     long currentTime = 0;
294         
295                     synchronized (this) {
296
297                         if (control.isGrantable(
298                                 control.firstWaiter() == waitingLock,
299                                 compatabilitySpace,
300                                 qualifier)) {
301
302                             // Yes, we are granted, put us on the granted queue.
303
control.grant(waitingLock);
304
305                             // Remove from the waiting queue & get next waiter
306
nextWaitingLock =
307                                 control.getNextWaiter(waitingLock, true, this);
308
309                             // this is where we need to re-obtain the latch,
310
// it's safe to call this lockObject() which will
311
// get the synchronization we already hold, because
312
// java allows nested synchronization and it will
313
// be released automatically if we have to wait
314

315                             if (latch != null) {
316                                 lockObject(
317                                     compatabilitySpace, latch.getLockable(),
318                                     latch.getQualifier(),
319                                     C_LockFactory.WAIT_FOREVER,
320                                     (Latch) null);
321                             }
322                             return waitingLock;
323                         }
324
325                         // try again later
326
waitingLock.clearPotentiallyGranted();
327
328                         willQuitWait =
329                             (wakeupReason != Constants.WAITING_LOCK_GRANT);
330
331                         StandardException deadlockException = null;
332
333                         if (((wakeupReason == Constants.WAITING_LOCK_IN_WAIT) &&
334                                     deadlockWait) ||
335                             (wakeupReason == Constants.WAITING_LOCK_DEADLOCK))
336                         {
337
338                             // check for a deadlock, even if we were woken up
339
// because we were selected as a victim we still
340
// check because the situation may have changed.
341
deadlockData =
342                                 Deadlock.look(
343                                     factory, this, control, waitingLock,
344                                     wakeupReason);
345
346                             if (deadlockData == null) {
347                                 // we don't have a deadlock
348
deadlockWait = false;
349
350                                 actualTimeout = timeout;
351                                 startWaitTime = 0;
352                                 willQuitWait = false;
353                             } else {
354                                 willQuitWait = true;
355                             }
356                         }
357
358                         nextWaitingLock =
359                             control.getNextWaiter(
360                                 waitingLock, willQuitWait, this);
361
362
363                         // If we were not woken by another then we have
364
// timed out. Either deadlock out or timeout
365
if (willQuitWait) {
366
367                             // Even if we deadlocked trying to get the lock,
368
// still reget the latch so that client's need not
369
// know latch was released.
370

371                             if (latch != null) {
372                                 lockObject(
373                                     compatabilitySpace, latch.getLockable(),
374                                     latch.getQualifier(),
375                                     C_LockFactory.WAIT_FOREVER, (Latch) null);
376                             }
377
378                             if (SanityManager.DEBUG)
379                             {
380                                 if (SanityManager.DEBUG_ON("DeadlockTrace"))
381                                 {
382
383                                     SanityManager.showTrace(new Throwable JavaDoc());
384
385                                     // The following dumps the lock table as it
386
// exists at the time a timeout is about to
387
// cause a deadlock exception to be thrown.
388

389                                     lockDebug =
390                                     DiagnosticUtil.toDiagString(waitingLock) +
391                                     "\nGot deadlock/timeout, here's the table" +
392                                     this.toDebugString();
393                                 }
394                             }
395                             
396                             if (deadlockTrace && (deadlockData == null))
397                             {
398                                 // if ending lock request due to lock timeout
399
// want a copy of the LockTable and the time,
400
// in case of deadlock deadlockData has the
401
// info we need.
402
currentTime = System.currentTimeMillis();
403                                 timeoutLockTable =
404                                     factory.makeVirtualLockTable();
405                             }
406                         }
407
408                     } // synchronized block
409

410                     // need to do this outside of the synchronized block as the
411
// message text building (timeouts and deadlocks) may
412
// involve getting locks to look up table names from
413
// identifiers.
414

415                     if (willQuitWait)
416                     {
417                         if (SanityManager.DEBUG)
418                         {
419                             if (lockDebug != null)
420                             {
421                                 String JavaDoc type =
422                                     ((deadlockData != null) ?
423                                          "deadlock:" : "timeout:");
424
425                                 SanityManager.DEBUG_PRINT(
426                                     type,
427                                     "wait on lockitem caused " + type +
428                                     lockDebug);
429                             }
430
431                         }
432
433                         if (deadlockData == null)
434                         {
435                             // ending wait because of lock timeout.
436

437                             if (deadlockTrace)
438                             {
439                                 // Turn ON derby.locks.deadlockTrace to build
440
// the lockTable.
441

442                                 
443                                 throw Timeout.buildException(
444                                     waitingLock, timeoutLockTable, currentTime);
445                             }
446                             else
447                             {
448                                 StandardException se =
449                                     StandardException.newException(
450                                         SQLState.LOCK_TIMEOUT);
451
452                                 throw se;
453                             }
454                         }
455                         else
456                         {
457                             // ending wait because of lock deadlock.
458

459                             throw Deadlock.buildException(
460                                     factory, deadlockData);
461                         }
462                     }
463                 } finally {
464                     if (nextWaitingLock != null) {
465                         nextWaitingLock.wakeUp(Constants.WAITING_LOCK_GRANT);
466                         nextWaitingLock = null;
467                     }
468                 }
469
470                 if (actualTimeout != C_LockFactory.WAIT_FOREVER) {
471
472                     if (wakeupReason != Constants.WAITING_LOCK_IN_WAIT)
473                         earlyWakeupCount++;
474
475                     if (earlyWakeupCount > 5) {
476
477                         long now = System.currentTimeMillis();
478
479                         if (startWaitTime != 0) {
480
481                             long sleepTime = now - startWaitTime;
482
483                             actualTimeout -= sleepTime;
484                         }
485
486                         startWaitTime = now;
487                     }
488                 }
489
490
491             } // for(;;)
492
} finally {
493             if (deadlockTrace)
494             {
495                     // I am out of the wait state, either I got my lock or I
496
// am the one who is going to detect the deadlock, don't
497
// need the stack trace anymore.
498
lockTraces.remove(waitingLock);
499             }
500         }
501     }
502
503     /**
504         Unlock an object, previously locked by lockObject().
505
506         If unlockCOunt is not zero then the lock will be unlocked
507         that many times, otherwise the unlock count is taken from
508         item.
509
510     */

511     void unlock(Latch item, int unlockCount) {
512
513         if (SanityManager.DEBUG) {
514             if (SanityManager.DEBUG_ON(Constants.LOCK_TRACE)) {
515                 /*
516                 ** I don't like checking the trace flag twice, but SanityManager
517                 ** doesn't provide a way to get to the debug trace stream
518                 ** directly.
519                 */

520                 SanityManager.DEBUG(
521                     Constants.LOCK_TRACE,
522                     "Release lock: " + DiagnosticUtil.toDiagString(item));
523             }
524         }
525
526         boolean tryGrant = false;
527         ActiveLock nextGrant = null;
528
529         synchronized (this) {
530
531             Control control = getControl(item.getLockable());
532             
533             if (SanityManager.DEBUG) {
534
535                 // only valid Lock's expected
536
if (item.getLockable() == null)
537                 {
538                     SanityManager.THROWASSERT(
539                         "item.getLockable() = null." +
540                         "unlockCount " + unlockCount +
541                         "item = " + DiagnosticUtil.toDiagString(item));
542                 }
543
544                 // only valid Lock's expected
545
if (control == null)
546                 {
547                     SanityManager.THROWASSERT(
548                         "control = null." +
549                         "unlockCount " + unlockCount +
550                         "item = " + DiagnosticUtil.toDiagString(item));
551                 }
552
553                 if (getControl(control.getLockable()) != control)
554                 {
555                     SanityManager.THROWASSERT(
556                         "unlock mismatched lock items " +
557                         getControl(control.getLockable()) + " " + control);
558                 }
559
560                 if ((unlockCount != 0) && (unlockCount > item.getCount()))
561                     SanityManager.THROWASSERT("unlockCount " + unlockCount +
562                         " larger than actual lock count " + item.getCount() + " item " + item);
563             }
564
565             tryGrant = control.unlock(item, unlockCount);
566             item = null;
567
568             boolean mayBeEmpty = true;
569             if (tryGrant) {
570                 nextGrant = control.firstWaiter();
571                 if (nextGrant != null) {
572                     mayBeEmpty = false;
573                     if (!nextGrant.setPotentiallyGranted())
574                         nextGrant = null;
575                 }
576             }
577
578             if (mayBeEmpty) {
579                 if (control.isEmpty()) {
580                     // no-one granted, no-one waiting, remove lock control
581
remove(control.getLockable());
582                 }
583                 return;
584             }
585         } // synchronized (this)
586

587         if (tryGrant && (nextGrant != null)) {
588             nextGrant.wakeUp(Constants.WAITING_LOCK_GRANT);
589         }
590     }
591     
592     /*
593     ** Non public methods
594     */

595 //EXCLUDE-START-lockdiag-
596

597     void setDeadlockTrace(boolean val)
598     {
599         // set this without synchronization
600
deadlockTrace = val;
601
602         if (val && lockTraces == null)
603         {
604             lockTraces = new Hashtable JavaDoc();
605         }
606         else if (!val && lockTraces != null)
607         {
608             lockTraces = null;
609         }
610     }
611 //EXCLUDE-END-lockdiag-
612

613     public String JavaDoc toDebugString()
614     {
615         if (SanityManager.DEBUG)
616         {
617             String JavaDoc str = new String JavaDoc();
618
619             int i = 0;
620             for (Enumeration JavaDoc e = this.elements();
621                  e.hasMoreElements();
622                  i++)
623             {
624                 str += "\n lock[" + i + "]: " +
625                     DiagnosticUtil.toDiagString(e.nextElement());
626             }
627
628             return(str);
629         }
630         else
631         {
632             return(null);
633         }
634     }
635
636 //EXCLUDE-START-lockdiag-
637
/*
638      * make a shallow clone of myself and my lock controls
639      */

640     /* package */
641     synchronized LockSet shallowClone()
642     {
643         LockSet clone = new LockSet(factory);
644
645         for (Enumeration JavaDoc e = keys(); e.hasMoreElements(); )
646         {
647             Lockable lockable = (Lockable)e.nextElement();
648             Control control = getControl(lockable);
649
650             clone.put(lockable, control.shallowClone());
651         }
652
653         return clone;
654     }
655 //EXCLUDE-END-lockdiag-
656

657     /*
658     ** Support for anyoneBlocked(). These methods assume that caller
659     ** is synchronized on this LockSet object.
660     */

661     void oneMoreWaiter() {
662         blockCount++;
663     }
664
665     void oneLessWaiter() {
666         blockCount--;
667     }
668
669     boolean anyoneBlocked() {
670         if (SanityManager.DEBUG) {
671             SanityManager.ASSERT(
672                 blockCount >= 0, "blockCount should not be negative");
673         }
674
675         // no synchronization needed because reads of ints are atomic
676
return blockCount != 0;
677     }
678
679     public final Control getControl(Lockable ref) {
680         return (Control) get(ref);
681     }
682 }
683
Popular Tags