1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.myfaces.orchestra.lib;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23
24
25 /**
26 * A reentrant mutual exclusion with the same basic
27 * behavior and semantics as the implicit monitor lock accessed using
28 * {@code synchronized} methods and statements.
29 * <p>
30 * Serialization of this class behaves in the same way as built-in
31 * locks: a deserialized lock is in the unlocked state, regardless of
32 * its state when serialized.
33 * <p>
34 * This class exists just for the purposes of Java 1.4 compatibility;
35 * it is equivalent to the Java 1.5 ReentrantLock class. It probably
36 * doesn't perform as well as the "real" lock class, but Orchestra
37 * doesn't use it in any critical paths.
38 *
39 * @since 1.1
40 */
41 public class _ReentrantLock implements java.io.Serializable
42 {
43 private static final long serialVersionUID = 7373984872572414699L;
44 private static final long WAIT_WARN_MILLIS = 30L * 1000L; // 30 seconds
45
46 private final Log log = LogFactory.getLog(_ReentrantLock.class);
47 private transient Thread lockedBy;
48 private int lockCount;
49
50 public void lockInterruptibly() throws InterruptedException
51 {
52 Thread caller = Thread.currentThread();
53 for(;;)
54 {
55 synchronized(this)
56 {
57 if (lockedBy == null)
58 {
59 lockedBy = caller;
60 lockCount = 1;
61 return;
62 }
63
64 if (lockedBy == caller)
65 {
66 ++lockCount;
67 return;
68 }
69
70 long waitedFor;
71 try
72 {
73 if (log.isDebugEnabled())
74 {
75 log.debug("Waiting for lock " + this.toString()
76 + " which is owned by thread "
77 + lockedBy.getName());
78 }
79 long beganAt = System.currentTimeMillis();
80 this.wait(WAIT_WARN_MILLIS);
81 waitedFor = System.currentTimeMillis() - beganAt;
82 }
83 catch(InterruptedException e)
84 {
85 throw e;
86 }
87
88 if (waitedFor >= WAIT_WARN_MILLIS)
89 {
90 // Hopefully this will not get invoked very often!
91 if (log.isWarnEnabled())
92 {
93 String lockedByName;
94 if (lockedBy != null)
95 {
96 lockedByName = lockedBy.getName();
97 }
98 else
99 {
100 // Unlikely but possible due to race conditions
101 lockedByName = "(none)";
102 }
103 log.warn("Waited for longer than " + WAIT_WARN_MILLIS
104 + " milliseconds for access to lock " + this.toString()
105 + " which is locked by thread " + lockedByName);
106 }
107 }
108 }
109 }
110 }
111
112 public void unlock()
113 {
114 Thread caller = Thread.currentThread();
115 synchronized(this)
116 {
117 if (lockedBy != caller)
118 {
119 throw new IllegalStateException("Unlock on lock not owned by caller");
120 }
121
122 --lockCount;
123 if (lockCount == 0)
124 {
125 lockedBy = null;
126 this.notifyAll();
127 }
128 }
129 }
130
131 public boolean isHeldByCurrentThread()
132 {
133 Thread caller = Thread.currentThread();
134 synchronized(this)
135 {
136 return lockedBy == caller;
137 }
138 }
139 }