Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed: #1645 Impossible to shut down server before it has started #1646

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions httplib.h
Original file line number Diff line number Diff line change
Expand Up @@ -827,9 +827,15 @@ class Server {
bool listen(const std::string &host, int port, int socket_flags = 0);

bool is_running() const;

// True when the server should never start again.
bool is_decommissioned() const;
void wait_until_ready() const;
void stop();

// Call when you want the server to stop and never start.
void decommission();

std::function<TaskQueue *(void)> new_task_queue;

protected:
Expand Down Expand Up @@ -900,6 +906,7 @@ class Server {
virtual bool process_and_close_socket(socket_t sock);

std::atomic<bool> is_running_{false};
std::atomic<bool> is_decommissioned_{false};
std::atomic<bool> done_{false};

struct MountPointEntry {
Expand Down Expand Up @@ -5736,6 +5743,8 @@ inline bool Server::listen(const std::string &host, int port,

inline bool Server::is_running() const { return is_running_; }

inline bool Server::is_decommissioned() const { return is_decommissioned_; }

inline void Server::wait_until_ready() const {
while (!is_running() && !done_) {
std::this_thread::sleep_for(std::chrono::milliseconds{1});
Expand All @@ -5751,6 +5760,11 @@ inline void Server::stop() {
}
}

inline void Server::decommission() {
is_decommissioned_ = true;
stop();
}

inline bool Server::parse_request_line(const char *s, Request &req) {
auto len = strlen(s);
if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; }
Expand Down Expand Up @@ -6143,6 +6157,11 @@ inline bool Server::listen_internal() {
is_running_ = true;
auto se = detail::scope_exit([&]() { is_running_ = false; });

// Do not start if we have been decommissioned.
// Note: it is vital we check this after we have set is_running to true,
// because that guarantees that a later call to stop will stop us.
if( is_decommissioned() ) { return false; }

{
std::unique_ptr<TaskQueue> task_queue(new_task_queue());

Expand Down
28 changes: 28 additions & 0 deletions test/test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4132,6 +4132,34 @@ TEST(ServerStopTest, ListenFailure) {
t.join();
}

TEST(ServerStopTest, DecommissionBeforeStart) {
// This mutex is used to force the sequencing that may occur randomly
// in the real world: the server thread won't start until after the main thread
// wants it dead.
std::mutex threadStopper;
threadStopper.lock();

Server svr;
int port = 0;
auto thread = std::thread([&]() {
// Block until the main thread has requested a shutdown.
threadStopper.lock();
threadStopper.unlock();
port = svr.bind_to_any_port("localhost");
svr.listen_after_bind();
});

// Decommission the server before it has finished starting.
// Unlike stop, this isn't ignored.
svr.decommission();

// Allow the server thread to try starting.
threadStopper.unlock();

// This won't hang forever.
thread.join();
}

TEST(StreamingTest, NoContentLengthStreaming) {
Server svr;

Expand Down