Chromium Code Reviews| 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..c14059b2b0d60b2cde9377b1bb1b34ed5655bbd8 |
| --- /dev/null |
| +++ b/remoting/android/java/src/org/chromium/chromoting/Event.java |
| @@ -0,0 +1,182 @@ |
| +// 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.util.HashSet; |
| +import java.util.Set; |
| + |
| +/** |
| + * 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 |
|
Lambros
2016/05/27 00:38:51
Why subclass, instead of adding raise() to Event?
Hzj_jie
2016/05/27 02:55:20
This pattern can avoid any potential misusage of a
|
| + * execute appended {@link ParameterRunnable}. |
| + */ |
| + public static final class Executor<ParamType> extends Event<ParamType> { |
| + public void clear() { |
| + synchronized (mLock) { |
| + mSet.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) { |
| + Set<ParameterRunnable<ParamType>> set = eventSet(); |
| + Preconditions.notNull(set); |
|
Lambros
2016/05/27 00:38:52
Don't need this Preconditions. It's obvious that e
Hzj_jie
2016/05/27 02:55:20
Done.
|
| + int r = 0; |
|
Lambros
2016/05/27 00:38:51
Avoid single-letter names (for-loop variables and
Hzj_jie
2016/05/27 02:55:19
Done.
|
| + for (ParameterRunnable<ParamType> e : set) { |
| + e.run(parameter); |
| + r++; |
| + } |
| + return r; |
| + } |
| + |
| + /** |
| + * Returns a shadow copy of {@link mSet} as a {@link Set}. |
| + */ |
| + @SuppressWarnings("unchecked") |
| + private Set<ParameterRunnable<ParamType>> eventSet() { |
|
Lambros
2016/05/27 00:38:52
If you use toArray() instead of clone(), you don't
Hzj_jie
2016/05/27 02:55:19
Done.
|
| + synchronized (mLock) { |
| + return (Set<ParameterRunnable<ParamType>>) mSet.clone(); |
| + } |
| + } |
| + } |
| + |
| + /** |
| + * 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; |
|
Lambros
2016/05/27 00:38:52
Don't abbreviate names (in general; sometimes it's
Hzj_jie
2016/05/27 02:55:19
Done.
|
| + private final Event<ParamType> mOwner; |
| + // This lock is used to make sure mId is correctly set before remove in run function. i.e. |
|
Lambros
2016/05/27 00:38:51
Blank line before comment.
Also, mId doesn't exist
Hzj_jie
2016/05/27 02:55:20
If the logic runs in following order, it will fail
|
| + // mOwner.add and assigment of mId need to be atomic. |
| + private final Object mLock; |
| + private final Object mEvent; |
| + |
| + private SelfRemovableParameterRunnable(Event<ParamType> owner, |
| + ParameterCallback<Boolean, ParamType> callback) { |
| + Preconditions.notNull(owner); |
|
Lambros
2016/05/27 00:38:51
This is OK for now, but I'd generally avoid over-u
Hzj_jie
2016/05/27 02:55:19
Yes, updated.
|
| + Preconditions.notNull(callback); |
| + mRef = callback; |
| + mOwner = owner; |
| + mLock = new Object(); |
| + synchronized (mLock) { |
| + mEvent = mOwner.add(this); |
|
Lambros
2016/05/27 00:38:51
I'm not sure, but this might be too much work to d
Hzj_jie
2016/05/27 02:55:20
If we move some logic in to a new function, we wil
|
| + } |
| + Preconditions.notNull(mEvent); |
| + } |
| + |
| + public final void run(ParamType p) { |
| + if (!mRef.run(p)) { |
| + synchronized (mLock) { |
| + Preconditions.isTrue(mOwner.remove(mEvent)); |
|
Lambros
2016/05/27 00:38:52
I don't think this is safe if two threads call Eve
Hzj_jie
2016/05/27 02:55:19
Done, but this class is designed to be single read
Lambros
2016/05/27 22:26:06
Even if Event.Executor.raise() is always called fr
Hzj_jie
2016/05/28 00:32:06
Oh, yes, I forgot Event.Executor.clear. I will rem
|
| + } |
| + } |
| + } |
| + |
| + // 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. |
|
Lambros
2016/05/27 00:38:51
I don't think this should be called create() - it
Hzj_jie
2016/05/27 02:55:20
Done.
|
| + public static final <ParamType> void create( |
| + Event<ParamType> owner, |
| + ParameterCallback<Boolean, ParamType> callback) { |
| + new SelfRemovableParameterRunnable<ParamType>(owner, callback); |
| + } |
| + } |
| + |
| + protected final HashSet<ParameterRunnable<ParamType>> mSet; |
| + protected final Object mLock; |
|
Lambros
2016/05/27 00:38:51
Since mSet is final, you don't need a separate mLo
Hzj_jie
2016/05/27 02:55:19
Done.
|
| + |
| + public Event() { |
| + mLock = new Object(); |
| + mSet = new HashSet<>(); |
| + } |
| + |
| + /** |
| + * Adds a {@link ParameterRunnable} object into current instance, returns an object which can |
| + * be used to {@link remove} the runnable from this instance. If |runnable| is null or it has |
| + * been added to this instance already, this function returns null. |
| + */ |
| + public Object add(ParameterRunnable<ParamType> runnable) { |
| + if (runnable == null) { |
| + return null; |
| + } |
| + synchronized (mLock) { |
| + if (mSet.add(runnable)) { |
| + return runnable; |
| + } |
| + } |
| + return null; |
| + } |
| + |
| + /** |
| + * Adds a self removable {@link ParameterRunnable} object into current instance, since |
| + * {@link SelfRemovableParameterRunnable} will remove itself from {@link Event} instance, this |
|
Lambros
2016/05/27 00:38:51
SelfRemovableParameterRunnable is a private implem
Hzj_jie
2016/05/27 02:55:19
Done.
|
| + * function returns a boolean value to indicate the success or not of the operation. |
|
Lambros
2016/05/27 00:38:51
The boolean return is not needed (it's always true
Hzj_jie
2016/05/27 02:55:19
Done.
|
| + */ |
| + public boolean addSelfRemovable(ParameterCallback<Boolean, ParamType> callback) { |
| + if (callback == null) { |
| + return false; |
| + } |
| + SelfRemovableParameterRunnable.<ParamType>create(this, callback); |
| + return true; |
| + } |
| + |
| + /** |
| + * Removes an object represents an event and returned by {@link add} function from this |
|
Lambros
2016/05/27 00:38:52
s/represents/representing
Or maybe rephrase:
Remo
Hzj_jie
2016/05/27 02:55:20
Done.
|
| + * instance. Returns false if the object is not in the event queue, or not returned by |
| + * {@link add} function. |
| + */ |
| + public boolean remove(Object obj) { |
| + if (obj == null) { |
| + return false; |
| + } |
| + synchronized (mLock) { |
| + return mSet.remove(obj); |
| + } |
| + } |
| + |
| + /** |
| + * Returns the total count of runnables attached to current instance. |
| + */ |
| + public int size() { |
| + synchronized (mLock) { |
| + return mSet.size(); |
| + } |
| + } |
| + |
| + /** |
| + * Returns true if there are at least one runnable attached to current instance. |
|
Lambros
2016/05/27 00:38:51
s/are/is
Hzj_jie
2016/05/27 02:55:20
Done.
|
| + */ |
|
Lambros
2016/05/27 00:38:52
'attached' is maybe a little bit strange. Maybe ha
Hzj_jie
2016/05/27 02:55:20
Done.
|
| + public boolean attached() { |
| + synchronized (mLock) { |
| + return !mSet.isEmpty(); |
| + } |
| + } |
| +} |