org.eclipse.jetty.util.Blocker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jetty-util Show documentation
Show all versions of jetty-util Show documentation
Utility classes for Jetty
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.util;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import org.eclipse.jetty.util.thread.Invocable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility class that provides blocking {@link java.lang.Runnable} and {@link org.eclipse.jetty.util.Callback}
* instances. These can either be shared (and mutually excluded from concurrent usage) or single usage.
* The instances are autocloseable and will emit a warning if the instance is not completed within close.
*
* Non shared Runnable
*
* try(Blocker.Runnable onAction = Blocker.runnable())
* {
* someMethod(onAction);
* onAction.block();
* }
*
*
* Shared Runnable
*
* Blocker.SharedRunnable shared = new Blocker.Shared();
* // ...
* try(Blocker.Runnable onAction = shared.runnable())
* {
* someMethod(onAction);
* onAction.block();
* }
*
*
* Non shared Callback
*
* try(Blocker.Callback callback = Blocker.callback())
* {
* someMethod(callback);
* callback.block();
* }
*
*
* Shared Callback
*
* Blocker.Shared blocker = new Blocker.Shared();
* // ...
* try(Blocker.Callback callback = blocker.callback())
* {
* someMethod(callback);
* callback.block();
* }
*
*/
public class Blocker
{
private static final Logger LOG = LoggerFactory.getLogger(Blocker.class);
private static final Throwable ACQUIRED = new Throwable()
{
@Override
public Throwable fillInStackTrace()
{
return this;
}
};
private static final Throwable SUCCEEDED = new Throwable()
{
@Override
public Throwable fillInStackTrace()
{
return this;
}
};
public interface Runnable extends java.lang.Runnable, AutoCloseable, Invocable
{
void block() throws IOException;
@Override
void close();
}
public static Runnable runnable()
{
return new Runnable()
{
final CountDownLatch _complete = new CountDownLatch(1);
@Override
public void run()
{
_complete.countDown();
}
@Override
public InvocationType getInvocationType()
{
return InvocationType.NON_BLOCKING;
}
@Override
public void block() throws IOException
{
try
{
_complete.await();
}
catch (Throwable t)
{
throw IO.rethrow(t);
}
}
@Override
public void close()
{
if (_complete.getCount() != 0)
{
if (LOG.isDebugEnabled())
LOG.warn("Blocking.Runnable incomplete", new Throwable());
else
LOG.warn("Blocking.Runnable incomplete");
}
}
};
}
public interface Callback extends org.eclipse.jetty.util.Callback, AutoCloseable, Invocable
{
void block() throws IOException;
@Override
void close();
}
public static Callback callback()
{
return new Callback()
{
private final CompletableFuture _future = new CompletableFuture<>();
@Override
public InvocationType getInvocationType()
{
return InvocationType.NON_BLOCKING;
}
@Override
public void succeeded()
{
_future.complete(SUCCEEDED);
}
@Override
public void failed(Throwable x)
{
_future.complete(x == null ? new Throwable() : x);
}
@Override
public void block() throws IOException
{
Throwable result;
try
{
result = _future.get();
}
catch (Throwable t)
{
result = t;
}
if (result == SUCCEEDED)
return;
throw IO.rethrow(result);
}
@Override
public void close()
{
if (!_future.isDone())
{
if (LOG.isDebugEnabled())
LOG.warn("Blocking.Callback incomplete", new Throwable());
else
LOG.warn("Blocking.Callback incomplete");
}
}
};
}
public interface Promise extends org.eclipse.jetty.util.Promise, AutoCloseable, Invocable
{
C block() throws IOException;
@Override
void close();
}
public static Promise promise()
{
return new Promise<>()
{
private final CompletableFuture _future = new CompletableFuture<>();
@Override
public InvocationType getInvocationType()
{
return InvocationType.NON_BLOCKING;
}
@Override
public C block() throws IOException
{
try
{
return _future.get();
}
catch (Throwable t)
{
throw IO.rethrow(t);
}
}
@Override
public void close()
{
if (!_future.isDone())
{
if (LOG.isDebugEnabled())
LOG.warn("Blocking.Promise incomplete", new Throwable());
else
LOG.warn("Blocking.Promise incomplete");
}
}
@Override
public void succeeded(C result)
{
_future.complete(result);
}
@Override
public void failed(Throwable x)
{
_future.completeExceptionally(x);
}
};
}
/**
* A shared reusable Blocking source.
*/
public static class Shared
{
private final ReentrantLock _lock = new ReentrantLock();
private final Condition _idle = _lock.newCondition();
private final Condition _complete = _lock.newCondition();
private Throwable _completed;
private final Callback _callback = new Callback()
{
@Override
public InvocationType getInvocationType()
{
return InvocationType.NON_BLOCKING;
}
@Override
public void succeeded()
{
_lock.lock();
try
{
if (_completed == ACQUIRED)
{
_completed = SUCCEEDED;
_complete.signalAll();
}
}
finally
{
_lock.unlock();
}
}
@Override
public void failed(Throwable x)
{
_lock.lock();
try
{
if (_completed == ACQUIRED)
{
_completed = x;
_complete.signalAll();
}
}
finally
{
_lock.unlock();
}
}
@Override
public void block() throws IOException
{
_lock.lock();
Throwable result;
try
{
while (_completed == ACQUIRED)
{
_complete.await();
}
result = _completed;
}
catch (Throwable t)
{
result = t;
}
finally
{
_lock.unlock();
}
if (result != SUCCEEDED)
throw IO.rethrow(result);
}
@Override
public void close()
{
boolean completed;
_lock.lock();
try
{
completed = _completed != ACQUIRED;
}
finally
{
_completed = null;
_idle.signalAll();
_lock.unlock();
}
if (!completed)
{
if (LOG.isDebugEnabled())
LOG.warn("Blocking.Shared incomplete", new Throwable());
else
LOG.warn("Blocking.Shared incomplete");
}
}
};
private final Runnable _runnable = new Runnable()
{
@Override
public InvocationType getInvocationType()
{
return InvocationType.NON_BLOCKING;
}
@Override
public void run()
{
_callback.succeeded();
}
@Override
public void block() throws IOException
{
_callback.block();
}
@Override
public void close()
{
_callback.close();
}
};
public Callback callback() throws IOException
{
_lock.lock();
try
{
while (_completed != null)
_idle.await();
_completed = ACQUIRED;
return _callback;
}
catch (InterruptedException x)
{
throw new InterruptedIOException();
}
finally
{
_lock.unlock();
}
}
public Runnable runnable() throws IOException
{
_lock.lock();
try
{
while (_completed != null)
_idle.await();
_completed = ACQUIRED;
return _runnable;
}
catch (InterruptedException x)
{
throw new InterruptedIOException();
}
finally
{
_lock.unlock();
}
}
}
}