Commit 2f69b239 authored by desiredeveloper's avatar desiredeveloper

adding sync solutions

parent e4ca1df5
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "gcc build and debug active file",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "gcc build active file",
"miDebuggerPath": "/usr/bin/gdb"
}
]
}
\ No newline at end of file
{
"files.associations": {
"cs744_thread.h": "c"
}
}
\ No newline at end of file
{
"tasks": [
{
"type": "shell",
"label": "gcc build active file",
"command": "/usr/bin/gcc",
"args": [
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "/usr/bin"
}
}
],
"version": "2.0.0"
}
\ No newline at end of file
# Default to debugging flags. Set to -O3 if you want optimisations
# (either change it here, or type 'make CFLAGS=-O3' in the shell).
CFLAGS?=-g -Wall -Wno-unused-value
all: metro reaction
metro: metro_runner.c metro.c cs744_thread.h
$(CC) $(CFLAGS) -o metro metro_runner.c -lpthread
reaction: reaction-runner.c reaction.c cs744_thread.h
$(CC) $(CFLAGS) -o reaction reaction-runner.c -lpthread
run: metro reaction
./metro
./metro
./metro
./reaction 0
./reaction 0
./reaction 20
./reaction 20
./reaction 40
./reaction 40
./reaction 60
./reaction 60
./reaction 80
./reaction 80
./reaction 100
./reaction 100
clean:
$(RM) metro reaction
$(RM) -r *.dSYM
This package contains cs744 assignment2 starter code. The included Makefile
will compile your solutions to the metro and reaction synchronisation
problems. The resulting binaries will try to stress-test what you
wrote and should hopefully help you to shake out any bugs.
You'll need to fill in metro.c and reaction.c appropriately to
complete this assignment (the tests will certainly fail before you do
so). These two files, and only these two files, should be submitted for
grading. See the moodle post for assignment details.
You are to use the cs744 lock and condition variable APIs. The following
types are defined already for your use:
struct lock
struct condition
As are the following functions:
void lock_init(struct lock *lock);
void lock_acquire(struct lock *lock);
void lock_release(struct lock *lock);
void cond_init(struct condition *cond);
void cond_wait(struct condition *cond, struct lock *lock);
void cond_signal(struct condition *cond, struct lock *lock);
void cond_broadcast(struct condition *cond, struct lock *lock);
No other files should need to be modified. Compile using 'make'. To
run a bunch of tests, you can try 'make run'.
The tests have a lot of randomness built in and aren't flawless (their
passing doesn't guarantee a perfect implementation). You may want to
run many times to get better assurance of your solution's sanity. You're
welcome to extend the tests any way you see fit, but they won't be
submitted. You should refrain from using any other libraries or functions
in your solutions. We should be able to untar this distribution, copy over
your metro.c and reaction.c files, and compile and run the original
tests.
/*
* Should run under Unix/Linux/OS X. Just #include this header
* in your code and compile with the '-lpthread' flag, e.g.:
* cc -o myBinary myCode.c -lpthread
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
// So we can hopefully ensure that locks and condition variables
// are initialised before use.
#define LOCK_COND_INIT_MAGIC 0x4a3acd94
struct lock {
int init;
pthread_mutex_t mutex;
};
struct condition {
int init;
pthread_cond_t cond;
};
// Sush up -Wall if any of these are unused.
static void lock_init(struct lock *lock) __attribute__((unused));
static void lock_acquire(struct lock *lock) __attribute__((unused));
static void lock_release(struct lock *lock) __attribute__((unused));
static void cond_init(struct condition *cond) __attribute__((unused));
static void cond_wait(struct condition *cond, struct lock *lock) __attribute__((unused));
static void cond_signal(struct condition *cond, struct lock *lock) __attribute__((unused));
static void cond_broadcast(struct condition *cond, struct lock *lock) __attribute__((unused));
static void
__assert(int satisfied, const char *message)
{
if (!satisfied) {
fprintf(stderr, "FATAL ERROR: %s\n", message);
exit(1);
}
}
static void
lock_init(struct lock *lock)
{
lock->init = LOCK_COND_INIT_MAGIC;
pthread_mutex_init(&lock->mutex, NULL);
}
static void
lock_acquire(struct lock *lock)
{
__assert(lock->init == LOCK_COND_INIT_MAGIC,
"lock_acquire used before lock was initialised!");
pthread_mutex_lock(&lock->mutex);
}
static void
lock_release(struct lock *lock)
{
__assert(lock->init == LOCK_COND_INIT_MAGIC,
"lock_release used before lock was initialised!");
__assert(pthread_mutex_trylock(&lock->mutex) != 0,
"lock_release on unlocked lock!");
pthread_mutex_unlock(&lock->mutex);
}
static void
cond_init(struct condition *cond)
{
cond->init = LOCK_COND_INIT_MAGIC;
pthread_cond_init(&cond->cond, NULL);
}
static void
cond_wait(struct condition *cond, struct lock *lock)
{
// Assert the lock is already held. It should be held
// by this caller, so while this simple check won't
// catch all bad invocations (somebody else could have
// locked it, rather than us) at least it's a start...
__assert(pthread_mutex_trylock(&lock->mutex) != 0,
"cond_wait not called with lock held!");
__assert(cond->init == LOCK_COND_INIT_MAGIC,
"cond_wait used before cond was initialised!");
__assert(lock->init == LOCK_COND_INIT_MAGIC,
"cond_wait used before lock was initialised!");
pthread_cond_wait(&cond->cond, &lock->mutex);
}
static void
cond_signal(struct condition *cond, struct lock *lock)
{
// See comment in cond_wait().
__assert(pthread_mutex_trylock(&lock->mutex) != 0,
"cond_signal not called with lock held!");
__assert(cond->init == LOCK_COND_INIT_MAGIC,
"cond_signal used before cond was initialised!");
__assert(lock->init == LOCK_COND_INIT_MAGIC,
"cond_signal used before lock was initialised!");
pthread_cond_signal(&cond->cond);
}
static void
cond_broadcast(struct condition *cond, struct lock *lock)
{
// See comment in cond_wait().
__assert(pthread_mutex_trylock(&lock->mutex) != 0,
"cond_broadcast not called with lock held!");
__assert(cond->init == LOCK_COND_INIT_MAGIC,
"cond_broadcast used before cond was initialised!");
__assert(lock->init == LOCK_COND_INIT_MAGIC,
"cond_broadcast used before lock was initialised!");
pthread_cond_broadcast(&cond->cond);
}
#include "cs744_thread.h"
int local;
struct station {
// FILL ME IN
struct lock m1;
struct condition cond1;
struct condition cond2;
int passengers;
};
void
station_init(struct station *station)
{
station->passengers = 0;
lock_init(&station->m1);
cond_init(&station->cond1);
cond_init(&station->cond2);
// FILL ME IN
}
void
station_load_train(struct station *station, int count)
{
// FILL ME IN
lock_acquire(&station->m1);
if(count==0 || station->passengers==0){
}
else{
while(count-- && (station->passengers)--){
local++;
cond_signal(&station->cond1,&station->m1);
}
cond_wait(&station->cond2,&station->m1);
}
lock_release(&station->m1);
}
void
station_wait_for_train(struct station *station)
{
// FILL ME IN
lock_acquire(&station->m1);
(station->passengers)++;
cond_wait(&station->cond1,&station->m1);
lock_release(&station->m1);
}
void
station_on_board(struct station *station)
{
// FILL ME IN
lock_acquire(&station->m1);
if(--local==0)
cond_signal(&station->cond2,&station->m1);
lock_release(&station->m1);
}
/*
* Note that passing these tests doesn't guarantee that your code is correct
* or meets the specifications given, but hopefully it's at least pretty
* close.
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include "metro.c"
// Count of passenger threads that have completed (i.e. station_wait_for_train
// has returned) and are awaiting a station_on_board() invocation.
volatile int threads_completed = 0;
void*
passenger_thread(void *arg)
{
struct station *station = (struct station*)arg;
station_wait_for_train(station);
__sync_add_and_fetch(&threads_completed, 1);
return NULL;
}
struct load_train_args {
struct station *station;
int free_seats;
};
volatile int load_train_returned = 0;
void*
load_train_thread(void *args)
{
struct load_train_args *ltargs = (struct load_train_args*)args;
station_load_train(ltargs->station, ltargs->free_seats);
load_train_returned = 1;
return NULL;
}
const char* alarm_error_str;
int alarm_timeout;
void
_alarm(int seconds, const char *error_str)
{
alarm_timeout = seconds;
alarm_error_str = error_str;
alarm(seconds);
}
void
alarm_handler(int foo)
{
fprintf(stderr, "Error: Failed to complete after %d seconds. Something's "
"wrong, or your system is terribly slow. Possible error hint: [%s]\n",
alarm_timeout, alarm_error_str);
exit(1);
}
#ifndef MIN
#define MIN(_x,_y) ((_x) < (_y)) ? (_x) : (_y)
#endif
/*
* This creates a bunch of threads to simulate arriving trains and passengers.
*/
int main()
{
struct station station;
station_init(&station);
srandom(getpid() ^ time(NULL));
signal(SIGALRM, alarm_handler);
// Make sure station_load_train() returns immediately if no waiting passengers.
_alarm(1, "station_load_train() did not return immediately when no waiting passengers");
station_load_train(&station, 0);
station_load_train(&station, 10);
_alarm(0, NULL);
// Create a bunch of 'passengers', each in their own thread.
int i;
const int total_passengers = 1000;
int passengers_left = total_passengers;
for (i = 0; i < total_passengers; i++) {
pthread_t tid;
int ret = pthread_create(&tid, NULL, passenger_thread, &station);
if (ret != 0) {
// If this fails, perhaps we exceeded some system limit.
// Try reducing 'total_passengers'.
perror("pthread_create");
exit(1);
}
}
// Make sure station_load_train() returns immediately if no free seats.
_alarm(2, "station_load_train() did not return immediately when no free seats");
station_load_train(&station, 0);
_alarm(0, NULL);
// Tons of random tests.
int total_passengers_boarded = 0;
const int max_free_seats_per_train = 50;
int pass = 0;
while (passengers_left > 0) {
_alarm(2, "Some more complicated issue appears to have caused passengers "
"not to board when given the opportunity");
int free_seats = random() % max_free_seats_per_train;
printf("Train entering station with %d free seats\n", free_seats);
load_train_returned = 0;
struct load_train_args args = { &station, free_seats };
pthread_t lt_tid;
int ret = pthread_create(&lt_tid, NULL, load_train_thread, &args);
if (ret != 0) {
perror("pthread_create");
exit(1);
}
int threads_to_reap = MIN(passengers_left, free_seats);
int threads_reaped = 0;
while (threads_reaped < threads_to_reap) {
if (load_train_returned) {
fprintf(stderr, "Error: station_load_train returned early!\n");
exit(1);
}
if (threads_completed > 0) {
if ((pass % 2) == 0)
usleep(random() % 2);
threads_reaped++;
station_on_board(&station);
__sync_sub_and_fetch(&threads_completed, 1);
}
}
// Wait a little bit longer. Give station_load_train() a chance to return
// and ensure that no additional passengers board the train. One second
// should be tons of time, but if you're on a horribly overloaded system,
// this may need to be tweaked.
for (i = 0; i < 1000; i++) {
if (i > 50 && load_train_returned)
break;
usleep(1000);
}
if (!load_train_returned) {
fprintf(stderr, "Error: station_load_train failed to return\n");
exit(1);
}
while (threads_completed > 0) {
threads_reaped++;
__sync_sub_and_fetch(&threads_completed, 1);
}
passengers_left -= threads_reaped;
total_passengers_boarded += threads_reaped;
printf("Train departed station with %d new passenger(s) (expected %d)%s\n",
threads_to_reap, threads_reaped,
(threads_to_reap != threads_reaped) ? " *****" : "");
if (threads_to_reap != threads_reaped) {
fprintf(stderr, "Error: Too many passengers on this train!\n");
exit(1);
}
pass++;
}
if (total_passengers_boarded == total_passengers) {
printf("Looks good!\n");
return 0;
} else {
// I don't think this is reachable, but just in case.
fprintf(stderr, "Error: expected %d total boarded passengers, but got %d!\n",
total_passengers, total_passengers_boarded);
return 1;
}
}
/*
* This file tests the implementation in reaction.c. You shouldn't need to
* modify it, but you're welcome to do so. Please report any bugs to us via
* Piazza or email (cs140ta@cs).
*
* Note that passing these tests doesn't guarantee that your code is correct
* or meets the specifications given, but hopefully it's at least pretty
* close.
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include "reaction.c"
volatile int water_molecules = 0;
volatile int hydrogen_atoms_bonded = 0;
volatile int oxygen_atoms_bonded = 0;
void
make_water()
{
__sync_add_and_fetch(&water_molecules, 1);
}
void*
hydrogen_thread(void *arg)
{
struct reaction *reaction = (struct reaction*)arg;
reaction_h(reaction);
__sync_add_and_fetch(&hydrogen_atoms_bonded, 1);
return NULL;
}
void*
oxygen_thread(void *arg)
{
struct reaction *reaction = (struct reaction*)arg;
reaction_o(reaction);
__sync_add_and_fetch(&oxygen_atoms_bonded, 1);
return NULL;
}
void
alarm_handler(int foo)
{
fprintf(stderr, "Error: Failed to complete after 10 seconds. Something's "
"wrong, or your system is terribly slow.\n");
exit(1);
}
#ifndef MIN
#define MIN(_x,_y) ((_x) < (_y)) ? (_x) : (_y)
#endif
/*
* This creates a bunch of threads to simulate H and O molecules. Hopefully
* mother nature will be decent enough to bond them when asked.
*/
int
main(int argc, char** argv)
{
struct reaction reaction;
reaction_init(&reaction);
if (argc != 2 || atoi(argv[1]) < 0 || atoi(argv[1]) > 100) {
fprintf(stderr, "usage: %s percentageOfHydrogenAtoms\n"
" e.g.: %s 50\n", argv[0], argv[0]);
exit(1);
}
srandom(getpid() ^ time(NULL));
signal(SIGALRM, alarm_handler);
alarm(10);
int i;
const int total_atoms = 200;
int hydrogen_atoms = 0;
int oxygen_atoms = 0;
int hydrogen_pct = atoi(argv[1]);
for (i = 0; i < total_atoms; i++) {
pthread_t tid;
int ret;
if ((random() % 100) < hydrogen_pct) {
hydrogen_atoms++;
ret = pthread_create(&tid, NULL, hydrogen_thread, &reaction);
} else {
oxygen_atoms++;
ret = pthread_create(&tid, NULL, oxygen_thread, &reaction);
}
if (ret != 0) {
// If this fails, perhaps we exceeded some system limit.
// Try reducing 'total_atoms'.
perror("pthread_create");
exit(1);
}
}
int expected_molecules = MIN(hydrogen_atoms / 2, oxygen_atoms);
printf("Created %d H and %d O atoms (%.1f%% H), expecting %d H2O molecules\n",
hydrogen_atoms, oxygen_atoms, (double)hydrogen_atoms / total_atoms * 100.0,
expected_molecules);
// Wait for expected reactions to complete.
while (water_molecules != expected_molecules)
;
// Wait a little longer in case the student's implementation is buggy and
// we see too many reactions.
usleep(100000);
if (water_molecules != expected_molecules) {
fprintf(stderr, "Error: expected %d H20 molecules, but %d were created!\n",
expected_molecules, water_molecules);
exit(1);
}
if (hydrogen_atoms_bonded != (expected_molecules * 2)) {
fprintf(stderr, "Error: expected %d reaction_h() calls to return, but "
"%d did instead!\n", expected_molecules * 2,
hydrogen_atoms_bonded);
exit(1);
}
if (oxygen_atoms_bonded != expected_molecules) {
fprintf(stderr, "Error: expected %d reaction_o() calls to return, but "
"%d did instead!\n", expected_molecules, oxygen_atoms_bonded);
exit(1);
}
printf("Looks good!\n");
return 0;
}
#include "cs744_thread.h"
// Forward declaration. This function is implemented in reaction-runner.c,
// but you needn't care what it does. Just be sure it's called when
// appropriate within reaction_o()/reaction_h().
void make_water();
int hydrogen,oxygen;
struct reaction {
// FILL ME IN
struct lock m1;
struct condition oxygen;
struct condition hydrogen;
};
void
reaction_init(struct reaction *reaction)
{
// FILL ME IN
lock_init(&reaction->m1);
cond_init(&reaction->oxygen);
cond_init(&reaction->hydrogen);
}
void
reaction_h(struct reaction *reaction)
{
// FILL ME IN
lock_acquire(&reaction->m1);
hydrogen++;
if(hydrogen>=2 && oxygen>=1){
cond_signal(&reaction->hydrogen,&reaction->m1);
cond_signal(&reaction->oxygen,&reaction->m1);
hydrogen-=2;
oxygen--;
make_water();
}
else
cond_wait(&reaction->oxygen,&reaction->m1);
lock_release(&reaction->m1);
}
void
reaction_o(struct reaction *reaction)
{
// FILL ME IN
lock_acquire(&reaction->m1);
oxygen++;
if(hydrogen>=2){
cond_signal(&reaction->oxygen,&reaction->m1);
cond_signal(&reaction->oxygen,&reaction->m1);
hydrogen-=2;
oxygen--;
make_water();
}
else
cond_wait(&reaction->hydrogen,&reaction->m1);
lock_release(&reaction->m1);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment