KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > resources > WorkManager


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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 Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.internal.resources;
12
13 import org.eclipse.core.internal.utils.Messages;
14 import org.eclipse.core.internal.utils.Policy;
15 import org.eclipse.core.resources.IResource;
16 import org.eclipse.core.resources.IResourceStatus;
17 import org.eclipse.core.runtime.CoreException;
18 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.core.runtime.jobs.*;
20
21 /**
22  * The work manager governs concurrent access to the workspace tree. The {@link #lock}
23  * field is used to protect the workspace tree data structure from concurrent
24  * write attempts. This is an internal lock that is generally not held while
25  * client code is running. Scheduling rules are used by client code to obtain
26  * exclusive write access to a portion of the workspace.
27  *
28  * This class also tracks operation state for each thread that is involved in an
29  * operation. This includes prepared and running operation depth, auto-build
30  * strategy and cancel state.
31  */

32 public class WorkManager implements IManager {
33     /**
34      * Scheduling rule for use during resource change notification. This rule
35      * must always be allowed to nest within a resource rule of any granularity
36      * since it is used from within the scope of all resource changing
37      * operations. The purpose of this rule is two-fold: 1. To prevent other
38      * resource changing jobs from being scheduled while the notification is
39      * running 2. To cause an exception if a resource change listener tries to
40      * begin a resource rule during a notification. This also prevents
41      * deadlock, because the notification thread owns the workspace lock, and
42      * threads that own the workspace lock must never block trying to acquire a
43      * resource rule.
44      */

45     class NotifyRule implements ISchedulingRule {
46         public boolean contains(ISchedulingRule rule) {
47             return (rule instanceof IResource) || rule.getClass().equals(NotifyRule.class);
48         }
49
50         public boolean isConflicting(ISchedulingRule rule) {
51             return contains(rule);
52         }
53     }
54
55     /**
56      * Indicates that the last checkIn failed, either due to cancelation or due to the
57      * workspace tree being locked for modifications (during resource change events).
58      */

59     private final ThreadLocal JavaDoc checkInFailed = new ThreadLocal JavaDoc();
60     /**
61      * Indicates whether any operations have run that may require a build.
62      */

63     private boolean hasBuildChanges = false;
64     private IJobManager jobManager;
65     /**
66      * The primary workspace lock. This lock must be held by any thread
67      * modifying the workspace tree.
68      */

69     private final ILock lock;
70     
71     /**
72      * The current depth of running nested operations.
73      */

74     private int nestedOperations = 0;
75     
76     private NotifyRule notifyRule = new NotifyRule();
77     
78     private boolean operationCanceled = false;
79     
80     /**
81      * The current depth of prepared operations.
82      */

83     private int preparedOperations = 0;
84     private Workspace workspace;
85
86     public WorkManager(Workspace workspace) {
87         this.workspace = workspace;
88         this.jobManager = Job.getJobManager();
89         this.lock = jobManager.newLock();
90     }
91     
92     /**
93      * Releases the workspace lock without changing the nested operation depth.
94      * Must be followed eventually by endUnprotected. Any
95      * beginUnprotected/endUnprotected pair must be done entirely within the
96      * scope of a checkIn/checkOut pair. Returns the old lock depth.
97      * @see #endUnprotected(int)
98      */

99     public int beginUnprotected() {
100         int depth = lock.getDepth();
101         for (int i = 0; i < depth; i++)
102             lock.release();
103         return depth;
104     }
105
106     /**
107      * An operation calls this method and it only returns when the operation is
108      * free to run.
109      */

110     public void checkIn(ISchedulingRule rule, IProgressMonitor monitor) throws CoreException {
111         boolean success = false;
112         try {
113             if (workspace.isTreeLocked()) {
114                 String JavaDoc msg = Messages.resources_cannotModify;
115                 throw new ResourceException(IResourceStatus.WORKSPACE_LOCKED, null, msg, null);
116             }
117             jobManager.beginRule(rule, monitor);
118             lock.acquire();
119             incrementPreparedOperations();
120             success = true;
121         } finally {
122             //remember if we failed to check in, so we can avoid check out
123
if (!success)
124                 checkInFailed.set(Boolean.TRUE);
125         }
126     }
127
128     /**
129      * Returns true if the check in for this thread failed, in which case the
130      * check out and other end of operation code should not run.
131      * <p>
132      * The failure flag is reset immediately after calling this method. Subsequent
133      * calls to this method will indicate no failure (unless a new failure has occurred).
134      * @return <code>true</code> if the checkIn failed, and <code>false</code> otherwise.
135      */

136     public boolean checkInFailed(ISchedulingRule rule) {
137         if (checkInFailed.get() != null) {
138             //clear the failure flag for this thread
139
checkInFailed.set(null);
140             //must still end the rule even in the case of failure
141
if (!workspace.isTreeLocked())
142                 jobManager.endRule(rule);
143             return true;
144         }
145         return false;
146     }
147
148     /**
149      * Inform that an operation has finished.
150      */

151     public synchronized void checkOut(ISchedulingRule rule) {
152         decrementPreparedOperations();
153         rebalanceNestedOperations();
154         //reset state if this is the end of a top level operation
155
if (preparedOperations == 0)
156             operationCanceled = hasBuildChanges = false;
157         try {
158             lock.release();
159         } finally {
160             //end rule in finally in case lock.release throws an exception
161
jobManager.endRule(rule);
162         }
163     }
164
165     /**
166      * This method can only be safely called from inside a workspace
167      * operation. Should NOT be called from outside a
168      * prepareOperation/endOperation block.
169      */

170     private void decrementPreparedOperations() {
171         preparedOperations--;
172     }
173
174     /**
175      * Re-acquires the workspace lock that was temporarily released during an
176      * operation, and restores the old lock depth.
177      * @see #beginUnprotected()
178      */

179     public void endUnprotected(int depth) {
180         for (int i = 0; i < depth; i++)
181             lock.acquire();
182     }
183
184     /**
185      * Returns the work manager's lock
186      */

187     ILock getLock() {
188         return lock;
189     }
190     
191     /**
192      * Returns the scheduling rule used during resource change notifications.
193      */

194     public ISchedulingRule getNotifyRule() {
195         return notifyRule;
196     }
197
198     /**
199      * This method can only be safely called from inside a workspace
200      * operation. Should NOT be called from outside a
201      * prepareOperation/endOperation block.
202      */

203     public synchronized int getPreparedOperationDepth() {
204         return preparedOperations;
205     }
206
207     /**
208      * This method can only be safely called from inside a workspace
209      * operation. Should NOT be called from outside a
210      * prepareOperation/endOperation block.
211      */

212     void incrementNestedOperations() {
213         nestedOperations++;
214     }
215
216     /**
217      * This method can only be safely called from inside a workspace
218      * operation. Should NOT be called from outside a
219      * prepareOperation/endOperation block.
220      */

221     private void incrementPreparedOperations() {
222         preparedOperations++;
223     }
224
225     /**
226      * Returns true if the nested operation depth is the same as the prepared
227      * operation depth, and false otherwise. This method can only be safely
228      * called from inside a workspace operation. Should NOT be called from
229      * outside a prepareOperation/endOperation block.
230      */

231     boolean isBalanced() {
232         return nestedOperations == preparedOperations;
233     }
234
235     /**
236      * Returns true if the workspace lock has already been acquired by this
237      * thread, and false otherwise.
238      */

239     public boolean isLockAlreadyAcquired() {
240         boolean result = false;
241         try {
242             boolean success = lock.acquire(0L);
243             if (success) {
244                 //if lock depth is greater than one, then we already owned it
245
// before
246
result = lock.getDepth() > 1;
247                 lock.release();
248             }
249         } catch (InterruptedException JavaDoc e) {
250             // ignore
251
}
252         return result;
253     }
254
255     /**
256      * This method can only be safely called from inside a workspace
257      * operation. Should NOT be called from outside a
258      * prepareOperation/endOperation block.
259      */

260     public void operationCanceled() {
261         operationCanceled = true;
262     }
263
264     /**
265      * Used to make things stable again after an operation has failed between a
266      * workspace.prepareOperation() and workspace.beginOperation(). This method
267      * can only be safely called from inside a workspace operation. Should NOT
268      * be called from outside a prepareOperation/endOperation block.
269      */

270     public void rebalanceNestedOperations() {
271         nestedOperations = preparedOperations;
272     }
273
274     /**
275      * Indicates if the operation that has just completed may potentially
276      * require a build.
277      */

278     public void setBuild(boolean hasChanges) {
279         hasBuildChanges = hasBuildChanges || hasChanges;
280     }
281
282     /**
283      * This method can only be safely called from inside a workspace operation.
284      * Should NOT be called from outside a prepareOperation/endOperation block.
285      */

286     public boolean shouldBuild() {
287         if (hasBuildChanges) {
288             if (operationCanceled)
289                 return Policy.buildOnCancel;
290             return true;
291         }
292         return false;
293     }
294
295     public void shutdown(IProgressMonitor monitor) {
296         // do nothing
297
}
298
299     public void startup(IProgressMonitor monitor) {
300         // do nothing
301
}
302 }
303
Popular Tags