简体   繁体   中英

Context switching executes same statement twice

I am trying to learn how context switching works and how to make your process switch context after receiving a particular signal. Here is my code

#include<stdio.h>
#include<stdlib.h>
#include<ucontext.h>
#include<signal.h>
#include<sys/time.h>
#include<sys/types.h>
#include<unistd.h>

#define STACK_SIZE 4096

static ucontext_t thread1, thread2;

void thread1_fun() {
    static int a = 1;
    while (1) {
        printf("calling thread1 for %d time\n", a);
        sleep(5);
        a > 20 ? a = 0 : a++;
    }
}

void thread2_fun() {
    static int a = 1;
    while (1) {
        printf("calling thread2 for %d time\n", a);
        sleep(5);
        a > 20 ? a = 0 : a++;
    }
}

void sig_handler(int signal) {
    static int curr_thread = 0;
    printf("received signal %d\n", signal);
    if (curr_thread == 1) {
        curr_thread = 0;
        printf("switching from thread1 to thread2\n");
        setcontext(&thread1);
    } else {
        curr_thread = 1;
        printf("switching from thread2 to thread1\n");    
        setcontext(&thread2);
    }
}

int main() {
    int i = 0;
    struct sigaction act;
    act.sa_handler = sig_handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGUSR1, &act, NULL);
    /* sigaction(SIGTERM, &act, NULL); */

    getcontext(&thread1);
    thread1.uc_stack.ss_sp = malloc (STACK_SIZE);
    thread1.uc_stack.ss_size = STACK_SIZE;
    thread1.uc_stack.ss_flags = 0;
    makecontext(&thread1, thread1_fun, 0);

    getcontext(&thread2);
    thread2.uc_stack.ss_sp = malloc (STACK_SIZE);
    thread2.uc_stack.ss_size = STACK_SIZE;
    thread2.uc_stack.ss_flags = 0;
    makecontext(&thread2, thread2_fun, 0);

    printf("%d\n", getpid());
    while (1);
}

Now I give command 'kill -s SIGUSR1 ' from terminal. The process switches the context after receiving this signal but the problem is that it prints the 'calling thread for %d time' twice.

For example if thread1 prints ' calling thread1 for 3rd time ' and goes to sleep and while thread1 is sleeping if I send signal to switch the context, thread2 starts executing, now if I again send signal to switch context then thread1 again prints 'calling thread1 for 3rd time' . Ideally it should come out of its sleep and increment the value of a, correct? Why is it printing same value twice?

Here is the output printed by code:

received signal 10

switching from thread2 to thread1

calling thread2 for 1 time

received signal 10

switching from thread1 to thread2

calling thread1 for 1 time

received signal 10

switching from thread2 to thread1

calling thread2 for 1 time

received signal 10

switching from thread1 to thread2

calling thread1 for 1 time

Please help me with this.

Although minor, the uc_link member of ucontext_t should be assigned a value before passing to makecontext .

Otherwise, from the Linux man page, setcontext will call the function that was passed to makecontext. This means that each time setcontext is called, no information is saved about the previous execution of either thread1_fun or thread2_fun. This is why the same message is printed multiple times. On the previous invocation, the function was within sleep before the counter could be incremented. On the next invocation, the function prints the unaltered value. swapcontext on the other hand will save the current context in the first argument and activates the context in the second argument.

Another thing of note is that a signal handler runs in a new context. This makes it problematic to call such functions as setcontext or swapcontext. See the discussion here about swapcontext and signals. As suggested in that link, a flag of type sig_atomic_t can be used to signal to the current state of execution in thread1_fun and thread2_fun to call swapcontext.

Here is the new signal variable declared right below the includes:

static sig_atmoic_t switch_context = 0;

Here is the updated thread1_fun (thread2_fun is simlar with thread1 and thread2 switched):

void thread1_fun() {
    static int a = 1;
    while (1) {
        printf("calling thread1 for %d time\n", a);
        sleep(5);
        a > 20 ? a = 0 : a++;
        if(switch_context) {
            switch_context = 0;
            swapcontext(&thread1, &thread2);
        }
    }
}

Here is the new sig_handler:

void sig_handler(int signal) {
    static int curr_thread = 1;
    printf("received signal %d\n", signal);
    if (curr_thread == 1) {
        curr_thread = 0;
        printf("switching from thread1 to thread2\n");
    } else {
        curr_thread = 1;
        printf("switching from thread2 to thread1\n");
    }
    switch_context = 1;
}

Additionally, I replaced the while (1); at the end of the main function to start the context on thread1.

ucontext_t main_thread;
if (swapcontext(&main_thread, &thread1) == -1) {
    perror("swapcontext");
    exit(EXIT_FAILURE);
}
return 0;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM