Last active
November 29, 2021 13:50
-
-
Save szaydel/1f2a02cfb80f3e67d97f10926cc7a1fb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #define _GNU_SOURCE | |
| #include <getopt.h> | |
| #include <pthread.h> | |
| #include <stdbool.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <syslog.h> | |
| #include <time.h> | |
| #include <unistd.h> | |
| #define NTHREADS 10 // upper limit on number of threads | |
| static void | |
| random_str(char* buf, size_t n) | |
| { | |
| const char* runes = | |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | |
| struct timespec now = { 0 }; | |
| clock_gettime(CLOCK_MONOTONIC, &now); | |
| long seed = now.tv_nsec + (1e9 * now.tv_sec); | |
| srandom((unsigned int)seed); | |
| for (size_t i = 0; i < n; i++) { | |
| buf[i] = runes[random() % strlen(runes)]; | |
| } | |
| } | |
| typedef struct data data; | |
| struct data | |
| { | |
| long ident; | |
| size_t ret; | |
| struct timespec time_limit; | |
| char payload[1024]; | |
| }; | |
| static bool | |
| deadline_exceeded(struct timespec* start, struct timespec* limit) | |
| { | |
| struct timespec now; | |
| clock_gettime(CLOCK_MONOTONIC, &now); | |
| if ((now.tv_sec - start->tv_sec) > limit->tv_sec) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| void* | |
| task(void* in) | |
| { | |
| const int pri = LOG_USER | LOG_INFO; | |
| data* d; | |
| d = (data*)in; | |
| bool runtime_is_limited = d->time_limit.tv_sec != 0; | |
| struct timespec start; | |
| clock_gettime(CLOCK_MONOTONIC, &start); | |
| size_t i = 0; | |
| while (1) { | |
| // Terminate if we ran past the deadline | |
| if ((i > 0) && (i % 200 == 0)) { | |
| d->ret = i; | |
| if (runtime_is_limited && deadline_exceeded(&start, &d->time_limit)) { | |
| goto done; | |
| } | |
| } | |
| syslog(pri, "(%ld) %s [%zu]", d->ident, d->payload, i); | |
| i++; | |
| } | |
| done: | |
| pthread_exit((void*)d->ret); | |
| } | |
| int | |
| main(int argc, char** argv) | |
| { | |
| int c; | |
| int nthreads = 0; | |
| int active_time_secs = 0; | |
| // If we don't specify anything, just use the number of available CPUs | |
| // as the thread count. It is hard to imagine more threads than CPUs | |
| // will make things any better. | |
| nthreads = (int)sysconf(_SC_NPROCESSORS_ONLN); | |
| char* source = "test.source"; | |
| while (1) { | |
| int option_index = 0; | |
| static struct option long_options[] = { | |
| { "active-time-secs", required_argument, 0, 0 }, | |
| { "threads", required_argument, 0, 1 }, | |
| { "source", required_argument, 0, 2 }, | |
| { 0, 0, 0, 0 } | |
| }; | |
| c = getopt_long(argc, argv, "012", long_options, &option_index); | |
| if (c == -1) | |
| break; | |
| switch (c) { | |
| case 0: | |
| // --active-time-secs argument | |
| active_time_secs = atoi(optarg); | |
| if (active_time_secs < 0) { | |
| active_time_secs = 0; | |
| } | |
| printf("Setting active-time-secs = %d\n", active_time_secs); | |
| break; | |
| case 1: | |
| // --threads argument | |
| nthreads = atoi(optarg); | |
| if (nthreads <= 0 || nthreads > NTHREADS) { | |
| nthreads = NTHREADS; | |
| } | |
| printf("Setting nthreads = %d\n", nthreads); | |
| break; | |
| case 2: | |
| // --source argument | |
| source = optarg; | |
| if (strlen(source) < 2) { | |
| source = "test.source"; | |
| } | |
| printf("Setting source = %s\n", source); | |
| break; | |
| case '?': | |
| break; | |
| default: | |
| printf("?? getopt returned character code 0%o ??\n", c); | |
| } | |
| } | |
| if (optind < argc) { | |
| printf("non-option ARGV-elements: "); | |
| while (optind < argc) | |
| printf("%s ", argv[optind++]); | |
| printf("\n"); | |
| } | |
| long i; | |
| int ret, stat = 0; | |
| pthread_t tid[nthreads]; // Add one thread to account for main() | |
| pthread_attr_t attr; | |
| /* Init the thread attribute structure to defaults */ | |
| pthread_attr_init(&attr); | |
| /* Create all threads as joinable */ | |
| pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); | |
| openlog(source, LOG_NDELAY | LOG_PID, LOG_USER); | |
| // Thread creation loop | |
| for (i = 0; i < nthreads; i++) { | |
| printf("main: creating thread #%ld ...\n", i); | |
| data* dptr = (data*)malloc(sizeof(data)); | |
| dptr->ident = i; | |
| dptr->time_limit.tv_sec = active_time_secs; | |
| random_str(dptr->payload, 60); | |
| ret = pthread_create(&tid[i], &attr, task, (void*)dptr); | |
| if (ret) | |
| printf("pthread_create() failed! [%d]\n", ret); | |
| } | |
| pthread_attr_destroy(&attr); | |
| size_t tot_iters = 0; | |
| // Wait and join all threads | |
| for (i = 0; i < nthreads; i++) { | |
| printf("main: waiting on thread #%ld\n", i); | |
| size_t n; | |
| ret = pthread_join(tid[i], (void*)&n); | |
| tot_iters += n; | |
| char msg[32] = { 0 }; | |
| if (ret) { | |
| snprintf(msg, 32, "pthread_join() failed! [%ld]\n", i); | |
| perror(msg); | |
| } else { | |
| printf("Thread #%ld successfully joined; it terminated with " | |
| "status=%d\n", | |
| i, | |
| stat); | |
| } | |
| } | |
| printf("Completed a total of %zu iterations\n", tot_iters); | |
| pthread_exit(NULL); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment