Skip to content
This repository has been archived by the owner on Jun 12, 2018. It is now read-only.

Deterministic way to know if server has started #176

Open
sharpie7 opened this issue Nov 29, 2017 · 10 comments
Open

Deterministic way to know if server has started #176

sharpie7 opened this issue Nov 29, 2017 · 10 comments

Comments

@sharpie7
Copy link

Hi,

Is there a deterministic way to know from the parent thread if the server has started successfully? In the example application it just calls the start method and waits for a second and then assumes the server is running. But, I guess it is possible that the server start-up may take more than a second (e.g. on a machine that has very high CPU occupancy).

I am particularly thinking of how best to detect from the parent thread exceptions in the server thread (such as port conflicts) that prevent the server starting successfully.

With thanks for a great tool.

Iain

@vitor-alves
Copy link
Contributor

vitor-alves commented Nov 29, 2017

I have a piece of code that may help you. Not exactly what you are asking for, but you certanly can extend it to your personal needs.
One option for you would be to catch an exception on server.start() inside the thread and pass this information to the parent thread. My code only catches the exception and logs it, but modifying it to pass this information to the parent is not hard.

Note: HttpServer server is defined outside the lambda and passed by reference

void RestAPI::start_server() {
	server_thread = std::make_unique<std::thread>( [&]() { 
				try {
					server.start();
					LOG_INFO << "Web UI and REST API server have been started and are listening on "
				       	<< server.config.address << ":" << server.config.port;
				}
				catch(std::exception const &e) {
					LOG_ERROR << "Web server error: " << e.what() << ". Is port " <<
				       	server.config.port << " already in use?" ;
					std::cerr << "Web server error: " << e.what() << ". Is port " <<
				       	server.config.port << " already in use?"
					<< " Check log files for more information." << std::endl;
				}} );

	server.on_error = [](std::shared_ptr<HttpServer::Request> request, const SimpleWeb::error_code & ec) {
		LOG_ERROR << "Web server error: " << ec.message();
	};
}

Edit:
What you really want to do is to catch a thread exception in the parent, so take a look at this:
https://stackoverflow.com/questions/233127/how-can-i-propagate-exceptions-between-threads

@sharpie7
Copy link
Author

@vitor-alves

Thanks for the suggestion. I understand the technique you describe, but it addresses a different aspect of the problem. Because the start() method doesn't exit until the server has been stopped, the catch statement in your example will catch exceptions during server start-up, and also exceptions while the server is running (e.g. due to a bug or resoure limitations).

That's fine, but if I want to block the calling thread until the server has successfully started I have no way of knowing how long I should wait for except for guessing a maximum amount of time that the server might need to start.

There are two reasons for an application to know the difference between a server that is running and one that is still starting up:

  1. So that application knows whether it is capable of accepting requests or not
  2. So that application knows whether the server has successfully gone past the start-up phase when it is most at risk of encountering unrecoverable errors.

@vitor-alves
Copy link
Contributor

Wow, I just realized we have the same problem.
I have assumed server.start() exits right after the server is started, but I was wrong. In fact LOG_INFO in my code only gets executed when the server is stopping. This is no big deal, since my main concern is catching the exceptions, but I am looking forward to a solution for this.

@eidheim
Copy link
Owner

eidheim commented Nov 30, 2017

I did a quick implementation here:

#include "server_http.hpp"

using HttpServer = SimpleWeb::Server<SimpleWeb::HTTP>;

int main() {
  HttpServer server;
  server.config.port = 8080;

  std::thread server_thread([&server] {
    server.start();
  });

  {
    // Wait till server is started
    while(!server.io_service)
      std::this_thread::sleep_for(std::chrono::milliseconds(10));
    std::condition_variable cv;
    std::mutex cv_mutex;
    server.io_service->post([&cv, &cv_mutex] {
      std::unique_lock<std::mutex> lock(cv_mutex);
      cv.notify_one();
    });
    std::unique_lock<std::mutex> lock(cv_mutex);
    cv.wait(lock);
    std::cout << "Server is now accepting requests" << std::endl;
  }

  server_thread.join();
}

I'm not very fond of the while-loop, but that can be fixed by using an external io_service:

#include "server_http.hpp"

using HttpServer = SimpleWeb::Server<SimpleWeb::HTTP>;

int main() {
  HttpServer server;
  server.config.port = 8080;
  server.io_service = std::make_shared<SimpleWeb::asio::io_service>();

  std::thread server_thread([&server] {
    server.start();
    server.io_service->run();
  });

  {
    // Wait till server is started
    std::condition_variable cv;
    std::mutex cv_mutex;
    server.io_service->post([&cv, &cv_mutex] {
      std::unique_lock<std::mutex> lock(cv_mutex);
      cv.notify_one();
    });
    std::unique_lock<std::mutex> lock(cv_mutex);
    cv.wait(lock);
    std::cout << "Server is now accepting requests" << std::endl;
  }

  server_thread.join();
}

In both examples above, I post a task to the io_service, and when this task has been performed I know that the server's io_service is up and running and is accepting tasks through async_accept call.

@sharpie7
Copy link
Author

sharpie7 commented Dec 1, 2017

Thanks - that addresses the problem!

Would it be sensible to add some code similar to that to the library as a testIfServerIsRunning() type method?

@ghost
Copy link

ghost commented Dec 1, 2017 via email

@vitor-alves
Copy link
Contributor

click the unsubscribe button in the right

@314159
Copy link

314159 commented Dec 1, 2017 via email

@jschrotter
Copy link

I think there is a synchronization issue in suggested code:
std::unique_lock<std::mutex> lock(cv_mutex);
should be created prior to server.io_service->post... otherwise the thread might be executed before the lock is created and then cv.wait(lock); waits forever.

@eidheim
Copy link
Owner

eidheim commented Apr 20, 2018

@jschrotter you are right. This is hopefully a better solution:

#include "server_http.hpp"

using HttpServer = SimpleWeb::Server<SimpleWeb::HTTP>;

int main() {
  HttpServer server;
  server.config.port = 8080;
  server.io_service = std::make_shared<SimpleWeb::asio::io_service>();

  std::thread server_thread([&server] {
    server.start();
    server.io_service->run();
  });

  std::promise<void> started;
  server.io_service->post([&started] {
    started.set_value();
  });
  started.get_future().get();
  std::cout << "Server is now accepting requests" << std::endl;

  server_thread.join();
}

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants