Changes between Version 5 and Version 6 of signals
- Timestamp:
- 11/22/2010 02:51:01 PM (13 years ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
signals
v5 v6 1 [[PageOutline(1-3)]] 2 1 3 = Introduction = 2 4 … … 6 8 === Object Dependents === 7 9 8 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:`''.10 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:`. 9 11 10 12 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. 11 13 12 ''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.14 ''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. 13 15 14 16 === Object Events === … … 47 49 === Morphic Callbacks === 48 50 49 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.51 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. 50 52 51 53 This approach is only limited to these standard events and cannot be used so create arbitrary connections. … … 69 71 || Events are defined for a specific class/subclass || || || || || ? || ● || 70 72 || Bindings are at Event level || ● || ● || || ● || ? || ● || 71 || Bindings are at Sender/Receiver level || || || ● || || ? || ||73 || Bindings are at !Sender/Receiver level || || || ● || || ? || || 72 74 || Events are first-class objects || || ● || || || ? || || 73 || Automatic truncation of arguments || 75 || Automatic truncation of arguments || ● || || || ● || ? || ● || 74 76 || Explicit argument count check at binding-setup-time || || || || || ? || ● || 75 77 || Explicit argument count check at event-trigger-time || || ● || || || ? || || … … 80 82 || Event triggering can be limited to the sender || || || || || ? || ● || 81 83 || Provides synchronisation mechansism for thread-safety || || || || || ? || ● || 82 || Explicit Event/Callback check at binding-setup-time || || || || || ? || ● ||84 || Explicit !Event/Callback check at binding-setup-time || || || || || ? || ● || 83 85 84 86 … … 89 91 One sender was bound to 10000 receivers. Then one event was triggered and processed synchronously. The benchmark code is in the ''Signals'' package. 90 92 91 '' Test-System:'' Core2Duo @ 2.54 GHz, 4096 MB DDR2 RAM, Windows 7 Professional, Squeak 4.193 '''Test-System:''' Core2Duo @ 2.54 GHz, 4096 MB DDR2 RAM, Windows 7 Professional, Squeak 4.1 92 94 93 95 || || Message[[br]]Sends || Announce-[[br]]ments || Object[[br]]Dependents || Object[[br]]Events || Bindings || Signals || … … 96 98 97 99 98 == = Terms ===100 == Terms == 99 101 100 102 A ''signal'' is a method that can be called to trigger a callback. Then the signal will be ''emitted''. … … 103 105 104 106 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. 107 108 = How to Install = 105 109 106 110 {{{ … … 109 113 || [[Image(media/icons/custom:squeak_16.png, title="Recommended Squeak Version", nolink, right)]] || 4.1, 4.2 Alpha || 110 114 || [[Image(media/icons/silk:application_home.png, title="Recommended Squeak VM Version", nolink, right)]] || 4.0.2 (Win), ? (Mac) || 111 || [[Image(media/icons/silk:cog.png, title="Recommended Cog VM Version", nolink, right)]] || ''not supported'' ||115 || [[Image(media/icons/silk:cog.png, title="Recommended Cog VM Version", nolink, right)]] || ''not tested with cog'' || 112 116 ||'''Sources'''|| || 113 || [[Image(media/icons/silk:script_gear.png, title="Metacello Configuration", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/MetacelloRepository/ YourConfiguration YourConfiguration] ||117 || [[Image(media/icons/silk:script_gear.png, title="Metacello Configuration", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/MetacelloRepository/ ConfigurationOfSignals] || 114 118 || [[Image(media/icons/silk:database.png, title="Repository", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/SwaUtilities/ SwaUtilities] || 115 119 116 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || ''`Signals`''||117 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || ''`SI-Wrapper`''(optional) ||118 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || ''`SI-OB-Morphic`''(optional) ||119 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || ''`SI-Reflection`''(optional) ||120 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || ''`SI-Benchmarks`''(optional) ||121 122 || [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [http://www.squeaksource.com/OmniBrowser.html OmniBrowser] (opti nal) ||120 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `Signals` || 121 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-Wrapper` (optional) || 122 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-OB-Morphic` (optional) || 123 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-Reflection` (optional) || 124 || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || `SI-Benchmarks` (optional) || 125 126 || [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [http://www.squeaksource.com/OmniBrowser.html OmniBrowser] (optional) || 123 127 || [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [wiki:methodwrappers Method Wrappers] (optional) || 124 128 || [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [http://www.squeaksource.com/AXAnnouncements.html AXAnnouncements] (optional) || … … 130 134 Just load the ''Signals'' package from the Monticello HTTP repository. 131 135 132 If you do not have OmniBrowserinstalled, just skip the ''SI-OB-Morphic'' package. You will not have a visual indicator in front of your method list in the code browser.133 134 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.135 136 If you want to use ''wrapped signals'', you should have installed [ [wiki:methodwrappers Method Wrappers]] first.137 138 After the installation has finished, start the Test Runner and run all tests you find in ''Signals-Tests''. They should all pass.136 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. 137 138 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. 139 140 If you want to use ''wrapped signals'', you should have installed [wiki:methodwrappers Method Wrappers] first. 141 142 After the installation has finished, start the Test-Runner and run all tests you find in ''Signals-Tests''. They should all pass. 139 143 140 144 The ''Signals'' package contains several sub-packages: 141 145 142 * ''`Signals-Core`''... main signals implementation143 * ''`Signals-Tests`''... tests for the core implementation144 * ''`SI-OB-Morphic`'' ... morphic extensions to OmniBrowser145 * ''`SI-Benchmarks`''... benchmmarks for Signals and other mechanisms, e.g., Announcements146 * ''`SI-Wrapper`''... turns each message into a signal using method wrappers147 * ''`SI-Reflection`''... search and browse signals in the image146 * `Signals-Core` ... main signals implementation 147 * `Signals-Tests` ... tests for the core implementation 148 * `SI-OB-Morphic` ... morphic extensions to `OmniBrowser` 149 * `SI-Benchmarks` ... benchmmarks for Signals and other mechanisms, e.g., Announcements 150 * `SI-Wrapper` ... turns each message into a signal using method wrappers 151 * `SI-Reflection` ... search and browse signals in the image 148 152 149 153 {{{ … … 163 167 }}} 164 168 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 ''`self emit`''not inside a method context but a block context works as well.169 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. 166 170 167 171 Obviously, a signal is emitted by calling the method that is meant to be the signal: … … 171 175 }}} 172 176 173 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>:177 You '''must not''' emit an object's signal from the outside. The following fails if not executed within the same instance as `myCounter`: 174 178 175 179 {{{ … … 206 210 == Basic Signal Processing == 207 211 208 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:212 In the simplest way, a signal is emitted from within the UI process of Squeak, e.g., in response to a Morphic `#mouseDown:` event: 209 213 210 214 {{{ … … 250 254 }}} 251 255 252 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>.256 The automatic truncation can be used to avoid patterns like `#(1 2)` or `#()`. Arguments can be duplicated, i.e., `#(1 1)`. 253 257 254 258 == Disconnection == … … 304 308 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. 305 309 306 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.310 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. 307 311 308 312 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: … … 331 335 }}} 332 336 333 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.337 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. 334 338 335 339 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. … … 338 342 === Waiting for Signals === 339 343 340 The ''`SignalSpy`''can be used to wait for signals explicitly. Normally, it should be used in tests and not in the application itself:344 The `SignalSpy` can be used to wait for signals explicitly. Normally, it should be used in tests and not in the application itself: 341 345 342 346 {{{ … … 348 352 349 353 350 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:`''.351 352 If you want to test for some properties of the next signal besides its name, use ''`#waitForNextSignalSatisfying:`'':354 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:`. 355 356 If you want to test for some properties of the next signal besides its name, use `#waitForNextSignalSatisfying:`: 353 357 354 358 {{{ … … 359 363 360 364 361 The structre stored by the <code>SignalSpy</code>is an array with two fields: signal name and signal arguments.365 The structre stored by the `SignalSpy` is an array with two fields: signal name and signal arguments. 362 366 363 367 == Awareness == … … 365 369 There is an extension to the OmniBrowser Framework that shows a small icon in front of signals in the message list: 366 370 367 : [[Image:SignalsOmniBrowserMorphic.PNG]]371 [[Image(SignalsOmniBrowserMorphic.PNG, title="Signals have a custom icon in the message list.", nolink)]] 368 372 369 373 Trying to load the Signals package into a Squeak image without OmniBrowser installed results in a warning that can be ignored safely. … … 380 384 381 385 382 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:386 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: 383 387 384 388 {{{ … … 431 435 432 436 433 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:437 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: 434 438 435 439 {{{ … … 466 470 467 471 468 To avoid this, it is possible to use '''any arbitrary message''' as a signal as long has the sub-package ''S ignals-Wrapper'' is installed which needs the package/project [[wiki:methodwrappers Method Wrappers]] as well:472 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: 469 473 470 474 {{{ … … 478 482 === Advanced Patterns === 479 483 480 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:484 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: 481 485 482 486 {{{ … … 515 519 }}} 516 520 517 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.521 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. 518 522 519 523 '''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. … … 523 527 == General Implementation Approach == 524 528 525 * one central repository ''`SignalConnectionsRepository`''stores all connections in the system526 * "''`self emit`''"looks into the call-stack to retrieve arguments and do other checks527 * the repository uses ''weak'' data structures to not interfere with the garbage collector529 * one central repository `SignalConnectionsRepository` stores all connections in the system 530 * `self emit` looks into the call-stack to retrieve arguments and do other checks 531 * the repository uses ''weak'' data structures to not interfere with the garbage collector 528 532 529 533 == Next Possible Steps == … … 531 535 * ''arguments processor'' to transform arguments before doing the callback 532 536 * detect recursions on connection level to prevent endless loops, e.g., count and reset a number in each connection 533 * Test Runner coverage testing is broken because signals will not be emitted in the correct context and fail537 * Test-Runner coverage testing is broken because signals will not be emitted in the correct context and fail 534 538 535 539