yhirose/cpp-httplib

Why does server.stop() kill the program?

kyrylolvov opened this issue · 5 comments

I have a server.stop() in a child process after a fork. When it gets called, the child process is not killed, but the parent is. And if I remove the server.stop() from the child, the exit of the program kills the child process.

@kyrylolvov could you show me the smallest possible code which can show the issue?

@yhirose Yes, sure! Sorry for not providing it right away.

So, I have the main.cpp file, where I define server, then call my controller init

int main() {
    std::cout << "Starting ArcHPC Baremetal API..." << std::endl;

    api::ResourcesController::init();

    std::signal(SIGINT, signal_handler);

    server.listen("0.0.0.0", 18080);
}

And in that controller I have a route, which does a fork, and tries to stop server inside it (in the child process), but somehow it stops the server in parent and exits the app.

void ResourcesController::init() {
    server.Get("/resources", listResources);
    server.Post("/resources", createResource);
}

void ResourcesController::createResource(const httplib::Request &, httplib::Response &res) {
    pid_t pid = fork();

    if (pid == -1) {
        std::cerr << "Fork failed" << std::endl;

        exit(1);
    } else if (pid == 0) {
        // Child process
        std::cout << "Child process: PID = " << getpid() << std::endl;

        server.stop();

        // Simulate some work or call exec to start a new process
        while (1) {
        };

        std::cout << "Child process exiting" << std::endl;

        exit(0);
    } else {
        // Parent process
        std::cout << "Parent process: Child PID = " << pid << std::endl;

        res.set_content("Hello World!", "text/plain");
    }
}

@kyrylolvov the problem is that the server object in the child process isn't the same as the server object in the parent process. This isn’t a cpp-httplib issue; it’s a common problem associated with using fork.

The code below shows the problem more clearly.

#include <chrono>
#include <iostream>
#include <thread>
#include <unistd.h>

int val = 0;

void func() {
  pid_t pid = fork();

  if (pid == -1) {
    std::cerr << "Fork failed" << std::endl;
    exit(1);
  } else if (pid == 0) {
    // Child process
    val = 10;
    std::cout << "Child val: " << val << std::endl;
    exit(0);
  } else {
    // Parent process
    std::cout << "Parent val: " << val << std::endl;
  }
}

int main() {
  func();
  std::this_thread::sleep_for(std::chrono::seconds(1));
  func();
}

The val in the child process isn't the same as the val in the parent process.

Here is the result of this example:

Parent val: 0
Child val: 10
Parent val: 0
Child val: 10

So you can't use fork for this purpose. Instead, server.stop() must be called in the same parent process.

In your example, you can simply do this:

void ResourcesController::createResource(const httplib::Request &, httplib::Response &res) {
    res.set_content("Hello World!", "text/plain");
    server.stop();
}

Hope it helps!

@kyrylolvov the problem is that the server object in the child process isn't the same as the server object in the parent process. This isn’t a cpp-httplib issue; it’s a common problem associated with using fork.

The code below shows the problem more clearly.

#include <chrono>
#include <iostream>
#include <thread>
#include <unistd.h>

int val = 0;

void func() {
  pid_t pid = fork();

  if (pid == -1) {
    std::cerr << "Fork failed" << std::endl;
    exit(1);
  } else if (pid == 0) {
    // Child process
    val = 10;
    std::cout << "Child val: " << val << std::endl;
    exit(0);
  } else {
    // Parent process
    std::cout << "Parent val: " << val << std::endl;
  }
}

int main() {
  func();
  std::this_thread::sleep_for(std::chrono::seconds(1));
  func();
}

The val in the child process isn't the same as the val in the parent process.

Here is the result of this example:

Parent val: 0
Child val: 10
Parent val: 0
Child val: 10

So you can't use fork for this purpose. Instead, server.stop() must be called in the same parent process.

In your example, you can simply do this:

void ResourcesController::createResource(const httplib::Request &, httplib::Response &res) {
    res.set_content("Hello World!", "text/plain");
    server.stop();
}

Hope it helps!

Yeah, but calling server.stop() in the createResource directly will just stop the server. What I was trying to achieve in the fork, is that the httplib is going to have its independent copy in the child, and calling stop() on it would stop it in the child, so when I exit my program, no port is going to be taken. However, when doing so, even if I call stop() in the child, it stops the server in the parent somehow.

#include "app.h"
#include "controllers/resources_controller.h"

using namespace httplib;

httplib::Server server;

void signal_handler(int signal) {
    if (signal == SIGINT) {
        std::cout << "Closing server..." << std::endl;
        server.stop();
        exit(0);
    }
}

int main() {
    std::cout << "Starting ArcHPC Baremetal API..." << std::endl;

    api::ResourcesController::init();

    std::signal(SIGINT, signal_handler);

    server.listen("0.0.0.0", 18080);
}

I wrote this function, so on exit it stops the server, and somehow this stops the server in the child as well.

I have a route, which does a fork

Note of warning. Route handlers (callbacks passed to .Get(...)) are not executed in the main thread, but in a worker thread spawned by httplib. Forking from a thread can lead to inconsistent behaviour