Class Signal() Foundation

Class function of the Signal class.

Description

Signal objects are used to control program flow within one or more threads from one other thread. To achieve this, it is necessary that one and the same Signal object is visible in at least two threads. Therefore, Signal objects can only be used in conjunction with Thread objects. In other words, at least one additional Thread object must exist in an application for the Signal class to become useful (note: the Main procedure is executed by an implicit Thread object).

There are two methods in the Signal class: :signal() and :wait(). The method :signal() triggers a signal, while the :wait() method causes a thread to enter a wait state. If a thread executes the :wait()method, it waits for the signal and suspends program execution. The thread resumes as soon as a second thread executes the :signal()method with the same Signal object.

With the aid of Signal objects it becomes possible for the current thread to control program flow in other threads. Each thread which executes the :wait() method enters a wait state until another thread executes the :signal() method.

Multiple Signal objects can be used to control program execution in multiple threads. Since a Signal object limits program control in threads to a wait state, so-called "dead lock" situations can occur when multiple Signal objects are used. A dead lock is reached when thread A waits for a signal from thread B, while, at the same time, thread B waits for a signal from thread A.

Class methods
:new()
Creates an instance of the Signal class.
Instance variables
:cargo
Instance variable for ad-hoc use.
Methods
:signal()
Triggers a signal for other threads.
:wait()
Waits until another thread triggers the signal
Examples
Simple screen saver

// A simple screen saver for text mode applications is programmed 
// in this example. The code for the screen saver runs in 
// a separate thread which waits to be signalled for a maximum of 
// 5 seconds. If no signal is triggered, the screen saver is 
// activated. The thread is signalled from the Main procedure 
// each time an event (keyboard, mouse) is taken from the 
// event queue. 

#include "AppEvent.ch" 

PROCEDURE Main 
   LOCAL nEvent, oThread, oSignal 

   ? "Press a key. ESC quits" 

   oThread := Thread():new() 
   oSignal := Signal():new() 

   oThread:start( "ScreenSaver", oSignal, 5 ) 

   DO WHILE nEvent <> xbeK_ESC 
      nEvent := AppEvent(,,,0) 
      oSignal:signal() 

      ? "Event =", nEvent 
   ENDDO 
RETURN 

** Procedure runs in separate thread 
PROCEDURE ScreenSaver( oSignal, nSeconds ) 
   LOCAL cScreen, cTemp 

   DO WHILE .T. 

      IF .NOT. oSignal:wait( nSeconds * 100 ) 
         // This code is executed if no signal is triggered 
         // for more than <nSeconds> seconds 
         cScreen := SaveScreen( 0,0, MaxRow(), MaxCol() ) 
         cTemp   := SubStr( cScreen, 2 ) + Left( cScreen, 1 ) 

         // Change screen until thread is signalled 
         DO WHILE .NOT. oSignal:wait(10) 
            cTemp := SubStr( cTemp, 3 ) + Left( cTemp, 2 ) 
            RestScreen( 0,0, MaxRow(), MaxCol(), cTemp ) 
         ENDDO 

         RestScreen( 0,0, MaxRow(), MaxCol(), cScreen ) 
         cScreen := ; 
         cTemp   := NIL 
      ENDIF 

   ENDDO 
RETURN 
Controlling Append in two threads using signals

// A new database is created in this example and records from 
// an existing database are appended. An array is used as a 
// read/write buffer to transfer data from the existing to the 
// new database. Reading and writing data runs simultaneously 
// in two threads. Two Signal objects coordinate which thread 
// may read and which one may write. A third signal causes 
// the second thread to terminate. 

PROCEDURE Main 
   LOCAL oReadDone, oWriteDone, oDoExit, oThread, aValues 

   USE LargeDB 

   DbCreate( "Test", DbStruct() ) 

   oThread     := Thread():new() 
   aValues     := Array( FCount() ) 

   oReadDone   := Signal():new() 
   oWriteDone  := Signal():new() 
   oDoExit     := Signal():new() 


   oThread:start( "AppendTo", "Test"    , aValues , ; 
                   oReadDone, oWriteDone, oDoExit ) 
   oWriteDone:wait()           // Wait until 2nd database 
                               // is open 

   DO WHILE .T.                // Transfer record to array 
      Aeval( aValues, {|x,i| x:=FieldGet(i) },,, .T. ) 

      IF ! Eof() 
         oReadDone:signal()    // 1st signal: record is read 
         SKIP 
      ELSE 
         oDoExit:signal()      // 2nd signal: terminate thread #2 
         EXIT 
      ENDIF 

      oWriteDone:wait()        // Wait until data is written 
   ENDDO                       // to new database 

   oThread:synchronize(0) 

   USE 
RETURN 


** Procedure runs in separate thread 
PROCEDURE AppendTo( cDbFile, aValues, ; 
                    oReadDone, oWriteDone, oDoExit ) 

   USE (cDbFile) EXCLUSIVE 
   oWriteDone:signal()         // Signal: database is open 

   DO WHILE .T. 

      IF oReadDone:wait(10)    // Check if record is read 

         DbAppend()            // Write data 
         AEval( aValues, {|x,i| FieldPut(i,x) } ) 
         oWriteDone:signal()   // Signal: Data is written 

      ELSEIF oDoExit:wait(10)  // Got an Exit signal? 
         EXIT 
      ENDIF 

   ENDDO 

   USE 
RETURN 
Feedback

If you see anything in the documentation that is not correct, does not match your experience with the particular feature or requires further clarification, please use this form to report a documentation issue.