diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..5d4653d --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: gcc 生成活动文件", + "command": "/usr/bin/gcc", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "调试器生成的任务。" + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/exp1/.vscode/launch.json b/exp1/.vscode/launch.json new file mode 100644 index 0000000..f980ab9 --- /dev/null +++ b/exp1/.vscode/launch.json @@ -0,0 +1,7 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [] +} \ No newline at end of file diff --git a/exp1/.vscode/tasks.json b/exp1/.vscode/tasks.json new file mode 100644 index 0000000..5d4653d --- /dev/null +++ b/exp1/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: gcc 生成活动文件", + "command": "/usr/bin/gcc", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "调试器生成的任务。" + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/exp1/badcount b/exp1/badcount new file mode 100755 index 0000000..01e7892 Binary files /dev/null and b/exp1/badcount differ diff --git a/exp1/badcount.c b/exp1/badcount.c new file mode 100644 index 0000000..3b6acf9 --- /dev/null +++ b/exp1/badcount.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include + +int cnt = 0; +sem_t mutex; + +void *increase(void *vargp) +{ + unsigned int niters = (unsigned int)vargp; + for (unsigned int i = 0; i < niters; i++) + { + sem_wait(&mutex); + cnt++; + sem_post(&mutex); + } + return NULL; +} + +void *decrease(void *vargp) +{ + unsigned int niters = (unsigned int)vargp; + for (unsigned int i = 0; i < niters; i++) + { + sem_wait(&mutex); + cnt--; + sem_post(&mutex); + } + return NULL; +} + +int main(int argc, char **argv) +{ + unsigned int niters; + pthread_t t1,t2; + + if (argc != 2) + { + printf("Usage: %s \n", argv[0]); + exit(2); + } + niters = atoll(argv[1]); + + sem_init(&mutex, 0, 1); + pthread_create(&t1,NULL,increase,(void*)niters); + pthread_create(&t2,NULL,decrease,(void*)niters); + pthread_join(t1,NULL); + pthread_join(t2,NULL); + + if (cnt != 0) + { + printf("Error! cnt=%d\n", cnt); + } + else + { + printf("Correct! cnt=%d\n", cnt); + } + sem_destroy(&mutex); + exit(0); +} \ No newline at end of file diff --git a/exp1/task61 b/exp1/task61 new file mode 100755 index 0000000..1b8468a Binary files /dev/null and b/exp1/task61 differ diff --git a/exp1/task61.c b/exp1/task61.c new file mode 100644 index 0000000..67dee6d --- /dev/null +++ b/exp1/task61.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include + +void *wokerT1(void *vargp) +{ + for(int i=0; i<5; i++) + { + printf("My name is Lvjinzhong\n"); + int sleep_time = rand() % 5 + 1; + sleep(sleep_time); + } + return NULL; +} + +void *wokerT2(void *vargp) +{ + for(int i=0; i<5; i++) + { + printf("My student number is 2024414290124\n"); + int sleep_time = rand() % 5 + 1; + sleep(sleep_time); + } + return NULL; +} + +void *wokerT3(void *vargp) +{ + for(int i=0; i<5; i++) + { + time_t t = time(NULL); + printf("Current time %s\n",ctime(&t)); + int sleep_time = rand() % 5 + 1; + sleep(sleep_time); + } + return NULL; +} + +int main() +{ + pthread_t t1,t2,t3; + pthread_create(&t1, NULL, wokerT1, NULL); + pthread_create(&t2, NULL, wokerT2, NULL); + pthread_create(&t3, NULL, wokerT3, NULL); + + + pthread_join(t1, NULL); + pthread_join(t2, NULL); + pthread_join(t3, NULL); + return 0; +} \ No newline at end of file diff --git a/吕锦中202441429012406/2024414290124-吕锦中-lab6.tar.gz b/吕锦中202441429012406/2024414290124-吕锦中-lab6.tar.gz new file mode 100644 index 0000000..81e1f2f Binary files /dev/null and b/吕锦中202441429012406/2024414290124-吕锦中-lab6.tar.gz differ diff --git a/吕锦中202441429012406/badcount.c b/吕锦中202441429012406/badcount.c new file mode 100644 index 0000000..5a10fe9 --- /dev/null +++ b/吕锦中202441429012406/badcount.c @@ -0,0 +1,34 @@ +#include +#include +#include + +volatile long long counter = 0; +long long niters; + +void *thread_func(void *arg) { + for (long long i = 0; i < niters; i++) { + counter++; + } + return NULL; +} + +int main(int argc, char *argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + niters = atoll(argv[1]); + + pthread_t t1, t2; + pthread_create(&t1, NULL, thread_func, NULL); + pthread_create(&t2, NULL, thread_func, NULL); + + pthread_join(t1, NULL); + pthread_join(t2, NULL); + + printf("Expected: %lld, Got: %lld\n", 2 * niters, counter); + if (counter != 2 * niters) { + printf("ERROR: Race condition detected!\n"); + } + return 0; +} diff --git a/吕锦中202441429012406/matmult.c b/吕锦中202441429012406/matmult.c new file mode 100644 index 0000000..fff4cb7 --- /dev/null +++ b/吕锦中202441429012406/matmult.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include + +#define MAX_THREADS 64 + +int N; +double **A, **B, **C_parallel, **C_serial; +int nthreads; +int rows_per_thread; + +void **allocate_matrix(int n) { + double **mat = (double **)malloc(n * sizeof(double *)); + for (int i = 0; i < n; i++) { + mat[i] = (double *)malloc(n * sizeof(double)); + } + return (void **)mat; +} + +void free_matrix(double **mat, int n) { + for (int i = 0; i < n; i++) free(mat[i]); + free(mat); +} + +void init_matrix(double **mat, int n) { + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + mat[i][j] = (double)(rand() % 100) / 10.0; +} + +void *multiply_thread(void *arg) { + int start_row = *(int *)arg; + int end_row = start_row + rows_per_thread; + if (end_row > N) end_row = N; + + for (int i = start_row; i < end_row; i++) { + for (int j = 0; j < N; j++) { + double sum = 0.0; + for (int k = 0; k < N; k++) { + sum += A[i][k] * B[k][j]; + } + C_parallel[i][j] = sum; + } + } + return NULL; +} + +void serial_multiply() { + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + double sum = 0.0; + for (int k = 0; k < N; k++) { + sum += A[i][k] * B[k][j]; + } + C_serial[i][j] = sum; + } + } +} + +double get_time() { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec + tv.tv_usec / 1000000.0; +} + +int verify() { + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + if (C_parallel[i][j] - C_serial[i][j] > 0.001 || + C_serial[i][j] - C_parallel[i][j] > 0.001) { + printf("Mismatch at [%d][%d]: parallel=%.6f, serial=%.6f\n", + i, j, C_parallel[i][j], C_serial[i][j]); + return 0; + } + } + } + return 1; +} + +int main(int argc, char *argv[]) { + if (argc != 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + N = atoi(argv[1]); + nthreads = atoi(argv[2]); + if (nthreads > MAX_THREADS) nthreads = MAX_THREADS; + if (nthreads > N) nthreads = N; + + rows_per_thread = N / nthreads; + + srand(42); + + A = (double **)allocate_matrix(N); + B = (double **)allocate_matrix(N); + C_parallel = (double **)allocate_matrix(N); + C_serial = (double **)allocate_matrix(N); + + init_matrix(A, N); + init_matrix(B, N); + + pthread_t threads[MAX_THREADS]; + int starts[MAX_THREADS]; + + double t_start = get_time(); + + for (int i = 0; i < nthreads; i++) { + starts[i] = i * rows_per_thread; + pthread_create(&threads[i], NULL, multiply_thread, &starts[i]); + } + for (int i = 0; i < nthreads; i++) { + pthread_join(threads[i], NULL); + } + + double t_end = get_time(); + double t_parallel = t_end - t_start; + + /* Serial version for verification */ + t_start = get_time(); + serial_multiply(); + t_end = get_time(); + double t_serial = t_end - t_start; + + printf("Matrix size: %d x %d, Threads: %d\n", N, N, nthreads); + printf("Parallel time: %.6f s\n", t_parallel); + printf("Serial time: %.6f s\n", t_serial); + printf("Speedup: %.4f\n", t_serial / t_parallel); + printf("Efficiency: %.4f\n", t_serial / t_parallel / nthreads); + + if (verify()) { + printf("Verification: SUCCESS\n"); + } else { + printf("Verification: FAILED\n"); + } + + free_matrix(A, N); + free_matrix(B, N); + free_matrix(C_parallel, N); + free_matrix(C_serial, N); + + return 0; +} diff --git a/吕锦中202441429012406/psum64.c b/吕锦中202441429012406/psum64.c new file mode 100644 index 0000000..afcf8fe --- /dev/null +++ b/吕锦中202441429012406/psum64.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include + +#define MAX_THREADS 64 +#define N 1000000000LL + +long long global_sum = 0; +long long nelems_per_thread; +pthread_mutex_t mutex; + +void *sum_thread(void *arg) { + long long start = *(long long *)arg; + long long local_sum = 0; + long long end = start + nelems_per_thread; + if (end > N) end = N; + for (long long i = start; i < end; i++) { + local_sum += i; + } + pthread_mutex_lock(&mutex); + global_sum += local_sum; + pthread_mutex_unlock(&mutex); + return NULL; +} + +double get_time() { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec + tv.tv_usec / 1000000.0; +} + +int main(int argc, char *argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + int nthreads = atoi(argv[1]); + if (nthreads > MAX_THREADS) nthreads = MAX_THREADS; + + nelems_per_thread = N / nthreads; + pthread_mutex_init(&mutex, NULL); + + pthread_t threads[MAX_THREADS]; + long long starts[MAX_THREADS]; + + double t_start = get_time(); + + for (int i = 0; i < nthreads; i++) { + starts[i] = i * nelems_per_thread; + pthread_create(&threads[i], NULL, sum_thread, &starts[i]); + } + + for (int i = 0; i < nthreads; i++) { + pthread_join(threads[i], NULL); + } + + double t_end = get_time(); + double elapsed = t_end - t_start; + + printf("Threads: %d, Sum: %lld, Time: %.6f s\n", nthreads, global_sum, elapsed); + + pthread_mutex_destroy(&mutex); + return 0; +} diff --git a/吕锦中202441429012406/task61.c b/吕锦中202441429012406/task61.c new file mode 100644 index 0000000..93b86d7 --- /dev/null +++ b/吕锦中202441429012406/task61.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include + +void *thread_T1(void *arg) { + for (int i = 1; i <= 5; i++) { + printf("My name is 吕锦中\n"); + if (i < 5) { + int sleep_time = rand() % 5 + 1; + sleep(sleep_time); + } + } + return NULL; +} + +void *thread_T2(void *arg) { + for (int i = 1; i <= 5; i++) { + printf("My student number is 2024414290124\n"); + if (i < 5) { + int sleep_time = rand() % 5 + 1; + sleep(sleep_time); + } + } + return NULL; +} + +void *thread_T3(void *arg) { + for (int i = 1; i <= 5; i++) { + time_t now = time(NULL); + struct tm *tm_info = localtime(&now); + char time_str[64]; + strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info); + printf("Current time %s\n", time_str); + if (i < 5) { + int sleep_time = rand() % 5 + 1; + sleep(sleep_time); + } + } + return NULL; +} + +int main() { + srand(time(NULL)); + pthread_t t1, t2, t3; + + pthread_create(&t1, NULL, thread_T1, NULL); + pthread_create(&t2, NULL, thread_T2, NULL); + pthread_create(&t3, NULL, thread_T3, NULL); + + pthread_join(t1, NULL); + pthread_join(t2, NULL); + pthread_join(t3, NULL); + + printf("All threads finished.\n"); + return 0; +} diff --git a/吕锦中202441429012406/task62.c b/吕锦中202441429012406/task62.c new file mode 100644 index 0000000..be4caa5 --- /dev/null +++ b/吕锦中202441429012406/task62.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +long long counter = 0; +long long niters; +sem_t mutex; + +void *thread_func(void *arg) { + for (long long i = 0; i < niters; i++) { + sem_wait(&mutex); + counter++; + sem_post(&mutex); + } + return NULL; +} + +int main(int argc, char *argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + niters = atoll(argv[1]); + + sem_init(&mutex, 0, 1); + + pthread_t t1, t2; + pthread_create(&t1, NULL, thread_func, NULL); + pthread_create(&t2, NULL, thread_func, NULL); + + pthread_join(t1, NULL); + pthread_join(t2, NULL); + + sem_destroy(&mutex); + + printf("Expected: %lld, Got: %lld\n", 2 * niters, counter); + if (counter == 2 * niters) { + printf("SUCCESS: No race condition.\n"); + } else { + printf("ERROR: Race condition detected!\n"); + } + return 0; +} diff --git a/吕锦中202441429012406/task63.c b/吕锦中202441429012406/task63.c new file mode 100644 index 0000000..9f1ad48 --- /dev/null +++ b/吕锦中202441429012406/task63.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include +#include + +#define POISON -1 + +typedef struct { + int *buf; + int n; + int outpos; + int inpos; + sem_t mutex; + sem_t slots; + sem_t items; +} sbuf_t; + +long long produced_sum = 0; +long long consumed_sum = 0; +sem_t sum_mutex; +int consumed_count = 0; +int total_items; +sem_t count_mutex; + +void sbuf_init(sbuf_t *sp, int n) { + sp->buf = (int *)malloc(n * sizeof(int)); + sp->n = n; + sp->outpos = 0; + sp->inpos = 0; + sem_init(&sp->mutex, 0, 1); + sem_init(&sp->slots, 0, n); + sem_init(&sp->items, 0, 0); +} + +void sbuf_deinit(sbuf_t *sp) { + free(sp->buf); + sem_destroy(&sp->mutex); + sem_destroy(&sp->slots); + sem_destroy(&sp->items); +} + +void sbuf_insert(sbuf_t *sp, int item) { + sem_wait(&sp->slots); + sem_wait(&sp->mutex); + sp->buf[sp->inpos] = item; + sp->inpos = (sp->inpos + 1) % sp->n; + sem_post(&sp->mutex); + sem_post(&sp->items); +} + +int sbuf_remove(sbuf_t *sp) { + sem_wait(&sp->items); + sem_wait(&sp->mutex); + int item = sp->buf[sp->outpos]; + sp->outpos = (sp->outpos + 1) % sp->n; + sem_post(&sp->mutex); + sem_post(&sp->slots); + return item; +} + +typedef struct { + sbuf_t *sp; + int num_items; + int id; +} producer_arg_t; + +typedef struct { + sbuf_t *sp; + int id; +} consumer_arg_t; + +void *producer(void *arg) { + producer_arg_t *pa = (producer_arg_t *)arg; + for (int i = 0; i < pa->num_items; i++) { + int val = rand() % 1000; + sbuf_insert(pa->sp, val); + sem_wait(&sum_mutex); + produced_sum += val; + sem_post(&sum_mutex); + printf("[Producer %d] produced %d\n", pa->id, val); + } + return NULL; +} + +void *consumer(void *arg) { + consumer_arg_t *ca = (consumer_arg_t *)arg; + while (1) { + int item = sbuf_remove(ca->sp); + if (item == POISON) { + /* Put poison back for other consumers, then exit */ + sbuf_insert(ca->sp, POISON); + break; + } + sem_wait(&sum_mutex); + consumed_sum += item; + sem_post(&sum_mutex); + sem_wait(&count_mutex); + consumed_count++; + sem_post(&count_mutex); + printf("[Consumer %d] consumed %d\n", ca->id, item); + } + return NULL; +} + +int main(int argc, char *argv[]) { + if (argc != 5) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + int k = atoi(argv[1]); + int items_per_producer = atoi(argv[2]); + int m = atoi(argv[3]); + int N = atoi(argv[4]); + total_items = k * items_per_producer; + + srand(time(NULL)); + sem_init(&sum_mutex, 0, 1); + sem_init(&count_mutex, 0, 1); + + sbuf_t buf; + sbuf_init(&buf, N); + + pthread_t *producers = malloc(k * sizeof(pthread_t)); + pthread_t *consumers = malloc(m * sizeof(pthread_t)); + producer_arg_t *pargs = malloc(k * sizeof(producer_arg_t)); + consumer_arg_t *cargs = malloc(m * sizeof(consumer_arg_t)); + + for (int i = 0; i < k; i++) { + pargs[i].sp = &buf; + pargs[i].num_items = items_per_producer; + pargs[i].id = i + 1; + pthread_create(&producers[i], NULL, producer, &pargs[i]); + } + + for (int i = 0; i < m; i++) { + cargs[i].sp = &buf; + cargs[i].id = i + 1; + pthread_create(&consumers[i], NULL, consumer, &cargs[i]); + } + + /* Wait for all producers to finish */ + for (int i = 0; i < k; i++) { + pthread_join(producers[i], NULL); + } + + /* Insert one poison pill to start the termination chain */ + sbuf_insert(&buf, POISON); + + /* Wait for all consumers to finish */ + for (int i = 0; i < m; i++) { + pthread_join(consumers[i], NULL); + } + + printf("\n=== Verification ===\n"); + printf("Produced sum : %lld\n", produced_sum); + printf("Consumed sum : %lld\n", consumed_sum); + if (produced_sum == consumed_sum) { + printf("SUCCESS: Sums match! Program is correct.\n"); + } else { + printf("ERROR: Sum mismatch! Difference = %lld\n", + produced_sum - consumed_sum); + } + + free(producers); + free(consumers); + free(pargs); + free(cargs); + sbuf_deinit(&buf); + sem_destroy(&sum_mutex); + sem_destroy(&count_mutex); + + return 0; +} diff --git a/吕锦中202441429012406/task64.c b/吕锦中202441429012406/task64.c new file mode 100644 index 0000000..4d1978d --- /dev/null +++ b/吕锦中202441429012406/task64.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include + +#define MAX_THREADS 64 +#define N 1000000000LL + +long long global_sum = 0; +long long nelems_per_thread; +pthread_mutex_t mutex; + +void *sum_squares_thread(void *arg) { + long long start = *(long long *)arg; + long long end = start + nelems_per_thread; + if (end > N) end = N; + long long local_sum = 0; + for (long long i = start; i < end; i++) { + local_sum += i * i; + } + pthread_mutex_lock(&mutex); + global_sum += local_sum; + pthread_mutex_unlock(&mutex); + return NULL; +} + +double get_time() { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec + tv.tv_usec / 1000000.0; +} + +int main(int argc, char *argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + int nthreads = atoi(argv[1]); + if (nthreads > MAX_THREADS) nthreads = MAX_THREADS; + + nelems_per_thread = N / nthreads; + pthread_mutex_init(&mutex, NULL); + + pthread_t threads[MAX_THREADS]; + long long starts[MAX_THREADS]; + + double t_start = get_time(); + + for (int i = 0; i < nthreads; i++) { + starts[i] = i * nelems_per_thread; + pthread_create(&threads[i], NULL, sum_squares_thread, &starts[i]); + } + + for (int i = 0; i < nthreads; i++) { + pthread_join(threads[i], NULL); + } + + double t_end = get_time(); + double elapsed = t_end - t_start; + + /* Expected sum of squares: (n-1)*n*(2n-1)/6 */ + long long expected = (N - 1) * N * (2 * N - 1) / 6; + printf("Threads: %d, Sum of squares: %lld, Expected: %lld, Time: %.6f s\n", + nthreads, global_sum, expected, elapsed); + + pthread_mutex_destroy(&mutex); + return 0; +} diff --git a/吕锦中202441429012406/task66.c b/吕锦中202441429012406/task66.c new file mode 100644 index 0000000..79a8bdf --- /dev/null +++ b/吕锦中202441429012406/task66.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include + +#define ITERATIONS 10000 + +double get_time() { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec + tv.tv_usec / 1000000.0; +} + +void *dummy_thread(void *arg) { + return NULL; +} + +int main() { + double t_start, t_end; + double fork_total = 0, pthread_total = 0; + + /* Measure fork() */ + t_start = get_time(); + for (int i = 0; i < ITERATIONS; i++) { + pid_t pid = fork(); + if (pid == 0) { + _exit(0); + } else if (pid > 0) { + waitpid(pid, NULL, 0); + } + } + t_end = get_time(); + fork_total = t_end - t_start; + + /* Measure pthread_create() */ + t_start = get_time(); + for (int i = 0; i < ITERATIONS; i++) { + pthread_t tid; + pthread_create(&tid, NULL, dummy_thread, NULL); + pthread_join(tid, NULL); + } + t_end = get_time(); + pthread_total = t_end - t_start; + + printf("=== Performance Comparison ===\n"); + printf("Iterations: %d\n", ITERATIONS); + printf("fork() total time: %.6f s (avg: %.3f us)\n", + fork_total, fork_total / ITERATIONS * 1000000); + printf("pthread_create() total time: %.6f s (avg: %.3f us)\n", + pthread_total, pthread_total / ITERATIONS * 1000000); + printf("Ratio (fork/pthread): %.2fx\n", fork_total / pthread_total); + printf("\nExplanation: fork() creates a new process with a copy of the\n"); + printf("entire address space, which is much heavier than pthread_create()\n"); + printf("which only creates a new thread sharing the same address space.\n"); + + return 0; +} diff --git a/吕锦中202441429012406/task67.c b/吕锦中202441429012406/task67.c new file mode 100644 index 0000000..6264274 --- /dev/null +++ b/吕锦中202441429012406/task67.c @@ -0,0 +1,177 @@ +#include +#include +#include +#include +#include +#include + +typedef struct { + int *buf; + int n; + int outpos; + int inpos; + sem_t mutex; + sem_t slots; + sem_t items; +} sbuf_t; + +int num_workers = 5; +int target_workers = 5; +pthread_mutex_t worker_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_t *workers = NULL; +sbuf_t *sbuf_ptr = NULL; +volatile int running = 1; + +void sbuf_init(sbuf_t *sp, int n) { + sp->buf = (int *)malloc(n * sizeof(int)); + sp->n = n; + sp->outpos = 0; + sp->inpos = 0; + sem_init(&sp->mutex, 0, 1); + sem_init(&sp->slots, 0, n); + sem_init(&sp->items, 0, 0); +} + +void sbuf_deinit(sbuf_t *sp) { + free(sp->buf); + sem_destroy(&sp->mutex); + sem_destroy(&sp->slots); + sem_destroy(&sp->items); +} + +void sbuf_insert(sbuf_t *sp, int item) { + sem_wait(&sp->slots); + sem_wait(&sp->mutex); + sp->buf[sp->inpos] = item; + sp->inpos = (sp->inpos + 1) % sp->n; + sem_post(&sp->mutex); + sem_post(&sp->items); +} + +int sbuf_remove(sbuf_t *sp) { + sem_wait(&sp->items); + sem_wait(&sp->mutex); + int item = sp->buf[sp->outpos]; + sp->outpos = (sp->outpos + 1) % sp->n; + sem_post(&sp->mutex); + sem_post(&sp->slots); + return item; +} + +void *worker_thread(void *arg) { + int id = *(int *)arg; + free(arg); + sbuf_t *sp = sbuf_ptr; + + while (running) { + int seconds = sbuf_remove(sp); + if (seconds == -1) break; + printf("[Worker %d] executing task: sleep %d seconds\n", id, seconds); + sleep(seconds); + printf("[Worker %d] task completed\n", id); + } + return NULL; +} + +void adjust_workers() { + pthread_mutex_lock(&worker_mutex); + int current = num_workers; + int target = target_workers; + + if (target > current) { + /* Increase workers */ + workers = realloc(workers, target * sizeof(pthread_t)); + for (int i = current; i < target; i++) { + int *id = malloc(sizeof(int)); + *id = i + 1; + pthread_create(&workers[i], NULL, worker_thread, id); + } + num_workers = target; + } else if (target < current) { + /* Decrease workers - insert poison pills */ + int to_remove = current - target; + for (int i = 0; i < to_remove; i++) { + sbuf_insert(sbuf_ptr, -1); + } + for (int i = target; i < current; i++) { + pthread_join(workers[i], NULL); + } + num_workers = target; + } + pthread_mutex_unlock(&worker_mutex); +} + +int main(int argc, char *argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + int buf_size = atoi(argv[1]); + sbuf_t buf; + sbuf_ptr = &buf; + sbuf_init(&buf, buf_size); + + /* Create initial 5 workers */ + workers = malloc(5 * sizeof(pthread_t)); + for (int i = 0; i < 5; i++) { + int *id = malloc(sizeof(int)); + *id = i + 1; + pthread_create(&workers[i], NULL, worker_thread, id); + } + + printf("=== Dynamic Thread Pool ===\n"); + printf("Commands:\n"); + printf(" - Add tasks\n"); + printf(" quit - Exit\n"); + printf("Initial workers: %d, Buffer size: %d\n\n", num_workers, buf_size); + + char line[256]; + while (1) { + printf("> "); + fflush(stdout); + if (!fgets(line, sizeof(line), stdin)) break; + + if (strncmp(line, "quit", 4) == 0) break; + + int task_count, seconds; + if (sscanf(line, "%d %d", &task_count, &seconds) == 2) { + printf("Adding %d tasks, each %d seconds...\n", task_count, seconds); + + for (int i = 0; i < task_count; i++) { + /* Check if buffer is full - double workers */ + int slots_avail; + sem_getvalue(&buf.slots, &slots_avail); + if (slots_avail == 0) { + target_workers = num_workers * 2; + printf("Buffer full, doubling workers to %d\n", target_workers); + adjust_workers(); + } + + sbuf_insert(&buf, seconds); + + /* Check if buffer is empty - halve workers */ + int items_avail; + sem_getvalue(&buf.items, &items_avail); + if (items_avail == 0 && num_workers > 2) { + target_workers = num_workers / 2; + if (target_workers < 2) target_workers = 2; + printf("Buffer empty, halving workers to %d\n", target_workers); + adjust_workers(); + } + } + } else { + printf("Invalid command. Usage: \n"); + } + } + + /* Shutdown */ + running = 0; + target_workers = 0; + adjust_workers(); + + sbuf_deinit(&buf); + free(workers); + printf("Done.\n"); + return 0; +}