// thread_recursive.c // Create a recursive tree of pthreads to observe scalability and resource usage. // Each thread creates `branch` child threads (if depth < max_depth), then joins them. // Usage: thread_recursive [-b branch] [-d max_depth] [-s hold_seconds] #define _GNU_SOURCE #include #include #include #include #include #include #include #include typedef struct { int depth; int max_depth; int branch; int hold_seconds; } tr_args_t; static pthread_mutex_t count_mtx = PTHREAD_MUTEX_INITIALIZER; static long total_created = 0; static double now_wall(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec + ts.tv_nsec / 1e9; } static void *thread_fn(void *varg) { tr_args_t args = *(tr_args_t *)varg; pthread_t *children = NULL; tr_args_t *children_args = NULL; if (args.depth >= args.max_depth) { sleep((unsigned)args.hold_seconds); return NULL; } children = calloc((size_t)args.branch, sizeof(pthread_t)); if (!children) { perror("calloc"); return NULL; } children_args = calloc((size_t)args.branch, sizeof(tr_args_t)); if (!children_args) { perror("calloc"); free(children); return NULL; } for (int i = 0; i < args.branch; ++i) { children_args[i].depth = args.depth + 1; children_args[i].max_depth = args.max_depth; children_args[i].branch = args.branch; children_args[i].hold_seconds = args.hold_seconds; int rc = pthread_create(&children[i], NULL, thread_fn, &children_args[i]); if (rc != 0) { fprintf(stderr, "pthread_create failed at depth %d (branch %d): %s\n", args.depth, i, strerror(rc)); children[i] = 0; break; } pthread_mutex_lock(&count_mtx); total_created++; pthread_mutex_unlock(&count_mtx); sched_yield(); } for (int i = 0; i < args.branch; ++i) { if (children[i] == 0) continue; pthread_join(children[i], NULL); } free(children_args); free(children); return NULL; } static void usage(const char *p) { fprintf(stderr, "Usage: %s [-b branch] [-d max_depth] [-s hold_seconds]\n", p); fprintf(stderr, "Defaults: branch=2, max_depth=6, hold_seconds=30\n"); } int main(int argc, char **argv) { int branch = 2; int max_depth = 6; int hold_seconds = 30; int opt; while ((opt = getopt(argc, argv, "b:d:s:h")) != -1) { switch (opt) { case 'b': branch = atoi(optarg); break; case 'd': max_depth = atoi(optarg); break; case 's': hold_seconds = atoi(optarg); break; case 'h': default: usage(argv[0]); return 1; } } if (branch < 1) branch = 1; if (max_depth < 0) max_depth = 0; if (hold_seconds < 0) hold_seconds = 0; printf("Thread recursive scaling: branch=%d, max_depth=%d, hold_seconds=%d\n", branch, max_depth, hold_seconds); double estimated = -1.0; if (branch == 1) { estimated = (double)(max_depth + 1); } else { double b = (double)branch; estimated = (pow(b, (double)(max_depth + 1)) - 1.0) / (b - 1.0); } printf("Estimated full-tree threads (including root): %.0f\n", estimated); double t0 = now_wall(); pthread_t root; tr_args_t root_args = {0, max_depth, branch, hold_seconds}; pthread_mutex_lock(&count_mtx); total_created = 1; // root counts as created pthread_mutex_unlock(&count_mtx); int rc = pthread_create(&root, NULL, thread_fn, &root_args); if (rc != 0) { fprintf(stderr, "Failed to create root thread: %s\n", strerror(rc)); return 2; } pthread_join(root, NULL); double t1 = now_wall(); printf("\nResult:\n"); printf(" total_created_threads: %ld\n", total_created); printf(" wall_time: %.6fs\n", t1 - t0); printf(" estimated_full_tree: %.0f\n", estimated); return 0; }