Monitors

Monitors, in the context of synchronization, are high-level synchronization constructs used in concurrent programming. They provide a mechanism to control access to shared resources by multiple threads in a concurrent system. Here's a basic overview of their components and operations:

  1. Initialization Code: This is where the shared resource and its initial state are defined. Also, any necessary synchronization primitives (like locks) are initialized here.

  2. Procedures: These are methods or functions that operate on the shared resource. They typically begin by acquiring a lock to ensure exclusive access to the resource and end by releasing the lock.

  3. Entry Queue: This is a queue of processes that are waiting to enter the monitor to execute one of its procedures. A process is placed in this queue if it attempts to execute a procedure while another process is already executing a procedure within the monitor. The constraints are such that at any given time, only one process can be executing any of the monitor's procedures.

  4. Condition Variables: These are used for waiting inside a monitor's procedure when some condition is not met (like waiting for a resource to become available). They help in organizing the waiting processes into different queues based on different conditions.

  5. Condition Variable Queues: Each condition variable has its own queue. A process that finds the condition it's waiting for is not met, it can wait on a condition variable, which effectively places it in the condition variable's queue. This is separate from the entry queue for the monitor itself.

  6. Operations on Condition Variables: The primary operations are wait, signal, and broadcast.

    • wait: This operation causes the calling process to block and be placed in the condition variable's queue. Before blocking, it releases the lock on the monitor, allowing other processes to enter.

    • signal: This operation wakes up one of the processes waiting in the condition variable's queue (if any). The signaling process usually continues its execution and releases the monitor's lock at some point afterward, allowing the woken process to acquire the lock and resume execution.

    • broadcast: Similar to signal, but wakes up all processes waiting on the condition variable.

Here's a simple pseudo-code to illustrate these concepts:

monitor MyMonitor
    // Shared resource
    resource_type shared_resource

    // Initialization
    initialize() {
        shared_resource = initial_value
    }

    // Condition variables
    condition_variable cv1, cv2

    // Procedure 1
    procedure_1() {
        acquire_lock()
        while (not condition_for_procedure_1)
            cv1.wait()
        // Operate on shared_resource
        release_lock()
    }

    // Procedure 2
    procedure_2() {
        acquire_lock()
        while (not condition_for_procedure_2)
            cv2.wait()
        // Operate on shared_resource
        if (new_condition_met)
            cv1.signal() // or cv1.broadcast()
        release_lock()
    }
end monitor

In this pseudo-code, acquire_lock() and release_lock() are implicit in entering and leaving the monitor's procedures. The wait operation on a condition variable automatically releases the lock, and re-acquires it upon returning from the wait. The signal or broadcast operations are used to notify other threads waiting on a condition variable that they might proceed if the condition they are waiting for is potentially met.