Changes between Version 4 and Version 5 of signals
- Timestamp:
- 11/22/2010 01:54:37 PM (13 years ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
signals
v4 v5 134 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 135 136 If you want to use ''wrapped signals'', you should have installed [[ PUBLIC:MethodWrappers|Method Wrappers]] first.136 If you want to use ''wrapped signals'', you should have installed [[wiki:methodwrappers Method Wrappers]] first. 137 137 138 138 After the installation has finished, start the TestRunner and run all tests you find in ''Signals-Tests''. They should all pass. … … 158 158 A signal is an object's method that is able to trigger callbacks. It is implemented like this: 159 159 160 <pre> 160 {{{ 161 161 MyCounter>>valueChanged: newValue 162 162 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 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. 166 166 167 167 Obviously, a signal is emitted by calling the method that is meant to be the signal: 168 168 169 <pre> 169 {{{ 170 170 self valueChanged: 42. 171 </pre> 171 }}} 172 172 173 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>: 174 174 175 <pre> 175 {{{ 176 176 myCounter valueChanged: 42. 177 </pre> 177 }}} 178 178 179 179 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. … … 181 181 See the ''Advanced Usage'' section and read about ''public signals'' if you want to bypass this constraint for some reason. 182 182 183 == = Basic Connections ===183 == Basic Connections == 184 184 185 185 The simplest form of a connection is created from within the UI process of Squeak, e.g., using the workspace, in the following way: 186 186 187 <pre> 187 {{{ 188 188 self 189 189 connect: myCounter … … 191 191 to: Transcript 192 192 selector: #show:. 193 </pre> 193 }}} 194 194 195 195 196 This more or less mediator approach can be changed if an object itself connects to a signal: 196 197 197 <pre> 198 {{{ 198 199 self 199 200 connect: myCounter 200 201 signal: #valueChanged: 201 202 toSelector: #show:. 202 </pre> 203 204 === Basic Signal Processing === 203 }}} 204 205 206 == Basic Signal Processing == 205 207 206 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: 207 209 208 <pre> 210 {{{ 209 211 "The signal." 210 212 MyMorph>>mouseDownReceived: evt … … 214 216 MyMorph>>mouseDown: evt 215 217 self mouseDownReceived: evt. 216 </pre> 218 }}} 219 217 220 218 221 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. 219 222 220 == = Arguments and Patterns ===223 == Arguments and Patterns == 221 224 222 225 Each callback should expect at most arguments as the signal contains. By default, trailing arguments will be truncated and ignored: 223 226 224 <pre> 227 {{{ 225 228 "Works." 226 229 self connect: s signal: #a: toSelector: #b:. … … 229 232 "Does not work." 230 233 self connect: s signal: #a toSelector: #b:. 231 </pre> 234 }}} 232 235 233 236 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: 234 237 235 <pre> 238 {{{ 236 239 self 237 240 connect: aSender … … 245 248 toSelector: #d:e:f: 246 249 pattern: #(4 1). "Error!" 247 </pre> 250 }}} 248 251 249 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>. 250 253 251 == = Disconnection ===254 == Disconnection == 252 255 253 256 A connection can be removed from the sender: 254 257 255 <pre> 258 {{{ 256 259 self "the sender" 257 260 disconnectSignal: #valueChanged: 258 261 from: aReceiver 259 262 selector: #processValue:. 260 </pre> 263 }}} 264 261 265 262 266 You can also remove all connections from a specific receiver, all connections from a specific signal and any connection that was ever created. … … 264 268 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. 265 269 266 === Using Processes === 270 271 == Using Processes == 267 272 268 273 The connection and disconnection of signals is thread-safe. … … 272 277 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''. 273 278 274 ==== Queued Connections ==== 279 280 === Queued Connections === 275 281 276 282 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! 277 283 278 <pre> 284 {{{ 279 285 "Here: Make you data structures thread-safe!" 280 286 [anObject … … 282 288 signal: #valueChanged: 283 289 toSelector: #processValue:] fork. 284 </pre> 290 }}} 291 285 292 286 293 To encounter this problem, connections can be queued: 287 294 288 <pre> 295 {{{ 289 296 Transcript 290 297 connect: myCounter … … 292 299 toSelector: #show: 293 300 queue: aSharedQueue. 294 </pre> 301 }}} 302 295 303 296 304 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. 297 305 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.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. 299 307 300 308 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: 301 309 302 <pre> 310 {{{ 303 311 Transcript 304 312 connect: myCounter … … 307 315 queue: aSharedQueue 308 316 blocking: true. 309 </pre> 317 }}} 318 310 319 311 320 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. 312 321 313 ==== Avoiding Deadlocks ==== 322 323 === Avoiding Deadlocks === 314 324 315 325 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: 316 326 317 <pre> 327 {{{ 318 328 [self signalWasSent "non-blocking"] 319 329 whileFalse: [ 320 330 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 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. 324 334 325 335 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. 326 336 337 327 338 === Waiting for Signals === 328 339 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> 340 The ''`SignalSpy`'' can be used to wait for signals explicitly. Normally, it should be used in tests and not in the application itself: 341 342 {{{ 332 343 spy := SignalSpy 333 344 onSender: aSender 334 345 signal: #signalEmitted. 335 346 signal := 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 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:`'': 353 354 {{{ 343 355 spy waitForNextSignalSatisfying: [:signal | |arguments| 344 356 arguments := signal second. 345 357 arguments first = #foobar]. 346 </pre> 358 }}} 359 347 360 348 361 The structre stored by the <code>SignalSpy</code> is an array with two fields: signal name and signal arguments. 349 362 350 == = Awareness ===363 == Awareness == 351 364 352 365 There is an extension to the OmniBrowser Framework that shows a small icon in front of signals in the message list: … … 358 371 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: 359 372 360 <pre> 373 {{{ 361 374 self 362 375 connect: aSender … … 364 377 to: aReceiver 365 378 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 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: 383 384 {{{ 371 385 SystemNavigation default 372 386 allConnectionsOn: #valueChanged: … … 377 391 allSignalEmitsFrom: aSenderClass 378 392 signal: #valueChanged:. 379 </pre> 380 381 === Advanced Usage === 382 383 ==== Public Signals ==== 393 }}} 394 395 396 == Advanced Usage == 397 398 === Public Signals === 384 399 385 400 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: 386 401 387 <pre> 402 {{{ 388 403 MyCounter>>valueChanged: newValue 389 404 self emitAlways. 390 </pre> 405 }}} 391 406 392 407 Use them with caution because they could lead to less readable code. 393 408 394 ==== Connect Signal to Signal ==== 409 410 === Connect Signal to Signal === 395 411 396 412 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: 397 413 398 <pre> 414 {{{ 399 415 self 400 416 connect: aSender 401 417 signal: #valueChanged "Signal #1" 402 418 toSelector: #switchToggled. "Signal #2 - Has to be public if sender <> receiver!" 403 </pre> 404 405 ==== Automatic Connections ==== 419 }}} 420 421 422 === Automatic Connections === 406 423 407 424 It is possible to create methods that look like they want to be connected to a senders signal: 408 425 409 <pre> 426 {{{ 410 427 MyDialog>>onNameEditTextChanged: aText 411 428 self availabilityLabel contents: 412 429 (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 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: 434 435 {{{ 418 436 MyDialog>>initialize 419 437 super initialize. 420 438 nameEdit := MyNameEdit new. 421 439 self createAutoSignalConnections. 422 </pre> 440 }}} 441 423 442 424 443 If the sender does not send this signal the connection will silently not be created. 425 444 426 === = Wrapped Signals ====445 === Wrapped Signals === 427 446 428 447 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: 429 448 430 <pre> 449 {{{ 431 450 MyMorph>>position: aPosition 432 451 super position: aPosition. 433 452 self emitAlways. "Would have to be a public signal." 434 </pre> 453 }}} 454 435 455 436 456 Or like this: 437 457 438 <pre> 458 {{{ 439 459 MyMorph>>position: aPosition 440 460 super position: aPosition. … … 443 463 MyMorph>>positionChanged: newPosition 444 464 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 468 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 [[wiki:methodwrappers Method Wrappers]] as well: 469 470 {{{ 450 471 "Installs a method wrapper on #position:." 451 472 self connect: aMorph signal: #position: to: Transcript selector: #show:. 452 </pre> 473 }}} 474 453 475 454 476 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. 455 477 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 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: 481 482 {{{ 461 483 self 462 484 connect: anAction … … 470 492 toSelector: #logAction: 471 493 pattern: #(0). 472 </pre> 494 }}} 473 495 474 496 Patterns can have default values that will be send if the signal is processed: 475 497 476 <pre> 498 {{{ 477 499 "self changed: #counterValue." 478 500 self … … 481 503 toSelector: #changed: 482 504 pattern: #(=counterValue). 483 </pre> 505 }}} 484 506 485 507 You can send whole objects as default value: 486 508 487 <pre> 509 {{{ 488 510 self 489 511 connect: myCounter … … 491 513 toSelector: #addMorphBack: 492 514 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 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. 496 518 497 519 '''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. 498 520 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 system504 * " <code>self emit</code>" looks into the call-stack to retrieve arguments and do other checks521 = 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 505 527 * the repository uses ''weak'' data structures to not interfere with the garbage collector 506 528 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 = 514 537 515 538 The signals mechanism was inspired by the signals/slots concept in the [http://qt.nokia.com Nokia Qt Framework].