/* * Copyright (C) 2015 SoftIndex LLC. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.datakernel.eventloop; import io.datakernel.async.*; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public final class BlockingEventloopExecutor implements EventloopExecutor { private final Eventloop eventloop; private final Lock lock = new ReentrantLock(); private final Condition notFull = lock.newCondition(); private final AtomicInteger tasks = new AtomicInteger(); private final int limit; // region builders private BlockingEventloopExecutor(Eventloop eventloop, int limit) { this.eventloop = eventloop; this.limit = limit; } public static BlockingEventloopExecutor create(Eventloop eventloop, int limit) { return new BlockingEventloopExecutor(eventloop, limit); } // endregion public int getLimit() { return limit; } private void post(final Runnable runnable) throws InterruptedException { lock.lock(); try { while (tasks.get() > limit) { notFull.await(); } tasks.incrementAndGet(); eventloop.execute(runnable); } finally { lock.unlock(); } } private void post(final Runnable runnable, ResultCallbackFuture<?> future) { try { post(runnable); } catch (InterruptedException e) { future.setException(e); } } private void complete() { lock.lock(); try { tasks.decrementAndGet(); notFull.signal(); } finally { lock.unlock(); } } @Override public void execute(final Runnable runnable) { try { post(new Runnable() { @Override public void run() { try { runnable.run(); } finally { complete(); } } }); } catch (InterruptedException ignored) { } } public void execute(final AsyncRunnable asyncRunnable) { try { post(new Runnable() { @Override public void run() { asyncRunnable.run(new CompletionCallback() { @Override protected void onComplete() { complete(); } @Override protected void onException(Exception exception) { complete(); } }); } }); } catch (InterruptedException ignored) { } } @Override public Future<?> submit(Runnable runnable) { return submit(runnable, null); } @Override public Future<?> submit(AsyncRunnable asyncRunnable) { return submit(asyncRunnable, null); } @Override public <T> Future<T> submit(final Runnable runnable, final T result) { final ResultCallbackFuture<T> future = ResultCallbackFuture.create(); post(new Runnable() { @Override public void run() { Exception exception = null; try { runnable.run(); } catch (Exception e) { exception = e; } complete(); if (exception == null) { future.setResult(result); } else { future.setException(exception); } } }, future); return future; } @Override public <T> Future<T> submit(final AsyncRunnable asyncRunnable, final T result) { final ResultCallbackFuture<T> future = ResultCallbackFuture.create(); post(new Runnable() { @Override public void run() { asyncRunnable.run(new CompletionCallback() { @Override protected void onComplete() { setComplete(); future.setResult(result); } @Override protected void onException(Exception exception) { setComplete(); future.setException(exception); } }); } }, future); return future; } @Override public <T> Future<T> submit(final Callable<T> callable) { final ResultCallbackFuture<T> future = ResultCallbackFuture.create(); post(new Runnable() { @Override public void run() { T result = null; Exception exception = null; try { result = callable.call(); } catch (Exception e) { exception = e; } complete(); if (exception == null) { future.setResult(result); } else { future.setException(exception); } } }, future); return future; } @Override public <T> Future<T> submit(final AsyncCallable<T> asyncCallable) { final ResultCallbackFuture<T> future = ResultCallbackFuture.create(); post(new Runnable() { @Override public void run() { asyncCallable.call(new ResultCallback<T>() { @Override protected void onResult(T result) { complete(); future.setResult(result); } @Override protected void onException(Exception exception) { complete(); future.setException(exception); } }); } }, future); return future; } }