001package jmri.util; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004import java.util.Calendar; 005 006/** 007 * Common utility class for handling the "spurious wakeup from wait()" problem 008 * documented in {@link java.lang.Object#wait(long)}. 009 * 010 * Generally, when waiting for a notify() operation, you need to provide a test 011 * that a valid notify had happened due to a state change or other . 012 * <pre><code> 013 * new WaitHandler(this, 120) { 014 * protected boolean wasSpurious() { 015 * return !(state == expectedNextState); 016 * } 017 * }; 018 * </code></pre> 019 * 020 * By default, interrupting the thread leaves the wait early with the 021 * interrupted flag set. InterruptedException is not thrown. You can modify this 022 * behavior via the handleInterruptedException routine. 023 * 024 * @author Bob Jacobsen Copyright 2010 025 */ 026public class WaitHandler { 027 028 /** 029 * Wait for a specified interval, robustly handling "spurious wake" 030 * 031 * @param self waiting Object 032 * @param interval in milliseconds 033 */ 034 public WaitHandler(Object self, long interval) { 035 long currentTime = Calendar.getInstance().getTimeInMillis(); 036 long endTime = currentTime + interval; 037 038 // loop until interrupted, or non-spurious wake 039 while (endTime > (currentTime = Calendar.getInstance().getTimeInMillis())) { 040 long wait = endTime - currentTime; 041 try { 042 synchronized (self) { 043 self.wait(wait); 044 045 if (!wasSpurious()) { 046 break; 047 } 048 } 049 } catch (InterruptedException e) { 050 Thread.currentThread().interrupt(); // retain if needed later 051 break; // and leave the wait now 052 } 053 } 054 } 055 056 /** 057 * Wait forever, robustly handling "spurious wake" 058 * 059 * @param self waiting Object 060 */ 061 @SuppressFBWarnings(value = "UW_UNCOND_WAIT", justification = "unguarded wait() used intentionally here as part of utility class") 062 public WaitHandler(Object self) { 063 // loop until interrupted, or non-spurious wake 064 while (true) { 065 try { 066 synchronized (self) { 067 self.wait(); 068 069 if (!wasSpurious()) { 070 break; 071 } 072 } 073 } catch (InterruptedException e) { 074 if (handleInterruptedException(e)) { 075 break; 076 } 077 } 078 } 079 } 080 081 /** 082 * Method to determine if a wake was spurious or not. By default, all wakes 083 * are considered not spurious and the full time may not elapse. Override to 084 * provide a test (returning true) when there's a way to tell that a wake 085 * was spurious and the wait() should continue. 086 * 087 * @return false unless overridden by a subclass 088 */ 089 protected boolean wasSpurious() { 090 return false; 091 } 092 093 /** 094 * Define interrupt processing. 095 * 096 * By default, just records and leaves the wait early. 097 * 098 * @param e the exception to handle 099 * @return true if should break out of wait 100 */ 101 boolean handleInterruptedException(InterruptedException e) { 102 Thread.currentThread().interrupt(); // retain if needed later 103 return true; // and leave the wait now 104 } 105}