Starting from:
$35

$29

CISC2005 Lab 07

1. Prerequisites

• Successful installation of *nix terminal (e.g., WSL, cygwin) or a *nix operating system (e.g., Linux, Mac).

• Successful installation of a C compiler, GCC is recommended.

You can also use an online c compiler: https://www.onlinegdb.com/online_c_compiler

• Successful installation of a text editor, VIM or Emacs is recommended.

2. Exercises

Semaphore is a synchronization tool that is used to control access to a shared resource in a concurrent system. There are two types of semaphores: mutex semaphore and counting semaphore. A binary semaphore can only take on two values, 0 and 1, and is typically used for mutual exclusion. A counting semaphore, on the other hand, can take on a range of non-negative values and is typically used to control access to a finite pool of resources.

In a *nix operating system (e.g., Linux, Mac), the <semaphore.h> header defines the sem_t type, used in performing semaphore operations. Here are some important functions about semaphore operations.

In this exercise, students are required to complete missing codes ( ??? ) to implement several basic synchronization algorithms using semaphore. For each program, students should capture screenshots of the successful execution, and answer the questions for the program. In the submission file, please attach the added code and execution screenshot in sequence.

Example 1: Mutex semaphore

In this example, we use a global variable “counter” and a semaphore variable “s” to count the number of accesses to a shared resource. Please use sem_init, sem_wait, and sem_post to complete the following code to correctly count accesses.

1. // Global variables  

2. static volatile long counter = 0;  

3. sem_t s; // Semaphore variable  

4.   

5. void *increment(void *vargp) {  

6.     // Enter critical region  

7.     for (long i = 0; i < N_ITERATIONS; i++) {  

8.         // Block on a semaphore count  

9.          ??? ; // P(S)  

10.         // Shared resource  

11.         counter++;  

12.         // Increment a semaphore  

13.          ??? ; // V(S)  

14.     }  

15.     // Leave critical region  

16.     return NULL;  

17. }  

18.   

19. int main(int argc, char **argv) {  

20.     // Initialize the semaphore  

21.     sem_init(&s, 0,  ??? );  

22.     // Create threads  

23.     pthread_t threads[NUM_THREADS];  

24.     // Create threads  

25.     for (int i = 0; i < NUM_THREADS; i++) {  

26.         pthread_create(&threads[i], NULL, increment, NULL);  

27.     }  

28.     printf("Create %d threads.\n", NUM_THREADS);  

29.     // Wait for threads to finish  

30.     for (int i = 0; i < NUM_THREADS; i++) {  

31.         pthread_join(threads[i], NULL);  

32.     }  

33.     printf("counter=%ld.\n", counter);  

34.     // Destroy the semaphore  

35.     sem_destroy(&s);  

36.     return 0;  

37. }

Code:

1. Download the sample code from UMMoodle (example1.c).

2. Compile the above C program and execute. (Tips: use the following compile command.)

Question 1:

1.1 Please complete the code in example1.c.

1.2 How does the semaphore “s” change as the program runs?

Requirements:

1. Capture 1 screenshots of the successful execution.

2. Answer Question 1.

Example 2: Counting semaphore

Imagine the following scenario: there are two workers serving 5 customers, and each worker can only serve one customer at a time. Complete the code in example 2 to simulate this scenario.

1. // Number of customers  

2. int NUM_CUSTOMERS = 5;  

3.   

4. sem_t s; // Semaphore variable  

5.   

6. // Simulate the process of service  

7. void *get_service(void *arg)  

8. {  

9.     int id = *((int*)arg);  

10.     // Block on a semaphore count  

11.      ??? ; // P(S)

12.     // Enter critical region  

13.     printf("Customer [%d] is getting service.\n", id);  

14.     sleep(2);  

15.     printf("Customer [%d] has left.\n", id);  

16.     // Leave critical region  

17.     // Increment a semaphore  

18.      ??? ; // V(S)

19.     return 0;  

20. }  

21.   

22. int main()  

23. {  

24.     // Initialize the semaphore  

25.     sem_init(&s, 0,  ??? );  

26.     // Create threads  

27.     pthread_t threads[NUM_CUSTOMERS];  

28.     int thread_ids[NUM_CUSTOMERS];  

29.     printf("Create %d threads.\n", NUM_CUSTOMERS);  

30.     for (int i = 0; i < NUM_CUSTOMERS; i++) {  

31.         thread_ids[i] = i + 1;  

32.         pthread_create(&threads[i], NULL, get_service, &thread_ids[i]);  

33.     }  

34.     // Wait for threads to finish  

35.     for (int i = 0; i < NUM_CUSTOMERS; i++) {  

36.         pthread_join(threads[i], NULL);  

37.     }  

38.     // Destroy the semaphore  

39.     sem_destroy(&s);  

40.     return 0;  

41. }

Code:

1. Download the sample code from UMMoodle (example2.c).

2. Compile the above C program and execute. (Tips: use the following compile command.)

Question 2:

2.1 Please complete the code in example2.c.

2.2 How does the semaphore “s” change as the program runs?

Requirements:

1. Capture 1 screenshots of the successful execution.

2. Answer Question 2.

Example 3: Possible deadlocks with semaphores

Deadlock is the permanent and unresolvable blocking of two or more threads that results from each waiting on the other. The following code in example3.c provides a simple illustration of how deadlock can happen. There are two threads, running the first and second functions. The code for first tries to acquire semaphore “A” before “B”, while second acquires them in the opposite order. Figure 1 shows the state model for this example. Please execute example3 and explain why the deadlock occurs.

Figure 1. State model for example 3.

Example3 code:

1. /* struct contains shared semaphores */  

2. typedef struct {  

3.   sem_t A;  

4.   sem_t B;  

5. } SEMAPHORE;  

6.   

7. void *first (void * args)  

8. {  

9.   SEMAPHORE *sem = (SEMAPHORE *) args;  

10.   /* Acquire "A" before "B" */  

11.   printf("Job started in the first thread.\n");  

12.   sem_wait(&sem->A);  

13.   sleep(1);  

14.   while (-1 == sem_trywait(&sem->B))  

15.   {  

16.     printf("The first thread has acquired 'A' and waits for 'B'.\n");  

17.     sleep(1);  

18.   }  

19.   /* other code here */  

20.   return NULL;  

21. }  

22.   

23. void *second (void * args)  

24. {  

25.   SEMAPHORE *sem = (SEMAPHORE *) args;  

26.   /* Acquire "B" before "A" */  

27.   printf("Job started in the second thread.\n");  

28.   sem_wait(&sem->B);  

29.   sleep(1);  

30.   while (-1 == sem_trywait(&sem->A))  

31.   {  

32.     printf("The second thread has acquired 'B' and waits for 'A'.\n");  

33.     sleep(1);  

34.   }  

35.   /* other code here */  

36.   return NULL;  

37. } 

Code:

1. Download the sample code from UMMoodle (example3.c).

2. Compile the above C program and execute. (Tips: use the following compile command.)

Question 3:

Why does the deadlock occur in example 3?

Requirements:

1. Capture 1 screenshots of the successful execution.

2. Answer Question 3.

Example 4: Bounded Buffer with semaphores

This example provides an implementation of Bounded Buffer with semaphores.

1. // Global variables  

2. buffer_item START_NUMBER = 0;  

3. buffer_item buffer[BUFFER_SIZE];  

4. sem_t mutex;  

5. sem_t empty;  

6. sem_t full;  

7.   

8. void initialization(){  

9.     sem_init(&mutex, 0, 1);  

10.     sem_init(&full, 0, BUFFER_SIZE);  

11.     sem_init(&empty, 0, 0);  

12. }  

13.   

14. void *producer(void *arg)  

15. {  

16.     int thread_id = *(int*)arg;  

17.     buffer_item item;  

18.   

19.     while(TRUE) {  

20.         sleep(1);  

21.         sem_wait(&full);  

22.         sem_wait(&mutex);  

23.   

24.         item = START_NUMBER++;  

25.         insert_item(item);  

26.   

27.         printf("Producer %d produced %d \n", thread_id, item);  

28.   

29.         sem_post(&mutex);  

30.         sem_post(&empty);  

31.     }  

32.   

33.     return NULL;  

34. }  

35.   

36. void *consumer(void *arg)  

37. {  

38.     int thread_id = *(int*)arg;  

39.     buffer_item item;  

40.   

41.     while(TRUE){  

42.         sleep(1);  

43.         sem_wait(&empty);  

44.         sem_wait(&mutex);  

45.   

46.         remove_item(&item);  

47.         printf("Consumer %d consumed %d \n", thread_id, item);  

48.   

49.         sem_post(&mutex);  

50.         sem_post(&full);  

51.     }  

52.   

53.     return NULL;  

54. }  

Code:

1. Download the sample code from UMMoodle (example4.c).

2. Compile the above C program and execute. (Tips: use the following compile command.)

Requirements:

1. Capture 1 screenshots of the successful execution.

More products