diff options
author | Alan Young <consult.awy@gmail.com> | 2016-04-18 16:37:46 (GMT) |
---|---|---|
committer | Alan Young <consult.awy@gmail.com> | 2016-04-20 16:29:51 (GMT) |
commit | 9d50093bee736d2b7b43756e9a41cfafbd568ee2 (patch) | |
tree | 50923023315027b6a099f7349506868d58b4d736 | |
parent | 82f43f69b1cdb1d2f6cd11c64a71dc99c8533d5a (diff) | |
download | stroke-9d50093bee736d2b7b43756e9a41cfafbd568ee2.zip stroke-9d50093bee736d2b7b43756e9a41cfafbd568ee2.tar.bz2 |
Rework Signals for time and space optimizations
Each Signal* class extends BaseSignal. Each Slot* class extends
BaseSlot.
BaseSignal manages the set of binds associated with it via addBind() and
getBinds() which are called from Signal*. It is optimized for the cases
of zero or one bind, only allocating a HashMap to hold a larger set when
needed.
The interaction with SignalConnection to handle disconnection is
effected via a callback interface rather than another Signal.
Change-Id: Ifa44c1eb40b778c303db947a6e74fe20d1b41a90
-rw-r--r-- | src/com/isode/stroke/muc/MUCImpl.java | 2 | ||||
-rw-r--r-- | src/com/isode/stroke/signals/BaseSignal.java | 62 | ||||
-rw-r--r-- | src/com/isode/stroke/signals/BaseSlot.java | 9 | ||||
-rw-r--r-- | src/com/isode/stroke/signals/Signal.java | 35 | ||||
-rw-r--r-- | src/com/isode/stroke/signals/Signal1.java | 31 | ||||
-rw-r--r-- | src/com/isode/stroke/signals/Signal2.java | 31 | ||||
-rw-r--r-- | src/com/isode/stroke/signals/Signal3.java | 39 | ||||
-rw-r--r-- | src/com/isode/stroke/signals/Signal4.java | 40 | ||||
-rw-r--r-- | src/com/isode/stroke/signals/Signal7.java | 38 | ||||
-rw-r--r-- | src/com/isode/stroke/signals/SignalConnection.java | 17 | ||||
-rw-r--r-- | src/com/isode/stroke/signals/Slot.java | 2 | ||||
-rw-r--r-- | src/com/isode/stroke/signals/Slot1.java | 2 | ||||
-rw-r--r-- | src/com/isode/stroke/signals/Slot2.java | 2 | ||||
-rw-r--r-- | src/com/isode/stroke/signals/Slot3.java | 2 | ||||
-rw-r--r-- | src/com/isode/stroke/signals/Slot4.java | 2 | ||||
-rw-r--r-- | src/com/isode/stroke/signals/Slot7.java | 2 |
16 files changed, 137 insertions, 179 deletions
diff --git a/src/com/isode/stroke/muc/MUCImpl.java b/src/com/isode/stroke/muc/MUCImpl.java index 444b331..26be309 100644 --- a/src/com/isode/stroke/muc/MUCImpl.java +++ b/src/com/isode/stroke/muc/MUCImpl.java @@ -724,7 +724,7 @@ public class MUCImpl extends MUC { @Override public void disconnect() { if (scopedConnection_ != null) { - scopedConnection_.onDestroyed.emit(); + scopedConnection_.disconnect(); scopedConnection_ = null; } } diff --git a/src/com/isode/stroke/signals/BaseSignal.java b/src/com/isode/stroke/signals/BaseSignal.java new file mode 100644 index 0000000..de1bd6e --- /dev/null +++ b/src/com/isode/stroke/signals/BaseSignal.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015, Isode Limited, London, England. + * All rights reserved. + */ +package com.isode.stroke.signals; + +import java.util.HashMap; +import java.util.Map; + +/* package */ abstract class BaseSignal implements SignalConnection.DisconnectListener { + + // Optimized for the case of 0 or 1 bind + private SignalConnection connection_; + private BaseSlot bind_; + private Map<SignalConnection, BaseSlot> binds_; + + protected final synchronized SignalConnection addBind(final BaseSlot bind) { + final SignalConnection connection = new SignalConnection(this); + if (binds_ != null) { + binds_.put(connection, bind); + } else if (connection_ != null) { + binds_ = new HashMap<SignalConnection, BaseSlot>(); + binds_.put(connection_, bind_); + connection_ = null; + bind_ = null; + binds_.put(connection, bind); + } else { + connection_ = connection; + bind_ = bind; + } + return connection; + } + + protected final synchronized BaseSlot[] getBinds() { + if (binds_ != null) { + return binds_.values().toArray(new BaseSlot[binds_.size()]); + } else if (connection_ != null) { + return new BaseSlot[]{bind_}; + } else { + return null; // return null rather than allocate an empty array + } + } + + @Override + public final synchronized void onSignalConnectionDisconnect(final SignalConnection connection) { + if (binds_ != null) { + binds_.remove(connection); + } else if (connection_ == connection) { + connection_ = null; + bind_ = null; + } + } + + public final synchronized void disconnectAll() { + if (binds_ != null) { + binds_.clear(); + } else { + connection_ = null; + bind_ = null; + } + } +} diff --git a/src/com/isode/stroke/signals/BaseSlot.java b/src/com/isode/stroke/signals/BaseSlot.java new file mode 100644 index 0000000..9b8ac13 --- /dev/null +++ b/src/com/isode/stroke/signals/BaseSlot.java @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2015, Isode Limited, London, England. + * All rights reserved. + */ +package com.isode.stroke.signals; + +/* package */ interface BaseSlot { + +} diff --git a/src/com/isode/stroke/signals/Signal.java b/src/com/isode/stroke/signals/Signal.java index cfa8665..1821cb0 100644 --- a/src/com/isode/stroke/signals/Signal.java +++ b/src/com/isode/stroke/signals/Signal.java @@ -1,31 +1,17 @@ /* - * Copyright (c) 2010, Isode Limited, London, England. + * Copyright (c) 2010-2015, Isode Limited, London, England. * All rights reserved. */ package com.isode.stroke.signals; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; /** * An approximation of the boost::signals system, although a little more warty. */ -public class Signal { - - private final Map<SignalConnection, Slot> binds_ = Collections.synchronizedMap(new HashMap<SignalConnection, Slot>()); +public final class Signal extends BaseSignal { public SignalConnection connect(Slot bind) { - final SignalConnection connection = new SignalConnection(); - binds_.put(connection, bind); - connection.onDestroyed.connectWithoutReturn(new Slot() { - - public void call() { - binds_.remove(connection); - } - }); - return connection; + return addBind(bind); } public SignalConnection connect(final Signal target) { @@ -36,19 +22,12 @@ public class Signal { }); } - void connectWithoutReturn(Slot bind) { - binds_.put(null, bind); - } - public void emit() { - ArrayList<Slot> binds = new ArrayList<Slot>(); - binds.addAll(binds_.values()); - for (Slot bind : binds) { - bind.call(); + final BaseSlot[] binds = getBinds(); + if (binds == null) {return;} + for (BaseSlot bind : binds) { + ((Slot)bind).call(); } } - public void disconnectAll() { - binds_.clear(); - } } diff --git a/src/com/isode/stroke/signals/Signal1.java b/src/com/isode/stroke/signals/Signal1.java index c799d4c..9787096 100644 --- a/src/com/isode/stroke/signals/Signal1.java +++ b/src/com/isode/stroke/signals/Signal1.java @@ -1,36 +1,25 @@ /* - * Copyright (c) 2010, Isode Limited, London, England. + * Copyright (c) 2010-2015, Isode Limited, London, England. * All rights reserved. */ package com.isode.stroke.signals; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; /** * An approximation of the boost::signals system, although a little more warty. */ -public class Signal1<T1> { - private final Map<SignalConnection, Slot1<T1> > binds_ = Collections.synchronizedMap(new HashMap<SignalConnection, Slot1<T1> >()); +public final class Signal1<T1> extends BaseSignal { public SignalConnection connect(Slot1<T1> bind) { - final SignalConnection connection = new SignalConnection(); - binds_.put(connection, bind); - connection.onDestroyed.connect(new Slot() { - public void call() { - binds_.remove(connection); - } - }); - return connection; + return addBind(bind); } + @SuppressWarnings("unchecked") public void emit(T1 p1) { - ArrayList<Slot1<T1>> binds = new ArrayList<Slot1<T1>>(); - binds.addAll(binds_.values()); - for (Slot1<T1> bind : binds) { - bind.call(p1); + final BaseSlot[] binds = getBinds(); + if (binds == null) {return;} + for (BaseSlot bind : binds) { + ((Slot1<T1>)bind).call(p1); } } @@ -41,8 +30,4 @@ public class Signal1<T1> { } }); } - - public void disconnectAll() { - binds_.clear(); - } } diff --git a/src/com/isode/stroke/signals/Signal2.java b/src/com/isode/stroke/signals/Signal2.java index fa3c4b5..72c9de0 100644 --- a/src/com/isode/stroke/signals/Signal2.java +++ b/src/com/isode/stroke/signals/Signal2.java @@ -1,40 +1,25 @@ /* - * Copyright (c) 2010, Isode Limited, London, England. + * Copyright (c) 2010-2015, Isode Limited, London, England. * All rights reserved. */ package com.isode.stroke.signals; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; /** * An approximation of the boost::signals system, although a little more warty. */ -public class Signal2<T1, T2> { - private final Map<SignalConnection, Slot2<T1, T2> > binds_ = Collections.synchronizedMap(new HashMap<SignalConnection, Slot2<T1, T2> >()); +public class Signal2<T1, T2> extends BaseSignal { public SignalConnection connect(Slot2<T1, T2> bind) { - final SignalConnection connection = new SignalConnection(); - binds_.put(connection, bind); - connection.onDestroyed.connect(new Slot() { - public void call() { - binds_.remove(connection); - } - }); - return connection; + return addBind(bind); } + @SuppressWarnings("unchecked") public void emit(T1 p1, T2 p2) { - ArrayList<Slot2<T1,T2>> binds = new ArrayList<Slot2<T1, T2>>(); - binds.addAll(binds_.values()); - for (Slot2<T1, T2> bind : binds) { - bind.call(p1, p2); + final BaseSlot[] binds = getBinds(); + if (binds == null) {return;} + for (BaseSlot bind : binds) { + ((Slot2<T1, T2>)bind).call(p1, p2); } } - - public void disconnectAll() { - binds_.clear(); - } } diff --git a/src/com/isode/stroke/signals/Signal3.java b/src/com/isode/stroke/signals/Signal3.java index b9151dc..014f047 100644 --- a/src/com/isode/stroke/signals/Signal3.java +++ b/src/com/isode/stroke/signals/Signal3.java @@ -1,15 +1,10 @@ /* - * Copyright (c) 2012, Isode Limited, London, England. + * Copyright (c) 2010-2015, Isode Limited, London, England. * All rights reserved. */ package com.isode.stroke.signals; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; /** * An approximation of the boost::signals system with 3 parameters @@ -17,24 +12,14 @@ import java.util.Map; * @param <T2> Type 2 * @param <T3> Type 3 */ -public class Signal3<T1, T2, T3> { - private final Map<SignalConnection, Slot3<T1, T2,T3> > binds_ = Collections.synchronizedMap( - new HashMap<SignalConnection, Slot3<T1, T2,T3> >()); - +public class Signal3<T1, T2, T3> extends BaseSignal { /** * Add a slot which will be notified * @param bind slot, not null * @return signal connection */ public SignalConnection connect(Slot3<T1, T2,T3> bind) { - final SignalConnection connection = new SignalConnection(); - binds_.put(connection, bind); - connection.onDestroyed.connect(new Slot() { - public void call() { - binds_.remove(connection); - } - }); - return connection; + return addBind(bind); } /** @@ -43,18 +28,12 @@ public class Signal3<T1, T2, T3> { * @param p2 parameter value 2 * @param p3 parameter value 3 */ - public void emit(T1 p1, T2 p2,T3 p3) { - List<Slot3<T1,T2, T3>> binds = new ArrayList<Slot3<T1, T2, T3>>(); - binds.addAll(binds_.values()); - for (Slot3<T1, T2, T3> bind : binds) { - bind.call(p1, p2, p3); + @SuppressWarnings("unchecked") + public void emit(T1 p1, T2 p2, T3 p3) { + final BaseSlot[] binds = getBinds(); + if (binds == null) {return;} + for (BaseSlot bind : binds) { + ((Slot3<T1, T2, T3>)bind).call(p1, p2, p3); } } - - /** - * Remove all slots(listeners) - */ - public void disconnectAll() { - binds_.clear(); - } } diff --git a/src/com/isode/stroke/signals/Signal4.java b/src/com/isode/stroke/signals/Signal4.java index 6a29641..edf30ed 100644 --- a/src/com/isode/stroke/signals/Signal4.java +++ b/src/com/isode/stroke/signals/Signal4.java @@ -1,18 +1,10 @@ /* - * Copyright (c) 2012-2013, Isode Limited, London, England. + * Copyright (c) 2012-2015, Isode Limited, London, England. * All rights reserved. */ package com.isode.stroke.signals; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.isode.stroke.signals.SignalConnection; -import com.isode.stroke.signals.Slot; /** @@ -22,24 +14,14 @@ import com.isode.stroke.signals.Slot; * @param <T3> Type 3 * @param <T4> Type 4 */ -public class Signal4<T1, T2, T3, T4> { - private final Map<SignalConnection, Slot4<T1, T2, T3, T4> > binds_ = Collections.synchronizedMap( - new HashMap<SignalConnection, Slot4<T1, T2, T3, T4> >()); - +public class Signal4<T1, T2, T3, T4> extends BaseSignal { /** * Add a slot which will be notified * @param bind slot, not null * @return signal connection */ public SignalConnection connect(Slot4<T1, T2, T3, T4> bind) { - final SignalConnection connection = new SignalConnection(); - binds_.put(connection, bind); - connection.onDestroyed.connect(new Slot() { - public void call() { - binds_.remove(connection); - } - }); - return connection; + return addBind(bind); } /** @@ -49,18 +31,12 @@ public class Signal4<T1, T2, T3, T4> { * @param p3 parameter value 3 * @param p4 parameter value 4 */ + @SuppressWarnings("unchecked") public void emit(T1 p1, T2 p2, T3 p3, T4 p4) { - List<Slot4<T1, T2, T3, T4>> binds = new ArrayList<Slot4<T1, T2, T3, T4>>(); - binds.addAll(binds_.values()); - for (Slot4<T1, T2, T3, T4> bind : binds) { - bind.call(p1, p2, p3, p4); + final BaseSlot[] binds = getBinds(); + if (binds == null) {return;} + for (BaseSlot bind : binds) { + ((Slot4<T1, T2, T3, T4>)bind).call(p1, p2, p3, p4); } } - - /** - * Remove all slots(listeners) - */ - public void disconnectAll() { - binds_.clear(); - } } diff --git a/src/com/isode/stroke/signals/Signal7.java b/src/com/isode/stroke/signals/Signal7.java index 296cc02..bbf6559 100644 --- a/src/com/isode/stroke/signals/Signal7.java +++ b/src/com/isode/stroke/signals/Signal7.java @@ -5,14 +5,6 @@ package com.isode.stroke.signals; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.isode.stroke.signals.SignalConnection; -import com.isode.stroke.signals.Slot; /** @@ -22,24 +14,14 @@ import com.isode.stroke.signals.Slot; * @param <T3> Type 3 * @param <T4> Type 4 */ -public class Signal7<T1, T2, T3, T4, T5, T6, T7> { - private final Map<SignalConnection, Slot7<T1, T2, T3, T4, T5, T6, T7> > binds_ = Collections.synchronizedMap( - new HashMap<SignalConnection, Slot7<T1, T2, T3, T4, T5, T6, T7> >()); - +public class Signal7<T1, T2, T3, T4, T5, T6, T7> extends BaseSignal { /** * Add a slot which will be notified * @param bind slot, not null * @return signal connection */ public SignalConnection connect(Slot7<T1, T2, T3, T4, T5, T6, T7> bind) { - final SignalConnection connection = new SignalConnection(); - binds_.put(connection, bind); - connection.onDestroyed.connect(new Slot() { - public void call() { - binds_.remove(connection); - } - }); - return connection; + return addBind(bind); } /** @@ -49,18 +31,12 @@ public class Signal7<T1, T2, T3, T4, T5, T6, T7> { * @param p3 parameter value 3 * @param p4 parameter value 4 */ + @SuppressWarnings("unchecked") public void emit(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7) { - List<Slot7<T1, T2, T3, T4, T5, T6, T7>> binds = new ArrayList<Slot7<T1, T2, T3, T4, T5, T6, T7>>(); - binds.addAll(binds_.values()); - for (Slot7<T1, T2, T3, T4, T5, T6, T7> bind : binds) { - bind.call(p1, p2, p3, p4, p5, p6, p7); + final BaseSlot[] binds = getBinds(); + if (binds == null) {return;} + for (BaseSlot bind : binds) { + ((Slot7<T1, T2, T3, T4, T5, T6, T7>)bind).call(p1, p2, p3, p4, p5, p6, p7); } } - - /** - * Remove all slots(listeners) - */ - public void disconnectAll() { - binds_.clear(); - } } diff --git a/src/com/isode/stroke/signals/SignalConnection.java b/src/com/isode/stroke/signals/SignalConnection.java index 452c80b..30bbf82 100644 --- a/src/com/isode/stroke/signals/SignalConnection.java +++ b/src/com/isode/stroke/signals/SignalConnection.java @@ -1,15 +1,22 @@ /* - * Copyright (c) 2010, Isode Limited, London, England. + * Copyright (c) 2010-2015, Isode Limited, London, England. * All rights reserved. */ package com.isode.stroke.signals; - public class SignalConnection { - public final Signal onDestroyed = new Signal(); - + interface DisconnectListener { + void onSignalConnectionDisconnect(SignalConnection connection); + } + + private final DisconnectListener listener; + + SignalConnection(DisconnectListener listener) { + this.listener = listener; + } + public void disconnect() { - onDestroyed.emit(); + listener.onSignalConnectionDisconnect(this); } } diff --git a/src/com/isode/stroke/signals/Slot.java b/src/com/isode/stroke/signals/Slot.java index 35b8c70..780892d 100644 --- a/src/com/isode/stroke/signals/Slot.java +++ b/src/com/isode/stroke/signals/Slot.java @@ -8,6 +8,6 @@ package com.isode.stroke.signals; /** * Bind class for connecting to a signal. */ -public interface Slot { +public interface Slot extends BaseSlot { void call(); } diff --git a/src/com/isode/stroke/signals/Slot1.java b/src/com/isode/stroke/signals/Slot1.java index a8f0179..001dca7 100644 --- a/src/com/isode/stroke/signals/Slot1.java +++ b/src/com/isode/stroke/signals/Slot1.java @@ -8,6 +8,6 @@ package com.isode.stroke.signals; /** * Bind class for connecting to a signal. */ -public interface Slot1<T1> { +public interface Slot1<T1> extends BaseSlot { void call(T1 p1); } diff --git a/src/com/isode/stroke/signals/Slot2.java b/src/com/isode/stroke/signals/Slot2.java index b3b9330..03eb9a6 100644 --- a/src/com/isode/stroke/signals/Slot2.java +++ b/src/com/isode/stroke/signals/Slot2.java @@ -8,6 +8,6 @@ package com.isode.stroke.signals; /** * Bind class for connecting to a signal. */ -public interface Slot2<T1, T2> { +public interface Slot2<T1, T2> extends BaseSlot { void call(T1 p1, T2 p2); } diff --git a/src/com/isode/stroke/signals/Slot3.java b/src/com/isode/stroke/signals/Slot3.java index ee5ccbc..63c371c 100644 --- a/src/com/isode/stroke/signals/Slot3.java +++ b/src/com/isode/stroke/signals/Slot3.java @@ -11,7 +11,7 @@ package com.isode.stroke.signals; * @param <T2> Type 2 * @param <T3> Type 3 */ -public interface Slot3<T1, T2,T3> { +public interface Slot3<T1, T2,T3> extends BaseSlot { /** * This method will be called on notification from a signal * @param p1 parameter value 1 diff --git a/src/com/isode/stroke/signals/Slot4.java b/src/com/isode/stroke/signals/Slot4.java index 02e6fd9..93401db 100644 --- a/src/com/isode/stroke/signals/Slot4.java +++ b/src/com/isode/stroke/signals/Slot4.java @@ -12,7 +12,7 @@ package com.isode.stroke.signals; * @param <T3> Type 3 * @param <T4> Type 4 */ -public interface Slot4<T1, T2, T3, T4> { +public interface Slot4<T1, T2, T3, T4> extends BaseSlot { /** * This method will be called on notification from a signal * @param p1 parameter value 1 diff --git a/src/com/isode/stroke/signals/Slot7.java b/src/com/isode/stroke/signals/Slot7.java index 7df6973..752d9a8 100644 --- a/src/com/isode/stroke/signals/Slot7.java +++ b/src/com/isode/stroke/signals/Slot7.java @@ -15,7 +15,7 @@ package com.isode.stroke.signals; * @param <T6> Type 6 * @param <T7> Type 7 */ -public interface Slot7<T1, T2, T3, T4, T5, T6, T7> { +public interface Slot7<T1, T2, T3, T4, T5, T6, T7> extends BaseSlot { /** * This method will be called on notification from a signal * @param p1 parameter value 1 |