Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(238)

Unified Diff: remoting/android/java/src/org/chromium/chromoting/Event.java

Issue 1999583002: Add Event and EventTest (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: remoting/android/java/src/org/chromium/chromoting/Event.java
diff --git a/remoting/android/java/src/org/chromium/chromoting/Event.java b/remoting/android/java/src/org/chromium/chromoting/Event.java
new file mode 100644
index 0000000000000000000000000000000000000000..a25a1bc7cd4db7ae646f10b05136bfd0f2823508
--- /dev/null
+++ b/remoting/android/java/src/org/chromium/chromoting/Event.java
@@ -0,0 +1,223 @@
+// Copyright 2016 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.chromoting;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * A thread-safe event queue which provides both {@link #add} and {@link #remove} functions with
+ * O(log(n)) time complex, and a {@link raise} function in Executor inheritance to execute all
+ * queued object.
+ *
+ * @param <ParamType> The parameter used in {@link ParameterRunnable} callback.
+ */
+public class Event<ParamType> {
+ /**
+ * A runnable with parameter.
+ */
+ public static interface ParameterRunnable<ParamType> {
+ void run(ParamType p);
+ }
+
+ /**
+ * A callback with parameter.
+ */
+ public static interface ParameterCallback<ReturnType, ParamType> {
+ ReturnType run(ParamType p);
+ }
+
+ /**
+ * An event provider version of {@link Event} implementation, provides {@link raise} function to
+ * execute appended {@link ParameterRunnable}.
+ */
+ public static final class Executor<ParamType> extends Event<ParamType> {
+ public void clear() {
+ synchronized (mLock) {
+ mArray.clear();
+ mFreeSet.clear();
+ }
+ }
+
+ /**
+ * Executes all queued {@link ParameterRunnable} with |parameter|, returns an integer of
+ * total callbacks executed. Note, if an 'add' function call is executing concurrently
+ * with the 'raise' function call, the newly added object may not be executed.
+ */
+ public int raise(ParamType parameter) {
+ int r = 0;
+ for (int i = 0; i < mArray.size(); i++) {
Hzj_jie 2016/05/23 23:39:14 This is memory ordering safe, in Java, "After we e
Lambros 2016/05/24 17:56:41 I think that quote is misleading, and you do need
Hzj_jie 2016/05/24 18:24:38 I totally agree we would get a random value withou
+ ParameterRunnable<ParamType> e = mArray.get(i);
+ if (e != null) {
+ e.run(parameter);
+ r++;
+ }
+ }
+ return r;
+ }
+ }
+
+ /**
+ * A self removable {@link ParameterRunner}, uses a boolean {@link ParameterCallback} to decide
+ * whether removes self from {@link Event} or not.
+ */
+ private static class SelfRemovableParameterRunnable<ParamType>
+ implements ParameterRunnable<ParamType> {
+ private final ParameterCallback<Boolean, ParamType> mRef;
+ private final Event<ParamType> mOwner;
+ // This lock is used to make sure mId is correctly set before remove in run function. i.e.
+ // mOwner.add and assigment of mId need to be atomic.
+ private final Object mLock;
+ private final int mId;
+
+ private SelfRemovableParameterRunnable(Event<ParamType> owner,
+ ParameterCallback<Boolean, ParamType> callback) {
+ Preconditions.notNull(owner);
+ Preconditions.notNull(callback);
+ mRef = callback;
+ mOwner = owner;
+ mLock = new Object();
+ synchronized (mLock) {
+ mId = mOwner.add(this);
+ }
+ Preconditions.isTrue(mId >= 0);
+ }
+
+ public final void run(ParamType p) {
+ if (!mRef.run(p)) {
+ synchronized (mLock) {
+ Preconditions.isTrue(mOwner.remove(mId));
+ }
+ }
+ }
+
+ // This class is self-contained, i.e. consumers do not need to have a reference of this
+ // instance, but all the logic is in new function.
+ public static final <ParamType> void create(
+ Event<ParamType> owner,
+ ParameterCallback<Boolean, ParamType> callback) {
+ new SelfRemovableParameterRunnable<ParamType>(owner, callback);
+ }
+ }
+
+ /**
+ * A weak reference based {@link ParameterRunner}, so a registered callback won't block GC to
+ * finalize the instance.
+ */
+ private static class WeakParameterRunnable<ParamType>
+ extends SelfRemovableParameterRunnable<ParamType> {
+ private WeakParameterRunnable(Event<ParamType> owner,
+ final WeakReference<ParameterRunnable<ParamType>> ref) {
+ super(owner, new ParameterCallback<Boolean, ParamType>() {
+ @Override
+ public Boolean run(ParamType param) {
+ ParameterRunnable<ParamType> runnable = ref.get();
+ if (runnable == null) {
+ return false;
+ }
+ runnable.run(param);
+ return true;
+ }
+ });
+ }
+
+ public static final <ParamType> void create(
+ Event<ParamType> owner,
+ ParameterRunnable<ParamType> runnable) {
+ new WeakParameterRunnable<ParamType>(owner, new WeakReference<>(runnable));
+ }
+ }
+
+ protected final ArrayList<ParameterRunnable<ParamType>> mArray;
+ protected final HashSet<Integer> mFreeSet;
+ protected final Object mLock;
+
+ public Event() {
+ mArray = new ArrayList<>();
+ mFreeSet = new HashSet<>();
+ mLock = new Object();
+ }
+
+ /**
+ * Adds a {@link ParameterRunnable} object into current instance, returns an integer as its
+ * position.
+ */
+ public int add(ParameterRunnable<ParamType> runnable) {
+ if (runnable == null) {
+ return -1;
+ }
+ synchronized (mLock) {
+ if (mFreeSet.isEmpty()) {
+ Preconditions.isTrue(mArray.add(runnable));
+ return mArray.size() - 1;
+ }
+ Iterator<Integer> it = mFreeSet.iterator();
+ Preconditions.isTrue(it.hasNext());
+ int id = it.next();
+ mArray.set(id, runnable);
+ Preconditions.isTrue(mFreeSet.remove(id));
+ return id;
+ }
+ }
+
+ /**
+ * Adds a self removable {@link ParameterRunnable} object into current instance, since
+ * {@link SelfRemovableParameterRunnable} will remove itself from {@link Event} instance, this
+ * function returns a boolean value to indicate the success or not of the operation.
+ */
+ public boolean addSelfRemovable(ParameterCallback<Boolean, ParamType> callback) {
+ if (callback == null) {
+ return false;
+ }
+ SelfRemovableParameterRunnable.<ParamType>create(this, callback);
+ return true;
+ }
+
+ /**
+ * Adds a weak referred {@link ParameterRunnable} object into current instance, since
+ * {@link WeakParameterRunnable} will remove itself from {@link Event} instance, this function
+ * returns a boolean value to indicate the success or not of the operation.
+ */
+ public boolean addWeakReferred(ParameterRunnable<ParamType> runnable) {
+ if (runnable == null) {
+ return false;
+ }
+ WeakParameterRunnable.<ParamType>create(this, runnable);
+ return true;
+ }
+
+ /**
+ * Removes a Runnable object at position |id|, returns false if the |id| is not in the range of
+ * {@link mArray}.
+ */
+ public boolean remove(int id) {
+ if (id < 0 || id >= mArray.size() || mArray.get(id) == null) {
+ return false;
+ }
+ synchronized (mLock) {
+ mArray.set(id, null);
+ mFreeSet.add(id);
+ }
+ return true;
+ }
+
+ /**
+ * Returns the total count of runnables attached to current instance.
+ */
+ public int size() {
+ synchronized (mLock) {
+ return mArray.size() - mFreeSet.size();
+ }
+ }
+
+ /**
+ * Returns true if there are at least one runnable attached to current instance.
+ */
+ public boolean attached() {
+ return size() > 0;
+ }
+}
« no previous file with comments | « remoting/android/client_java_tmpl.gni ('k') | remoting/android/java/src/org/chromium/chromoting/Preconditions.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698