Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: daixieit

CSCI 3120 Assignment 4

Due date:   11:59pm, Monday, July 31, 2023, submitted via Git

Objectives

This assignment has several objectives: (i) to provide an opportunity to implement thread synchroniza- tion; (ii) to reinforce your understanding of locks, monitors, and message passing; and (iii) to provide ad- ditional practice in C programming and understanding specifications.

Preparation:

1.    Complete Assignment 3 or review Assignment 3 solution.  Assignment 4 builds on Assignment 3. 2.    Clone your assignment repository:

https://git.cs.dal.ca/courses/2023-summer/csci-3120/assignment-4/????.git

where ???? is your CSID.

3.    Copy either your own solution or the provided solution as a starting point for Assignment 4 because

Assignment 4 builds on Assignment 3.  A solution will be accessible on July 13 from Brightspace. 4. Important: If using CLion with CMake, ensure CMakeLists.txt is modified as in Assignment 3.

The repository has the same structure and caveats as Assignment 1, Assignment 2, and Assignment 3.

Background

One way that processes communicate and synchronize with each other is by passing messages via a mes- sage passing system that is provided by the OS.   We would like to simulate a multi-node system that provides processes the ability to send and receive messages between themselves.   One important feature of multi-node model that we developed in Assignment 3 is that each node has its own clock, which is not necessarily synchronized with the other nodes.  We will need to change the model to ensure that the clocks are synchronized so that we can determine the absolute time of each event in the simulation.

Problem Statement

Extend your program from Assignment 3 (or the provided solution) in the following manner:

1.    Synchronize the clocks among all the nodes by implementing a barrier right before the clock variable is incremented.  A barrier is a synchronization operation that allows threads or processes to synchro- nize at a point in their execution.  When a thread performs a barrier, the thread is suspended until all other threads perform the same barrier.  Once all the threads have performed the barrier, they are resumed.  I.e., performing a barrier prior to incrementing the clock variable ensures that all nodes are incrementing the variable at roughly the same time and hence have synchronized clocks.

2.    Implement two additional  primitives, SEND and RECV, to model processes synchronously sending and receiving messages.  The SEND primitive models a process performing a send operation and the RECV primitive models a process performing a receive operation.  The processes performing the com- munication start in the running state and then move into the blocked state until the communication completes.  Consequently, you will need to:

a.    Modify the loading of processes to handle the two additional primitives (see Input section).

b.    Implement a simple message passing facility to model the communication.

c.    Modify the main simulation loop to perform message passing (see Processing section).

d.    Modify the code performing the summary after simulation completes.

Input

Your program will read its input from stdin.  The input format is the same as in Assignment 3 with the addition of two primitives: SEND and RECV.

Primitive      Format Description

SEND

SEND Address

Address is a positive integer specifying the destination process.

RECV

RECV Address

Address is a positive integer specifying the source process.

The Address is a positive integer that encodes the node and the process ID of the process using the fol- lowing formula:

Address = (Node ID) * 100 + (Process ID)

For example, process address 314 means the process is on node 3 and has process ID 14.  Both Node IDs and Process IDs are positive values.

Processing

Processing is the same as in Assignment 3 along with the following additions.

Clock Synchronization

The clocks need to be synchronized across all nodes. The standard solution is to invoke a barrier operation immediately before incrementing the clock variable.  While some pthread libraries do provide a barrier operation, it is recommended that you implement your own.  The reason is that the provided implemen- tation does not support decreasing the number of threads when a thread finishes its simulation.

Implement your own barrier as a monitor, using a mutex lock and condition variables.   The recommended approach is to implement a monitor with three functions:

.    barrier init(n) : initialize barrier with initial number of threads

max_thre(_)ads = n

cur_threads = 0

.    barrier wait() : perform barrier operation

cur_thre(_)ads++

if cur threads < max threads then

wai(_)t _

else

cur_threads = 0

end if

signal

.    barrier_done() : called by thread that is done its simulation (decrements number of threads that need to synchronize)

max_threads--

if cur_threads == max_threads then

cur_threads = 0

signal

end if

Note: the above is pseudo code and does not show the lock you will need to protect the monitor or the condition variable(s).  Also, this implementation suffers from a small bug where a thread may clear the barrier, increment the clock, loop around and try to perform the barrier again before all the other threads have left. Hint: To avoid this, use two condition variables (instead of one) and alternate between them, so that all the odd barrier synchronizations use one condition variable and all the even barrier synchroni- zations use the other condition variable.

Main Simulation Loop

A SEND and RECV are special forms of DOOP.  If a process’s next primitive it a SEND or RECV, it should be treated exactly as a process about to perform a DOOP and be inserted into the ready queue.  In the clock tick after the process transition from ready to running, it performs a send to or receive from the process specified by the address and transitions from running to blocked (send) or blocked (recv) state.

The send or receive is done by calling the send() and recv() functions provided by the message passing facility that you will implement.  Thus, these primitives take at minimum two clock ticks to execute.

The main simulation loop (in each node) should be adjusted as follows:

1. Unblock processes:

a.    Unblock processes that have completed their SEND or RECV

b.    Remove processes from the blocked queue that are ready to be unblocked.

For each unblocked process handle them in the same way as in Assignment 3

Note: treat a SEND or a RECV like a DOOP in terms of deciding which queue to insert processes into. 2. Update Running process, if a process is currently in a Running state,

a.    If a process is performing a SEND or RECV call the send() or recv() functions provided by the mes- sage passing facility and transition the processes to blocked (send) or blocked (recv) states.

b.    Otherwise, treat the process in the same way as in Assignment 3.

3. Select next process to run, same way as in Assignment 3

4. Increment clock, be sure to use a barrier before the increment.

Once all nodes have completed the simulation, the main thread prints out a summary of all processes in the order that the finished.  (See Output section.)

The Message Passing Facility

A message passing facility should be implemented to model the message passing between processes. There is no need to copy any data between processes, but the facility should model the synchronization between a sender and a receiver during a synchronous send.  Only two operations need to be modeled:

. Synchronous Send: process sends a message to a specified receiver and blocks if the receiver is not waiting to receive.

. Synchronous Receive: process receives a message from a specified sender and blocks if the sender is not waiting to send.

Note: “Receive Any” is not required, and hence your facility will not need message queues.

It is recommended that the message passing facility provide (at least) the following two operations with the following semantics:

send(sender, receiver):

if receiver is waiting then

sender is done

receiver is done

else

sender is waiting

end if

recv(receiver, sender):

if sender is waiting then

receiver is done

sender is done

else

receiver is waiting

end if

Note that the sender and receivers may be on different nodes or the same node.

An additional function that returns a list of all processes (on the current node) that have completed their SEND or RECV will be useful to implement Step 1(a) of the simulation loop.   If two or more processes complete their communication at the same time, then they should be ordered according to their process IDs.  I.e., processes with lower IDs come first.

Summary Statistics

The simulator should keep the same summary statistics as before, as well as the number of SENDs and RECVs that are performed.

Output

The output for this assignment is the same as that of Assignment 3, except that there are two additional blocked states:

.    blocked (send) : when a process calls the send() function of the message passing facility

.    blocked (recv) : when a process calls the recv() function of the message passing facility

The output summary for each process is extended from Assignment 3 and should be of the form:

Time | Proc Node.PID | Run run_time, Block block_time, Wait wait_time , Sends send_count, Recvs recv_count

where

. Time is the finish time of the process.

. Node is the node on which the process was simulated.

. PID is the process id of the process.

. run_time is the sum ticks of all DOOPs performed by the process.

. block_time is the sum ticks of all BLOCKs performed by the process.

. wait_time is the sum ticks that the process spent in the ready queue.

. send_cont is the number of sends performed by the process

. recv_count is the number of receives perfomed by the process

All conditions from Assignment 3 apply.

Example:

Input                                           Output: Order of thread may differ on each run!

4 5 2

Proc1 3 1 1

SEND 201

RECV 202

HALT

Proc2 3 1 1

RECV 202

SEND 201

HALT

Proc3 3 1 2

RECV 101

RECV 102

HALT

Proc4 3 1 2

SEND 102

SEND 101

HALT

[02] 00000: process 1 new

[02] 00000: process 1 ready

[02] 00000: process 2 new

[02] 00000: process 2 ready

[01] 00000: process 1 new

[01] 00000: process 1 ready

[01] 00000: process 2 new

[01] 00000: process 2 ready

[01] 00000: process 1 running

[02] 00000: process 1 running

[02] 00001: process 1 blocked (recv)

[02] 00001: process 2 running

[01] 00001: process 1 blocked (send)

[01] 00001: process 2 running

[01] 00002: process 1 ready

[01] 00002: process 2 blocked (recv)

[01] 00002: process 1 running

[02] 00002: process 1 ready

[02] 00002: process 2 blocked (send)

[02] 00002: process 1 running

[02] 00003: process 2 ready

[02] 00003: process 1 blocked (recv)

[02] 00003: process 2 running

[01] 00003: process 2 ready

[01] 00003: process 1 blocked (recv)

[01] 00003: process 2 running

[01] 00004: process 2 blocked (send)

[02] 00004: process 2 blocked (send)

[02] 00005: process 1 finished

[02] 00005: process 2 finished

[01] 00005: process 1 finished

[01] 00005: process 2 finished

| 00005 | Proc 01.01 | Run 2, Block 0,&nb