summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorqsr@chromium.org <qsr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-20 15:36:34 +0000
committerqsr@chromium.org <qsr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-20 15:36:34 +0000
commit7d9d82e1b08b031b24451d6cb7f035d0cc3f7365 (patch)
treee879929a8d6574a0d5c7f1b2fc862f053d263295
parent07177bac1c498f5f3e8307058dd8a36ca02b1756 (diff)
downloadchromium_src-7d9d82e1b08b031b24451d6cb7f035d0cc3f7365.zip
chromium_src-7d9d82e1b08b031b24451d6cb7f035d0cc3f7365.tar.gz
chromium_src-7d9d82e1b08b031b24451d6cb7f035d0cc3f7365.tar.bz2
Add AsyncWaiter implementation to java API.
Review URL: https://codereview.chromium.org/288993002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@271660 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--mojo/android/javatests/core_test.cc35
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/system/CoreTest.java307
-rw-r--r--mojo/android/system/core_impl.cc82
-rw-r--r--mojo/android/system/src/org/chromium/mojo/system/CoreImpl.java106
-rw-r--r--mojo/mojo.gyp1
-rw-r--r--mojo/public/java/src/org/chromium/mojo/system/AsyncWaiter.java53
-rw-r--r--mojo/public/java/src/org/chromium/mojo/system/Core.java6
7 files changed, 582 insertions, 8 deletions
diff --git a/mojo/android/javatests/core_test.cc b/mojo/android/javatests/core_test.cc
index 3d4032f..38aa6c4 100644
--- a/mojo/android/javatests/core_test.cc
+++ b/mojo/android/javatests/core_test.cc
@@ -6,7 +6,22 @@
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/test_support_android.h"
#include "jni/CoreTest_jni.h"
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace {
+
+struct TestEnvironment {
+ mojo::Environment environment;
+ base::MessageLoopForUI message_loop;
+};
+
+} // namespace
namespace mojo {
namespace android {
@@ -16,6 +31,26 @@ static void InitApplicationContext(JNIEnv* env,
jobject context) {
base::android::ScopedJavaLocalRef<jobject> scoped_context(env, context);
base::android::InitApplicationContext(env, scoped_context);
+ base::InitAndroidTestMessageLoop();
+}
+
+static jlong SetupTestEnvironment(JNIEnv* env, jobject jcaller) {
+ return reinterpret_cast<intptr_t>(new TestEnvironment());
+}
+
+static void TearDownTestEnvironment(JNIEnv* env,
+ jobject jcaller,
+ jlong test_environment) {
+ delete reinterpret_cast<TestEnvironment*>(test_environment);
+}
+
+static void RunLoop(JNIEnv* env, jobject jcaller, jlong timeout_ms) {
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::MessageLoop::QuitClosure(),
+ base::TimeDelta::FromMilliseconds(timeout_ms));
+ base::RunLoop run_loop;
+ run_loop.Run();
}
bool RegisterCoreTest(JNIEnv* env) {
diff --git a/mojo/android/javatests/src/org/chromium/mojo/system/CoreTest.java b/mojo/android/javatests/src/org/chromium/mojo/system/CoreTest.java
index bf941b0..b534752 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/system/CoreTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/system/CoreTest.java
@@ -10,6 +10,8 @@ import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.base.JNINamespace;
import org.chromium.base.library_loader.LibraryLoader;
+import org.chromium.mojo.system.AsyncWaiter.Callback;
+import org.chromium.mojo.system.AsyncWaiter.Cancellable;
import org.chromium.mojo.system.Core.WaitFlags;
import org.chromium.mojo.system.Core.WaitManyResult;
import org.chromium.mojo.system.MessagePipeHandle.ReadFlags;
@@ -33,16 +35,31 @@ import java.util.concurrent.TimeUnit;
@JNINamespace("mojo::android")
public class CoreTest extends InstrumentationTestCase {
+ private static final long RUN_LOOP_TIMEOUT_MS = 5;
+
private static final ScheduledExecutorService WORKER =
Executors.newSingleThreadScheduledExecutor();
+ private long mTestEnvironmentPointer;
+
/**
* @see junit.framework.TestCase#setUp()
*/
@Override
protected void setUp() throws Exception {
+ super.setUp();
LibraryLoader.ensureInitialized();
nativeInitApplicationContext(getInstrumentation().getTargetContext());
+ mTestEnvironmentPointer = nativeSetupTestEnvironment();
+ }
+
+ /**
+ * @see android.test.InstrumentationTestCase#tearDown()
+ */
+ @Override
+ protected void tearDown() throws Exception {
+ nativeTearDownTestEnvironment(mTestEnvironmentPointer);
+ super.tearDown();
}
/**
@@ -518,5 +535,295 @@ public class CoreTest extends InstrumentationTestCase {
}
}
+ private static class AsyncWaiterResult implements Callback {
+ private int mResult = Integer.MIN_VALUE;
+ private MojoException mException = null;
+
+ /**
+ * @see Callback#onResult(int)
+ */
+ @Override
+ public void onResult(int result) {
+ this.mResult = result;
+ }
+
+ /**
+ * @see Callback#onError(MojoException)
+ */
+ @Override
+ public void onError(MojoException exception) {
+ this.mException = exception;
+ }
+
+ /**
+ * @return the result
+ */
+ public int getResult() {
+ return mResult;
+ }
+
+ /**
+ * @return the exception
+ */
+ public MojoException getException() {
+ return mException;
+ }
+
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterCorrectResult() {
+ Core core = CoreImpl.getInstance();
+
+ // Checking a correct result.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe();
+ try {
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ core.getDefaultAsyncWaiter().asyncWait(handles.first,
+ WaitFlags.none().setReadable(true), Core.DEADLINE_INFINITE, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ handles.second.writeMessage(ByteBuffer.allocateDirect(1), null,
+ MessagePipeHandle.WriteFlags.none());
+ nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+ assertNull(asyncWaiterResult.getException());
+ assertEquals(MojoResult.OK, asyncWaiterResult.getResult());
+ } finally {
+ handles.first.close();
+ handles.second.close();
+ }
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterClosingPeerHandle() {
+ Core core = CoreImpl.getInstance();
+
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe();
+ try {
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ core.getDefaultAsyncWaiter().asyncWait(handles.first,
+ WaitFlags.none().setReadable(true), Core.DEADLINE_INFINITE, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ handles.second.close();
+ nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+ assertNull(asyncWaiterResult.getException());
+ assertEquals(MojoResult.FAILED_PRECONDITION, asyncWaiterResult.getResult());
+ } finally {
+ handles.first.close();
+ handles.second.close();
+ }
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterClosingWaitingHandle() {
+ Core core = CoreImpl.getInstance();
+
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe();
+ try {
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ core.getDefaultAsyncWaiter().asyncWait(handles.first,
+ WaitFlags.none().setReadable(true), Core.DEADLINE_INFINITE, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ handles.first.close();
+ nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+ // TODO(qsr) Re-enable when MojoWaitMany handles it correctly.
+ // assertNull(asyncWaiterResult.getException());
+ // assertEquals(MojoResult.CANCELLED, asyncWaiterResult.getResult());
+ } finally {
+ handles.first.close();
+ handles.second.close();
+ }
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterWaitingOnInvalidHandle() {
+ Core core = CoreImpl.getInstance();
+
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe();
+ try {
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ handles.first.close();
+ core.getDefaultAsyncWaiter().asyncWait(handles.first,
+ WaitFlags.none().setReadable(true), Core.DEADLINE_INFINITE, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+ assertNotNull(asyncWaiterResult.getException());
+ assertEquals(MojoResult.INVALID_ARGUMENT,
+ asyncWaiterResult.getException().getMojoResult());
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ } finally {
+ handles.first.close();
+ handles.second.close();
+ }
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterWaitingOnDefaultInvalidHandle() {
+ Core core = CoreImpl.getInstance();
+
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ core.getDefaultAsyncWaiter().asyncWait(new InvalidHandle(),
+ WaitFlags.none().setReadable(true), Core.DEADLINE_INFINITE, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+ assertNotNull(asyncWaiterResult.getException());
+ assertEquals(MojoResult.INVALID_ARGUMENT,
+ asyncWaiterResult.getException().getMojoResult());
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterWaitingWithTimeout() {
+ Core core = CoreImpl.getInstance();
+
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe();
+ try {
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ core.getDefaultAsyncWaiter().asyncWait(handles.first,
+ WaitFlags.none().setReadable(true), RUN_LOOP_TIMEOUT_MS, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ nativeRunLoop(10 * RUN_LOOP_TIMEOUT_MS);
+ assertNull(asyncWaiterResult.getException());
+ assertEquals(MojoResult.DEADLINE_EXCEEDED, asyncWaiterResult.getResult());
+ } finally {
+ handles.first.close();
+ handles.second.close();
+ }
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterCancelWaiting() {
+ Core core = CoreImpl.getInstance();
+
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe();
+ try {
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ Cancellable cancellable = core.getDefaultAsyncWaiter().asyncWait(handles.first,
+ WaitFlags.none().setReadable(true), Core.DEADLINE_INFINITE, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ cancellable.cancel();
+ nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ handles.second.writeMessage(ByteBuffer.allocateDirect(1), null,
+ MessagePipeHandle.WriteFlags.none());
+ nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+ } finally {
+ handles.first.close();
+ handles.second.close();
+ }
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterImmediateCancelOnInvalidHandle() {
+ Core core = CoreImpl.getInstance();
+
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe();
+ try {
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ handles.first.close();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ Cancellable cancellable = core.getDefaultAsyncWaiter().asyncWait(handles.first,
+ WaitFlags.none().setReadable(true), Core.DEADLINE_INFINITE, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+ cancellable.cancel();
+
+ nativeRunLoop(RUN_LOOP_TIMEOUT_MS);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+ } finally {
+ handles.first.close();
+ handles.second.close();
+ }
+ }
+
private native void nativeInitApplicationContext(Context context);
+
+ private native long nativeSetupTestEnvironment();
+
+ private native void nativeTearDownTestEnvironment(long testEnvironment);
+
+ private native void nativeRunLoop(long timeoutMS);
}
diff --git a/mojo/android/system/core_impl.cc b/mojo/android/system/core_impl.cc
index d7552c1..34d009c 100644
--- a/mojo/android/system/core_impl.cc
+++ b/mojo/android/system/core_impl.cc
@@ -8,10 +8,43 @@
#include "base/android/jni_android.h"
#include "base/android/jni_registrar.h"
#include "base/android/library_loader/library_loader_hooks.h"
-#include "base/logging.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
#include "jni/CoreImpl_jni.h"
#include "mojo/embedder/embedder.h"
+#include "mojo/public/c/environment/async_waiter.h"
#include "mojo/public/c/system/core.h"
+#include "mojo/public/cpp/environment/default_async_waiter.h"
+
+namespace {
+
+// |AsyncWait| is guaranteed never to return 0.
+const MojoAsyncWaitID kInvalidHandleCancelID = 0;
+
+struct AsyncWaitCallbackData {
+ base::android::ScopedJavaGlobalRef<jobject> core_impl;
+ base::android::ScopedJavaGlobalRef<jobject> callback;
+ base::android::ScopedJavaGlobalRef<jobject> cancellable;
+
+ AsyncWaitCallbackData(JNIEnv* env, jobject core_impl, jobject callback) {
+ this->core_impl.Reset(env, core_impl);
+ this->callback.Reset(env, callback);
+ }
+};
+
+void AsyncWaitCallback(void* data, MojoResult result) {
+ scoped_ptr<AsyncWaitCallbackData> callback_data(
+ static_cast<AsyncWaitCallbackData*>(data));
+ mojo::android::Java_CoreImpl_onAsyncWaitResult(
+ base::android::AttachCurrentThread(),
+ callback_data->core_impl.obj(),
+ result,
+ callback_data->callback.obj(),
+ callback_data->cancellable.obj());
+}
+
+} // namespace
namespace mojo {
namespace android {
@@ -285,6 +318,53 @@ static int Unmap(JNIEnv* env, jobject jcaller, jobject buffer) {
return MojoUnmapBuffer(buffer_start);
}
+static jobject AsyncWait(JNIEnv* env,
+ jobject jcaller,
+ jint mojo_handle,
+ jint flags,
+ jlong deadline,
+ jobject callback) {
+ AsyncWaitCallbackData* callback_data =
+ new AsyncWaitCallbackData(env, jcaller, callback);
+ MojoAsyncWaitID cancel_id;
+ if (static_cast<MojoHandle>(mojo_handle) != MOJO_HANDLE_INVALID) {
+ MojoAsyncWaiter* async_waiter = mojo::GetDefaultAsyncWaiter();
+ cancel_id = async_waiter->AsyncWait(async_waiter,
+ mojo_handle,
+ flags,
+ deadline,
+ AsyncWaitCallback,
+ callback_data);
+ } else {
+ cancel_id = kInvalidHandleCancelID;
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &AsyncWaitCallback, callback_data, MOJO_RESULT_INVALID_ARGUMENT));
+ }
+ base::android::ScopedJavaLocalRef<jobject> cancellable =
+ Java_CoreImpl_newAsyncWaiterCancellableImpl(
+ env, jcaller, cancel_id, reinterpret_cast<intptr_t>(callback_data));
+ callback_data->cancellable.Reset(env, cancellable.obj());
+ return cancellable.Release();
+}
+
+static void CancelAsyncWait(JNIEnv* env,
+ jobject jcaller,
+ jlong id,
+ jlong data_ptr) {
+ if (id == 0) {
+ // If |id| is |kInvalidHandleCancelID|, the async wait was done on an
+ // invalid handle, so the AsyncWaitCallback will be called and will clear
+ // the data_ptr.
+ return;
+ }
+ scoped_ptr<AsyncWaitCallbackData> deleter(
+ reinterpret_cast<AsyncWaitCallbackData*>(data_ptr));
+ MojoAsyncWaiter* async_waiter = mojo::GetDefaultAsyncWaiter();
+ async_waiter->CancelWait(async_waiter, id);
+}
+
bool RegisterCoreImpl(JNIEnv* env) {
return RegisterNativesImpl(env);
}
diff --git a/mojo/android/system/src/org/chromium/mojo/system/CoreImpl.java b/mojo/android/system/src/org/chromium/mojo/system/CoreImpl.java
index cd6cee5..39d17f4 100644
--- a/mojo/android/system/src/org/chromium/mojo/system/CoreImpl.java
+++ b/mojo/android/system/src/org/chromium/mojo/system/CoreImpl.java
@@ -5,6 +5,7 @@
package org.chromium.mojo.system;
import org.chromium.base.CalledByNative;
+import org.chromium.base.JNIAdditionalImport;
import org.chromium.base.JNINamespace;
import org.chromium.mojo.system.DataPipe.ConsumerHandle;
import org.chromium.mojo.system.DataPipe.ProducerHandle;
@@ -19,8 +20,9 @@ import java.util.List;
/**
* Implementation of {@link Core}.
*/
+@JNIAdditionalImport(AsyncWaiter.class)
@JNINamespace("mojo::android")
-public class CoreImpl implements Core {
+public class CoreImpl implements Core, AsyncWaiter {
/**
* Discard flag for the |MojoReadData| operation.
@@ -154,6 +156,24 @@ public class CoreImpl implements Core {
return new SharedBufferHandleImpl(this, result.getMojoHandle1());
}
+ /**
+ * @see Core#getDefaultAsyncWaiter()
+ */
+ @Override
+ public AsyncWaiter getDefaultAsyncWaiter() {
+ return this;
+ }
+
+ /**
+ * @see AsyncWaiter#asyncWait(Handle, Core.WaitFlags, long, Callback)
+ */
+ @Override
+ public Cancellable asyncWait(Handle handle, WaitFlags flags, long deadline,
+ Callback callback) {
+ return nativeAsyncWait(getMojoHandle(handle),
+ flags.getFlags(), deadline, callback);
+ }
+
int closeWithResult(int mojoHandle) {
return nativeClose(mojoHandle);
}
@@ -372,19 +392,31 @@ public class CoreImpl implements Core {
return 0;
}
- private static int filterMojoResultForWait(int code) {
- if (code >= 0) {
- return MojoResult.OK;
- }
+ private static boolean isUnrecoverableError(int code) {
switch (code) {
+ case MojoResult.OK:
case MojoResult.DEADLINE_EXCEEDED:
case MojoResult.CANCELLED:
case MojoResult.FAILED_PRECONDITION:
- return code;
+ return false;
default:
- throw new MojoException(code);
+ return true;
+ }
+ }
+
+ private static int filterMojoResult(int code) {
+ if (code >= 0) {
+ return MojoResult.OK;
}
+ return code;
+ }
+ private static int filterMojoResultForWait(int code) {
+ int finalCode = filterMojoResult(code);
+ if (isUnrecoverableError(finalCode)) {
+ throw new MojoException(finalCode);
+ }
+ return finalCode;
}
private static ByteBuffer allocateDirectBuffer(int capacity) {
@@ -427,6 +459,62 @@ public class CoreImpl implements Core {
}
+ /**
+ * Implementation of {@link AsyncWaiter.Cancellable}.
+ */
+ private class AsyncWaiterCancellableImpl implements AsyncWaiter.Cancellable {
+
+ private final long mId;
+ private final long mDataPtr;
+ private boolean mActive = true;
+
+ private AsyncWaiterCancellableImpl(long id, long dataPtr) {
+ this.mId = id;
+ this.mDataPtr = dataPtr;
+ }
+
+ /**
+ * @see AsyncWaiter.Cancellable#cancel()
+ */
+ @Override
+ public void cancel() {
+ if (mActive) {
+ mActive = false;
+ nativeCancelAsyncWait(mId, mDataPtr);
+ }
+ }
+
+ private boolean isActive() {
+ return mActive;
+ }
+
+ private void deactivate() {
+ mActive = false;
+ }
+ }
+
+ @CalledByNative
+ private AsyncWaiterCancellableImpl newAsyncWaiterCancellableImpl(long id, long dataPtr) {
+ return new AsyncWaiterCancellableImpl(id, dataPtr);
+ }
+
+ @CalledByNative
+ private void onAsyncWaitResult(int mojoResult,
+ AsyncWaiter.Callback callback,
+ AsyncWaiterCancellableImpl cancellable) {
+ if (!cancellable.isActive()) {
+ // If cancellable is not active, the user cancelled the wait.
+ return;
+ }
+ cancellable.deactivate();
+ int finalCode = filterMojoResult(mojoResult);
+ if (isUnrecoverableError(finalCode)) {
+ callback.onError(new MojoException(finalCode));
+ return;
+ }
+ callback.onResult(finalCode);
+ }
+
@CalledByNative
private static NativeCodeAndBufferResult newNativeCodeAndBufferResult(int mojoResult,
ByteBuffer buffer) {
@@ -592,4 +680,8 @@ public class CoreImpl implements Core {
private native int nativeUnmap(ByteBuffer buffer);
+ private native AsyncWaiterCancellableImpl nativeAsyncWait(int mojoHandle, int flags,
+ long deadline, AsyncWaiter.Callback callback);
+
+ private native void nativeCancelAsyncWait(long mId, long dataPtr);
}
diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp
index 9d0b02c..5c9198f 100644
--- a/mojo/mojo.gyp
+++ b/mojo/mojo.gyp
@@ -725,6 +725,7 @@
'type': 'shared_library',
'dependencies': [
'../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
'libmojo_system_java',
'mojo_jni_headers',
],
diff --git a/mojo/public/java/src/org/chromium/mojo/system/AsyncWaiter.java b/mojo/public/java/src/org/chromium/mojo/system/AsyncWaiter.java
new file mode 100644
index 0000000..f62ac76
--- /dev/null
+++ b/mojo/public/java/src/org/chromium/mojo/system/AsyncWaiter.java
@@ -0,0 +1,53 @@
+// 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.system;
+
+import org.chromium.mojo.system.Core.WaitFlags;
+
+/**
+ * A class which implements the {@link AsyncWaiter} allows asynchronously waiting on a background
+ * thread.
+ */
+public interface AsyncWaiter {
+
+ /**
+ * Allows cancellation of an asyncWait operation.
+ */
+ interface Cancellable {
+ /**
+ * Cancels an asyncWait operation. Has no effect if the operation has already been canceled
+ * or the callback has already been called.
+ * <p>
+ * Must be called from the same thread as {@link AsyncWaiter#asyncWait} was called from.
+ */
+ void cancel();
+ }
+
+ /**
+ * Callback passed to {@link AsyncWaiter#asyncWait}.
+ */
+ public interface Callback {
+ /**
+ * Called when the handle is ready.
+ */
+ public void onResult(int result);
+
+ /**
+ * Called when an error occurred while waiting.
+ */
+ public void onError(MojoException exception);
+ }
+
+ /**
+ * Asynchronously call wait on a background thread. The given {@link Callback} will be notified
+ * of the result of the wait on the same thread as asyncWait was called.
+ *
+ * @return a {@link Cancellable} object that can be used to cancel waiting. The cancellable
+ * should only be used on the current thread, and becomes invalid once the callback has
+ * been notified.
+ */
+ Cancellable asyncWait(Handle handle, WaitFlags flags, long deadline, Callback callback);
+
+}
diff --git a/mojo/public/java/src/org/chromium/mojo/system/Core.java b/mojo/public/java/src/org/chromium/mojo/system/Core.java
index 8d084b3..02b6618 100644
--- a/mojo/public/java/src/org/chromium/mojo/system/Core.java
+++ b/mojo/public/java/src/org/chromium/mojo/system/Core.java
@@ -175,4 +175,10 @@ public interface Core {
*/
public SharedBufferHandle createSharedBuffer(SharedBufferHandle.CreateOptions options,
long numBytes);
+
+ /**
+ * Returns a default implementation of {@link AsyncWaiter}.
+ */
+ public AsyncWaiter getDefaultAsyncWaiter();
+
}