Class Signal() Foundation

Signal class to manage thread synchronization.

Description

The Signal() function returns the class object for the Signal class, which implements a synchronization primitive based on event semaphores. A Signal object allows multiple threads or processes to coordinate their execution by signaling events and waiting for them to occur.

The Signal class provides a thread-safe mechanism for inter-thread communication where one or more threads can wait for an event to be signaled by another thread. When a signal is posted, all waiting threads are awakened. This pattern is useful for producer-consumer scenarios, thread coordination, and event-driven programming.

Key concepts:

- Event Semaphore: A synchronization object that can be in two states: signaled or non-signaled

- Signaling: Setting the semaphore to the signaled state, which releases waiting threads

- Waiting: Blocking thread execution until the semaphore becomes signaled

- Auto-reset: After a thread is released from waiting, the semaphore can be automatically reset to non-signaled state to ensure only one thread proceeds at a time

Use case scenarios:

1. Worker Thread Coordination: A main thread creates multiple worker threads that process data in parallel. Each worker signals completion using a Signal object. The main thread waits for all workers to complete before proceeding with result aggregation.

2. Event Notification System: A monitoring thread watches for file system changes or network events. When an event occurs, it signals waiting threads that are responsible for handling specific types of events, enabling responsive event-driven architecture.

Constructors
:new()
Creates an instance of the Signal class.
Class Methods
:waitAll()
Waits for multiple signals to be posted simultaneously.
Runtime Data
:cargo
User-defined data storage associated with the Signal object.
Methods
:signal()
Posts a signal to wake up waiting threads.
:wait()
Waits for the signal to be posted with optional timeout.
Examples
Producer-Consumer pattern
// Use case 1: Producer-Consumer pattern with timeout 
// Producer thread signals when data is ready 
PROCEDURE ProducerThread(oSignal, aSharedData) 
LOCAL nCount := 1 
DO WHILE nCount <= 10 
   Sleep(100)  // Simulate work 
   AAdd(aSharedData, "Item " + Str(nCount)) 
   oSignal:signal()  // Wake up consumer 
   nCount++ 
ENDDO 
RETURN 

// Consumer thread waits for signal with 5 second timeout 
PROCEDURE ConsumerThread(oSignal, aSharedData) 
LOCAL lSuccess 
DO WHILE .T. 
   lSuccess := oSignal:wait(500)  // Wait up to 5 seconds 
   IF lSuccess 
      IF Len(aSharedData) > 0 
         ? "Processed:", ATail(aSharedData) 
         ASize(aSharedData, Len(aSharedData)-1) 
      ENDIF 
   ELSE 
      ? "Timeout - no data received" 
      EXIT 
   ENDIF 
ENDDO 
RETURN 

Parallel task coordination
// Use case 2: Parallel task coordination 
// Wait for multiple worker threads to complete their tasks 
PROCEDURE Main() 
LOCAL aSignals := {} 
LOCAL aThreads := {} 
LOCAL i, oSignal 

// Create signals and start worker threads 
FOR i := 1 TO 3 
   oSignal := Signal():new() 
   oSignal:cargo := "Task" + Str(i) 
   AAdd(aSignals, oSignal) 
   AAdd(aThreads, Thread():new():start({|o| WorkerTask(o)}, oSignal)) 
NEXT 

? "Waiting for all tasks to complete..." 
IF Signal():waitAll(aSignals, 1000)  // Wait up to 10 seconds 
   ? "All tasks completed successfully!" 
   FOR i := 1 TO Len(aSignals) 
      ? "Completed:", aSignals[i]:cargo 
   NEXT 
ELSE 
   ? "Timeout - not all tasks completed" 
ENDIF 
RETURN 

PROCEDURE WorkerTask(oSignal) 
LOCAL nDelay 

// Simulate variable working times from 1000 to 3000 ms 
nDelay := RandomInt( 1000, 3000 ) 
Sleep(nDelay/10) 

// Provide result and signal completed 
oSignal:cargo += " - Done in " + Str(nDelay) + " ms" 
oSignal:signal()  // Signal completion 

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.