Great Deal! Get Instant $10 FREE in Account on First Order + 10% Cashback on Every Order Order Now

OS_HW_04_F2020 Assignment Four: Synchronization Support and pthreads EECE 4029: Operating Systems and Systems Programming ELTN 4022: Operating Systems Department of Electrical Engineering and Computer...

1 answer below »
OS_HW_04_F2020
Assignment Four: Synchronization Support and pthreads
EECE 4029: Operating Systems and Systems Programming
ELTN 4022: Operating Systems

Department of Electrical Engineering and Computer Science
University of Cincinnati, Cincinnati OH
Revision Date: Oct 07, 2020

Purpose
In this assignment, you will become more familiar with multithreading as implemented by the
pthreads li
ary and you will explore the benefits and drawbacks of various synchronization
primitives.
a) You will write basic multithreaded code and examine the occu
ence of and the causes of race
conditions. This will serve as a practical code supplement to concepts discussed in lecture.
) You will use mutexes, semaphores, and spinlocks to sequence memory operations to prevent
ace conditions. You will also examine and learn about the various benefits and drawbacks to
each choice.
c) You will use one or more of the discussed synchronization primitives to implement a “readers-
writer lock” – another type of synchronization primitive.
Activity Summary
This assignment is worth 100 points and is
oken into two parts:
Activity Set A (50 points): A number of guided programming exercises with discussion questions
which all together are worth 50 points. You may work on these guided programming exercises
in small groups, but you must make your own writeup of the results of these exercises and turn
that in as part of this assignment.
Activity Set B (50 points): You will write your own set of su
outines to implement “readers-
writer locks” using existing primitives. You will be required to demonstrate that your
implementation functions by linking your code to a multi-threaded test application that will be
provided. The test application must run without race conditions or locking of any kind.
The text describing your specific deliverables is given in line with the na
ative writeup and is
endered in green text. Included with this na
ative writeup is a MS Word format answer sheet
template that contains all of the green text questions and space for you to fill in your answers.
Please use that template to produce a single PDF containing your Activity Set A answers. Your
activity set B answer should be a single C code source file containing the source code of your
eaders-writer lock li
ary. This source file should be commented in detail to explain what you
are doing and why. Please refer to the answer template file for more details on how to format
and submit your answers.

Activity Set A: Guided Programming Exercises (50 points total for Activity A)
Activity A, Task 1: Race Condition Example (10/40 points)
In class, we would have discussed the basics of multi-threading and race conditions. Here we’ll
iefly summarize the highlights of those conversations and introduce some code that does, indeed,
spawn multiple threads. The code we talk about in this activity will NOT properly protect its own
critical section and will be somewhat susceptible to race conditions. In subsequent activity A tasks,
we’ll introduce various synchronization primitives as a means to prevent the improper racing
ehaviors.
Note that sample code below is TERRIBLE and is VERY unsafe. It will be rife with race conditions and
actual outputs will not be predictable because order of operations on shared memory will not be
guaranteed. This code is te
ible on purpose. Once we look at it, how it’s te
ible, and why, we’ll fix
it.
#include
#include
#include
This is the header file that contains prototypes for

pthreads structures and user functions

int c = 0;
I'm declaring a global variable. The memory for this variable

will be in the process DATA segment and will have global scope.

Every thread that accesses this variable will be accessing the

exact same value in the same memory spot in the data segment.

Naturally, this is potentially dangerous and could be a cause

of race conditions if there is more than one thread trying

to write or read and write at the same time.

The way threads work is that we "register" a function as a thread. Once
the function is "registered" and "launched" -- it will run LIKE a process
in that it will be scheduled like one, but it will share the same text, data,
and heap segments with EVERY OTHER thread launched in the same process.
Sometime threads are refe
ed to as "lightweight processes". This kind of makes
sense. It allows parallelism and multi-programming across multiple lines of
execution, but does not require full context switches in which the whole process
memory map is spun down and the whole process put into a wait state whenever
a context switch occurs. Threads can be scheduled and suspended with far less
overhead.

Now, if you look at this code, you may note that I'm defining it with a *
in front of the name. This essentially means that the token fnC is actually
a POINTER to the function while *fnc() is actually a CALL to the function.
Pointers to functions are actually kind of cool and you may want to review
them if you are not familiar with them. We'll use it here so we can tell
one function (the pthreads routine that registers and launches threads) that
the thread should RUN the code that lives at the end of a function pointer.

The function itself is kind of simple. It just counts from zero to nine and
for each step it increments the global variable "c" and then prints the value
to the screen. Now, if I were to make two calls to *fnC() as NORMAL functions,
not threads, this would print the following:

XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX

Of course, a naive programmer might think that if I were to run fnC() as two
threads, I'd get the same result -- but potentially "faster" because of
multiple cores. Of course, you already know this won't happen :)

void *fnC()
{
int i;
for(i=0;i<10;i++)
{ c++;
XXXXXXXXXXprintf(" %d", c);
}
}

int main()
{

These will be return values from the routine that launches threads
int rt1, rt2;


These are variables of pthread_t, they hold bookkeeping values for threads.

In this case we’ll launch two threads, so we need two of these.
pthread_t t1, t2;


We’re going to run a bunch of thread experiments and examine the outputs.

This variable will keep track of which trial we’re on.

int trial_count = 0;


For every trial, lets zero out the counter and run the count routine “twice”

as threads that can be scheduled onto independent cores instead of running

in sequence.

for (trial_count = 0; trial_count < 1000; trial_count++)
{
Zero out the global counter
XXXXXXXXXXc = 0;


Create two thread with the routine pthread_create(). You can use

reference materials to get definitions of what the various parameters

mean.

XXXXXXXXXXif((rt1=pthread_create( &t1, NULL, &fnC, NULL)))
XXXXXXXXXXprintf("Thread creation failed: %d\n", rt1);

XXXXXXXXXXif((rt2=pthread_create( &t2, NULL, &fnC, NULL)))
XXXXXXXXXXprintf("Thread creation failed: %d\n", rt2);


Wait for both threads to finish. The main process thread will block

until the threads that were launched terminate. Once they both finish,

then the “main thread” unblocks and continues.

XXXXXXXXXXpthread_join(t1, NULL);
XXXXXXXXXXpthread_join(t2, NULL);
XXXXXXXXXXprintf("\n");
}

return 0;
}
You may at this point wish to compile and run the above code. Examine, carefully, the lines of
output you get. Often the output will be what you would have expected if *fn(c) were run twice,
serially, as standard function calls. Sometimes, however, you will not. After you run and examine
some outputs, complete the following discussion questions:
Activity A, Task
Answered 177 days After Oct 14, 2021

Solution

Karthi answered on Apr 09 2022
107 Votes
SOLUTION.PDF

Answer To This Question Is Available To Download

Related Questions & Answers

More Questions »

Submit New Assignment

Copy and Paste Your Assignment Here