Changes between Version 4 and Version 5 of signals


Ignore:
Timestamp:
11/22/2010 01:54:37 PM (13 years ago)
Author:
tobias.mohr
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • signals

    v4 v5  
    134134There 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.
    135135
    136 If you want to use ''wrapped signals'', you should have installed [[PUBLIC:MethodWrappers|Method Wrappers]] first.
     136If you want to use ''wrapped signals'', you should have installed [[wiki:methodwrappers Method Wrappers]] first.
    137137
    138138After the installation has finished, start the TestRunner and run all tests you find in ''Signals-Tests''. They should all pass.
     
    158158A signal is an object's method that is able to trigger callbacks. It is implemented like this:
    159159
    160 <pre>
     160{{{
    161161MyCounter>>valueChanged: newValue
    162162   self emit.
    163 </pre>
    164 
    165 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.
     163}}}
     164
     165Each 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.
    166166
    167167Obviously, a signal is emitted by calling the method that is meant to be the signal:
    168168
    169 <pre>
     169{{{
    170170self valueChanged: 42.
    171 </pre>
     171}}}
    172172
    173173You '''must not''' emit an object's signal from the outside. The following fails if not executed within the same instance as <code>myCounter</code>:
    174174
    175 <pre>
     175{{{
    176176myCounter valueChanged: 42.
    177 </pre>
     177}}}
    178178
    179179One 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.
     
    181181See the ''Advanced Usage'' section and read about ''public signals'' if you want to bypass this constraint for some reason.
    182182
    183 === Basic Connections ===
     183== Basic Connections ==
    184184
    185185The simplest form of a connection is created from within the UI process of Squeak, e.g., using the workspace, in the following way:
    186186
    187 <pre>
     187{{{
    188188self
    189189   connect: myCounter
     
    191191   to: Transcript
    192192   selector: #show:.
    193 </pre>
     193}}}
     194
    194195
    195196This more or less mediator approach can be changed if an object itself connects to a signal:
    196197
    197 <pre>
     198{{{
    198199self
    199200   connect: myCounter
    200201   signal: #valueChanged:
    201202   toSelector: #show:.
    202 </pre>
    203 
    204 === Basic Signal Processing ===
     203}}}
     204
     205
     206== Basic Signal Processing ==
    205207
    206208In 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:
    207209
    208 <pre>
     210{{{
    209211"The signal."
    210212MyMorph>>mouseDownReceived: evt
     
    214216MyMorph>>mouseDown: evt
    215217   self mouseDownReceived: evt.
    216 </pre>
     218}}}
     219
    217220
    218221Having this, the signal is processed synchronously and blocking, which means that all callbacks were made after returning from the call that emitted the signal.
    219222
    220 === Arguments and Patterns ===
     223== Arguments and Patterns ==
    221224
    222225Each callback should expect at most arguments as the signal contains. By default, trailing arguments will be truncated and ignored:
    223226
    224 <pre>
     227{{{
    225228"Works."
    226229self connect: s signal: #a: toSelector: #b:.
     
    229232"Does not work."
    230233self connect: s signal: #a toSelector: #b:.
    231 </pre>
     234}}}
    232235
    233236It 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:
    234237
    235 <pre>
     238{{{
    236239self
    237240   connect: aSender
     
    245248   toSelector: #d:e:f:
    246249   pattern: #(4 1). "Error!"
    247 </pre>
     250}}}
    248251
    249252The 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>.
    250253
    251 === Disconnection ===
     254== Disconnection ==
    252255
    253256A connection can be removed from the sender:
    254257
    255 <pre>
     258{{{
    256259self "the sender"
    257260   disconnectSignal: #valueChanged:
    258261   from: aReceiver
    259262   selector: #processValue:.
    260 </pre>
     263}}}
     264
    261265
    262266You can also remove all connections from a specific receiver, all connections from a specific signal and any connection that was ever created.
     
    264268Although, 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.
    265269
    266 === Using Processes ===
     270
     271== Using Processes ==
    267272
    268273The connection and disconnection of signals is thread-safe.
     
    272277When 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''.
    273278
    274 ==== Queued Connections ====
     279
     280=== Queued Connections ===
    275281
    276282Sometimes 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!
    277283
    278 <pre>
     284{{{
    279285"Here: Make you data structures thread-safe!"
    280286[anObject
     
    282288   signal: #valueChanged:
    283289   toSelector: #processValue:] fork.
    284 </pre>
     290}}}
     291
    285292
    286293To encounter this problem, connections can be queued:
    287294
    288 <pre>
     295{{{
    289296Transcript
    290297   connect: myCounter
     
    292299   toSelector: #show:
    293300   queue: aSharedQueue.
    294 </pre>
     301}}}
     302
    295303
    296304Using 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.
    297305
    298 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.
     306The 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.
    299307
    300308Any queued connection can be blocking which means that the signal emitting process will be suspended until the callback waiting in the queue is processed:
    301309
    302 <pre>
     310{{{
    303311Transcript
    304312   connect: myCounter
     
    307315   queue: aSharedQueue
    308316   blocking: true.
    309 </pre>
     317}}}
     318
    310319
    311320Of 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.
    312321
    313 ==== Avoiding Deadlocks ====
     322
     323=== Avoiding Deadlocks ===
    314324
    315325When 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:
    316326
    317 <pre>
     327{{{
    318328[self signalWasSent "non-blocking"]
    319329   whileFalse: [
    320330      WorldState deferredUIMessages next value "blocking"].
    321 </pre>
    322 
    323 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.
     331}}}
     332
     333This 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.
    324334
    325335If 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.
    326336
     337
    327338=== Waiting for Signals ===
    328339
    329 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:
    330 
    331 <pre>
     340The ''`SignalSpy`'' can be used to wait for signals explicitly. Normally, it should be used in tests and not in the application itself:
     341
     342{{{
    332343spy := SignalSpy
    333344   onSender: aSender
    334345   signal: #signalEmitted.
    335346signal := spy waitForNextSignal.
    336 </pre>
    337 
    338 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>.
    339 
    340 If you want to test for some properties of the next signal besides its name, use <code>#waitForNextSignalSatisfying:</code>:
    341 
    342 <pre>
     347}}}
     348
     349
     350The 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:`''.
     351
     352If you want to test for some properties of the next signal besides its name, use ''`#waitForNextSignalSatisfying:`'':
     353
     354{{{
    343355spy waitForNextSignalSatisfying: [:signal | |arguments|
    344356   arguments := signal second.
    345357   arguments first = #foobar].
    346 </pre>
     358}}}
     359
    347360
    348361The structre stored by the <code>SignalSpy</code> is an array with two fields: signal name and signal arguments.
    349362
    350 === Awareness ===
     363== Awareness ==
    351364
    352365There is an extension to the OmniBrowser Framework that shows a small icon in front of signals in the message list:
     
    358371A 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:
    359372
    360 <pre>
     373{{{
    361374self
    362375   connect: aSender
     
    364377   to: aReceiver
    365378   selector: #value:. "Will be found."
    366 </pre>
    367 
    368 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:
    369 
    370 <pre>
     379}}}
     380
     381
     382Unfortunately 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:
     383
     384{{{
    371385SystemNavigation default
    372386   allConnectionsOn: #valueChanged:
     
    377391   allSignalEmitsFrom: aSenderClass
    378392   signal: #valueChanged:.
    379 </pre>
    380 
    381 === Advanced Usage ===
    382 
    383 ==== Public Signals ====
     393}}}
     394
     395
     396== Advanced Usage ==
     397
     398=== Public Signals ===
    384399
    385400To 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:
    386401
    387 <pre>
     402{{{
    388403MyCounter>>valueChanged: newValue
    389404   self emitAlways.
    390 </pre>
     405}}}
    391406
    392407Use them with caution because they could lead to less readable code.
    393408
    394 ==== Connect Signal to Signal ====
     409
     410=== Connect Signal to Signal ===
    395411
    396412Signals 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:
    397413
    398 <pre>
     414{{{
    399415self
    400416   connect: aSender
    401417   signal: #valueChanged "Signal #1"
    402418   toSelector: #switchToggled. "Signal #2 - Has to be public if sender <> receiver!"
    403 </pre>
    404 
    405 ==== Automatic Connections ====
     419}}}
     420
     421
     422=== Automatic Connections ===
    406423
    407424It is possible to create methods that look like they want to be connected to a senders signal:
    408425
    409 <pre>
     426{{{
    410427MyDialog>>onNameEditTextChanged: aText
    411428   self availabilityLabel contents:
    412429      (self checkAvailability: aText).
    413 </pre>
    414 
    415 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:
    416 
    417 <pre>
     430}}}
     431
     432
     433Such 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:
     434
     435{{{
    418436MyDialog>>initialize
    419437   super initialize.
    420438   nameEdit := MyNameEdit new.
    421439   self createAutoSignalConnections.
    422 </pre>
     440}}}
     441
    423442
    424443If the sender does not send this signal the connection will silently not be created.
    425444
    426 ==== Wrapped Signals ====
     445=== Wrapped Signals ===
    427446
    428447Sometimes 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:
    429448
    430 <pre>
     449{{{
    431450MyMorph>>position: aPosition
    432451   super position: aPosition.
    433452   self emitAlways. "Would have to be a public signal."
    434 </pre>
     453}}}
     454
    435455
    436456Or like this:
    437457
    438 <pre>
     458{{{
    439459MyMorph>>position: aPosition
    440460   super position: aPosition.
     
    443463MyMorph>>positionChanged: newPosition
    444464   self emit.
    445 </pre>
    446 
    447 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:
    448 
    449 <pre>
     465}}}
     466
     467
     468To 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 [[wiki:methodwrappers Method Wrappers]] as well:
     469
     470{{{
    450471"Installs a method wrapper on #position:."
    451472self connect: aMorph signal: #position: to: Transcript selector: #show:.
    452 </pre>
     473}}}
     474
    453475
    454476There 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.
    455477
    456 ==== Advanced Patterns ====
    457 
    458 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:
    459 
    460 <pre>
     478=== Advanced Patterns ===
     479
     480It 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:
     481
     482{{{
    461483self
    462484   connect: anAction
     
    470492   toSelector: #logAction:
    471493   pattern: #(0).
    472 </pre>
     494}}}
    473495
    474496Patterns can have default values that will be send if the signal is processed:
    475497
    476 <pre>
     498{{{
    477499"self changed: #counterValue."
    478500self
     
    481503   toSelector: #changed:
    482504   pattern: #(=counterValue).
    483 </pre>
     505}}}
    484506
    485507You can send whole objects as default value:
    486508
    487 <pre>
     509{{{
    488510self
    489511   connect: myCounter
     
    491513   toSelector: #addMorphBack:
    492514   pattern: {#=. Morph new}.
    493 </pre>
    494 
    495 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.
     515}}}
     516
     517Patterns 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.
    496518
    497519'''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.
    498520
    499 == [[Image:Icons_silk_wrench.png]] How to Extend ==
    500 
    501 === General Implementation Approach ===
    502 
    503 * one central repository <code>SignalConnectionsRepository</code> stores all connections in the system
    504 * "<code>self emit</code>" looks into the call-stack to retrieve arguments and do other checks
     521= How to Extend =
     522
     523== General Implementation Approach ==
     524
     525* one central repository ''`SignalConnectionsRepository`'' stores all connections in the system
     526* "''`self emit`''" looks into the call-stack to retrieve arguments and do other checks
    505527* the repository uses ''weak'' data structures to not interfere with the garbage collector
    506528
    507 === Next Possible Steps ===
    508 
    509 * ''arguments processor'' to transform arguments before doing the callback
    510 * detect recursions on connection level to prevent endless loops, e.g., count and reset a number in each connection
    511 * TestRunner coverage testing is broken because signals will not be emitted in the correct context and fail
    512 
    513 == [[Image:Icons_silk_star.png]] Acknowledgments ==
     529== Next Possible Steps ==
     530
     531 * ''arguments processor'' to transform arguments before doing the callback
     532 * detect recursions on connection level to prevent endless loops, e.g., count and reset a number in each connection
     533 * TestRunner coverage testing is broken because signals will not be emitted in the correct context and fail
     534
     535
     536= Acknowledgments =
    514537
    515538The signals mechanism was inspired by the signals/slots concept in the [http://qt.nokia.com Nokia Qt Framework].