Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package org.chromium.chromoting; | |
| 6 | |
| 7 import java.lang.ref.WeakReference; | |
| 8 import java.util.ArrayList; | |
| 9 import java.util.HashSet; | |
| 10 import java.util.Iterator; | |
| 11 | |
| 12 /** | |
| 13 * A thread-safe event queue which provides both {@link #add} and {@link #remove } functions with | |
| 14 * O(log(n)) time complex, and a {@link raise} function in Executor inheritance to execute all | |
| 15 * queued object. | |
| 16 * | |
| 17 * @param <ParamType> The parameter used in {@link ParameterRunnable} callback. | |
| 18 */ | |
| 19 public class Event<ParamType> { | |
| 20 /** | |
| 21 * A runnable with parameter. | |
| 22 */ | |
| 23 public static interface ParameterRunnable<ParamType> { | |
| 24 void run(ParamType p); | |
| 25 } | |
| 26 | |
| 27 /** | |
| 28 * A callback with parameter. | |
| 29 */ | |
| 30 public static interface ParameterCallback<ReturnType, ParamType> { | |
| 31 ReturnType run(ParamType p); | |
| 32 } | |
| 33 | |
| 34 /** | |
| 35 * An event provider version of {@link Event} implementation, provides {@lin k raise} function to | |
| 36 * execute appended {@link ParameterRunnable}. | |
| 37 */ | |
| 38 public static final class Executor<ParamType> extends Event<ParamType> { | |
| 39 public void clear() { | |
| 40 synchronized (mLock) { | |
| 41 mArray.clear(); | |
| 42 mFreeSet.clear(); | |
| 43 } | |
| 44 } | |
| 45 | |
| 46 /** | |
| 47 * Executes all queued {@link ParameterRunnable} with |parameter|, retur ns an integer of | |
| 48 * total callbacks executed. Note, if an 'add' function call is executin g concurrently | |
| 49 * with the 'raise' function call, the newly added object may not be exe cuted. | |
| 50 */ | |
| 51 public int raise(ParamType parameter) { | |
| 52 int r = 0; | |
| 53 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
| |
| 54 ParameterRunnable<ParamType> e = mArray.get(i); | |
| 55 if (e != null) { | |
| 56 e.run(parameter); | |
| 57 r++; | |
| 58 } | |
| 59 } | |
| 60 return r; | |
| 61 } | |
| 62 } | |
| 63 | |
| 64 /** | |
| 65 * A self removable {@link ParameterRunner}, uses a boolean {@link Parameter Callback} to decide | |
| 66 * whether removes self from {@link Event} or not. | |
| 67 */ | |
| 68 private static class SelfRemovableParameterRunnable<ParamType> | |
| 69 implements ParameterRunnable<ParamType> { | |
| 70 private final ParameterCallback<Boolean, ParamType> mRef; | |
| 71 private final Event<ParamType> mOwner; | |
| 72 // This lock is used to make sure mId is correctly set before remove in run function. i.e. | |
| 73 // mOwner.add and assigment of mId need to be atomic. | |
| 74 private final Object mLock; | |
| 75 private final int mId; | |
| 76 | |
| 77 private SelfRemovableParameterRunnable(Event<ParamType> owner, | |
| 78 ParameterCallback<Boolean, ParamT ype> callback) { | |
| 79 Preconditions.notNull(owner); | |
| 80 Preconditions.notNull(callback); | |
| 81 mRef = callback; | |
| 82 mOwner = owner; | |
| 83 mLock = new Object(); | |
| 84 synchronized (mLock) { | |
| 85 mId = mOwner.add(this); | |
| 86 } | |
| 87 Preconditions.isTrue(mId >= 0); | |
| 88 } | |
| 89 | |
| 90 public final void run(ParamType p) { | |
| 91 if (!mRef.run(p)) { | |
| 92 synchronized (mLock) { | |
| 93 Preconditions.isTrue(mOwner.remove(mId)); | |
| 94 } | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 // This class is self-contained, i.e. consumers do not need to have a re ference of this | |
| 99 // instance, but all the logic is in new function. | |
| 100 public static final <ParamType> void create( | |
| 101 Event<ParamType> owner, | |
| 102 ParameterCallback<Boolean, ParamType> callback) { | |
| 103 new SelfRemovableParameterRunnable<ParamType>(owner, callback); | |
| 104 } | |
| 105 } | |
| 106 | |
| 107 /** | |
| 108 * A weak reference based {@link ParameterRunner}, so a registered callback won't block GC to | |
| 109 * finalize the instance. | |
| 110 */ | |
| 111 private static class WeakParameterRunnable<ParamType> | |
| 112 extends SelfRemovableParameterRunnable<ParamType> { | |
| 113 private WeakParameterRunnable(Event<ParamType> owner, | |
| 114 final WeakReference<ParameterRunnable<Para mType>> ref) { | |
| 115 super(owner, new ParameterCallback<Boolean, ParamType>() { | |
| 116 @Override | |
| 117 public Boolean run(ParamType param) { | |
| 118 ParameterRunnable<ParamType> runnable = ref.get(); | |
| 119 if (runnable == null) { | |
| 120 return false; | |
| 121 } | |
| 122 runnable.run(param); | |
| 123 return true; | |
| 124 } | |
| 125 }); | |
| 126 } | |
| 127 | |
| 128 public static final <ParamType> void create( | |
| 129 Event<ParamType> owner, | |
| 130 ParameterRunnable<ParamType> runnable) { | |
| 131 new WeakParameterRunnable<ParamType>(owner, new WeakReference<>(runn able)); | |
| 132 } | |
| 133 } | |
| 134 | |
| 135 protected final ArrayList<ParameterRunnable<ParamType>> mArray; | |
| 136 protected final HashSet<Integer> mFreeSet; | |
| 137 protected final Object mLock; | |
| 138 | |
| 139 public Event() { | |
| 140 mArray = new ArrayList<>(); | |
| 141 mFreeSet = new HashSet<>(); | |
| 142 mLock = new Object(); | |
| 143 } | |
| 144 | |
| 145 /** | |
| 146 * Adds a {@link ParameterRunnable} object into current instance, returns an integer as its | |
| 147 * position. | |
| 148 */ | |
| 149 public int add(ParameterRunnable<ParamType> runnable) { | |
| 150 if (runnable == null) { | |
| 151 return -1; | |
| 152 } | |
| 153 synchronized (mLock) { | |
| 154 if (mFreeSet.isEmpty()) { | |
| 155 Preconditions.isTrue(mArray.add(runnable)); | |
| 156 return mArray.size() - 1; | |
| 157 } | |
| 158 Iterator<Integer> it = mFreeSet.iterator(); | |
| 159 Preconditions.isTrue(it.hasNext()); | |
| 160 int id = it.next(); | |
| 161 mArray.set(id, runnable); | |
| 162 Preconditions.isTrue(mFreeSet.remove(id)); | |
| 163 return id; | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 /** | |
| 168 * Adds a self removable {@link ParameterRunnable} object into current insta nce, since | |
| 169 * {@link SelfRemovableParameterRunnable} will remove itself from {@link Eve nt} instance, this | |
| 170 * function returns a boolean value to indicate the success or not of the op eration. | |
| 171 */ | |
| 172 public boolean addSelfRemovable(ParameterCallback<Boolean, ParamType> callba ck) { | |
| 173 if (callback == null) { | |
| 174 return false; | |
| 175 } | |
| 176 SelfRemovableParameterRunnable.<ParamType>create(this, callback); | |
| 177 return true; | |
| 178 } | |
| 179 | |
| 180 /** | |
| 181 * Adds a weak referred {@link ParameterRunnable} object into current instan ce, since | |
| 182 * {@link WeakParameterRunnable} will remove itself from {@link Event} insta nce, this function | |
| 183 * returns a boolean value to indicate the success or not of the operation. | |
| 184 */ | |
| 185 public boolean addWeakReferred(ParameterRunnable<ParamType> runnable) { | |
| 186 if (runnable == null) { | |
| 187 return false; | |
| 188 } | |
| 189 WeakParameterRunnable.<ParamType>create(this, runnable); | |
| 190 return true; | |
| 191 } | |
| 192 | |
| 193 /** | |
| 194 * Removes a Runnable object at position |id|, returns false if the |id| is not in the range of | |
| 195 * {@link mArray}. | |
| 196 */ | |
| 197 public boolean remove(int id) { | |
| 198 if (id < 0 || id >= mArray.size() || mArray.get(id) == null) { | |
| 199 return false; | |
| 200 } | |
| 201 synchronized (mLock) { | |
| 202 mArray.set(id, null); | |
| 203 mFreeSet.add(id); | |
| 204 } | |
| 205 return true; | |
| 206 } | |
| 207 | |
| 208 /** | |
| 209 * Returns the total count of runnables attached to current instance. | |
| 210 */ | |
| 211 public int size() { | |
| 212 synchronized (mLock) { | |
| 213 return mArray.size() - mFreeSet.size(); | |
| 214 } | |
| 215 } | |
| 216 | |
| 217 /** | |
| 218 * Returns true if there are at least one runnable attached to current insta nce. | |
| 219 */ | |
| 220 public boolean attached() { | |
| 221 return size() > 0; | |
| 222 } | |
| 223 } | |
| OLD | NEW |