online compiler and debugger for c/c++

code. compile. run. debug. share.
Source Code   
Language
#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <stdlib.h> #include <assert.h> #include "synch.h" #include "rwlock.h" #define NUM_READERS 5 #define NUM_WRITERS 2 #define NUM_ITERATIONS 10 static const char *reader_names[] = { "Robin", "Riley", "Raven", "Reese", "Renee", "Reena", "River", "Rohan", "Ronan", "Rania" }; static const char *writer_names[] = { "Wendy", "Wayne", "Wyatt", "Wilma", "Wylie", "Wanda", "Wells", "Warda", "Woody", "Walid", }; struct shared_data { int value; struct rwlock *rw_lock; }; struct shared_data data; void * reader(void *arg) { const char *id = (char *) arg; for (int i = 0; i < NUM_ITERATIONS; i++) { printf("Reader %s tries to acquire lock\n", id); rwlock_readlock(data.rw_lock); printf("Reader %s acquires lock, reads value: %d\n", id, data.value); usleep(10000 + rand() % 90000); // Sleep for 10-100ms rwlock_unlock(data.rw_lock); printf("Reader %s releases lock\n", id); usleep(10000 + rand() % 90000); // Sleep for 10-100ms } return NULL; } void * writer(void *arg) { const char *id = (char *) arg; for (int i = 0; i < NUM_ITERATIONS; i++) { printf("Writer %s tries to acquire lock\n", id); rwlock_writelock(data.rw_lock); data.value++; printf("Writer %s acquires lock, updates value to: %d\n", id, data.value); usleep(10000 + rand() % 90000); // Sleep for 10-100ms rwlock_unlock(data.rw_lock); printf("Writer %s releases lock\n", id); usleep(10000 + rand() % 90000); // Sleep for 10-100ms } return NULL; } #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) int main(void) { assert(ARRAY_SIZE(reader_names) >= NUM_READERS); assert(ARRAY_SIZE(writer_names) >= NUM_WRITERS); pthread_t readers[NUM_READERS]; pthread_t writers[NUM_WRITERS]; data.value = 0; data.rw_lock = rwlock_create("shared_data_lock"); if (data.rw_lock == NULL) { fprintf(stderr, "Failed to create reader-writer lock\n"); return 1; } // Create reader threads for (int i = 0; i < NUM_READERS; i++) { if (pthread_create(&readers[i], NULL, reader, (void *) reader_names[i]) != 0) { fprintf(stderr, "Failed to create reader thread\n"); return 1; } } // Create writer threads for (int i = 0; i < NUM_WRITERS; i++) { if (pthread_create(&writers[i], NULL, writer, (void *) writer_names[i]) != 0) { fprintf(stderr, "Failed to create writer thread\n"); return 1; } } // Wait for all threads to complete for (int i = 0; i < NUM_READERS; i++) { pthread_join(readers[i], NULL); } for (int i = 0; i < NUM_WRITERS; i++) { pthread_join(writers[i], NULL); } rwlock_destroy(data.rw_lock); printf("Final value: %d\n", data.value); return 0; }
#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <stdlib.h> #include <assert.h> #include "synch.h" #include "rwlock.h" /* * The gate enum represents the current state of access control. * ALLOW_ALL: Both readers and writers can attempt to acquire the lock. * PREFER_WRITER: Writers are given preference, new readers will wait. * PREFER_READER: Readers are given preference, but writers can still queue up. */ enum rwlock_gate { ALLOW_ALL, PREFER_WRITER, PREFER_READER }; struct rwlock { struct lock *lock; struct cv *read_cv; struct cv *write_cv; int readers; int writers; int waiting_writers; enum rwlock_gate gate; }; /* * rwlock_create: Creates and initializes a new reader-writer lock. * * The lock starts in an unlocked state with no active readers or writers, * and the gate set to ALLOW_ALL. */ struct rwlock * rwlock_create(const char *name) { struct rwlock *rw = malloc(sizeof(struct rwlock)); if (rw == NULL) { return NULL; } rw->lock = lock_create("rwlock_internal"); rw->read_cv = cv_create("rwlock_read_cv"); rw->write_cv = cv_create("rwlock_write_cv"); if (rw->lock == NULL || rw->read_cv == NULL || rw->write_cv == NULL) { rwlock_destroy(rw); return NULL; } rw->readers = 0; rw->writers = 0; rw->waiting_writers = 0; rw->gate = ALLOW_ALL; return rw; } /* * rwlock_destroy: Cleans up and frees all resources associated with the lock. * * Should only be called when no threads are using or waiting on the lock. */ void rwlock_destroy(struct rwlock *rw) { if (rw != NULL) { if (rw->lock != NULL) lock_destroy(rw->lock); if (rw->read_cv != NULL) cv_destroy(rw->read_cv); if (rw->write_cv != NULL) cv_destroy(rw->write_cv); free(rw); } } /* * rwlock_readlock: Acquires the lock in read mode. * * Readers wait if there are active writers or if the gate prefers writers. * This prevents writer starvation while still allowing concurrent reads. */ void rwlock_readlock(struct rwlock *rw) { lock_acquire(rw->lock); while (rw->writers > 0 || rw->gate == PREFER_WRITER) { cv_wait(rw->read_cv, rw->lock); } rw->readers++; lock_release(rw->lock); } /* * rwlock_writelock: Acquires the lock in write mode. * * Writers wait if there are active readers or writers. This code doesn't * care about the state of the gate -- if the lock is free it'll take it. */ void rwlock_writelock(struct rwlock *rw) { lock_acquire(rw->lock); rw->waiting_writers++; while (rw->readers > 0 || rw->writers > 0) { cv_wait(rw->write_cv, rw->lock); } rw->waiting_writers--; rw->writers++; lock_release(rw->lock); } /* * rwlock_unlock: Releases the lock, whether held in read or write mode. * * This function manages the gate to balance between reader and writer access. * It ensures neither readers nor writers can starve indefinitely. */ void rwlock_unlock(struct rwlock *rw) { lock_acquire(rw->lock); assert(rw->readers > 0 || rw->writers > 0); if (rw->writers > 0) { rw->writers--; rw->gate = PREFER_READER; // Give readers a chance after a write } else if (rw->readers > 0) { rw->readers--; // If writers are waiting, prefer them, otherwise allow all rw->gate = rw->waiting_writers > 0 ? PREFER_WRITER : ALLOW_ALL; } if (rw->readers == 0 && rw->writers == 0) { if (rw->gate != PREFER_WRITER) { cv_broadcast(rw->read_cv, rw->lock); } if (rw->waiting_writers > 0) { cv_signal(rw->write_cv, rw->lock); } } lock_release(rw->lock); }
#ifndef RWLOCK_H #define RWLOCK_H struct rwlock; // declare functions struct rwlock *rwlock_create(const char *name); void rwlock_destroy(struct rwlock *rw); void rwlock_readlock(struct rwlock *rw); void rwlock_writelock(struct rwlock *rw); void rwlock_unlock(struct rwlock *rw); #endif // RWLOCK_H
#ifndef SYNCH_H #define SYNCH_H struct lock; struct lock *lock_create(const char *name); int lock_do_i_hold(struct lock *lock); void lock_acquire(struct lock *lock); void lock_release(struct lock *lock); void lock_destroy(struct lock *lock); struct cv; struct cv *cv_create(const char *name); void cv_wait(struct cv *cv, struct lock *lock); void cv_signal(struct cv *cv, struct lock *lock); void cv_broadcast(struct cv *cv, struct lock *lock); void cv_destroy(struct cv *cv); #endif // SYNCH_H
/* * You don't need to look at this file. It defines the lock and condition * variable interfaces with error checking analogous to the ones found in * in OS/161. */ #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include "synch.h" #define ERR_CHECK(result, msg, ...) \ if (result != 0) { \ fprintf(stderr, "Error: " msg ": %s\n", ##__VA_ARGS__, \ strerror(result)); \ exit(1); \ } #define COND_CHECK(cond, msg, ...) \ if (!(cond)) { \ fprintf(stderr, "Error: " msg "\n", ##__VA_ARGS__); \ exit(1); \ } struct lock { const char *name; pthread_mutex_t posix_mutex; pthread_t lock_owner; }; struct lock * lock_create(const char *name) { struct lock *lock = malloc(sizeof(struct lock)); COND_CHECK(lock != NULL, "Failed to allocate memory for lock"); lock->name = name; int result = pthread_mutex_init(&(lock->posix_mutex), NULL); ERR_CHECK(result, "Failed to initialize mutex"); lock->lock_owner = (pthread_t) 0; return lock; } int lock_do_i_hold(struct lock *lock) { COND_CHECK(lock != NULL, "Attempted to check NULL lock"); return pthread_self() == lock->lock_owner; } void lock_acquire(struct lock *lock) { COND_CHECK(lock != NULL, "Attempted to acquire NULL lock"); int result = pthread_mutex_lock(&(lock->posix_mutex)); ERR_CHECK(result, "Failed to acquire lock %s", lock->name); lock->lock_owner = pthread_self(); } void lock_release(struct lock *lock) { COND_CHECK(lock != NULL, "Attempted to release NULL lock"); COND_CHECK(lock_do_i_hold(lock), "Attempted to release lock %s not held by current thread", lock->name); lock->lock_owner = (pthread_t) 0; int result = pthread_mutex_unlock(&(lock->posix_mutex)); ERR_CHECK(result, "Failed to release lock %s", lock->name); } void lock_destroy(struct lock *lock) { COND_CHECK(lock != NULL, "Attempted to destroy NULL lock"); int result = pthread_mutex_destroy(&(lock->posix_mutex)); ERR_CHECK(result, "Failed to destroy lock %s", lock->name); free(lock); } /* --- CONDITION VARIABLES ----------------------------------------------- */ struct cv { const char *name; pthread_cond_t posix_cv; }; struct cv * cv_create(const char *name) { struct cv *cv = malloc(sizeof(struct cv)); COND_CHECK(cv != NULL, "Failed to allocate memory for cv %s", name); int result = pthread_cond_init(&(cv->posix_cv), NULL); ERR_CHECK(result, "Failed to initialize condition variable %s", name); cv->name = name; return cv; } void cv_wait(struct cv *cv, struct lock *lock) { COND_CHECK(cv != NULL, "Attempted to wait on NULL cv"); COND_CHECK(lock != NULL, "Attempted to wait on NULL lock"); COND_CHECK(lock_do_i_hold(lock), "Attempted to wait on cv %s without holding lock %s", cv->name, lock->name); int result = pthread_cond_wait(&(cv->posix_cv), &(lock->posix_mutex)); ERR_CHECK(result, "Failed to wait on cv %s", cv->name); lock->lock_owner = pthread_self(); } void cv_signal(struct cv *cv, struct lock *lock) { COND_CHECK(cv != NULL, "Attempted to signal NULL cv"); COND_CHECK(lock != NULL, "Attempted to signal on NULL lock"); COND_CHECK(lock_do_i_hold(lock), "Attempted to signal cv %s without holding lock %s", cv->name, lock->name); int result = pthread_cond_signal(&(cv->posix_cv)); ERR_CHECK(result, "Failed to signal cv %s", cv->name); } void cv_broadcast(struct cv *cv, struct lock *lock) { COND_CHECK(cv != NULL, "Attempted to broadcast NULL cv"); COND_CHECK(lock != NULL, "Attempted to broadcast on NULL lock"); COND_CHECK(lock_do_i_hold(lock), "Attempted to broadcast cv %s without holding lock %s", cv->name, lock->name); int result = pthread_cond_broadcast(&(cv->posix_cv)); ERR_CHECK(result, "Failed to broadcast cv %s", cv->name); } void cv_destroy(struct cv *cv) { COND_CHECK(cv != NULL, "Attempted to destroy NULL cv"); int result = pthread_cond_destroy(&(cv->posix_cv)); ERR_CHECK(result, "Failed to destroy cv %s", cv->name); free(cv); }

Compiling Program...

Command line arguments:
Standard Input: Interactive Console Text
×

                

                

Program is not being debugged. Click "Debug" button to start program in debug mode.

#FunctionFile:Line
VariableValue
RegisterValue
ExpressionValue