Statement SYNC Foundation

Declares a method which is synchronized across any number of threads

Syntax
SYNC [CLASS] METHOD <MethodName>
Parameters
<MethodName>
<MethodName> is the identifier for the method to be declared.
Description

When an object is visible in multiple threads at the same time, the SYNC attribute makes sure that the program code of a method is executed entirely in one thread before another SYNC method of the same object can be executed in another thread. SYNC methods are automatically synchronized when executed by different threads. Therefore, when different SYNC methods are called for the same object (self) from multiple threads, they are executed in sequential order without being interrupted. Only one SYNC method can be executed using the same object (self) at any given point in time.

In multi-threaded programs, the SYNC attribute is necessary when a method performs multiple operations to achieve a consistent state of an object (self). This is the case, for example, when multiple instance variables are changed within a method and the object's consistency depends on all changes to be completed before the method is executed in another thread. The SYNC attribute guarantees that the program code of a method will run to completion within a single thread. No other thread can execute a SYNC method with the same object before the method has returned.

SYNC methods and Signals

There is one exception to the above stated rule. If thread A has suspended program execution of a SYNC method due to waiting for a signal, another SYNC method of the same object can be invoked in thread B. The suspended thread A will resume with method execution after being signalled only when the second SYNC method has run to completion in thread B (see the second example below).

Examples
SYNC attribute for methods
// A class for managing a queue is programmed in this example. 
// The queue is realized using an array that can be accessed 
// from multiple threads via the methods :put() and :get(). 
// Four threads are started in the Main() procedure which are 
// accessing the queue. If either method :put() or :get() is 
// declared without the SYNC attribute, a runtime error will 
// occur sooner or later because the Queue object will fall 
// into an inconsistent state. 

CLASS Queue 
   PROTECTED: 
   VAR aQueue 

   EXPORTED: 
   VAR nCount READONLY 

   INLINE METHOD init 
      ::aQueue := {} 
      ::nCount := 0 
   RETURN self 

   SYNC METHOD put, get 
ENDCLASS 

METHOD Queue:put( xValue ) 
   AAdd( ::aQueue, xValue ) 
   ::nCount ++ 
RETURN self 

METHOD Queue:get 
   LOCAL xReturn 

   IF ::nCount > 0 
      xReturn := ::aQueue[1] 
      ::nCount -- 
      ADel ( ::aQueue, 1 ) 
      ASize( ::aQueue, ::nCount ) 
   ENDIF 
RETURN xReturn 


** Code to test a Queue object 
PROCEDURE Main 
   LOCAL i, aT[4], oQueue := Queue():new() 
   CLS 

   FOR i:=1 TO 4             
      aT[i] := Thread():new()       // Create threads 
      aT[i]:start( "Test", oQueue ) // and start them 
   NEXT 

   FOR i:=1 TO 40000                // Write values 
      oQueue:put(i)                 // to queue 
   NEXT 

   ThreadWaitAll( aT )              // Wait until all threads 
RETURN                              // have terminated 


** Code runs simultaneously in 4 threads 
PROCEDURE Test( oQueue ) 
   LOCAL i, nRow, nCol 

   nRow := ThreadID() + 10 
   nCol := 0 
                                    // Read values 
   FOR i:=1 TO 10000                // from queue 
      DispOutAt( nRow, nCol+20, oQueue:nCount ) 
      DispOutAt( nRow, nCol+40, oQueue:get()  ) 
   NEXT 
RETURN 
Suspending a SYNC method
// The example demonstrates the program flow of two SYNC methods 
// when one SYNC method is suspended while waiting for a signal. 

  PROCEDURE Main 
     LOCAL oThreadA := MyThread():new() 
     LOCAL oThreadB := Thread():new() 

     oThreadA:start() 

     oThreadB:start( {|| oThreadA:signal() } ) 

     ThreadWaitAll( { oThreadA, oThreadB } )  

     AEval( oThreadA:aHistory, {|a| QOut( a[1], a[2] ) } ) 
  RETURN 


  CLASS MyThread FROM Thread 
     PROTECTED: 
     METHOD execute 

     EXPORTED: 
     VAR aHistory 
     VAR lTerminate 
     VAR oSyncSignal 

     SYNC METHOD add, signal 

     INLINE METHOD init 
        ::Thread:init() 
        ::aHistory    := {} 
        ::lTerminate  := .F. 
        ::oSyncSignal := Signal():new() 
     RETURN 
  ENDCLASS 


  METHOD MyThread:execute 
     Aadd( ::aHistory, { ThreadID(), "starting" } ) 

     DO WHILE .NOT. ::lTerminate 
        ::add() 
     ENDDO 

     Aadd( ::aHistory, { ThreadID(), "terminating" } ) 
     ::Quit() 
  RETURN self 


  METHOD MyThread:add 
     AAdd( ::aHistory, { ThreadId(), "suspended" } ) 
     ::oSyncSignal:Wait(0) 
     AAdd( ::aHistory, { ThreadId(), "signalled" } ) 
  RETURN self 


  METHOD MyThread:signal 
     AAdd( ::aHistory, { ThreadId(), "before signal" } ) 
     ::oSyncSignal:signal() 
     AAdd( ::aHistory, { ThreadId(), "after signal" } ) 

     ::lTerminate := .T. 

     AAdd( ::aHistory, { ThreadId(), "sleep 2 seconds" } ) 
     Sleep(200) 
     AAdd( ::aHistory, { ThreadId(), "terminating"} ) 
  RETURN self 
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.