A non-trivial example of how to use user contexts for trivial scheduling.
authorTomas Mudrunka <tomas@mudrunka.cz>
Fri, 7 Jan 2022 14:35:17 +0000 (15:35 +0100)
committerTomas Mudrunka <tomas@mudrunka.cz>
Fri, 7 Jan 2022 14:35:17 +0000 (15:35 +0100)
Originaly from https://gist.github.com/DanGe42/7148946

c/context_demo.c [new file with mode: 0644]

diff --git a/c/context_demo.c b/c/context_demo.c
new file mode 100644 (file)
index 0000000..a04cc66
--- /dev/null
@@ -0,0 +1,181 @@
+#include <ucontext.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <poll.h>
+
+/* ucontext sample program
+
+   Downloaded from https://gist.github.com/DanGe42/7148946
+   A non-trivial example of how to use user contexts for trivial scheduling. 
+
+   by Jon Kaplan and Robert Spier (October 24th, 1999)
+   Updated for 2000 and poll, Robert Spier
+   sigprocmask gaff fixed by Ben Slusky
+   ported to Linux by Eric Cronin
+   $Id: context_demo.c 37 2006-10-12 22:16:59Z ecronin $
+
+   Demonstrates swapping between multiple processor contexts (in a
+   _stable_ way).  n-1 contexts do nothing.  1 context accepts input
+   and outputs it.
+*/
+
+
+#define NUMCONTEXTS 10              /* how many contexts to make */
+#define STACKSIZE 4096              /* stack size */
+#define INTERVAL 100                /* timer interval in nanoseconds */
+
+sigset_t set;                       /* process wide signal mask */
+ucontext_t signal_context;          /* the interrupt context */
+void *signal_stack;                 /* global interrupt stack */
+
+ucontext_t contexts[NUMCONTEXTS];   /* store our context info */
+int curcontext = 0;                 /* whats the current context? */
+ucontext_t *cur_context;            /* a pointer to the current_context */
+
+/* The scheduling algorithm; selects the next context to run, then starts it. */
+void
+scheduler()
+{
+    printf("scheduling out thread %d\n", curcontext);
+
+    curcontext = (curcontext + 1) % NUMCONTEXTS; /* round robin */
+    cur_context = &contexts[curcontext];
+
+    printf("scheduling in thread %d\n", curcontext);
+
+    setcontext(cur_context); /* go */
+}
+
+/*
+  Timer interrupt handler.
+  Creates a new context to run the scheduler in, masks signals, then swaps
+  contexts saving the previously executing thread and jumping to the
+  scheduler.
+*/
+void
+timer_interrupt(int j, siginfo_t *si, void *old_context)
+{
+    /* Create new scheduler context */
+    getcontext(&signal_context);
+    signal_context.uc_stack.ss_sp = signal_stack;
+    signal_context.uc_stack.ss_size = STACKSIZE;
+    signal_context.uc_stack.ss_flags = 0;
+    sigemptyset(&signal_context.uc_sigmask);
+    makecontext(&signal_context, scheduler, 1);
+
+    /* save running thread, jump to scheduler */
+    swapcontext(cur_context,&signal_context);
+}
+
+/* Set up SIGALRM signal handler */
+void
+setup_signals(void)
+{
+    struct sigaction act;
+
+    act.sa_sigaction = timer_interrupt;
+    sigemptyset(&act.sa_mask);
+    act.sa_flags = SA_RESTART | SA_SIGINFO;
+
+    sigemptyset(&set);
+    sigaddset(&set, SIGALRM);
+
+    if(sigaction(SIGALRM, &act, NULL) != 0) {
+        perror("Signal handler");
+    }
+}
+
+
+/* Thread bodies */
+void
+thread1()
+{
+    while(1) {
+        poll(NULL,0,100);
+    };     /* do nothing nicely */
+}
+
+void
+thread2()
+{
+    char buf[1024];
+    /* get a string.. print a string.. ad infinitum */
+    while(1) {
+        fgets(buf, 1024, stdin);
+        printf("[[[[[[%s]]]]]]\n",buf);
+    }
+}
+
+/* helper function to create a context.
+   initialize the context from the current context, setup the new
+   stack, signal mask, and tell it which function to call.
+*/
+void
+mkcontext(ucontext_t *uc,  void *function)
+{
+    void * stack;
+
+    getcontext(uc);
+
+    stack = malloc(STACKSIZE);
+    if (stack == NULL) {
+        perror("malloc");
+        exit(1);
+    }
+    /* we need to initialize the ucontext structure, give it a stack,
+        flags, and a sigmask */
+    uc->uc_stack.ss_sp = stack;
+    uc->uc_stack.ss_size = STACKSIZE;
+    uc->uc_stack.ss_flags = 0;
+    if (sigemptyset(&uc->uc_sigmask) < 0){
+      perror("sigemptyset");
+      exit(1);
+    }
+
+    /* setup the function we're going to, and n-1 arguments. */
+    makecontext(uc, function, 1);
+
+    printf("context is %p\n", uc);
+}
+
+
+int
+main()
+{
+    int i;
+    struct itimerval it;
+
+    fprintf(stderr,"Process Id: %d\n", (int)getpid());
+
+    /* allocate the global signal/interrupt stack */
+    signal_stack = malloc(STACKSIZE);
+    if (signal_stack == NULL) {
+        perror("malloc");
+        exit(1);
+    }
+
+    /* make all our contexts */
+    mkcontext(&contexts[0], thread2);
+    for(i=1; i < NUMCONTEXTS; i++)
+        mkcontext(&contexts[i], thread1);
+
+
+    /* initialize the signal handlers */
+    setup_signals();
+
+    /* setup our timer */
+    it.it_interval.tv_sec = 0;
+    it.it_interval.tv_usec = INTERVAL * 1000;
+    it.it_value = it.it_interval;
+    if (setitimer(ITIMER_REAL, &it, NULL) ) perror("setitiimer");
+
+    /* force a swap to the first context */
+    cur_context = &contexts[0];
+    setcontext(&contexts[0]);
+
+    return 0; /* make gcc happy */
+}
This page took 0.222136 seconds and 4 git commands to generate.