001    /*
002     * Copyright 2009-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-2016 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.util;
022    
023    
024    
025    import java.io.Serializable;
026    import java.util.concurrent.atomic.AtomicBoolean;
027    import java.util.concurrent.atomic.AtomicLong;
028    
029    
030    
031    /**
032     * This class provides a utility that can be used to sleep for a specified
033     * period of time in a manner that allows it to be woken up if necessary.  A
034     * single instance of this class may only be used to allow one thread to sleep
035     * at any given time, so if multiple threads need to sleep at the same time then
036     * a separate {@code WakeableSleeper} instance should be used for each.
037     */
038    @ThreadSafety(level=ThreadSafetyLevel.MOSTLY_NOT_THREADSAFE)
039    public final class WakeableSleeper
040           implements Serializable
041    {
042      /**
043       * The serial version UID for this serializable class.
044       */
045      private static final long serialVersionUID = 755656862953269760L;
046    
047    
048    
049      // A flag used to prevent multiple concurrent attempts to sleep.
050      private final AtomicBoolean sleeping;
051    
052      // A flag used to indicate that this WakeableSleeper has been shut down.
053      private final AtomicBoolean shutDown;
054    
055      // The number of attempts to wake up this sleeper.
056      private final AtomicLong wakeupCount;
057    
058    
059    
060      /**
061       * Creates a new instance of this wakeable sleeper.
062       */
063      public WakeableSleeper()
064      {
065        sleeping    = new AtomicBoolean(false);
066        shutDown    = new AtomicBoolean(false);
067        wakeupCount = new AtomicLong(0L);
068      }
069    
070    
071    
072      /**
073       * Return {@code true} if this {@code WakeableSleeper} instance has been
074       * shutdown via the {@code shutDown()} method and {@code false} otherwise.
075       *
076       * @return  {@code true} if this {@code WakeableSleeper} instance has been
077       *          shutdown via the {@code shutDown()} method and {@code false}
078       *          otherwise.
079       */
080      public boolean isShutDown()
081      {
082        return shutDown.get();
083      }
084    
085    
086    
087      /**
088       * Attempts to sleep for the specified length of time in milliseconds, subject
089       * to the accuracy available within the JVM and underlying system.  It may
090       * wake up prematurely if the wakeup method is called, or if the thread is
091       * interrupted.  If {@code shutDown()} is called, then any active caller of
092       * this method will return immediately, and subsequent calls will return
093       * without sleeping.
094       * <BR><BR>
095       * This method must not be called on the same {@code WakeableSleeper} instance
096       * by multiple threads at the same time.
097       *
098       * @param  time  The length of time in milliseconds to sleep.
099       *
100       * @return  {@code true} if the sleep completed, or {@code false} if it was
101       *          woken or interrupted prematurely.
102       */
103      @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
104      public boolean sleep(final long time)
105      {
106        synchronized (wakeupCount)
107        {
108          if (isShutDown())
109          {
110            return false;
111          }
112    
113          Validator.ensureTrue(sleeping.compareAndSet(false, true),
114               "WakeableSleeper.sleep() must not be invoked concurrently by " +
115                    "multiple threads against the same instance.");
116    
117          try
118          {
119            final long beforeCount = wakeupCount.get();
120            wakeupCount.wait(time);
121            final long afterCount = wakeupCount.get();
122            return (beforeCount == afterCount);
123          }
124          catch (final InterruptedException ie)
125          {
126            Debug.debugException(ie);
127            return false;
128          }
129          finally
130          {
131            sleeping.set(false);
132          }
133        }
134      }
135    
136    
137    
138      /**
139       * Permanently shuts down this {@code WakeableSleeper} instance.  If a thread
140       * is currently blocked in the {@code sleep} method, it will return
141       * immediately, and all subsequent calls to that method will return without
142       * sleeping.  It is safe to call this method multiple times.
143       */
144      @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
145      public void shutDown()
146      {
147        shutDown.set(true);
148        wakeup();
149      }
150    
151    
152    
153      /**
154       * Indicates that the sleeper should wake up if it is currently sleeping.
155       * This method will not make any attempt to ensure that the thread had woken
156       * up before returning.  If multiple threads attempt to wake up the sleeper at
157       * the same time, then it will have the same effect as a single wakeup
158       * request.
159       */
160      @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
161      public void wakeup()
162      {
163        synchronized (wakeupCount)
164        {
165          wakeupCount.incrementAndGet();
166          wakeupCount.notifyAll();
167        }
168      }
169    }