C: 从 main 终止一个陷入无限循环的被调用函数

C: Terminate a called function that is stuck in infinite loop from main

假设我在 c 文件中有以下 main

int f();
int main(){
     //terminate f() if in infinite loop
     return f();
}

然后是一个单独的 c 文件,可能包含以下内容:

int f() {
    for(;;) {}
    return 0;
}

有什么方法可以检测到函数 f() 处于无限循环中并从主函数中终止它的执行吗?

编辑: 我需要这个功能,因为我正在编写一个测试台,其中调用的函数可能有一个无限循环——这就是我最后要检查的。因此,无论如何我都不能修改 f() 。我也在 Linux 环境中。

您可以在不同的线程中调用 f(),并在达到特定限制时让 main time-out f()。但是,我认为这不切实际,你真的应该先解决无限循环。

在 Posix 系统(Linux、MacOS)上,您可以在调用该函数之前使用 setitimer() 安排将来的闹钟。信号 SIGALRM 将在指定的延迟后传递给进程。确保你的程序有信号处理程序,你应该在启动计时器之前用 sigaction() 注册它。

当信号处理程序在信号发出后接管控制权时,如果带有 setjmp()longjmp() 的违规循环,您可能会退出。

如果您按照显示的方式调用 f()(来自 main),那么此时主要上下文位于 f,而不是 main,因此您不能"check f from main".

您可以尝试从一个单独的线程调用 f() 并检查该线程是否在指定的时间限制内完成。但是我不确定这是否实用。虽然我不知道您真正打算在该函数中做什么,但在某些情况下,您可能会在该函数执行需要清理的事情时停止执行该函数。我想到的一个例子是它调用 malloc 但能够在您中断它的地方调用 free

老实说,如果对给定函数必须完成的时间有特定要求,只需将检查放在函数本身中,然后 return false 表明它没有成功完成.

没有,there is no way to definitively determine if a function contains an infinite loop

但是,我们可以做一些假设来检测潜在的无限循环并在程序中优雅地退出程序(例如,我们不必按 Ctrl+C)。这种方法在 JS 中使用的几个测试框架中很常见。基本上,我们为函数完成设置了一些任意时间限制。如果函数没有在该时间限制内完成,我们假设它不会完成并抛出错误。

在 C/C++ 中,如果您使用的是 Unix 系统,则可以使用 pthreads 实现。在 Windows 中,您将使用 windows.h。我只有 pthreads 方面的经验,所以我将展示一个简单的示例,说明如何使用 pthreads 实现此功能。

#include <pthread.h>  // Load pthread
#include <signal.h>   // If f() does not exit, we will need this library to send it a signal to kill itself.
#include <stdbool.h>  // You could use an int or char.
#include <stddef.h>   // Defines NULL
#include <unistd.h>   // Defines sleep()

bool testComplete;   // Has the test completed?

/**
 * The function being tested.
 */
void f() {
    while(true);
}

/**
 * This method handles executing the test.  This is the function pthread will
 * use as its start routine.  It takes no arguments and returns no results.
 * The signature is required for pthread_create().
 */
void *runTest(void *ptr) {
    testComplete = false;

    f();

    testComplete = true;
}

int main() {
    pthread_t testThread;

    pthread_create(&testThread, NULL, runTest, NULL);  // Create and start the new thread.  It will begin executing runTest() eventually.

    sleep(5);      // Give it 5 seconds to complete (this should be adjusted or could even be made dynamic).

    if(testComplete) {
        // Test completed successfully.
        pthread_join(testThread, NULL);
    } else {
        // The test did not exit successfully within the time limit.  Kill it and you'll probably what to provide some feedback here.
        pthread_kill(testThread, SIGPIPE); // There are other signals, but this one cannot be ignored or caught.
    }
}

要编译它,您需要执行 gcc your_filename.c -o output_filename -lpthread.

如果您希望程序在 Unix 和 Windows 系统上都能 运行,您可能需要考虑制作一些统一的线程访问接口,然后将 OS-specific 接口适配到你的界面。它会让事情变得简单一些,尤其是在扩展这个库的时候。