gbtimmon
gbtimmon

Reputation: 4332

ucontext.h and uc_link not returning from main thread

Here is what I want to do as part of a larger thread scheduling api.

I want to create a thread and when the main thread (the one creating the thread) exits, the thread I just created should execute. I am trying to do this with ucontext and the uc_link, but it is not working. It appears that my uc_link does not work when I try to set it for the current thread.

Here is a slightly modified example from this link which is what I tired to make this work.

http://pubs.opengroup.org/onlinepubs/009695399/functions/makecontext.html

#include <stdio.h>
#include <ucontext.h>


static ucontext_t ctx[3];


static void
f1 (void)
{
    puts("start f1");
    swapcontext(&ctx[1], &ctx[2]);
    puts("finish f1");
}


static void
f2 (void)
{
    puts("start f2");
    swapcontext(&ctx[2], &ctx[1]);
    puts("finish f2");
}


int
main (void)
{
    char st1[8192];
    char st2[8192];


    getcontext(&ctx[1]);
    ctx[1].uc_stack.ss_sp = st1;
    ctx[1].uc_stack.ss_size = sizeof st1;
    ctx[1].uc_link = 0;
    makecontext(&ctx[1], f1, 0);


    getcontext(&ctx[2]);
    ctx[2].uc_stack.ss_sp = st2;
    ctx[2].uc_stack.ss_size = sizeof st2;
    ctx[2].uc_link = &ctx[1];
    makecontext(&ctx[2], f2, 0);

    getcontext(&ctx[0]);
    ctx[0].uc_link = &ctx[2];
    return 0;
}

Expected output :

finished main
start f2
start f1
finish f2
finish f1

Given output :

finished main

How do I go about setting the uc_link for the current thread/process in a meaningful manner ?

Upvotes: 1

Views: 3074

Answers (2)

Armali
Armali

Reputation: 19375

At the link you provided they say

The uc_link member is used to determine the context that shall be resumed when the context being modified by makecontext() returns.

Since your program doesn't resume (execute) any context modified by makecontext(), the above does not apply and the program ends without resuming any context.

To achieve what you want, you have to define some function like

    void main_context()
    {
        // do everything you yet wanted to do in main()
        puts("finished main");
    }

and replace the

    return 0;

with

    char st0[8192];
    ctx[0].uc_stack.ss_sp = st0;
    ctx[0].uc_stack.ss_size = sizeof st0;
    makecontext(&ctx[0], main_context, 0);
    return -setcontext(&ctx[0]);

Upvotes: 0

dan4thewin
dan4thewin

Reputation: 1184

Replacing main in the above code with the following produces the expected output.

int
main (void)
{
    char st1[8192];
    char st2[8192];

    getcontext(&ctx[1]);
    ctx[1].uc_stack.ss_sp = st1;
    ctx[1].uc_stack.ss_size = sizeof st1;
    ctx[1].uc_link = &ctx[0];
    makecontext(&ctx[1], f1, 0);

    getcontext(&ctx[2]);
    ctx[2].uc_stack.ss_sp = st2;
    ctx[2].uc_stack.ss_size = sizeof st2;
    ctx[2].uc_link = &ctx[1];
    makecontext(&ctx[2], f2, 0);

    getcontext(&ctx[0]);
    ctx[0].uc_mcontext.gregs[16] += 0x26;
    puts("finish main");
    setcontext(&ctx[2]);
    return 0;
}

BUT this doesn't do what you say you want.

The context functions are a way to put a specific return address on the stack.

  • getcontext captures the address of the next instruction into a struct
  • makecontext changes the address in the struct to that of its function argument
  • setcontext/swapcontext puts the address in the struct on the stack and returns to it

This program above has only one thread of control. I think you really want multiple threads, in which case you wouldn't use these context functions.

For more information about the stack and the C calling convention, Eli Bendersky has two nice articles with diagrams:

FWIW, to get the 0x26 constant in the above code, I had to disassemble main to find the first address after the setcontext call.

Upvotes: 1

Related Questions