platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/refresh/win32/Win32Monitor.java
changeset 40 eb3c938c7fef
equal deleted inserted replaced
39:2a03ec4dbf31 40:eb3c938c7fef
       
     1 /*******************************************************************************
       
     2  * Copyright (c) 2002, 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 - Initial API and implementation
       
    10  *******************************************************************************/
       
    11 package org.eclipse.core.internal.resources.refresh.win32;
       
    12 
       
    13 import java.io.File;
       
    14 import java.util.*;
       
    15 import org.eclipse.core.internal.refresh.RefreshManager;
       
    16 import org.eclipse.core.internal.utils.Messages;
       
    17 import org.eclipse.core.resources.IResource;
       
    18 import org.eclipse.core.resources.ResourcesPlugin;
       
    19 import org.eclipse.core.resources.refresh.IRefreshMonitor;
       
    20 import org.eclipse.core.resources.refresh.IRefreshResult;
       
    21 import org.eclipse.core.runtime.*;
       
    22 import org.eclipse.core.runtime.jobs.Job;
       
    23 import org.eclipse.osgi.util.NLS;
       
    24 import org.osgi.framework.Bundle;
       
    25 
       
    26 /**
       
    27  * A monitor that works on Win32 platforms. Provides simple notification of
       
    28  * entire trees by reporting that the root of the tree has changed to depth
       
    29  * DEPTH_INFINITE.
       
    30  */
       
    31 class Win32Monitor extends Job implements IRefreshMonitor {
       
    32 	private static final long RESCHEDULE_DELAY = 1000;
       
    33 
       
    34 	/**
       
    35 	 * A ChainedHandle is a linked list of handles.
       
    36 	 */
       
    37 	protected abstract class ChainedHandle extends Handle {
       
    38 		private ChainedHandle next;
       
    39 		private ChainedHandle previous;
       
    40 
       
    41 		public abstract boolean exists();
       
    42 
       
    43 		public ChainedHandle getNext() {
       
    44 			return next;
       
    45 		}
       
    46 
       
    47 		public ChainedHandle getPrevious() {
       
    48 			return previous;
       
    49 		}
       
    50 
       
    51 		public void setNext(ChainedHandle next) {
       
    52 			this.next = next;
       
    53 		}
       
    54 
       
    55 		public void setPrevious(ChainedHandle previous) {
       
    56 			this.previous = previous;
       
    57 		}
       
    58 	}
       
    59 
       
    60 	protected class FileHandle extends ChainedHandle {
       
    61 		private File file;
       
    62 
       
    63 		public FileHandle(File file) {
       
    64 			this.file = file;
       
    65 		}
       
    66 
       
    67 		public boolean exists() {
       
    68 			return file.exists();
       
    69 		}
       
    70 
       
    71 		public void handleNotification() {
       
    72 			if (!isOpen())
       
    73 				return;
       
    74 			ChainedHandle next = getNext();
       
    75 			if (next != null) {
       
    76 				if (next.isOpen()) {
       
    77 					if (!next.exists()) {
       
    78 						if (next instanceof LinkedResourceHandle) {
       
    79 							next.close();
       
    80 							LinkedResourceHandle linkedResourceHandle = (LinkedResourceHandle) next;
       
    81 							linkedResourceHandle.postRefreshRequest();
       
    82 						} else {
       
    83 							next.close();
       
    84 						}
       
    85 						ChainedHandle previous = getPrevious();
       
    86 						if (previous != null)
       
    87 							previous.open();
       
    88 					}
       
    89 				} else {
       
    90 					next.open();
       
    91 					if (next.isOpen()) {
       
    92 						Handle previous = getPrevious();
       
    93 						previous.close();
       
    94 						if (next instanceof LinkedResourceHandle)
       
    95 							((LinkedResourceHandle) next).postRefreshRequest();
       
    96 					}
       
    97 				}
       
    98 			}
       
    99 			findNextChange();
       
   100 		}
       
   101 
       
   102 		public void open() {
       
   103 			if (!isOpen()) {
       
   104 				Handle next = getNext();
       
   105 				if (next != null && next.isOpen()) {
       
   106 					openHandleOn(file);
       
   107 				} else {
       
   108 					if (exists()) {
       
   109 						openHandleOn(file);
       
   110 					}
       
   111 					Handle previous = getPrevious();
       
   112 					if (previous != null) {
       
   113 						previous.open();
       
   114 					}
       
   115 				}
       
   116 			}
       
   117 		}
       
   118 	}
       
   119 
       
   120 	protected abstract class Handle {
       
   121 		protected long handleValue;
       
   122 
       
   123 		public Handle() {
       
   124 			handleValue = Win32Natives.INVALID_HANDLE_VALUE;
       
   125 		}
       
   126 
       
   127 		public void close() {
       
   128 			if (isOpen()) {
       
   129 				if (!Win32Natives.FindCloseChangeNotification(handleValue)) {
       
   130 					int error = Win32Natives.GetLastError();
       
   131 					if (error != Win32Natives.ERROR_INVALID_HANDLE)
       
   132 						addException(NLS.bind(Messages.WM_errCloseHandle, Integer.toString(error)));
       
   133 				}
       
   134 				if (RefreshManager.DEBUG)
       
   135 					System.out.println(DEBUG_PREFIX + "removed handle: " + handleValue); //$NON-NLS-1$
       
   136 				handleValue = Win32Natives.INVALID_HANDLE_VALUE;
       
   137 			}
       
   138 		}
       
   139 
       
   140 		private long createHandleValue(String path, boolean monitorSubtree, int flags) {
       
   141 			long handle = Win32Natives.FindFirstChangeNotification(path, monitorSubtree, flags);
       
   142 			if (handle == Win32Natives.INVALID_HANDLE_VALUE) {
       
   143 				int error = Win32Natives.GetLastError();
       
   144 				addException(NLS.bind(Messages.WM_errCreateHandle, path, Integer.toString(error)));
       
   145 			}
       
   146 			return handle;
       
   147 		}
       
   148 
       
   149 		public void destroy() {
       
   150 			close();
       
   151 		}
       
   152 
       
   153 		protected void findNextChange() {
       
   154 			if (!Win32Natives.FindNextChangeNotification(handleValue)) {
       
   155 				int error = Win32Natives.GetLastError();
       
   156 				if (error != Win32Natives.ERROR_INVALID_HANDLE && error != Win32Natives.ERROR_SUCCESS) {
       
   157 					addException(NLS.bind(Messages.WM_errFindChange, Integer.toString(error)));
       
   158 				}
       
   159 				removeHandle(this);
       
   160 			}
       
   161 		}
       
   162 
       
   163 		public long getHandleValue() {
       
   164 			return handleValue;
       
   165 		}
       
   166 
       
   167 		public abstract void handleNotification();
       
   168 
       
   169 		public boolean isOpen() {
       
   170 			return handleValue != Win32Natives.INVALID_HANDLE_VALUE;
       
   171 		}
       
   172 
       
   173 		public abstract void open();
       
   174 
       
   175 		protected void openHandleOn(File file) {
       
   176 			openHandleOn(file.getAbsolutePath(), false);
       
   177 		}
       
   178 
       
   179 		protected void openHandleOn(IResource resource) {
       
   180 			openHandleOn(resource.getLocation().toOSString(), true);
       
   181 		}
       
   182 
       
   183 		private void openHandleOn(String path, boolean subtree) {
       
   184 			setHandleValue(createHandleValue(path, subtree, Win32Natives.FILE_NOTIFY_CHANGE_FILE_NAME | Win32Natives.FILE_NOTIFY_CHANGE_DIR_NAME | Win32Natives.FILE_NOTIFY_CHANGE_LAST_WRITE | Win32Natives.FILE_NOTIFY_CHANGE_SIZE));
       
   185 			if (isOpen()) {
       
   186 				fHandleValueToHandle.put(new Long(getHandleValue()), this);
       
   187 				setHandleValueArrays(createHandleArrays());
       
   188 			} else {
       
   189 				close();
       
   190 			}
       
   191 		}
       
   192 
       
   193 		protected void postRefreshRequest(IResource resource) {
       
   194 			//native callback occurs even if resource was changed within workspace
       
   195 			if (!resource.isSynchronized(IResource.DEPTH_INFINITE))
       
   196 				refreshResult.refresh(resource);
       
   197 		}
       
   198 
       
   199 		public void setHandleValue(long handleValue) {
       
   200 			this.handleValue = handleValue;
       
   201 		}
       
   202 	}
       
   203 
       
   204 	protected class LinkedResourceHandle extends ChainedHandle {
       
   205 		private List fileHandleChain;
       
   206 		private IResource resource;
       
   207 
       
   208 		/**
       
   209 		 * @param resource
       
   210 		 */
       
   211 		public LinkedResourceHandle(IResource resource) {
       
   212 			this.resource = resource;
       
   213 			createFileHandleChain();
       
   214 		}
       
   215 
       
   216 		protected void createFileHandleChain() {
       
   217 			fileHandleChain = new ArrayList(1);
       
   218 			File file = new File(resource.getLocation().toOSString());
       
   219 			file = file.getParentFile();
       
   220 			while (file != null) {
       
   221 				fileHandleChain.add(0, new FileHandle(file));
       
   222 				file = file.getParentFile();
       
   223 			}
       
   224 			int size = fileHandleChain.size();
       
   225 			for (int i = 0; i < size; i++) {
       
   226 				ChainedHandle handle = (ChainedHandle) fileHandleChain.get(i);
       
   227 				handle.setPrevious((i > 0) ? (ChainedHandle) fileHandleChain.get(i - 1) : null);
       
   228 				handle.setNext((i + 1 < size) ? (ChainedHandle) fileHandleChain.get(i + 1) : this);
       
   229 			}
       
   230 			setPrevious((size > 0) ? (ChainedHandle) fileHandleChain.get(size - 1) : null);
       
   231 		}
       
   232 
       
   233 		public void destroy() {
       
   234 			super.destroy();
       
   235 			for (Iterator i = fileHandleChain.iterator(); i.hasNext();) {
       
   236 				Handle handle = (Handle) i.next();
       
   237 				handle.destroy();
       
   238 			}
       
   239 		}
       
   240 
       
   241 		public boolean exists() {
       
   242 			IPath location = resource.getLocation();
       
   243 			return location == null ? false : location.toFile().exists();
       
   244 		}
       
   245 
       
   246 		public void handleNotification() {
       
   247 			if (isOpen()) {
       
   248 				postRefreshRequest(resource);
       
   249 				findNextChange();
       
   250 			}
       
   251 		}
       
   252 
       
   253 		public void open() {
       
   254 			if (!isOpen()) {
       
   255 				if (exists()) {
       
   256 					openHandleOn(resource);
       
   257 				}
       
   258 				FileHandle handle = (FileHandle) getPrevious();
       
   259 				if (handle != null && !handle.isOpen()) {
       
   260 					handle.open();
       
   261 				}
       
   262 			}
       
   263 		}
       
   264 
       
   265 		public void postRefreshRequest() {
       
   266 			postRefreshRequest(resource);
       
   267 		}
       
   268 	}
       
   269 
       
   270 	protected class ResourceHandle extends Handle {
       
   271 		private IResource resource;
       
   272 
       
   273 		public ResourceHandle(IResource resource) {
       
   274 			super();
       
   275 			this.resource = resource;
       
   276 		}
       
   277 
       
   278 		public IResource getResource() {
       
   279 			return resource;
       
   280 		}
       
   281 
       
   282 		public void handleNotification() {
       
   283 			if (isOpen()) {
       
   284 				postRefreshRequest(resource);
       
   285 				findNextChange();
       
   286 			}
       
   287 		}
       
   288 
       
   289 		public void open() {
       
   290 			if (!isOpen()) {
       
   291 				openHandleOn(resource);
       
   292 			}
       
   293 		}
       
   294 	}
       
   295 
       
   296 	private static final String DEBUG_PREFIX = "Win32RefreshMonitor: "; //$NON-NLS-1$
       
   297 	private static final int WAIT_FOR_MULTIPLE_OBJECTS_TIMEOUT = 300;
       
   298 	/**
       
   299 	 * Any errors that have occurred
       
   300 	 */
       
   301 	protected MultiStatus errors;
       
   302 	/**
       
   303 	 * Arrays of handles, split evenly when the number of handles is larger
       
   304 	 * than Win32Natives.MAXIMUM_WAIT_OBJECTS
       
   305 	 */
       
   306 	protected long[][] fHandleValueArrays;
       
   307 	/**
       
   308 	 * Mapping of handles (java.lang.Long) to absolute paths
       
   309 	 * (java.lang.String).
       
   310 	 */
       
   311 	protected Map fHandleValueToHandle;
       
   312 	protected IRefreshResult refreshResult;
       
   313 
       
   314 	/*
       
   315 	 * Creates a new monitor. @param result A result that will recieve refresh
       
   316 	 * callbacks and error notifications
       
   317 	 */
       
   318 	public Win32Monitor(IRefreshResult result) {
       
   319 		super(Messages.WM_jobName);
       
   320 		this.refreshResult = result;
       
   321 		setPriority(Job.DECORATE);
       
   322 		setSystem(true);
       
   323 		fHandleValueToHandle = new HashMap(1);
       
   324 		setHandleValueArrays(createHandleArrays());
       
   325 	}
       
   326 
       
   327 	/**
       
   328 	 * Logs an exception
       
   329 	 */
       
   330 	protected synchronized void addException(String message) {
       
   331 		if (errors == null) {
       
   332 			String msg = Messages.WM_errors;
       
   333 			errors = new MultiStatus(ResourcesPlugin.PI_RESOURCES, 1, msg, null);
       
   334 		}
       
   335 		errors.add(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, 1, message, null));
       
   336 	}
       
   337 
       
   338 	/*
       
   339 	 * Splits the given array into arrays of length no greater than <code> max
       
   340 	 * </code> . The lengths of the sub arrays differ in size by no more than
       
   341 	 * one element. <p> Examples: <ul><li> If an array of size 11 is split
       
   342 	 * with a max of 4, the resulting arrays are of size 4, 4, and 3. </li>
       
   343 	 * <li> If an array of size 18 is split with a max of 5, the resulting
       
   344 	 * arrays are of size 5, 5, 4, and 4. </li></ul>
       
   345 	 */
       
   346 	private long[][] balancedSplit(final long[] array, final int max) {
       
   347 		int elementCount = array.length;
       
   348 		// want to handle [1, max] rather than [0, max)
       
   349 		int subArrayCount = ((elementCount - 1) / max) + 1;
       
   350 		int subArrayBaseLength = elementCount / subArrayCount;
       
   351 		int overflow = elementCount % subArrayCount;
       
   352 		long[][] result = new long[subArrayCount][];
       
   353 		int count = 0;
       
   354 		for (int i = 0; i < subArrayCount; i++) {
       
   355 			int subArrayLength = subArrayBaseLength + (overflow-- > 0 ? 1 : 0);
       
   356 			long[] subArray = new long[subArrayLength];
       
   357 			for (int j = 0; j < subArrayLength; j++) {
       
   358 				subArray[j] = array[count++];
       
   359 			}
       
   360 			result[i] = subArray;
       
   361 		}
       
   362 		return result;
       
   363 	}
       
   364 
       
   365 	private Handle createHandle(IResource resource) {
       
   366 		if (resource.isLinked())
       
   367 			return new LinkedResourceHandle(resource);
       
   368 		return new ResourceHandle(resource);
       
   369 	}
       
   370 
       
   371 	/*
       
   372 	 * Since the Win32Natives.WaitForMultipleObjects(...) method cannot accept
       
   373 	 * more than a certain number of objects, we are forced to split the array
       
   374 	 * of objects to monitor and monitor each one individually. <p> This method
       
   375 	 * splits the list of handles into arrays no larger than
       
   376 	 * Win32Natives.MAXIMUM_WAIT_OBJECTS. The arrays are balenced so that they
       
   377 	 * differ in size by no more than one element.
       
   378 	 */
       
   379 	protected long[][] createHandleArrays() {
       
   380 		long[] handles;
       
   381 		// synchronized: in order to protect the map during iteration
       
   382 		synchronized (fHandleValueToHandle) {
       
   383 			Set keys = fHandleValueToHandle.keySet();
       
   384 			int size = keys.size();
       
   385 			if (size == 0) {
       
   386 				return new long[0][0];
       
   387 			}
       
   388 			handles = new long[size];
       
   389 			int count = 0;
       
   390 			for (Iterator i = keys.iterator(); i.hasNext();) {
       
   391 				handles[count++] = ((Long) i.next()).longValue();
       
   392 			}
       
   393 		}
       
   394 		return balancedSplit(handles, Win32Natives.MAXIMUM_WAIT_OBJECTS);
       
   395 	}
       
   396 
       
   397 	private Handle getHandle(IResource resource) {
       
   398 		if (resource == null) {
       
   399 			return null;
       
   400 		}
       
   401 		// synchronized: in order to protect the map during iteration
       
   402 		synchronized (fHandleValueToHandle) {
       
   403 			for (Iterator i = fHandleValueToHandle.values().iterator(); i.hasNext();) {
       
   404 				Handle handle = (Handle) i.next();
       
   405 				if (handle instanceof ResourceHandle) {
       
   406 					ResourceHandle resourceHandle = (ResourceHandle) handle;
       
   407 					if (resourceHandle.getResource().equals(resource)) {
       
   408 						return handle;
       
   409 					}
       
   410 				}
       
   411 			}
       
   412 		}
       
   413 		return null;
       
   414 	}
       
   415 
       
   416 	/*
       
   417 	 * Answers arrays of handles. The handles are split evenly when the number
       
   418 	 * of handles becomes larger than Win32Natives.MAXIMUM_WAIT_OBJECTS.
       
   419 	 * @return long[][]
       
   420 	 */
       
   421 	private long[][] getHandleValueArrays() {
       
   422 		return fHandleValueArrays;
       
   423 	}
       
   424 
       
   425 	/**
       
   426 	 * Adds a resource to be monitored by this native monitor
       
   427 	 */
       
   428 	public boolean monitor(IResource resource) {
       
   429 		IPath location = resource.getLocation();
       
   430 		if (location == null) {
       
   431 			// cannot monitor remotely managed containers
       
   432 			return false;
       
   433 		}
       
   434 		Handle handle = createHandle(resource);
       
   435 		// synchronized: handle creation must be atomic
       
   436 		synchronized (this) {
       
   437 			handle.open();
       
   438 		}
       
   439 		if (!handle.isOpen()) {
       
   440 			//ignore errors if we can't even create a handle on the resource
       
   441 			//it will fall back to polling anyway
       
   442 			errors = null;
       
   443 			return false;
       
   444 		}
       
   445 		//make sure the job is running
       
   446 		schedule(RESCHEDULE_DELAY);
       
   447 		if (RefreshManager.DEBUG)
       
   448 			System.out.println(DEBUG_PREFIX + " added monitor for: " + resource); //$NON-NLS-1$
       
   449 		return true;
       
   450 	}
       
   451 
       
   452 	/**
       
   453 	 * Removes the handle from the <code>fHandleValueToHandle</code> map.
       
   454 	 * 
       
   455 	 * @param handle
       
   456 	 *                  a handle, not <code>null</code>
       
   457 	 */
       
   458 	protected void removeHandle(Handle handle) {
       
   459 		List handles = new ArrayList(1);
       
   460 		handles.add(handle);
       
   461 		removeHandles(handles);
       
   462 	}
       
   463 
       
   464 	/**
       
   465 	 * Removes all of the handles in the given collection from the <code>fHandleValueToHandle</code>
       
   466 	 * map. If collections from the <code>fHandleValueToHandle</code> map are
       
   467 	 * used, copy them before passing them in as this method modifies the
       
   468 	 * <code>fHandleValueToHandle</code> map.
       
   469 	 * 
       
   470 	 * @param handles
       
   471 	 *                  a collection of handles, not <code>null</code>
       
   472 	 */
       
   473 	private void removeHandles(Collection handles) {
       
   474 		// synchronized: protect the array, removal must be atomic
       
   475 		synchronized (this) {
       
   476 			for (Iterator i = handles.iterator(); i.hasNext();) {
       
   477 				Handle handle = (Handle) i.next();
       
   478 				fHandleValueToHandle.remove(new Long(handle.getHandleValue()));
       
   479 				handle.destroy();
       
   480 			}
       
   481 			setHandleValueArrays(createHandleArrays());
       
   482 		}
       
   483 	}
       
   484 
       
   485 	/*
       
   486 	 * @see java.lang.Runnable#run()
       
   487 	 */
       
   488 	protected IStatus run(IProgressMonitor monitor) {
       
   489 		long start = -System.currentTimeMillis();
       
   490 		if (RefreshManager.DEBUG)
       
   491 			System.out.println(DEBUG_PREFIX + "job started."); //$NON-NLS-1$
       
   492 		try {
       
   493 			long[][] handleArrays = getHandleValueArrays();
       
   494 			monitor.beginTask(Messages.WM_beginTask, handleArrays.length);
       
   495 			// If changes occur to the list of handles,
       
   496 			// ignore them until the next time through the loop.
       
   497 			for (int i = 0, length = handleArrays.length; i < length; i++) {
       
   498 				if (monitor.isCanceled())
       
   499 					return Status.CANCEL_STATUS;
       
   500 				waitForNotification(handleArrays[i]);
       
   501 				monitor.worked(1);
       
   502 			}
       
   503 		} finally {
       
   504 			monitor.done();
       
   505 			start += System.currentTimeMillis();
       
   506 			if (RefreshManager.DEBUG)
       
   507 				System.out.println(DEBUG_PREFIX + "job finished in: " + start + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
       
   508 		}
       
   509 		//always reschedule the job - so it will come back after errors or cancelation
       
   510 		//make sure it doesn't hog more that 5% of CPU
       
   511 		long delay = Math.max(RESCHEDULE_DELAY, start * 30);
       
   512 		if (RefreshManager.DEBUG)
       
   513 			System.out.println(DEBUG_PREFIX + "rescheduling in: " + delay / 1000 + " seconds"); //$NON-NLS-1$ //$NON-NLS-2$
       
   514 		final Bundle bundle = Platform.getBundle(ResourcesPlugin.PI_RESOURCES);
       
   515 		//if the bundle is null then the framework has shutdown - just bail out completely (bug 98219)
       
   516 		if (bundle == null)
       
   517 			return Status.OK_STATUS;
       
   518 		//don't reschedule the job if the resources plugin has been shut down
       
   519 		if (bundle.getState() == Bundle.ACTIVE)
       
   520 			schedule(delay);
       
   521 		MultiStatus result = errors;
       
   522 		errors = null;
       
   523 		//just log native refresh failures
       
   524 		if (result != null && !result.isOK())
       
   525 			ResourcesPlugin.getPlugin().getLog().log(result);
       
   526 		return Status.OK_STATUS;
       
   527 	}
       
   528 
       
   529 	protected void setHandleValueArrays(long[][] arrays) {
       
   530 		fHandleValueArrays = arrays;
       
   531 	}
       
   532 
       
   533 	/* (non-Javadoc)
       
   534 	 * @see org.eclipse.core.runtime.jobs.Job#shouldRun()
       
   535 	 */
       
   536 	public boolean shouldRun() {
       
   537 		return !fHandleValueToHandle.isEmpty();
       
   538 	}
       
   539 
       
   540 	/*
       
   541 	 * @see org.eclipse.core.resources.refresh.IRefreshMonitor#unmonitor(IContainer)
       
   542 	 */
       
   543 	public void unmonitor(IResource resource) {
       
   544 		if (resource == null) {
       
   545 			// resource == null means stop monitoring all resources
       
   546 			synchronized (fHandleValueToHandle) {
       
   547 				removeHandles(new ArrayList(fHandleValueToHandle.values()));
       
   548 			}
       
   549 		} else {
       
   550 			Handle handle = getHandle(resource);
       
   551 			if (handle != null)
       
   552 				removeHandle(handle);
       
   553 		}
       
   554 		//stop the job if there are no more handles
       
   555 		if (fHandleValueToHandle.isEmpty())
       
   556 			cancel();
       
   557 	}
       
   558 
       
   559 	/**
       
   560 	 * Performs the native call to wait for notification on one of the given
       
   561 	 * handles.
       
   562 	 * 
       
   563 	 * @param handleValues
       
   564 	 *                  an array of handles, it must contain no duplicates.
       
   565 	 */
       
   566 	private void waitForNotification(long[] handleValues) {
       
   567 		int handleCount = handleValues.length;
       
   568 		int index = Win32Natives.WaitForMultipleObjects(handleCount, handleValues, false, WAIT_FOR_MULTIPLE_OBJECTS_TIMEOUT);
       
   569 		if (index == Win32Natives.WAIT_TIMEOUT) {
       
   570 			// nothing happened.
       
   571 			return;
       
   572 		}
       
   573 		if (index == Win32Natives.WAIT_FAILED) {
       
   574 			// we ran into a problem
       
   575 			int error = Win32Natives.GetLastError();
       
   576 			if (error != Win32Natives.ERROR_INVALID_HANDLE && error != Win32Natives.ERROR_SUCCESS) {
       
   577 				addException(NLS.bind(Messages.WM_nativeErr, Integer.toString(error)));
       
   578 				refreshResult.monitorFailed(this, null);
       
   579 			}
       
   580 			return;
       
   581 		}
       
   582 		// a change occurred
       
   583 		// WaitForMultipleObjects returns WAIT_OBJECT_0 + index
       
   584 		index -= Win32Natives.WAIT_OBJECT_0;
       
   585 		Handle handle = (Handle) fHandleValueToHandle.get(new Long(handleValues[index]));
       
   586 		if (handle != null)
       
   587 			handle.handleNotification();
       
   588 	}
       
   589 }