From 9d50093bee736d2b7b43756e9a41cfafbd568ee2 Mon Sep 17 00:00:00 2001 From: Alan Young Date: Mon, 18 Apr 2016 17:37:46 +0100 Subject: 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 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 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(); + 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 binds_ = Collections.synchronizedMap(new HashMap()); +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 binds = new ArrayList(); - 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 { - private final Map > binds_ = Collections.synchronizedMap(new HashMap >()); +public final class Signal1 extends BaseSignal { public SignalConnection connect(Slot1 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> binds = new ArrayList>(); - binds.addAll(binds_.values()); - for (Slot1 bind : binds) { - bind.call(p1); + final BaseSlot[] binds = getBinds(); + if (binds == null) {return;} + for (BaseSlot bind : binds) { + ((Slot1)bind).call(p1); } } @@ -41,8 +30,4 @@ public class Signal1 { } }); } - - 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 { - private final Map > binds_ = Collections.synchronizedMap(new HashMap >()); +public class Signal2 extends BaseSignal { public SignalConnection connect(Slot2 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> binds = new ArrayList>(); - binds.addAll(binds_.values()); - for (Slot2 bind : binds) { - bind.call(p1, p2); + final BaseSlot[] binds = getBinds(); + if (binds == null) {return;} + for (BaseSlot bind : binds) { + ((Slot2)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 Type 2 * @param Type 3 */ -public class Signal3 { - private final Map > binds_ = Collections.synchronizedMap( - new HashMap >()); - +public class Signal3 extends BaseSignal { /** * Add a slot which will be notified * @param bind slot, not null * @return signal connection */ public SignalConnection connect(Slot3 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 { * @param p2 parameter value 2 * @param p3 parameter value 3 */ - public void emit(T1 p1, T2 p2,T3 p3) { - List> binds = new ArrayList>(); - binds.addAll(binds_.values()); - for (Slot3 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)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 Type 3 * @param Type 4 */ -public class Signal4 { - private final Map > binds_ = Collections.synchronizedMap( - new HashMap >()); - +public class Signal4 extends BaseSignal { /** * Add a slot which will be notified * @param bind slot, not null * @return signal connection */ public SignalConnection connect(Slot4 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 { * @param p3 parameter value 3 * @param p4 parameter value 4 */ + @SuppressWarnings("unchecked") public void emit(T1 p1, T2 p2, T3 p3, T4 p4) { - List> binds = new ArrayList>(); - binds.addAll(binds_.values()); - for (Slot4 bind : binds) { - bind.call(p1, p2, p3, p4); + final BaseSlot[] binds = getBinds(); + if (binds == null) {return;} + for (BaseSlot bind : binds) { + ((Slot4)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 Type 3 * @param Type 4 */ -public class Signal7 { - private final Map > binds_ = Collections.synchronizedMap( - new HashMap >()); - +public class Signal7 extends BaseSignal { /** * Add a slot which will be notified * @param bind slot, not null * @return signal connection */ public SignalConnection connect(Slot7 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 { * @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> binds = new ArrayList>(); - binds.addAll(binds_.values()); - for (Slot7 bind : binds) { - bind.call(p1, p2, p3, p4, p5, p6, p7); + final BaseSlot[] binds = getBinds(); + if (binds == null) {return;} + for (BaseSlot bind : binds) { + ((Slot7)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 { +public interface Slot1 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 { +public interface Slot2 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 Type 2 * @param Type 3 */ -public interface Slot3 { +public interface Slot3 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 Type 3 * @param Type 4 */ -public interface Slot4 { +public interface Slot4 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 Type 6 * @param Type 7 */ -public interface Slot7 { +public interface Slot7 extends BaseSlot { /** * This method will be called on notification from a signal * @param p1 parameter value 1 -- cgit v0.10.2-6-g49f6