Changes between Version 11 and Version 12 of signals


Ignore:
Timestamp:
11/30/2010 09:13:01 PM (13 years ago)
Author:
tobias.pape
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • signals

    v11 v12  
    55A 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.
    66
    7 == Present Mechanisms ==
    8 === Object Dependents ===
     7= How to Install =
     8
     9{{{
     10#!div class="wiki_infotable" style="float:right;"
     11||'''Environment'''|| ||
     12|| [[Image(media/icons/custom:squeak_16.png, title="Recommended Squeak Version", nolink, right)]] || 4.1, 4.2 Alpha ||
     13|| [[Image(media/icons/silk:application_home.png, title="Recommended Squeak VM Version", nolink, right)]] || 4.0.2 (Win), ? (Mac) ||
     14|| [[Image(media/icons/silk:cog.png, title="Recommended Cog VM Version", nolink, right)]] || ''not tested with cog'' ||
     15||'''Sources'''|| ||
     16|| [[Image(media/icons/silk:script_gear.png, title="Metacello Configuration", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/MetacelloRepository/ ConfigurationOfSignals] ||
     17|| [[Image(media/icons/silk:database.png, title="Repository", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/SwaUtilities/ SwaUtilities] ||
     18
     19|| [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `Signals` ||
     20|| [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-Wrapper` (optional) ||
     21|| [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-OB-Morphic` (optional) ||
     22|| [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-Reflection` (optional) ||
     23|| [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-Benchmarks` (optional) ||
     24
     25|| [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [http://www.squeaksource.com/OmniBrowser.html OmniBrowser] (optional) ||
     26|| [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [wiki:methodwrappers Method Wrappers] (optional) ||
     27|| [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [http://www.squeaksource.com/AXAnnouncements.html AXAnnouncements] (optional) ||
     28
     29||'''Misc'''|| ||
     30|| [[Image(media/icons/silk:world.png, title="Website", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/SwaUtilities.html SwaUtilities@SqueakSource] ||
     31}}}
     32
     33Just load the ''Signals'' package from the Monticello HTTP repository.
     34
     35If 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.
     36
     37There is some benchmark code in ''SI-Benchmarks'' that compares different callback mechanisms. If you do not have `AXAnnouncements` in your system, just ignore the warning on package loading.
     38
     39If you want to use ''wrapped signals'', you should have installed [wiki:methodwrappers Method Wrappers] first.
     40
     41After the installation has finished, start the Test-Runner and run all tests you find in ''Signals-Tests''. They should all pass.
     42
     43The ''Signals'' package contains several sub-packages:
     44
     45 * `Signals-Core` ... main signals implementation
     46 * `Signals-Tests` ... tests for the core implementation
     47 * `SI-OB-Morphic` ... morphic extensions to `OmniBrowser`
     48 * `SI-Benchmarks` ... benchmmarks for Signals and other mechanisms, e.g., Announcements
     49 * `SI-Wrapper` ... turns each message into a signal using method wrappers
     50 * `SI-Reflection` ... search and browse signals in the image
     51
     52{{{
     53#!div style="clear:both;"
     54}}}
     55
     56
     57= How to Use =
     58
     59== Implementing Signals ==
     60
     61A signal is an object's method that is able to trigger callbacks. It is implemented like this:
     62
     63{{{
     64MyCounter>>valueChanged: newValue
     65   self emit.
     66}}}
     67
     68Each 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.
     69
     70Obviously, a signal is emitted by calling the method that is meant to be the signal:
     71
     72{{{
     73self valueChanged: 42.
     74}}}
     75
     76You '''must not''' emit an object's signal from the outside. The following fails if not executed within the same instance as `myCounter`:
     77
     78{{{
     79myCounter valueChanged: 42.
     80}}}
     81
     82One 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.
     83
     84See the [#AdvancedUsage advanced usage] section and read about [#PublicSignals public signals] if you want to bypass this constraint for some reason.
     85
     86== Basic Connections ==
     87
     88The simplest form of a connection is created from within the UI process of Squeak, e.g., using the workspace, in the following way:
     89
     90{{{
     91self
     92   connect: myCounter
     93   signal: #valueChanged:
     94   to: Transcript
     95   selector: #show:.
     96}}}
     97
     98
     99This more or less mediator approach can be changed if an object itself connects to a signal:
     100
     101{{{
     102self
     103   connect: myCounter
     104   signal: #valueChanged:
     105   toSelector: #show:.
     106}}}
     107
     108
     109== Basic Signal Processing ==
     110
     111In the simplest way, a signal is emitted from within the UI process of Squeak, e.g., in response to a Morphic `#mouseDown:` event:
     112
     113{{{
     114"The signal."
     115MyMorph>>mouseDownReceived: evt
     116   self emit.
     117   
     118"The default event handling routine in Morphic."
     119MyMorph>>mouseDown: evt
     120   self mouseDownReceived: evt.
     121}}}
     122
     123
     124Having this, the signal is processed synchronously and blocking, which means that all callbacks were made after returning from the call that emitted the signal.
     125
     126== Arguments and Patterns ==
     127
     128Each callback should expect at most arguments as the signal contains. By default, trailing arguments will be truncated and ignored:
     129
     130{{{
     131"Works."
     132self connect: s signal: #a: toSelector: #b:.
     133"Works. Auto-truncate."
     134self connect: s signal: #a: toSelector: #c.
     135"Does not work."
     136self connect: s signal: #a toSelector: #b:.
     137}}}
     138
     139It 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:
     140
     141{{{
     142self
     143   connect: aSender
     144   signal: #a:b:c:
     145   toSelector: #d:e:f:
     146   pattern: #(3 1 2). "c->d, a->e, b->f"
     147   
     148self
     149   connect: aSender
     150   signal: #a:b:c:
     151   toSelector: #d:e:f:
     152   pattern: #(4 1). "Error!"
     153}}}
     154
     155The automatic truncation can be used to avoid patterns like `#(1 2)` or `#()`. Arguments can be duplicated, i.e., `#(1 1)`.
     156
     157== Disconnection ==
     158
     159A connection can be removed from the sender:
     160
     161{{{
     162self "the sender"
     163   disconnectSignal: #valueChanged:
     164   from: aReceiver
     165   selector: #processValue:.
     166}}}
     167
     168
     169You can also remove all connections from a specific receiver, all connections from a specific signal and any connection that was ever created.
     170
     171Although, 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.
     172
     173
     174== Using Processes ==
     175
     176The connection and disconnection of signals is thread-safe.
     177
     178Signals 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.
     179
     180When 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''.
     181
     182
     183=== Queued Connections ===
     184
     185Sometimes 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!
     186
     187{{{
     188"Here: Make you data structures thread-safe!"
     189[anObject
     190   connect: myCounter
     191   signal: #valueChanged:
     192   toSelector: #processValue:] fork.
     193}}}
     194
     195
     196To encounter this problem, connections can be queued:
     197
     198{{{
     199Transcript
     200   connect: myCounter
     201   signal: #valueChanged:
     202   toSelector: #show:
     203   queue: aSharedQueue.
     204}}}
     205
     206
     207Using 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.
     208
     209The 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.
     210
     211Any queued connection can be blocking which means that the signal emitting process will be suspended until the callback waiting in the queue is processed:
     212
     213{{{
     214Transcript
     215   connect: myCounter
     216   signal: #valueChanged:
     217   toSelector: #show:
     218   queue: aSharedQueue
     219   blocking: true.
     220}}}
     221
     222
     223Of 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.
     224
     225
     226=== Avoiding Deadlocks ===
     227
     228When 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:
     229
     230{{{
     231[self signalWasSent "non-blocking"]
     232   whileFalse: [
     233      WorldState deferredUIMessages next value "blocking"].
     234}}}
     235
     236This 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.
     237
     238If 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.
     239
     240
     241=== Waiting for Signals ===
     242
     243The `SignalSpy` can be used to wait for signals explicitly. Normally, it should be used in tests and not in the application itself:
     244
     245{{{
     246spy := SignalSpy
     247   onSender: aSender
     248   signal: #signalEmitted.
     249signal := spy waitForNextSignal.
     250}}}
     251
     252
     253The 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:`.
     254
     255If you want to test for some properties of the next signal besides its name, use `#waitForNextSignalSatisfying:`:
     256
     257{{{
     258spy waitForNextSignalSatisfying: [:signal | |arguments|
     259   arguments := signal second.
     260   arguments first = #foobar].
     261}}}
     262
     263
     264The structre stored by the `SignalSpy` is an array with two fields: signal name and signal arguments.
     265
     266== Awareness ==
     267
     268There is an extension to the OmniBrowser Framework that shows a small icon in front of signals in the message list:
     269
     270  [[Image(SignalsOmniBrowserMorphic.PNG, title="Signals have a custom icon in the message list.", nolink)]]
     271
     272Trying to load the Signals package into a Squeak image without OmniBrowser installed results in a warning that can be ignored safely.
     273
     274A 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:
     275
     276{{{
     277self
     278   connect: aSender
     279   signal: #valueChanged: "Will be found."
     280   to: aReceiver
     281   selector: #value:. "Will be found."
     282}}}
     283
     284
     285Unfortunately 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:
     286
     287{{{
     288SystemNavigation default
     289   allConnectionsOn: #valueChanged:
     290   from: aClass. "Here is the connection code
     291                 somewhere."
     292   
     293SystemNavigation default
     294   allSignalEmitsFrom: aSenderClass
     295   signal: #valueChanged:.
     296}}}
     297
     298
     299== Advanced Usage ==
     300
     301=== Public Signals ===
     302
     303To 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:
     304
     305{{{
     306MyCounter>>valueChanged: newValue
     307   self emitAlways.
     308}}}
     309
     310Use them with caution because they could lead to less readable code.
     311
     312
     313=== Connect Signal to Signal ===
     314
     315Signals 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:
     316
     317{{{
     318self
     319   connect: aSender
     320   signal: #valueChanged "Signal #1"
     321   toSelector: #switchToggled. "Signal #2 - Has to be public if sender <> receiver!"
     322}}}
     323
     324
     325=== Automatic Connections ===
     326
     327It is possible to create methods that look like they want to be connected to a senders signal:
     328
     329{{{
     330MyDialog>>onNameEditTextChanged: aText
     331   self availabilityLabel contents:
     332      (self checkAvailability: aText).
     333}}}
     334
     335
     336Such 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:
     337
     338{{{
     339MyDialog>>initialize
     340   super initialize.
     341   nameEdit := MyNameEdit new.
     342   self createAutoSignalConnections.
     343}}}
     344
     345
     346If the sender does not send this signal the connection will silently not be created.
     347
     348=== Wrapped Signals ===
     349
     350Sometimes 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:
     351
     352{{{
     353MyMorph>>position: aPosition
     354   super position: aPosition.
     355   self emitAlways. "Would have to be a public signal."
     356}}}
     357
     358
     359Or like this:
     360
     361{{{
     362MyMorph>>position: aPosition
     363   super position: aPosition.
     364   self positionChanged: aPosition.
     365
     366MyMorph>>positionChanged: newPosition
     367   self emit.
     368}}}
     369
     370
     371To 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:
     372
     373{{{
     374"Installs a method wrapper on #position:."
     375self connect: aMorph signal: #position: to: Transcript selector: #show:.
     376}}}
     377
     378
     379There 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.
     380
     381=== Advanced Patterns ===
     382
     383It 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:
     384
     385{{{
     386self
     387   connect: anAction
     388   signal: #triggered
     389   toSelector: #logAction:
     390   pattern: #(0).
     391
     392self
     393   connect: anotherAction
     394   signal: #triggered
     395   toSelector: #logAction:
     396   pattern: #(0).
     397}}}
     398
     399Patterns can have default values that will be send if the signal is processed:
     400
     401{{{
     402"self changed: #counterValue."
     403self
     404   connect: myCounter
     405   signal: #valueChanged:
     406   toSelector: #changed:
     407   pattern: #(=counterValue).
     408}}}
     409
     410You can send whole objects as default value:
     411
     412{{{
     413self
     414   connect: myCounter
     415   signal: #valueChanged:
     416   toSelector: #addMorphBack:
     417   pattern: {#=. Morph new}.
     418}}}
     419
     420Patterns 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.
     421
     422'''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.
     423
     424= How to Extend =
     425
     426== General Implementation Approach ==
     427
     428 * one central repository `SignalConnectionsRepository` stores all connections in the system
     429 * `self emit` looks into the call-stack to retrieve arguments and do other checks
     430 * the repository uses ''weak'' data structures to not interfere with the garbage collector
     431
     432== Next Possible Steps ==
     433
     434 * ''arguments processor'' to transform arguments before doing the callback
     435 * detect recursions on connection level to prevent endless loops, e.g., count and reset a number in each connection
     436 * Test-Runner coverage testing is broken because signals will not be emitted in the correct context and fail
     437
     438
     439= Other Observer Mechanisms =
     440== Object Dependents ==
    9441
    10442Every 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:`.
     
    14446''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.
    15447
    16 === Object Events ===
     448== Object Events ==
    17449
    18450Squeak has a built-in callback mechanism that is lightweight and decouples sender and receiver in a convenient way.
     
    47479Although, it is possible to handle events that no one processes separately.
    48480
    49 === Morphic Callbacks ===
     481== Morphic Callbacks ==
    50482
    51483Default 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.
     
    53485This approach is only limited to these standard events and cannot be used so create arbitrary connections.
    54486
    55 === Announcements ===
     487== Announcements ==
    56488
    57489[[Image(media/icons/silk:world.png)]] [http://www.squeaksource.com/AXAnnouncements.html AXAnnouncements@SqueakSource]
    58490
    59 === Bindings ===
     491== Bindings ==
    60492
    61493[http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/Bindings.html Bindings@SqueakSource]
    62494
    63 === Feature Comparison ===
     495== Feature Comparison ==
    64496
    65497Every 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.
     
    88520''It is not guaranteed that the table is correct except for the Signals column.''
    89521
    90 === Benchmark ===
     522== Benchmark ==
    91523
    92524One sender was bound to 10000 receivers. Then one event was triggered and processed synchronously. The benchmark code is in the ''Signals'' package.
     
    109541A ''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.
    110542
    111 = How to Install =
    112 
    113 {{{
    114 #!div class="wiki_infotable" style="float:right;"
    115 ||'''Environment'''|| ||
    116 || [[Image(media/icons/custom:squeak_16.png, title="Recommended Squeak Version", nolink, right)]] || 4.1, 4.2 Alpha ||
    117 || [[Image(media/icons/silk:application_home.png, title="Recommended Squeak VM Version", nolink, right)]] || 4.0.2 (Win), ? (Mac) ||
    118 || [[Image(media/icons/silk:cog.png, title="Recommended Cog VM Version", nolink, right)]] || ''not tested with cog'' ||
    119 ||'''Sources'''|| ||
    120 || [[Image(media/icons/silk:script_gear.png, title="Metacello Configuration", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/MetacelloRepository/ ConfigurationOfSignals] ||
    121 || [[Image(media/icons/silk:database.png, title="Repository", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/SwaUtilities/ SwaUtilities] ||
    122 
    123 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `Signals` ||
    124 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-Wrapper` (optional) ||
    125 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-OB-Morphic` (optional) ||
    126 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-Reflection` (optional) ||
    127 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-Benchmarks` (optional) ||
    128 
    129 || [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [http://www.squeaksource.com/OmniBrowser.html OmniBrowser] (optional) ||
    130 || [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [wiki:methodwrappers Method Wrappers] (optional) ||
    131 || [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [http://www.squeaksource.com/AXAnnouncements.html AXAnnouncements] (optional) ||
    132 
    133 ||'''Misc'''|| ||
    134 || [[Image(media/icons/silk:world.png, title="Website", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/SwaUtilities.html SwaUtilities@SqueakSource] ||
    135 }}}
    136 
    137 Just load the ''Signals'' package from the Monticello HTTP repository.
    138 
    139 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.
    140 
    141 There is some benchmark code in ''SI-Benchmarks'' that compares different callback mechanisms. If you do not have `AXAnnouncements` in your system, just ignore the warning on package loading.
    142 
    143 If you want to use ''wrapped signals'', you should have installed [wiki:methodwrappers Method Wrappers] first.
    144 
    145 After the installation has finished, start the Test-Runner and run all tests you find in ''Signals-Tests''. They should all pass.
    146 
    147 The ''Signals'' package contains several sub-packages:
    148 
    149  * `Signals-Core` ... main signals implementation
    150  * `Signals-Tests` ... tests for the core implementation
    151  * `SI-OB-Morphic` ... morphic extensions to `OmniBrowser`
    152  * `SI-Benchmarks` ... benchmmarks for Signals and other mechanisms, e.g., Announcements
    153  * `SI-Wrapper` ... turns each message into a signal using method wrappers
    154  * `SI-Reflection` ... search and browse signals in the image
    155 
    156 {{{
    157 #!div style="clear:both;"
    158 }}}
    159 
    160 
    161 = How to Use =
    162 
    163 == Implementing Signals ==
    164 
    165 A signal is an object's method that is able to trigger callbacks. It is implemented like this:
    166 
    167 {{{
    168 MyCounter>>valueChanged: newValue
    169    self emit.
    170 }}}
    171 
    172 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.
    173 
    174 Obviously, a signal is emitted by calling the method that is meant to be the signal:
    175 
    176 {{{
    177 self valueChanged: 42.
    178 }}}
    179 
    180 You '''must not''' emit an object's signal from the outside. The following fails if not executed within the same instance as `myCounter`:
    181 
    182 {{{
    183 myCounter valueChanged: 42.
    184 }}}
    185 
    186 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.
    187 
    188 See the [#AdvancedUsage advanced usage] section and read about [#PublicSignals public signals] if you want to bypass this constraint for some reason.
    189 
    190 == Basic Connections ==
    191 
    192 The simplest form of a connection is created from within the UI process of Squeak, e.g., using the workspace, in the following way:
    193 
    194 {{{
    195 self
    196    connect: myCounter
    197    signal: #valueChanged:
    198    to: Transcript
    199    selector: #show:.
    200 }}}
    201 
    202 
    203 This more or less mediator approach can be changed if an object itself connects to a signal:
    204 
    205 {{{
    206 self
    207    connect: myCounter
    208    signal: #valueChanged:
    209    toSelector: #show:.
    210 }}}
    211 
    212 
    213 == Basic Signal Processing ==
    214 
    215 In the simplest way, a signal is emitted from within the UI process of Squeak, e.g., in response to a Morphic `#mouseDown:` event:
    216 
    217 {{{
    218 "The signal."
    219 MyMorph>>mouseDownReceived: evt
    220    self emit.
    221    
    222 "The default event handling routine in Morphic."
    223 MyMorph>>mouseDown: evt
    224    self mouseDownReceived: evt.
    225 }}}
    226 
    227 
    228 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.
    229 
    230 == Arguments and Patterns ==
    231 
    232 Each callback should expect at most arguments as the signal contains. By default, trailing arguments will be truncated and ignored:
    233 
    234 {{{
    235 "Works."
    236 self connect: s signal: #a: toSelector: #b:.
    237 "Works. Auto-truncate."
    238 self connect: s signal: #a: toSelector: #c.
    239 "Does not work."
    240 self connect: s signal: #a toSelector: #b:.
    241 }}}
    242 
    243 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:
    244 
    245 {{{
    246 self
    247    connect: aSender
    248    signal: #a:b:c:
    249    toSelector: #d:e:f:
    250    pattern: #(3 1 2). "c->d, a->e, b->f"
    251    
    252 self
    253    connect: aSender
    254    signal: #a:b:c:
    255    toSelector: #d:e:f:
    256    pattern: #(4 1). "Error!"
    257 }}}
    258 
    259 The automatic truncation can be used to avoid patterns like `#(1 2)` or `#()`. Arguments can be duplicated, i.e., `#(1 1)`.
    260 
    261 == Disconnection ==
    262 
    263 A connection can be removed from the sender:
    264 
    265 {{{
    266 self "the sender"
    267    disconnectSignal: #valueChanged:
    268    from: aReceiver
    269    selector: #processValue:.
    270 }}}
    271 
    272 
    273 You can also remove all connections from a specific receiver, all connections from a specific signal and any connection that was ever created.
    274 
    275 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.
    276 
    277 
    278 == Using Processes ==
    279 
    280 The connection and disconnection of signals is thread-safe.
    281 
    282 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.
    283 
    284 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''.
    285 
    286 
    287 === Queued Connections ===
    288 
    289 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!
    290 
    291 {{{
    292 "Here: Make you data structures thread-safe!"
    293 [anObject
    294    connect: myCounter
    295    signal: #valueChanged:
    296    toSelector: #processValue:] fork.
    297 }}}
    298 
    299 
    300 To encounter this problem, connections can be queued:
    301 
    302 {{{
    303 Transcript
    304    connect: myCounter
    305    signal: #valueChanged:
    306    toSelector: #show:
    307    queue: aSharedQueue.
    308 }}}
    309 
    310 
    311 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.
    312 
    313 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.
    314 
    315 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:
    316 
    317 {{{
    318 Transcript
    319    connect: myCounter
    320    signal: #valueChanged:
    321    toSelector: #show:
    322    queue: aSharedQueue
    323    blocking: true.
    324 }}}
    325 
    326 
    327 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.
    328 
    329 
    330 === Avoiding Deadlocks ===
    331 
    332 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:
    333 
    334 {{{
    335 [self signalWasSent "non-blocking"]
    336    whileFalse: [
    337       WorldState deferredUIMessages next value "blocking"].
    338 }}}
    339 
    340 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.
    341 
    342 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.
    343 
    344 
    345 === Waiting for Signals ===
    346 
    347 The `SignalSpy` can be used to wait for signals explicitly. Normally, it should be used in tests and not in the application itself:
    348 
    349 {{{
    350 spy := SignalSpy
    351    onSender: aSender
    352    signal: #signalEmitted.
    353 signal := spy waitForNextSignal.
    354 }}}
    355 
    356 
    357 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:`.
    358 
    359 If you want to test for some properties of the next signal besides its name, use `#waitForNextSignalSatisfying:`:
    360 
    361 {{{
    362 spy waitForNextSignalSatisfying: [:signal | |arguments|
    363    arguments := signal second.
    364    arguments first = #foobar].
    365 }}}
    366 
    367 
    368 The structre stored by the `SignalSpy` is an array with two fields: signal name and signal arguments.
    369 
    370 == Awareness ==
    371 
    372 There is an extension to the OmniBrowser Framework that shows a small icon in front of signals in the message list:
    373 
    374   [[Image(SignalsOmniBrowserMorphic.PNG, title="Signals have a custom icon in the message list.", nolink)]]
    375 
    376 Trying to load the Signals package into a Squeak image without OmniBrowser installed results in a warning that can be ignored safely.
    377 
    378 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:
    379 
    380 {{{
    381 self
    382    connect: aSender
    383    signal: #valueChanged: "Will be found."
    384    to: aReceiver
    385    selector: #value:. "Will be found."
    386 }}}
    387 
    388 
    389 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:
    390 
    391 {{{
    392 SystemNavigation default
    393    allConnectionsOn: #valueChanged:
    394    from: aClass. "Here is the connection code
    395                  somewhere."
    396    
    397 SystemNavigation default
    398    allSignalEmitsFrom: aSenderClass
    399    signal: #valueChanged:.
    400 }}}
    401 
    402 
    403 == Advanced Usage ==
    404 
    405 === Public Signals ===
    406 
    407 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:
    408 
    409 {{{
    410 MyCounter>>valueChanged: newValue
    411    self emitAlways.
    412 }}}
    413 
    414 Use them with caution because they could lead to less readable code.
    415 
    416 
    417 === Connect Signal to Signal ===
    418 
    419 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:
    420 
    421 {{{
    422 self
    423    connect: aSender
    424    signal: #valueChanged "Signal #1"
    425    toSelector: #switchToggled. "Signal #2 - Has to be public if sender <> receiver!"
    426 }}}
    427 
    428 
    429 === Automatic Connections ===
    430 
    431 It is possible to create methods that look like they want to be connected to a senders signal:
    432 
    433 {{{
    434 MyDialog>>onNameEditTextChanged: aText
    435    self availabilityLabel contents:
    436       (self checkAvailability: aText).
    437 }}}
    438 
    439 
    440 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:
    441 
    442 {{{
    443 MyDialog>>initialize
    444    super initialize.
    445    nameEdit := MyNameEdit new.
    446    self createAutoSignalConnections.
    447 }}}
    448 
    449 
    450 If the sender does not send this signal the connection will silently not be created.
    451 
    452 === Wrapped Signals ===
    453 
    454 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:
    455 
    456 {{{
    457 MyMorph>>position: aPosition
    458    super position: aPosition.
    459    self emitAlways. "Would have to be a public signal."
    460 }}}
    461 
    462 
    463 Or like this:
    464 
    465 {{{
    466 MyMorph>>position: aPosition
    467    super position: aPosition.
    468    self positionChanged: aPosition.
    469 
    470 MyMorph>>positionChanged: newPosition
    471    self emit.
    472 }}}
    473 
    474 
    475 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:
    476 
    477 {{{
    478 "Installs a method wrapper on #position:."
    479 self connect: aMorph signal: #position: to: Transcript selector: #show:.
    480 }}}
    481 
    482 
    483 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.
    484 
    485 === Advanced Patterns ===
    486 
    487 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:
    488 
    489 {{{
    490 self
    491    connect: anAction
    492    signal: #triggered
    493    toSelector: #logAction:
    494    pattern: #(0).
    495 
    496 self
    497    connect: anotherAction
    498    signal: #triggered
    499    toSelector: #logAction:
    500    pattern: #(0).
    501 }}}
    502 
    503 Patterns can have default values that will be send if the signal is processed:
    504 
    505 {{{
    506 "self changed: #counterValue."
    507 self
    508    connect: myCounter
    509    signal: #valueChanged:
    510    toSelector: #changed:
    511    pattern: #(=counterValue).
    512 }}}
    513 
    514 You can send whole objects as default value:
    515 
    516 {{{
    517 self
    518    connect: myCounter
    519    signal: #valueChanged:
    520    toSelector: #addMorphBack:
    521    pattern: {#=. Morph new}.
    522 }}}
    523 
    524 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.
    525 
    526 '''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.
    527 
    528 = How to Extend =
    529 
    530 == General Implementation Approach ==
    531 
    532  * one central repository `SignalConnectionsRepository` stores all connections in the system
    533  * `self emit` looks into the call-stack to retrieve arguments and do other checks
    534  * the repository uses ''weak'' data structures to not interfere with the garbage collector
    535 
    536 == Next Possible Steps ==
    537 
    538  * ''arguments processor'' to transform arguments before doing the callback
    539  * detect recursions on connection level to prevent endless loops, e.g., count and reset a number in each connection
    540  * Test-Runner coverage testing is broken because signals will not be emitted in the correct context and fail
    541 
    542543
    543544= Acknowledgments =
     
    546547
    547548[[Image(media/icons/silk:user.png, title="Target Group", nolink)]] To date the following contributors:
    548  * Marcel Täumel
     549 * Marcel Taeumel