线程之间的setjmp/longjmp处理超时

ssi*_*fod 3 c multithreading pthreads longjmp setjmp

我正在将软件从嵌入式计算机移植到Linux机器上.(Ubuntu 14.04或Raspbian(raspberry pi))

原始程序使用setjmp/longjmp来处理超时和CTRL + C事件.它运行在具有单个主(一个线程)的微控制器上.

我正在尝试使用线程(pthreads)时有类似的行为.

我的想法是我想要超时或CTRL + C来重启无限循环.

原始代码正在执行类似下面的代码.我不介意用别的东西放弃setjmp/longjmp.(例如:try/catch或signal或pthread_kill,条件变量等.)

知道如何用C/C++实现类似的行为吗?

这是代码似乎部分工作,可能不推荐/破坏:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <setjmp.h>

// Define
#define TICK_NS_TIME (10000000)                                                 // 0.01 sec = 10 ms (100 times per second)
#define NS_PER_SEC   (1000000000)                                               // Nano sec per second.
#define TICK_PER_SEC (NS_PER_SEC/TICK_NS_TIME)                                  // Number of tick per second (Ex:100)
#define TIMEOUT_COUNT (30*TICK_PER_SEC)                                         // 30 seconds timeout (with 100 tick per second)

// Env set/long jmp
#define ENV_SZ (2)
#define ENV_TIMEOUT (0)
#define ENV_CTRLC (1)
static jmp_buf env[ENV_SZ];

// Variables
int timeout_val;


// sig handler.
void signal_handler(int signo)
{
    pthread_t self = pthread_self();
    printf("Thread %lu in signal handler\n", (long)self);
    if (signo == SIGINT) {
        longjmp(env[ENV_CTRLC], 1);                                             // Q?: Is it in the same thread ? (Never, Always, Sometimes?)
    }
    else 
    {
        printf("Other signal received..quitting.");                             // Ex: kill -9 pid
        exit(0);
    }
}

// thread timer function
void* timer_function(void* in_param)
{
    // Loop approx 100x per second.
    for (;;) {
        nanosleep((const struct timespec[]){{0, TICK_NS_TIME }}, NULL);         // Sleep 10 ms seconds.
        if (timeout_val) {
            if (!--timeout_val) {
                longjmp(env[ENV_TIMEOUT], 1);                                   // longjmp when timer reaches 0. (Q?: Is this valid with multithread?)
            }
        }
    }
}

// main
int main(int argc, char **argv)
{
    int i;
    int val;
    struct sigaction actions;
    pthread_t thread;
    setvbuf (stdout, NULL, _IONBF, 0);                                          // Make sure stdout is not buffered (ex:printf, etc.)
    printf("[Program started]\r\n");

    memset(&actions, 0, sizeof(actions));
    sigemptyset(&actions.sa_mask);
    actions.sa_flags = 0;
    actions.sa_handler = signal_handler;
    val = sigaction(SIGINT, &actions, NULL);  
    pthread_create(&thread, NULL, timer_function, NULL);                        // timer thread for example
    printf("[Timer thread started]\r\n");

    // setting env.
    val = setjmp(env[ENV_TIMEOUT]);
    if (val!=0){ printf("[JMP TIMEOUT]\r\n"); }

    val = setjmp(env[ENV_CTRLC]);
    if (val!=0){ printf("[JMP CTRLC]\r\n"); }

    // main loop
    timeout_val = TIMEOUT_COUNT;
    i = 0;
    for (;;)
    {
        i++;
        if (i > 10){ i = 0; printf("[%d]", timeout_val/TICK_PER_SEC); }         // Number of seconds before time out.
        sleep(1);
        printf(".");
    }
    printf("Main completed\n");
    return 0;
}
//Compile: g++ -pthread main.cpp -o main
Run Code Online (Sandbox Code Playgroud)

由于我刚接触线程编程,因此对替代实现的建议会很棒!

Chr*_*phe 6

setjmp()保存恢复调用环境所需的信息. longjmp()然后可以恢复此环境,但只能在同一个线程内.

C11标准明确说明了具有相同线程的约束:

7.13.2.1/2 如果没有这样的调用(即:前一个setjmp),或者调用来自另一个执行线程,或者包含setjmp宏调用的函数在临时中终止执行,或者,如果setjmp宏的调用在具有可变修改类型的标识符的范围内,并且执行在临时中保留了该范围,则行为是未定义的.

实际上,setjmp/longjmp通常是通过保存堆栈指针来实现的,这样只有在相同的执行上下文中才能恢复它.

替代

除非我错过了什么,否则你只使用第二个线程作为计时器.您可以改为摆脱POSIX pthread,并使用POSIX激活的定时器信号timer_create().

但请注意,使用setjmp/longjmp信号处理程序(原来的代码中已经存在CTRL+C)非常棘手,如本SO答案所述.所以你要考虑一下sigsetjmp/siglongjmp.

对于记录:C还是C++?

你的问题被标记为C.但你提到c ++尝试并捕获.所以为了完整起见:

  • 在C++中setjmp应该用a替换try/catchlongjmp抛出异常. setjmp/longjmp只有当展开堆栈不需要调用任何非平凡的析构函数时,才支持C++(参见C++标准,18.10/4).
  • 异常不会在线程中传播,除非被捕获并且明确地重新使用std::rethrow_exception().这很精致,所以请参阅这个SO问题以获取更多细节.但它可能并且可以解决您的问题.