| Index: mojo/bindings/java/src/org/chromium/mojo/bindings/ExecutorFactory.java
|
| diff --git a/mojo/bindings/java/src/org/chromium/mojo/bindings/ExecutorFactory.java b/mojo/bindings/java/src/org/chromium/mojo/bindings/ExecutorFactory.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c153bc907d9b33902342b417cbbc9044688e1b0c
|
| --- /dev/null
|
| +++ b/mojo/bindings/java/src/org/chromium/mojo/bindings/ExecutorFactory.java
|
| @@ -0,0 +1,183 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package org.chromium.mojo.bindings;
|
| +
|
| +import org.chromium.mojo.system.AsyncWaiter;
|
| +import org.chromium.mojo.system.AsyncWaiter.Callback;
|
| +import org.chromium.mojo.system.Core;
|
| +import org.chromium.mojo.system.MessagePipeHandle;
|
| +import org.chromium.mojo.system.MessagePipeHandle.ReadMessageResult;
|
| +import org.chromium.mojo.system.MojoException;
|
| +import org.chromium.mojo.system.MojoResult;
|
| +import org.chromium.mojo.system.Pair;
|
| +
|
| +import java.nio.ByteBuffer;
|
| +import java.util.ArrayList;
|
| +import java.util.List;
|
| +import java.util.concurrent.Executor;
|
| +
|
| +/**
|
| + * A factory which provides per-thread executors, which enable execution on the thread from which
|
| + * they were obtained.
|
| + */
|
| +class ExecutorFactory {
|
| +
|
| + /**
|
| + * A null buffer which is used to send messages without any data on the PipedExecutor's
|
| + * signaling handles.
|
| + */
|
| + private static final ByteBuffer NOTIFY_BUFFER = null;
|
| +
|
| + /**
|
| + * Implementation of the executor which uses a pair of {@link MessagePipeHandle} for signaling.
|
| + * The executor will wait asynchronously on one end of a {@link MessagePipeHandle} on the thread
|
| + * on which it was created. Other threads can call execute with a {@link Runnable}, and the
|
| + * executor will queue the {@link Runnable} and write a message on the other end of the handle.
|
| + * This will wake up the executor which is waiting on the handle, which will then dequeue the
|
| + * {@link Runnable} and execute it on the original thread.
|
| + */
|
| + private static class PipedExecutor implements Executor, Callback {
|
| +
|
| + /**
|
| + * The handle which is written to. Access to this object must be protected with |mLock|.
|
| + */
|
| + private final MessagePipeHandle mWriteHandle;
|
| + /**
|
| + * The handle which is read from.
|
| + */
|
| + private final MessagePipeHandle mReadHandle;
|
| + /**
|
| + * The list of actions left to be run. Access to this object must be protected with |mLock|.
|
| + */
|
| + private final List<Runnable> mPendingActions;
|
| + /**
|
| + * Lock protecting access to |mWriteHandle| and |mPendingActions|.
|
| + */
|
| + private final Object mLock;
|
| + /**
|
| + * The {@link AsyncWaiter} to get notified of new message availability on |mReadHandle|.
|
| + */
|
| + private final AsyncWaiter mWaiter;
|
| +
|
| + /**
|
| + * Constructor.
|
| + */
|
| + public PipedExecutor(Core core) {
|
| + mWaiter = core.getDefaultAsyncWaiter();
|
| + assert mWaiter != null;
|
| + mLock = new Object();
|
| + Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe();
|
| + mReadHandle = handles.first;
|
| + mWriteHandle = handles.second;
|
| + mPendingActions = new ArrayList<Runnable>();
|
| + asyncWait();
|
| + }
|
| +
|
| + /**
|
| + * Asynchronously wait for the next command to arrive. This should only be called on the
|
| + * executor thread.
|
| + */
|
| + private void asyncWait() {
|
| + mWaiter.asyncWait(mReadHandle, Core.WaitFlags.READABLE, Core.DEADLINE_INFINITE,
|
| + this);
|
| + }
|
| +
|
| + /**
|
| + * @see Callback#onResult(int)
|
| + */
|
| + @Override
|
| + public void onResult(int result) {
|
| + if (result == MojoResult.OK && readNotifyBufferMessage()) {
|
| + runNextAction();
|
| + } else {
|
| + close();
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * @see Callback#onError(MojoException)
|
| + */
|
| + @Override
|
| + public void onError(MojoException exception) {
|
| + close();
|
| + }
|
| +
|
| + /**
|
| + * Close the handles. Should only be called on the executor thread.
|
| + */
|
| + private void close() {
|
| + synchronized (mLock) {
|
| + mWriteHandle.close();
|
| + mPendingActions.clear();
|
| + }
|
| + mReadHandle.close();
|
| + }
|
| +
|
| + /**
|
| + * Read the next message on |mReadHandle|, and return |true| if successful, |false|
|
| + * otherwise.
|
| + */
|
| + private boolean readNotifyBufferMessage() {
|
| + try {
|
| + ReadMessageResult readMessageResult = mReadHandle.readMessage(NOTIFY_BUFFER, 0,
|
| + MessagePipeHandle.ReadFlags.NONE);
|
| + if (readMessageResult.getMojoResult() == MojoResult.OK) {
|
| + asyncWait();
|
| + return true;
|
| + }
|
| + } catch (MojoException e) {
|
| + // Will be closed by the fall back at the end of this method.
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * Run the next action in the |mPendingActions| queue.
|
| + */
|
| + private void runNextAction() {
|
| + Runnable toRun = null;
|
| + synchronized (mWriteHandle) {
|
| + toRun = mPendingActions.remove(0);
|
| + }
|
| + toRun.run();
|
| + }
|
| +
|
| + /**
|
| + * Execute the given |command| in the executor thread. This can be called on any thread.
|
| + *
|
| + * @see Executor#execute(Runnable)
|
| + */
|
| + @Override
|
| + public void execute(Runnable command) {
|
| + // Accessing the write handle must be protected by the lock, because it can be closed
|
| + // from the executor's thread.
|
| + synchronized (mLock) {
|
| + if (!mWriteHandle.isValid()) {
|
| + throw new IllegalStateException(
|
| + "Trying to execute an action on a closed executor.");
|
| + }
|
| + mPendingActions.add(command);
|
| + mWriteHandle.writeMessage(NOTIFY_BUFFER, null, MessagePipeHandle.WriteFlags.NONE);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Keep one executor per executor thread.
|
| + */
|
| + private static final ThreadLocal<Executor> EXECUTORS = new ThreadLocal<Executor>();
|
| +
|
| + /**
|
| + * Returns an {@link Executor} that will run all of its actions in the current thread.
|
| + */
|
| + public static Executor getExecutorForCurrentThread(Core core) {
|
| + Executor executor = EXECUTORS.get();
|
| + if (executor == null) {
|
| + executor = new PipedExecutor(core);
|
| + EXECUTORS.set(executor);
|
| + }
|
| + return executor;
|
| + }
|
| +}
|
|
|