Version 3 (modified by tobias.mohr, 9 years ago) (diff)



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.

Present 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."
   triggerEvent: #surprised
   with: 23.

"Some other object triggers my event. Strange..."
      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 <code>#on:send:to:</code>. This avoids the need to implement, e.g., <code>#mouseDown:</code>, or <code>#keyStroke:</code> but messages with more readable names.

This approach is only limited to these standard events and cannot be used so create arbitrary connections.


media/icons/silk:world.png? AXAnnouncements@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.

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.


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

{| cellpadding="10" | ! Message Sends ! Announce- ments ! Object Dependents ! Object Events ! Bindings ! Signals

| align="right" | Bindings creation time | align="right" | 0 ms 0 % | align="right" | 44 ms 20 % | align="right" | 39765 ms 17832 % | align="right" | 64204 ms 28791 % | align="right" | ? | align="right" | 223 ms 100 %

| align="right" | Event triggering time | align="right" | 1 ms 4 % | align="right" | 4 ms 17 % | align="right" | 2 ms 8 % | align="right" | 78 ms 325 % | align="right" | ? | align="right" | 24 ms 100 % |}


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.

[[Image:Icons_silk_disk.png]] How to Install

<div style="float:right;"> {| style="color:#404040; background-color:#dedede;border-style:dotted;" cellpadding="10" cellspacing="0" border="0" | ; Environment : [[Image:Icons_squeak_16.png|Recommended Squeak Version]] 4.1, 4.2 Alpha : [[Image:Icons_silk_application_home.png|Recommended VM Version]] 4.0.2 (Win), ? (Mac) ; Sources : [[Image:Icons_silk_database.png|Repository]] SwaUtilities : [[Image:Icons_silk_package.png|Needed Packages from the Repository]] Signals : [[Image:Icons_silk_package.png|Needed Packages from the Repository]] SI-Wrapper (optional) : [[Image:Icons_silk_package.png|Needed Packages from the Repository]] SI-OB-Morphic (optional) : [[Image:Icons_silk_package.png|Needed Packages from the Repository]] SI-Reflection (optional) : [[Image:Icons_silk_package.png|Needed Packages from the Repository]] SI-Benchmarks (optional) : [[Image:Icons_silk_bullet_go.png|Dependents]] OmniBrowser (optional) : [[Image:Icons_silk_bullet_go.png|Dependents]] [[PUBLIC:MethodWrappers|Method Wrappers]] (optional) : [[Image:Icons_silk_bullet_go.png|Dependents]] AXAnnouncements (optional) ; Misc : [[Image:Icons_silk_world.png|Website]] SwaUtilities@SqueakSource |} </div>

Just load the Signals package from the Monticello HTTP repository.

If you do not have OmniBrowser? installed, just skip the SI-OB-Morphic package. You will not have a visual indicator in front of your method list in the code browser.

There is some benchmark code in SI-Benchmarks that compares different callback mechanisms. If you do not have <code>AXAnnouncements</code> in your system, just ignore the warning on package loading.

If you want to use wrapped signals, you should have installed [[PUBLIC:MethodWrappers|Method Wrappers]] first.

After the installation has finished, start the TestRunner? and run all tests you find in Signals-Tests. They should all pass.

The Signals package 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;"></div>

[[Image:Icons_silk_book_open.png]] How to Use

Implementing Signals

A signal is an object's method that is able to trigger callbacks. It is implemented like this:

<pre> 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 <code>self emit</code> 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:

<pre> self valueChanged: 42. </pre>

You must not emit an object's signal from the outside. The following fails if not executed within the same instance as <code>myCounter</code>:

<pre> myCounter valueChanged: 42. </pre>

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 Advanced Usage section and read about 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:

<pre> 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:

<pre> 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 <code>#mouseDown:</code> event:

<pre> "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:

<pre> "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:. </pre>

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:

<pre> self

connect: aSender signal: #a:b:c: toSelector: #d:e:f: pattern: #(3 1 2). "c->d, a->e, b->f"


connect: aSender signal: #a:b:c: toSelector: #d:e:f: pattern: #(4 1). "Error!"


The automatic truncation can be used to avoid patterns like <code>#(1 2)</code> or <code>#()</code>. Arguments can be duplicated, i.e., <code>#(1 1)</code>.


A connection can be removed from the sender:

<pre> 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!

<pre> "Here: Make you data structures thread-safe!" [anObject

connect: myCounter signal: #valueChanged: toSelector: #processValue:] fork.


To encounter this problem, connections can be queued:

<pre> 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: <code>WorldState?>>deferredUIMessages</code>. 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:

<pre> 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:

<pre> [self signalWasSent "non-blocking"]

whileFalse: [

WorldState? deferredUIMessages next value "blocking"].


This approach works with any queue. It is similar to <code>processEvents()</code> in the Qt Framework. In Squeak, the whole world can be kept responsive by calling <code>World>>doOneCycle</code> 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 <code>SignalSpy?</code> can be used to wait for signals explicitly. Normally, it should be used in tests and not in the application itself:

<pre> spy := SignalSpy?

onSender: aSender signal: #signalEmitted.

signal := spy waitForNextSignal. </pre>

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: <code>#waitForSignal:</code>.

If you want to test for some properties of the next signal besides its name, use <code>#waitForNextSignalSatisfying:</code>:

<pre> spy waitForNextSignalSatisfying: [:signal | |arguments|

arguments := signal second. arguments first = #foobar].


The structre stored by the <code>SignalSpy?</code> is an array with two fields: signal name and signal arguments.


There is an extension to the OmniBrowser? Framework that shows a small icon in front of signals in the message list:

: [[Image:SignalsOmniBrowserMorphic.PNG]]

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:

<pre> 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 <code>SystemNavigation?</code> to allow browsing for connections and signal sends:

<pre> SystemNavigation? default

allConnectionsOn: #valueChanged: from: aClass. "Here is the connection code


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:

<pre> 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:

<pre> 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:

<pre> MyDialog?>>onNameEditTextChanged: aText

self availabilityLabel contents:

(self checkAvailability: aText).


Such a method can benefit from its signature if there is an instance variable named <code>nameEdit</code> which sends the signal <code>#textChanged:</code>. Automatic connections can be created from such patterns after the referenced instance variables were initialized:

<pre> 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:

<pre> MyMorph?>>position: aPosition

super position: aPosition. self emitAlways. "Would have to be a public signal."


Or like this:

<pre> 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 Signals-Wrapper is installed which needs the package/project [[PUBLIC:MethodWrappers|Method Wrappers]] as well:

<pre> "Installs a method wrapper on #position:." self connect: aMorph signal: #position: to: Transcript selector: #show:. </pre>

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 <code>0</code> 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:

<pre> self

connect: anAction signal: #triggered toSelector: #logAction: pattern: #(0).


connect: anotherAction signal: #triggered toSelector: #logAction: pattern: #(0).


Patterns can have default values that will be send if the signal is processed:

<pre> "self changed: #counterValue." self

connect: myCounter signal: #valueChanged: toSelector: #changed: pattern: #(=counterValue).


You can send whole objects as default value:

<pre> self

connect: myCounter signal: #valueChanged: toSelector: #addMorphBack: pattern: {#=. Morph new}.


Patterns can mix index references to the sender's arguments and default values: <code>#(2 1 =foobar)</code>. As you see, the magic happens just because of the escape symbol <code>#=</code>. 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.

[[Image:Icons_silk_wrench.png]] How to Extend

General Implementation Approach

  • one central repository <code>SignalConnectionsRepository?</code> stores all connections in the system
  • "<code>self emit</code>" 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
  • TestRunner? coverage testing is broken because signals will not be emitted in the correct context and fail

[[Image:Icons_silk_star.png]] Acknowledgments

The signals mechanism was inspired by the signals/slots concept in the Nokia Qt Framework.

Attachments (1)

Download all attachments as: .zip