KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > jobs > LockManager


1 /*******************************************************************************
2  * Copyright (c) 2003, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM - Initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.internal.jobs;
12
13 import java.util.HashMap JavaDoc;
14 import java.util.Stack JavaDoc;
15 import org.eclipse.core.internal.runtime.RuntimeLog;
16 import org.eclipse.core.runtime.*;
17 import org.eclipse.core.runtime.jobs.ISchedulingRule;
18 import org.eclipse.core.runtime.jobs.LockListener;
19
20 /**
21  * Stores the only reference to the graph that contains all the known
22  * relationships between locks, rules, and the threads that own them.
23  * Synchronizes all access to the graph on the only instance that exists in this class.
24  *
25  * Also stores the state of suspended locks so that they can be re-acquired with
26  * the proper lock depth.
27  */

28 public class LockManager {
29     /**
30      * This class captures the state of suspended locks.
31      * Locks are suspended if deadlock is detected.
32      */

33     private static class LockState {
34         private int depth;
35         private OrderedLock lock;
36
37         /**
38          * Suspends ownership of the given lock, and returns the saved state.
39          */

40         protected static LockState suspend(OrderedLock lock) {
41             LockState state = new LockState();
42             state.lock = lock;
43             state.depth = lock.forceRelease();
44             return state;
45         }
46
47         /**
48          * Re-acquires a suspended lock and reverts to the correct lock depth.
49          */

50         public void resume() {
51             //spin until the lock is successfully acquired
52
//NOTE: spinning here allows the UI thread to service pending syncExecs
53
//if the UI thread is waiting to acquire a lock.
54
while (true) {
55                 try {
56                     if (lock.acquire(Long.MAX_VALUE))
57                         break;
58                 } catch (InterruptedException JavaDoc e) {
59                     //ignore and loop
60
}
61             }
62             lock.setDepth(depth);
63         }
64     }
65
66     //the lock listener for this lock manager
67
protected LockListener lockListener;
68     /*
69      * The internal data structure that stores all the relationships
70      * between the locks (or rules) and the threads that own them.
71      */

72     private DeadlockDetector locks = new DeadlockDetector();
73     /*
74      * Stores thread - stack pairs where every entry in the stack is an array
75      * of locks that were suspended while the thread was acquiring more locks
76      * (a stack is needed because when a thread tries to re-aquire suspended locks,
77      * it can cause deadlock, and some locks it owns can be suspended again)
78      */

79     private HashMap JavaDoc suspendedLocks = new HashMap JavaDoc();
80
81     public LockManager() {
82         super();
83     }
84
85     /* (non-Javadoc)
86      * Method declared on LockListener
87      */

88     public void aboutToRelease() {
89         if (lockListener == null)
90             return;
91         try {
92             lockListener.aboutToRelease();
93         } catch (Exception JavaDoc e) {
94             handleException(e);
95         } catch (LinkageError JavaDoc e) {
96             handleException(e);
97         }
98     }
99
100     /* (non-Javadoc)
101      * Method declared on LockListener
102      */

103     public boolean aboutToWait(Thread JavaDoc lockOwner) {
104         if (lockListener == null)
105             return false;
106         try {
107             return lockListener.aboutToWait(lockOwner);
108         } catch (Exception JavaDoc e) {
109             handleException(e);
110         } catch (LinkageError JavaDoc e) {
111             handleException(e);
112         }
113         return false;
114     }
115
116     /**
117      * This thread has just acquired a lock. Update graph.
118      */

119     void addLockThread(Thread JavaDoc thread, ISchedulingRule lock) {
120         if (locks == null)
121             return;
122         try {
123             synchronized (locks) {
124                 locks.lockAcquired(thread, lock);
125             }
126         } catch (Exception JavaDoc e) {
127             handleInternalError(e);
128         }
129     }
130
131     /**
132      * This thread has just been refused a lock. Update graph and check for deadlock.
133      */

134     void addLockWaitThread(Thread JavaDoc thread, ISchedulingRule lock) {
135         if (locks == null)
136             return;
137         try {
138             Deadlock found = null;
139             synchronized (locks) {
140                 found = locks.lockWaitStart(thread, lock);
141             }
142             if (found == null)
143                 return;
144             // if deadlock was detected, the found variable will contain all the information about it,
145
// including which locks to suspend for which thread to resolve the deadlock.
146
ISchedulingRule[] toSuspend = found.getLocks();
147             LockState[] suspended = new LockState[toSuspend.length];
148             for (int i = 0; i < toSuspend.length; i++)
149                 suspended[i] = LockState.suspend((OrderedLock) toSuspend[i]);
150             synchronized (suspendedLocks) {
151                 Stack JavaDoc prevLocks = (Stack JavaDoc) suspendedLocks.get(found.getCandidate());
152                 if (prevLocks == null)
153                     prevLocks = new Stack JavaDoc();
154                 prevLocks.push(suspended);
155                 suspendedLocks.put(found.getCandidate(), prevLocks);
156             }
157         } catch (Exception JavaDoc e) {
158             handleInternalError(e);
159         }
160     }
161
162     /**
163      * Handles exceptions that occur while calling third party code from within the
164      * LockManager. This is essentially an in-lined version of Platform.run(ISafeRunnable)
165      */

166     private static void handleException(Throwable JavaDoc e) {
167         IStatus status;
168         if (e instanceof CoreException) {
169             //logged message should not be translated
170
status = new MultiStatus(JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, "LockManager.handleException", e); //$NON-NLS-1$
171
((MultiStatus) status).merge(((CoreException) e).getStatus());
172         } else {
173             status = new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, "LockManager.handleException", e); //$NON-NLS-1$
174
}
175         RuntimeLog.log(status);
176     }
177
178     /**
179      * There was an internal error in the deadlock detection code. Shut the entire
180      * thing down to prevent further errors. Recovery is too complex as it
181      * requires freezing all threads and inferring the present lock state.
182      */

183     private void handleInternalError(Throwable JavaDoc t) {
184         try {
185             handleException(t);
186             handleException(new Exception JavaDoc(locks.toDebugString()));
187         } catch (Exception JavaDoc e2) {
188             //ignore failure to log or to create the debug string
189
}
190         //discard the deadlock detector for good
191
locks = null;
192     }
193
194     /**
195      * Returns true IFF the underlying graph is empty.
196      * For debugging purposes only.
197      */

198     public boolean isEmpty() {
199         return locks.isEmpty();
200     }
201
202     /**
203      * Returns true IFF this thread either owns, or is waiting for, any locks or rules.
204      */

205     public boolean isLockOwner() {
206         //all job threads have to be treated as lock owners because UI thread
207
//may try to join a job
208
Thread JavaDoc current = Thread.currentThread();
209         if (current instanceof Worker)
210             return true;
211         if (locks == null)
212             return false;
213         synchronized (locks) {
214             return locks.contains(Thread.currentThread());
215         }
216     }
217
218     /**
219      * Creates and returns a new lock.
220      */

221     public synchronized OrderedLock newLock() {
222         return new OrderedLock(this);
223     }
224
225     /**
226      * Releases all the acquires that were called on the given rule. Needs to be called only once.
227      */

228     void removeLockCompletely(Thread JavaDoc thread, ISchedulingRule rule) {
229         if (locks == null)
230             return;
231         try {
232             synchronized (locks) {
233                 locks.lockReleasedCompletely(thread, rule);
234             }
235         } catch (Exception JavaDoc e) {
236             handleInternalError(e);
237         }
238     }
239
240     /**
241      * This thread has just released a lock. Update graph.
242      */

243     void removeLockThread(Thread JavaDoc thread, ISchedulingRule lock) {
244         try {
245             synchronized (locks) {
246                 locks.lockReleased(thread, lock);
247             }
248         } catch (Exception JavaDoc e) {
249             handleInternalError(e);
250         }
251     }
252
253     /**
254      * This thread has just stopped waiting for a lock. Update graph.
255      */

256     void removeLockWaitThread(Thread JavaDoc thread, ISchedulingRule lock) {
257         try {
258             synchronized (locks) {
259                 locks.lockWaitStop(thread, lock);
260             }
261         } catch (Exception JavaDoc e) {
262             handleInternalError(e);
263         }
264     }
265
266     /**
267      * Resumes all the locks that were suspended while this thread was waiting to acquire another lock.
268      */

269     void resumeSuspendedLocks(Thread JavaDoc owner) {
270         LockState[] toResume;
271         synchronized (suspendedLocks) {
272             Stack JavaDoc prevLocks = (Stack JavaDoc) suspendedLocks.get(owner);
273             if (prevLocks == null)
274                 return;
275             toResume = (LockState[]) prevLocks.pop();
276             if (prevLocks.empty())
277                 suspendedLocks.remove(owner);
278         }
279         for (int i = 0; i < toResume.length; i++)
280             toResume[i].resume();
281     }
282
283     public void setLockListener(LockListener listener) {
284         this.lockListener = listener;
285     }
286 }
287
Popular Tags