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 @@ -721,13 +721,13 @@ public class MUCImpl extends MUC { * This method should be called when the MUC object is no longer in use * so as to enable the garbage collector to remove this object from used space. */ @Override public void disconnect() { if (scopedConnection_ != null) { - scopedConnection_.onDestroyed.emit(); + scopedConnection_.disconnect(); scopedConnection_ = null; } } protected void finalize() throws Throwable { try { 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,54 +1,33 @@ /* - * 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) { return connect(new Slot() { public void call() { target.emit(); } }); } - 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,48 +1,33 @@ /* - * 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); } } public SignalConnection connect(final Signal1<T1> target) { return connect(new Slot1<T1>() { public void call(T1 p1) { target.emit(p1); } }); } - - 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,60 +1,39 @@ /* - * 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 * @param <T1> Type 1 * @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); } /** * Notify all slots(listeners) * @param p1 parameter value 1 * @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,66 +1,42 @@ /* - * 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; /** * An approximation of the boost::signals system with 4 parameters * @param <T1> Type 1 * @param <T2> Type 2 * @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); } /** * Notify all slots(listeners) * @param p1 parameter value 1 * @param p2 parameter value 2 * @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 @@ -2,65 +2,41 @@ * 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; /** * An approximation of the boost::signals system with 4 parameters * @param <T1> Type 1 * @param <T2> Type 2 * @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); } /** * Notify all slots(listeners) * @param p1 parameter value 1 * @param p2 parameter value 2 * @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 @@ -5,9 +5,9 @@ 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 @@ -5,9 +5,9 @@ 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 @@ -5,9 +5,9 @@ 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 @@ -8,13 +8,13 @@ package com.isode.stroke.signals; /** * Bind class for connecting to a signal with 3 parameters. * @param <T1> Type 1 * @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 * @param p2 parameter value 2 * @param p3 parameter value 3 */ 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 @@ -9,13 +9,13 @@ package com.isode.stroke.signals; * Bind class for connecting to a signal with 4 parameters. * @param <T1> Type 1 * @param <T2> Type 2 * @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 * @param p2 parameter value 2 * @param p3 parameter value 3 * @param p4 parameter value 4 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 @@ -12,13 +12,13 @@ package com.isode.stroke.signals; * @param <T3> Type 3 * @param <T4> Type 4 * @param <T5> Type 5 * @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 * @param p2 parameter value 2 * @param p3 parameter value 3 * @param p4 parameter value 4 |
Swift