vibe.d icon indicating copy to clipboard operation
vibe.d copied to clipboard

Interrupting task causes 'bad' connection to go back to pool

Open yazd opened this issue 7 years ago • 1 comments

In an attempt to implement timeout for requestHTTP, I wrote the function below:

T timeout(T)(T delegate() expr, Duration period)
{
  auto event = createManualEvent();
  bool finishedNormally = false;

  Exception ex;
  T result;

  auto task = runTask({
    try { result = expr(); finishedNormally = true; }
    catch (Exception e) { ex = e; }
    finally { event.emit(); }
  });

  event.wait(period, 0);

  if (!finishedNormally)
    task.interrupt();

  if (ex !is null) throw ex;
  if (finishedNormally) return result;
  else throw new Exception("task interrupted");
}

I expected this to work correctly, however, as evidenced by the piece of code below, this is buggy:

import vibe.d;

void main()
{
  runTask({
    auto settings = new HTTPServerSettings();
    settings.port = 9889;

    listenHTTP(
      settings,
      (HTTPServerRequest req, HTTPServerResponse res)
      {
        sleep(5.seconds); // comment this for things to work correctly
        res.writeJsonBody(req.query["q"]);
      }
    );
  });

  runTask({
    timeout({ makeRequest("first_request"); return true; }, 2.seconds);
  });

  setTimer(6.seconds, {
    makeRequest("second_request");
  });

  runEventLoop();
}

void makeRequest(string query)
{
  auto res = requestHTTP("http://localhost:9889/?q=%s".format(query));
  scope(exit) res.dropBody();

  auto response = res.readJson().to!string();
  if (response != query)
    logWarn("unexpected response! Got '%s' while expecting '%s'.", response, query);
  else
    logInfo("everything is working correctly");
}

The code above creates a server that responds with the request's query param q after 5 seconds. However, the client timeouts after 2 seconds. And sends another request after 6 seconds from the start. The problem shows when the response from the first request is received by the second request. I believe that this is due to the connection being returned to the pool and reused by the second request.

The output from the code above is:

Listening for requests on http://[::]:9889/
Failed to listen on 0.0.0.0:9889
Task terminated with uncaught exception: Task interrupted.
unexpected response! Got 'first_request' while expecting 'second_request'.

Is this correct behavior from vibe.d and timeout should be implemented differently? Or in this case, because the connection wasn't finalized correctly, it shouldn't be returned to the pool and reused?

yazd avatar Apr 08 '17 14:04 yazd

Any advice or possible workaround?

yazd avatar Apr 19 '17 20:04 yazd