2 // ========================================================================
3 // Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.util;
21 import java.io.Closeable;
22 import java.io.IOException;
23 import java.io.InterruptedIOException;
24 import java.util.concurrent.CancellationException;
25 import java.util.concurrent.TimeUnit;
26 import java.util.concurrent.TimeoutException;
27 import java.util.concurrent.locks.Condition;
28 import java.util.concurrent.locks.ReentrantLock;
30 import org.eclipse.jetty.util.log.Log;
31 import org.eclipse.jetty.util.log.Logger;
32 import org.eclipse.jetty.util.thread.NonBlockingThread;
35 /* ------------------------------------------------------------ */
36 /** Provides a reusable BlockingCallback.
37 * A typical usage pattern is:
39 * void someBlockingCall(Object... args) throws IOException
41 * try(Blocker blocker=sharedBlockingCallback.acquire())
43 * someAsyncCall(args,blocker);
49 public class SharedBlockingCallback
51 static final Logger LOG = Log.getLogger(SharedBlockingCallback.class);
53 final ReentrantLock _lock = new ReentrantLock();
54 final Condition _idle = _lock.newCondition();
55 final Condition _complete = _lock.newCondition();
58 private static Throwable IDLE = new Throwable()
61 public String toString()
67 private static Throwable SUCCEEDED = new Throwable()
70 public String toString()
76 private static Throwable FAILED = new Throwable()
79 public String toString()
87 public SharedBlockingCallback()
89 _blocker=new Blocker();
92 protected long getIdleTimeout()
97 public Blocker acquire() throws IOException
100 long idle = getIdleTimeout();
103 while (_blocker._state != IDLE)
105 if (idle>0 && (idle < Long.MAX_VALUE/2))
107 // Wait a little bit longer than the blocker might block
108 if (!_idle.await(idle*2,TimeUnit.MILLISECONDS))
109 throw new IOException(new TimeoutException());
114 _blocker._state = null;
116 catch (final InterruptedException e)
118 throw new InterruptedIOException();
127 protected void notComplete(Blocker blocker)
129 LOG.warn("Blocker not complete {}",blocker);
130 if (LOG.isDebugEnabled())
131 LOG.debug(new Throwable());
134 /* ------------------------------------------------------------ */
135 /** A Closeable Callback.
136 * Uses the auto close mechanism to check block has been called OK.
138 public class Blocker implements Callback, Closeable
140 Throwable _state = IDLE;
147 public void succeeded()
155 _complete.signalAll();
158 throw new IllegalStateException(_state);
167 public void failed(Throwable cause)
176 else if (cause instanceof BlockerTimeoutException)
177 // Not this blockers timeout
178 _state=new IOException(cause);
181 _complete.signalAll();
184 throw new IllegalStateException(_state);
193 * Block until the Callback has succeeded or failed and after the return leave in the state to allow reuse. This is useful for code that wants to
194 * repeatable use a FutureCallback to convert an asynchronous API to a blocking API.
196 * @throws IOException
197 * if exception was caught during blocking, or callback was cancelled
199 public void block() throws IOException
201 if (NonBlockingThread.isNonBlockingThread())
202 LOG.warn("Blocking a NonBlockingThread: ",new Throwable());
205 long idle = getIdleTimeout();
208 while (_state == null)
210 if (idle>0 && (idle < Long.MAX_VALUE/2))
212 // Wait a little bit longer than expected callback idle timeout
213 if (!_complete.await(idle+idle/2,TimeUnit.MILLISECONDS))
214 // The callback has not arrived in sufficient time.
215 // We will synthesize a TimeoutException
216 _state=new BlockerTimeoutException();
222 if (_state == SUCCEEDED)
225 throw new IllegalStateException("IDLE");
226 if (_state instanceof IOException)
227 throw (IOException)_state;
228 if (_state instanceof CancellationException)
229 throw (CancellationException)_state;
230 if (_state instanceof RuntimeException)
231 throw (RuntimeException)_state;
232 if (_state instanceof Error)
234 throw new IOException(_state);
236 catch (final InterruptedException e)
238 throw new InterruptedIOException();
247 * Check the Callback has succeeded or failed and after the return leave in the state to allow reuse.
249 * @throws IOException
250 * if exception was caught during blocking, or callback was cancelled
253 public void close() throws IOException
259 throw new IllegalStateException("IDLE");
267 // If the blocker timed itself out, remember the state
268 if (_state instanceof BlockerTimeoutException)
269 // and create a new Blocker
270 _blocker=new Blocker();
272 // else reuse Blocker
275 _complete.signalAll();
285 public String toString()
290 return String.format("%s@%x{%s}",Blocker.class.getSimpleName(),hashCode(),_state);
299 private static class BlockerTimeoutException extends TimeoutException