如何通知编译器“ getcontext”可以返回多次?

时间:2018-11-01 12:45:46

标签: c gcc compiler-optimization ucontext

getcontext可以返回多次。例如,我已草绘了一个类似于已分解的here的C程序:

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

struct communication {
        const ucontext_t *return_ctx;
        int return_value;
};

static void
test(ucontext_t *thisctx, struct communication *comm)
{
        int i = 0;
        assert(getcontext(thisctx) == 0);
        // getcontext will return 3 times with i having different values

        comm->return_value = ++i;
        setcontext(comm->return_ctx);
        assert(0);
}

int
main(void)
{
        ucontext_t mainctx, testctx;
        struct communication comm;
        char test_stack[SIGSTKSZ];

        assert(getcontext(&testctx) == 0);
        testctx.uc_stack.ss_sp = test_stack;
        testctx.uc_stack.ss_size = sizeof test_stack;
        makecontext(&testctx, test, 2,
                &testctx, &comm);

        for (int i = 0; i < 3; ++i) {
                // Rewind test's execution where 'getcontext' returns
                comm.return_ctx = &mainctx;
                assert(swapcontext(&mainctx, &testctx) == 0);
                assert(printf("%d\n", comm.return_value) > 0);
        }

        return 0;
}

编译并运行

$ gcc -std=gnu99 -O3 -o getcontext_test getcontext_test.c
$ ./getcontext_test
1
1
1

没有给出预期的1 2 3,因为编译器认为i仅在分配给1时可以等于comm->return_value

我可以通过定义i volatile来解决此问题,但是我希望对这一问题采取更规范的方法。

1 个答案:

答案 0 :(得分:1)

i(在test函数中;您要询问的标识符的函数之间名称的重复是不幸的),您想要做的事情是必要的(但可能还不够) volatile。这已经是标准(7.13.2.1¶3)中的要求:

  

从调用longjmp函数时起,所有可访问的对象都具有值,并且抽象机的所有其他组件都具有状态,只是自动存储持续时间的对象的值在包含调用以下内容的函数中是局部的不确定具有volatile限定类型并且已在setjmp调用和longjmp调用之间更改的相应setjmp宏是不确定的。

对于可能在setjmp的连续返回之间进行修改的对象,因此,您应该(并且必须)对getcontext做同样的事情是非常有意义的。

由于其他原因,编译器可能有必要意识到getcontext返回多次。在GCC和兼容的编译器(除MSVC以外的大多数其他编译器)上,您可以使用__attribute__((__returns_twice__))来实现。但是,声明getcontext(或编译器内部)的标头应该已经在做这样的事情了。