#include #include #include #include #include #include #include #include #include #include "litmus.h" #include "common.h" #define LITMUS_STATS_FILE "/proc/litmus/stats" struct thread_context { int id; int exec; int period; double duration; }; void* rt_thread(void *tcontext); int job(double exec_time); /* global as they are needed in the signal handler */ static struct thread_context *ctx; static pthread_t *task; static int num_threads; #define CALL( exp ) do { \ int ret; \ ret = exp; \ if (ret < 0) \ fprintf(stderr, "%s failed: %m\n", #exp); \ } while (0) static void die(char *error) { fprintf(stderr, "Error: %s (errno: %m)\n", error); exit(-1); } static void usage(char *error) { fprintf(stdout, "Usage: progam [options] ts-file ts-size duration(sec)\n"); die(error); } /* BEGIN Emulate rtspin behavior */ static double wctime() { struct timeval tv; gettimeofday(&tv, NULL); return (tv.tv_sec + 1E-6 * tv.tv_usec); } #define NUMS 4096 static int num[NUMS]; static double loop_length = 1.0; static double cputime() { struct timespec ts; int err; err = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); if (err != 0) perror("clock_gettime"); return (ts.tv_sec + 1E-9 * ts.tv_nsec); } static int loop_once(void) { int i, j = 0; for (i = 0; i < NUMS; i++) j += num[i]++; return j; } static int loop_for(double exec_time) { double t = 0; int tmp = 0; double start = cputime(); double now = cputime(); while (now + loop_length < start + exec_time) { tmp += loop_once(); t += loop_length; now = cputime(); } return tmp; } static void fine_tune(double interval) { double start, end, delta; start = wctime(); loop_for(interval); end = wctime(); delta = (end - start - interval) / interval; if (delta != 0) loop_length = loop_length / (1 - delta); } static void configure_loop(void) { double start; /* prime cache */ loop_once(); loop_once(); loop_once(); /* measure */ start = wctime(); loop_once(); /* hope we didn't get preempted */ loop_length = wctime(); loop_length -= start; /* fine tune */ fine_tune(0.1); fine_tune(0.1); fine_tune(0.1); } /* END Emulate rtspin behavior */ /* * see release_ts; loop on the litmus stat file until all threads are * ready to be released */ ssize_t read_file(const char* fname, void* buf, size_t maxlen) { int fd; ssize_t n = 0; size_t got = 0; fd = open(fname, O_RDONLY); if (fd == -1) return -1; while (got < maxlen && (n = read(fd, buf + got, maxlen - got)) > 0) got += n; close(fd); if (n < 0) return -1; else return got; } static void wait_until_ready(int expected) { int ready = 0, all = 0; char buf[100]; int loops = 0; ssize_t len; do { if (loops++ > 0) sleep(1); len = read_file(LITMUS_STATS_FILE, buf, sizeof(buf) - 1); if (len < 0) { fprintf(stderr, "(EE) Error while reading '%s': %m.\n" "(EE) Ignoring -w option.\n", LITMUS_STATS_FILE); break; } else { len = sscanf(buf, "real-time tasks = %d\n" "ready for release = %d\n", &all, &ready); if (len != 2) { fprintf(stderr, "(EE) Could not parse '%s'.\n" "(EE) Ignoring -w option.\n", LITMUS_STATS_FILE); break; } } printf("expected = %d, ready = %d\n", expected, ready); } while (expected > ready || ready < all); } static void sigalrm_handler(int n) { int i; if(n == SIGALRM) { printf("SIGALRM received, terminating threads\n"); /* will not call pthread_cleanup_pop, will leak memory */ for (i = 0; i < num_threads; i++) pthread_cancel(task[i]); for (i = 0; i < num_threads; i++) pthread_join(task[i], NULL); free(ctx); free(task); exit(0); } } #define OPTSTR "m:l:" int main(int argc, char** argv) { char *ts_file; FILE *ts; int i; /* in ms */ int exec; int period; int opt; /* in second */ int forced_exit_after = -1; double duration; struct sigaction sa; /* delay for release ts */ lt_t delay = ms2lt(1000); while ((opt = getopt(argc, argv, OPTSTR)) != -1) { switch (opt) { case 'l': forced_exit_after = atoi(optarg); break; case ':': usage("Argument missing."); break; case '?': default: usage("Bad argument."); break; } } if ((argc - optind) < 3 ) { usage("Argument(s) missing"); } ts_file = argv[optind]; num_threads = atoi(argv[optind + 1]); duration = atof(argv[optind + 2]); /* allocate memory for threads structures */ ctx = malloc(num_threads * sizeof(struct thread_context)); if (!ctx) { die("Cannot allocate ctx\n"); } task = malloc(num_threads * sizeof(pthread_t)); if (!task) { free(ctx); die("Cannot allocate task\n"); return -1; } /* read space separated input file */ if ((ts = fopen(ts_file, "r")) == NULL) { fprintf(stderr, "Cannot open %s\n", ts_file); free(task); free(ctx); return -1; } i = 0; /* assume a two column file "exec period" */ while (fscanf(ts, "%d %d\n", &exec, &period) != EOF) { if (i < num_threads) { ctx[i].id = i; ctx[i].exec = exec; ctx[i].period = period; ctx[i].duration = duration; i++; } else { fprintf(stderr,"Wrong parameters\n"); free(task); free(ctx); return -1; } } if (ferror(ts)) fprintf(stderr, "Error while reading %s\n", ts_file); fclose(ts); /* rtspin emulation, configure loop */ configure_loop(); init_litmus(); for (i = 0; i < num_threads; i++) { pthread_create(task + i, NULL, rt_thread, (void *) (ctx + i)); } /* wait for all threads to be properly initialized */ wait_until_ready(num_threads); sa.sa_handler = sigalrm_handler; sa.sa_flags = 0; if (forced_exit_after > 0) { sigaction(SIGALRM, &sa, NULL); alarm(forced_exit_after); } /* release all threads */ if (release_ts(&delay) < 0) { perror("Cannot release tasks\n"); free(task); free(ctx); return -1; } /* wait for all threads to end */ for (i = 0; i < num_threads; i++) pthread_join(task[i], NULL); free(task); free(ctx); return 0; } void cleanup_rt_thread(void *args) { /* do nothing atm */ printf("Cleaning up thread %d\n", gettid()); } void* rt_thread(void *tcontext) { int do_exit = 0; double start = 0; struct thread_context *ctx = (struct thread_context *) tcontext; lt_t wcet_ns, period_ns; fprintf(stderr, "RT Thread (%d,%d) active.\n", gettid(), ctx->id); fprintf(stderr, "%d) e = %d, p = %d\n", ctx->id, ctx->exec, ctx->period); wcet_ns = ctx->exec * __NS_PER_MS; period_ns = ctx->period * __NS_PER_MS; pthread_cleanup_push(cleanup_rt_thread, NULL); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); /* initialize this thread */ CALL( init_rt_thread() ); CALL( sporadic_task_ns(wcet_ns, period_ns, 0, 0, RT_CLASS_HARD, 0) ); /* became litmus tasl */ CALL( task_mode(LITMUS_RT_TASK) ); /* synchronous release */ CALL( wait_for_ts_release() ); start = wctime(); while (!do_exit && ((start + ctx->duration) > wctime())) { /* Invoke job, run time is in sec, ctx->exec in ms */ do_exit = job(ctx->exec * 0.0009); /* Wait until the next job is released. */ sleep_next_period(); } fprintf(stderr, "id = %d, ETA = %f, real = %f, diff = %f\n", gettid(), start + ctx->duration, wctime(), wctime() - start - ctx->duration); CALL ( task_mode(BACKGROUND_TASK) ); /* remove cleanup handler */ pthread_cleanup_pop(0); return NULL; } int job(double exec_time) { loop_for(exec_time); /* sleep next period is performed by main rt_thread */ return 0; }