Getting started
wget
this link in your VM, unzip it, rename abc123_lab8.c
to your username, chmod +x compile.sh
, you know the deal by now.
What it will do
A common use for multiple threads is to do something that takes a long time on a second thread, so that the main thread can continue working.
This program supports 3 commands:
exit
- exits the main thread.status
- shows how many alarm threads are pending.alarm n
- sets an alarm forn
seconds from now.- when the alarm goes off, it prints a message.
This seems like a silly program, but the way it works is exactly how more complex programs work. Once you learn this pattern, you can apply it to many other kinds of problems, like:
- doing complicated calculations in the background
- downloading files in the background
- periodically reminding the user of something
- pretty much any long-running task in a GUI application
Here is an example interaction with the program:
$ ./lab8
> status
0 alarm(s) pending.
> alarm 5
> alarm 8
> status
2 alarm(s) pending.
> (RING RING!) status
1 alarm(s) pending.
> sta(RING RING!) tus
0 alarm(s) pending.
> alarm 3
> exit
still 1 alarm(s) pending...
(RING RING!) $
Above, you can see it act kind of funny… the (RING RING!)
are the alarms going off. Sometimes, they go off in the middle of the user typing something. Yep! That’s the point! :D
What to do
Compile it with ./compile.sh
.
If you run it right now, it won’t do much. You can’t even exit without using Ctrl+C.
The functions you have to implement are in _lab8.c
. See below for details.
change_thread_counter
and get_thread_counter
These are the only two functions which should access the num_threads
variable directly, and they should do it between locking/unlocking num_threads_mutex
.
Do what the comments in the functions say. (The “SAFELY” just means “between locking and unlocking the mutex.”) Have a look at the 22_thread_cooperating.c
example for how you use those functions.
exit_main_thread
(for the exit
command)
First, if there are any threads running (which you find out by calling get_thread_counter
), it should say how many there are left with this formatting:
"still waiting on %d alarm(s) before we exit...\n"
Important: do not call get_thread_counter
twice in this function. If you do that, it could actually change between calls! Consider the possibility that you call it the first time and there is 1 thread running. That thread exits before you get to the print. Then you call it a second time in the print, and it says "still waiting on 0 alarm(s)"
which is just silly. This is a harmless example of a TOCTTOU - “time of check to time of use” - bug. But real TOCTTOU bugs can be rather nasty.
Then, you can safely exit the main thread with pthread_exit(0)
. When you use this, the other threads will keep running, but if there are no other threads running, then this behaves like normal exit()
and the program exits immediately.
At this point the exit
command should now work. But it should always just exit immediately, since you haven’t created any threads yet.
show_status
(for the status
command)
This should show how many threads are running with this formatting:
"There are %d alarm(s) pending.\n"
Again, use get_thread_counter
for this. Never access num_threads
directly!
Now the status
command works, but it says there are 0 alarms pending. Of course.
set_alarm
(for the alarm
command)
So here’s the real meat and potatoes: creating the alarm threads. To do this, you will need to write a new function to serve as the thread’s main function.
Additionally, we need to get the duration from the main thread into the new thread. We can do that by allocating some space on the heap and passing that heap pointer into the thread’s main function in the pthread_create
call.
So here’s what you need to do:
- In
set_alarm
:-
First, like the comments say, you need to give an error if the duration is <= 0. It should be:
"hey, give me a valid duration...\n"
-
Otherwise, it should:
- Allocate enough space for a
long
on the heap - Store the
duration
argument into the heap-allocatedlong
- Use
pthread_create
to create the new thread, passing the heap-allocatedlong
as the last argument - Use
change_thread_counter
to add 1 to the number of threads
- Allocate enough space for a
-
- In your new thread’s main function (you can’t name it
main
, name italarm_main
orthread_main
or whatever):- Convert the
void*
that was passed in back to along*
and get the duration out of it free()
the heap-allocatedlong
- Use
sleep()
to sleep for the given duration - Print
"(RING RING!) "
- Use
fflush(stdout)
so that the message appears instantly (otherwise it wouldn’t show up until the next newline is printed) - Use
change_thread_counter
to subtract 1 from the number of threads
- Convert the
And that’s it. It should now work like the example at the top of this page.
Submission
Gradescope. WHEEEEEEEE