[[PageOutline(1-3)]] = Signals = A simple callback is a function call that is performed when another action took place. Normally, callback can be registered to an object and will be processed by that object whenever the time is right. There are several ways to implement such callbacks. Some are more general, others only apply to a subset of possible actions in the system. The ''Signals'' implementation tries to be usable in all cases but adds one or another constraint to keep code that uses signals readable. = How to Install = {{{ #!div class="wiki_infotable" style="float:right;" ||'''Environment'''|| || || [[Image(media/icons/custom:squeak_16.png, title="Recommended Squeak Version", nolink, right)]] || 4.1, 4.2 and later || || [[Image(media/icons/silk:application_home.png, title="Recommended Squeak VM Version", nolink, right)]] || 4.1.1 (Win) || || [[Image(media/icons/silk:cog.png, title="Recommended Cog VM Version", nolink, right)]] || r2316 || ||'''Sources'''|| || || [[Image(media/icons/silk:script_gear.png, title="Metacello Configuration", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/MetacelloRepository/ ConfigurationOfSignals] || || [[Image(media/icons/silk:database.png, title="Repository", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/SwaUtilities/ SwaUtilities] || || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `Signals` || || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-Wrapper` (optional) || || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-OB-Morphic` (optional) || || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-Reflection` (optional) || || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-Benchmarks` (optional) || || [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [http://www.squeaksource.com/OmniBrowser.html OmniBrowser] (optional) || || [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [wiki:methodwrappers Method Wrappers] (optional) || || [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [http://www.squeaksource.com/AXAnnouncements.html AXAnnouncements] (optional) || ||'''Misc'''|| || || [[Image(media/icons/silk:world.png, title="Website", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/SwaUtilities.html SwaUtilities@SqueakSource] || }}} Using Metacello, just run the following code in your workspace: {{{ (Installer mc http: 'http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/') project: 'MetacelloRepository'; install: 'ConfigurationOfSignals'. ConfigurationOfSignals load. }}} If you later want to get the latest development version of Signals, run this: {{{ ConfigurationOfSignals loadDevelopment. }}} The ''Signals'' project contains several sub-packages: * `Signals-Core` ... main signals implementation * `Signals-Tests` ... tests for the core implementation * `SI-OB-Morphic` ... morphic extensions to `OmniBrowser` * `SI-Benchmarks` ... benchmmarks for Signals and other mechanisms, e.g., Announcements * `SI-Wrapper` ... turns each message into a signal using method wrappers * `SI-Reflection` ... search and browse signals in the image {{{ #!div style="clear:both;" }}} '''Important Notes''' for development version users: Support for [#WrappedSignals wrapped signals] removed in `Signals-mt.32`. So there is no need to load `SI-Wrapper´ right now. = How to Use = == Terms == A ''signal'' is a method that can be called to trigger a callback. Then the signal will be ''emitted''. A ''callback'' is the method that is called or the process that is involved in performing this method call in response to an emitted signal. A ''connection'' is a pair of signal and callback. The pair is used to lookup all callbacks whenever a signal is emitted. A signal can have multiple connections. == Implementing Signals == A signal is an object's method that is able to trigger callbacks. It is implemented like this: {{{ MyCounter>>valueChanged: newValue self emit. }}} Each signal can have an arbitrary number of arguments. The emit-call triggers the callback. In this way, any method could be used as a signal. This could be very confusing for the developer, therefore it is not recommended. Calling `self emit` not inside a method context but a block context works as well. Obviously, a signal is emitted by calling the method that is meant to be the signal: {{{ self valueChanged: 42. }}} You '''must not''' emit an object's signal from the outside. The following fails if not executed within the same instance as `myCounter`: {{{ myCounter valueChanged: 42. }}} One could argue that this is an unnecessary constraint, but it is the idea of the signals, that an object decides itself when a signal is emitted. Allowing the signal sending from everywhere could lead to less readable code. See the [#AdvancedUsage advanced usage] section and read about [#PublicSignals public signals] if you want to bypass this constraint for some reason. == Basic Connections == The simplest form of a connection is created from within the UI process of Squeak, e.g., using the workspace, in the following way: {{{ self connect: myCounter signal: #valueChanged: to: Transcript selector: #show:. }}} This more or less mediator approach can be changed if an object itself connects to a signal: {{{ self connect: myCounter signal: #valueChanged: toSelector: #show:. }}} == Basic Signal Processing == In the simplest way, a signal is emitted from within the UI process of Squeak, e.g., in response to a Morphic `#mouseDown:` event: {{{ "The signal." MyMorph>>mouseDownReceived: evt self emit. "The default event handling routine in Morphic." MyMorph>>mouseDown: evt self mouseDownReceived: evt. }}} Having this, the signal is processed synchronously and blocking, which means that all callbacks were made after returning from the call that emitted the signal. == Arguments and Patterns == Each callback should expect at most arguments as the signal contains. By default, trailing arguments will be truncated and ignored: {{{ "Works." self connect: s signal: #a: toSelector: #b:. "Works. Auto-truncate." self connect: s signal: #a: toSelector: #c. "Does not work." self connect: s signal: #a toSelector: #b:. }}} It is possible to choose and reorder specific arguments from the signal with a ''pattern''. A pattern is an array of numbers that maps argument positions. It must have exactly as many elements as the callback needs: {{{ self connect: aSender signal: #a:b:c: toSelector: #d:e:f: pattern: #(3 1 2). "c->d, a->e, b->f" self connect: aSender signal: #a:b:c: toSelector: #d:e:f: pattern: #(4 1). "Error!" }}} The automatic truncation can be used to avoid patterns like `#(1 2)` or `#()`. Arguments can be duplicated, i.e., `#(1 1)`. == Disconnection == A connection can be removed from the sender: {{{ self "the sender" disconnectSignal: #valueChanged: from: aReceiver selector: #processValue:. }}} You can also remove all connections from a specific receiver, all connections from a specific signal and any connection that was ever created. Although, connections will be destroyed if either sender or receiver is deleted automatically, sometimes it could be useful to disconnect a signal to avoid endless loops. == Using Processes == The connection and disconnection of signals is thread-safe. Signals can be emitted from within any Squeak process. Normally, the creator of a connection should not bother where the signal is emitted but where the callback is processed to avoid considering thread-safety of involved data structures. When creating a connection from within the Squeak UI process, callbacks will be processed within that process no matter where the signal was emitted. This is a special case of so called ''queued connections''. === Queued Connections === Sometimes it is necessary to specify the process that is involved during signal processing. Except for the UI process, any connection made from within any other process will be handled in the process where the signal is emitted. This could lead to unexpected behavior if all involved data structures are not thread-safe! {{{ "Here: Make you data structures thread-safe!" [anObject connect: myCounter signal: #valueChanged: toSelector: #processValue:] fork. }}} To encounter this problem, connections can be queued: {{{ Transcript connect: myCounter signal: #valueChanged: toSelector: #show: queue: aSharedQueue. }}} Using a queue, an emitted signal causes the queue to be filled with the callbacks stored into blocks that have to be evaluated by a process frequently. The Squeak UI process has such a queue already that will be processed in the main world cycle frequently: `WorldState>>deferredUIMessages`. Using this, basic connections from within the UI process will be queued if the signal is emitted from within any other process than the UI process automatically. Otherwise they are processed synchronously and blocking. Any queued connection can be blocking which means that the signal emitting process will be suspended until the callback waiting in the queue is processed: {{{ Transcript connect: myCounter signal: #valueChanged: toSelector: #show: queue: aSharedQueue blocking: true. }}} Of course, anyone can resume a suspended process in Squeak, but this implementation of blocking connections was quite simple and should work for the most cases. === Avoiding Deadlocks === When working with queued connections, deadlocks can appear, e.g., if a process waits for a signal to be processed by the same process that normally looks for the queue frequently. This can happen especially in the UI process and fixed as follows: {{{ [self signalWasSent "non-blocking"] whileFalse: [ WorldState deferredUIMessages next value "blocking"]. }}} This approach works with any queue. It is similar to `processEvents()` in the Qt Framework. In Squeak, the whole world can be kept responsive by calling `World>>doOneCycle` frequently. If you create a blocking connection and a signal is emitted with a queued callback to be processed in the same process, nothing will happen until the process is resumed from the outside. You should not do that. === Waiting for Signals === The `SignalSpy` can be used to wait for signals explicitly. Normally, it should be used in tests and not in the application itself: {{{ spy := SignalSpy onSender: aSender signal: #signalEmitted. signal := spy waitForNextSignal. }}} The signal spy catches all registered signals emitted from any process. Wait operations are blocking so be sure that the signals are emitted in another process. You can wait for a specific signal using: `#waitForSignal:`. If you want to test for some properties of the next signal besides its name, use `#waitForNextSignalSatisfying:`: {{{ spy waitForNextSignalSatisfying: [:signal | |arguments| arguments := signal second. arguments first = #foobar]. }}} The structre stored by the `SignalSpy` is an array with two fields: signal name and signal arguments. == Awareness == There is an extension to the OmniBrowser Framework that shows a small icon in front of signals in the message list: [[Image(SignalsOmniBrowserMorphic.PNG, title="Signals have a custom icon in the message list.", nolink)]] Trying to load the Signals package into a Squeak image without OmniBrowser installed results in a warning that can be ignored safely. A connection creation needs symbols - one for the signal and one for the callback. Therefore it is possible to use the Squeak ''Browse Senders'' way to investigate them: {{{ self connect: aSender signal: #valueChanged: "Will be found." to: aReceiver selector: #value:. "Will be found." }}} Unfortunately the signal emitting place, which is just a message send, and other message sends that are not callbacks will be found as well. To solve this problem, some reflective methods were added to `SystemNavigation` to allow browsing for connections and signal sends: {{{ SystemNavigation default allConnectionsOn: #valueChanged: from: aClass. "Here is the connection code somewhere." SystemNavigation default allSignalEmitsFrom: aSenderClass signal: #valueChanged:. }}} == Advanced Usage == === Public Signals === To bypass the constraint that a signal cannot be emitted from the outside but must be emitted from the instance that has the signal, you can use ''public signals''. These types of signals just do not have that constraint and are implemented as the following: {{{ MyCounter>>valueChanged: newValue self emitAlways. }}} Use them with caution because they could lead to less readable code. === Connect Signal to Signal === Signals are just normal methods. Therefore they can be used as callbacks as well. If the receiver is not identical to the sender of the signal you need to use a ''public signal'' as target: {{{ self connect: aSender signal: #valueChanged "Signal #1" toSelector: #switchToggled. "Signal #2 - Has to be public if sender <> receiver!" }}} === Automatic Connections === It is possible to create methods that look like they want to be connected to a senders signal: {{{ MyDialog>>onNameEditTextChanged: aText self availabilityLabel contents: (self checkAvailability: aText). }}} Such a method can benefit from its signature if there is an instance variable named `nameEdit` which sends the signal `#textChanged:`. Automatic connections can be created from such patterns after the referenced instance variables were initialized: {{{ MyDialog>>initialize super initialize. nameEdit := MyNameEdit new. self createAutoSignalConnections. }}} If the sender does not send this signal the connection will silently not be created. === Wrapped Signals === Sometimes it could be useful to re-use a arbitrary message that an object understands as a signal, e.g., to be notified if a morph changes its position without having to subclass that morph like this: {{{ MyMorph>>position: aPosition super position: aPosition. self emitAlways. "Would have to be a public signal." }}} Or like this: {{{ MyMorph>>position: aPosition super position: aPosition. self positionChanged: aPosition. MyMorph>>positionChanged: newPosition self emit. }}} To avoid this, it is possible to use '''any arbitrary message''' as a signal as long has the sub-package ''SI-Wrapper'' is installed which needs the package/project [wiki:methodwrappers Method Wrappers] as well: {{{ "Installs a method wrapper on #position:." self connect: aMorph signal: #position: to: Transcript selector: #show:. }}} There will be only one method wrapper installed for each message that will be used in a connection for the first time. If no connections use that message as a signal, the wrapper will be removed from that message. === Advanced Patterns === It is possible to access the sender of a signal itself and use it as an argument for the receiver. This is achieved with a `0` in the pattern and can reduce the number of needed methods because the control flow may consider the sender, e.g. different actions are triggered and show themselves on a log: {{{ self connect: anAction signal: #triggered toSelector: #logAction: pattern: #(0). self connect: anotherAction signal: #triggered toSelector: #logAction: pattern: #(0). }}} Patterns can have default values that will be send if the signal is processed: {{{ "self changed: #counterValue." self connect: myCounter signal: #valueChanged: toSelector: #changed: pattern: #(=counterValue). }}} You can send whole objects as default value: {{{ self connect: myCounter signal: #valueChanged: toSelector: #addMorphBack: pattern: {#=. Morph new}. }}} Patterns can mix index references to the sender's arguments and default values: `#(2 1 =foobar)`. As you see, the magic happens just because of the escape symbol `#=`. Every value that comes after it, will be treated as itsself and not as an index reference. '''Hint:''' If you store an object into a pattern, the garbage collector will collect that object if it is not referenced somewhere else. In that case, ''nil'' will be supplied as argument. = How to Extend = == General Implementation Approach == * one central repository `SignalConnectionsRepository` stores all connections in the system * `self emit` looks into the call-stack to retrieve arguments and do other checks * the repository uses ''weak'' data structures to not interfere with the garbage collector == Next Possible Steps == * ''arguments processor'' to transform arguments before doing the callback * detect recursions on connection level to prevent endless loops, e.g., count and reset a number in each connection * Test-Runner coverage testing is broken because signals will not be emitted in the correct context and fail = Other Observer Mechanisms = == Object Dependents == Every object in Squeak has a list of dependent objects. You trigger an event with `#changed:` or `#changed:with:` and all dependents receive a `#update:` resp. `#update:with:` call. Dependents are managed using `#addDependent:` and `#removeDependent:`. Squeak processes will not be considered and data structures have to secured to avoid synchronization problems. Using this mechanism, objects are connected completely and not only in special cases because there is no filtering possible. ''Note on performance issues:'' As you see in the benchmarks, setting up thousands of bindings is quite slow. Therefore you should subclass `Model` instead of `Object` if you want to write faster code. `Model` uses a custom implementation of ''dependents''. But it is not possible for Morphs because they subclass from `Object` directly. == Object Events == Squeak has a built-in callback mechanism that is lightweight and decouples sender and receiver in a convenient way. {{{ self "sender" when: #valueChanged send: #value: to: aReceiver. }}} However, events can be triggered from everywhere and new event names can be made up at any time so that that it can be confusing to the developer to realize all possible events from an object: {{{ self "sender" triggerEvent: #valueChanged with: 42. "Surprisingly new event." self triggerEvent: #surprised with: 23. "Some other object triggers my event. Strange..." MyConfuser>>confuseCounter myCounter triggerEvent: #valueChanged with: 42. }}} Although, it is possible to handle events that no one processes separately. == Morphic Callbacks == Default mouse or keyboard input events can be connected using `#on:send:to:`. This avoids the need to implement, e.g., `#mouseDown:`, or `#keyStroke:` but messages with more readable names. This approach is only limited to these standard events and cannot be used so create arbitrary connections. == Announcements == [[Image(media/icons/silk:world.png)]] [http://www.squeaksource.com/AXAnnouncements.html AXAnnouncements@SqueakSource] == Bindings == [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/Bindings.html Bindings@SqueakSource] == Feature Comparison == Every callback implementation has another notion of the terms involved. For clarification, we use ''Event'' as the thing that can be triggered in notion of a ''Sender'' and a ''Callback'' that is processed in notion of a ''Receiver'' if the necessary ''Binding'' was configured correctly. {{{ #!div class="wiki_table" || || Morphic[[br]]Callbacks || Announce-[[br]]ments || Object[[br]]Dependents || Object[[br]]Events || Bindings || Signals || || Allows definiton of new Events || || ● || ● || ● || ? || ● || || Available Events are defined explicitly || ● || ● || || || ? || ● || || Events are defined for a specific class/subclass || || || || || ? || ● || || Bindings are at Event level || ● || ● || || ● || ? || ● || || Bindings are at !Sender/Receiver level || || || ● || || ? || || || Events are first-class objects || || ● || || || ? || || || Automatic truncation of arguments || ● || || || ● || ? || ● || || Explicit argument count check at binding-setup-time || || || || || ? || ● || || Explicit argument count check at event-trigger-time || || ● || || || ? || || || Access to sending object at binding-level || || || || || ? || ● || || Access to sending object at event-level || || ● || ● || ● || ? || ● || || Event encodes number of available arguments || || ● || || || ? || ● || || Unhandled Events can be processed separately || || || || ● || ? || || || Event triggering can be limited to the sender || || || || || ? || ● || || Provides synchronisation mechansism for thread-safety || || || || || ? || ● || || Explicit !Event/Callback check at binding-setup-time || || || || || ? || ● || }}} ''It is not guaranteed that the table is correct except for the Signals column.'' == Benchmark == One sender was bound to 10000 receivers. Then one event was triggered and processed synchronously. The benchmark code is in the ''Signals'' package. '''Test-System:''' Core2Duo @ 2.54 GHz, 4096 MB DDR2 RAM, Windows 7 Professional, Squeak 4.1 {{{ #!div class="wiki_table" || || Message[[br]]Sends || Announce-[[br]]ments || Object[[br]]Dependents || Object[[br]]Events || Bindings || Signals || || Bindings creation time || 0 ms[[br]]'''0 %'''|| 44 ms[[br]]'''20 %'''|| 39765 ms[[br]]'''17832 %'''|| 64204 ms[[br]]'''28791 %'''|| ?|| 223 ms[[br]]'''100 %'''|| || Event triggering time || 1 ms[[br]]'''4 %'''|| 4 ms[[br]]'''17 %'''|| 2 ms[[br]]'''8 %''' || 78 ms[[br]]'''325 %''' || ?|| 24 ms[[br]]'''100 %'''|| }}} = Acknowledgments = The signals mechanism was inspired by the signals/slots concept in the [http://qt.nokia.com Nokia Qt Framework]. [[Image(media/icons/silk:user.png, title="Target Group", nolink)]] To date the following people contributed to this project: * Marcel Taeumel