Skip to content

Sometimes a closed Socket::Shared reads indefinitely and does not close session endpoint (part two) #154

@KevinEady

Description

@KevinEady

Ref: #101

I'm still having this issue sometimes, at least on Mac OS X (Sonoma 14.0). I'm using commit 82beeea and there doesn't seem to have been any relevant changes since then 82beeea...main

Here's an example server:

hello_debugger.cpp

[I know it's not the best code with the condition variables but it shows the issue I'm facing]

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <condition_variable>
#include <iostream>
#include <thread>
#include <vector>

#include "dap/io.h"
#include "dap/network.h"
#include "dap/protocol.h"
#include "dap/session.h"

class Event {
 public:
  // wait() blocks until the event is fired.
  void wait();

  // fire() sets signals the event, and unblocks any calls to wait().
  void fire();

 private:
  std::mutex mutex;
  std::condition_variable cv;
  bool fired = false;
};

void Event::wait() {
  std::unique_lock<std::mutex> lock(mutex);
  cv.wait(lock, [&] { return fired; });
}

void Event::fire() {
  std::unique_lock<std::mutex> lock(mutex);
  fired = true;
  cv.notify_all();
}

Event terminate;
std::vector<std::thread> threads;

void my_handler(int s) {
  printf("Caught signal %d\n", s);
  terminate.fire();
}

int main(int argc, char** argv) {
  struct sigaction sigIntHandler;

  sigIntHandler.sa_handler = my_handler;
  sigemptyset(&sigIntHandler.sa_mask);
  sigIntHandler.sa_flags = 0;

  auto _server = dap::net::Server::create();

  auto address = "0.0.0.0";

  auto started = _server->start(
      address, 12345, [&](const std::shared_ptr<dap::ReaderWriter>& rw) {
        threads.emplace_back([=] {
          auto session = dap::Session::create();

          session->onError([&](const char* msg) {
            std::cout << "Session error: " << msg << "\n";
            terminate.fire();
          });

          session->bind(rw,
                        [&]() { std::cout << "Session endpoint closed\n"; });

          terminate.wait();
        });
      });

  if (!started) {
    std::cerr << "Failed to start DAP server.\n";
    _server.reset();
  }

  sigaction(SIGINT, &sigIntHandler, NULL);

  terminate.wait();
  std::cout << "Exiting\n";

  for (auto& thread : threads) {
    thread.join();
  }

  return 0;
}

When I run the server and nc localhost 12345 and then Ctrl+C the nc process, my CPU usage flies through the roof, and it looks like there is still an infinite read:

* thread #6, stop reason = breakpoint 3.1
  * frame #0: 0x000000010008a520 hello_debugger`dap::Socket::Shared::read(this=0x000000013ef044e8, buffer=0x0000000170142c78, bytes=15) at socket.cpp:210:13
    frame #1: 0x000000010000d678 hello_debugger`dap::ContentReader::buffer(this=0x0000000140104390, bytes=15) at content_stream.cpp:151:27
    frame #2: 0x000000010000d8b4 hello_debugger`dap::ContentReader::scan(this=0x0000000140104390, seq="Content-Length:", len=15) at content_stream.cpp:96:10
    frame #3: 0x000000010000d4c0 hello_debugger`dap::ContentReader::scan(this=0x0000000140104390, str="Content-Length:") at content_stream.cpp:107:10
    frame #4: 0x000000010000d1d8 hello_debugger`dap::ContentReader::read(this=0x0000000140104390) at content_stream.cpp:56:10

Again, the recv() call is returning 0 on the closed socket:

cppdap/src/socket.cpp

Lines 208 to 209 in 6464cd7

auto len =
recv(s, reinterpret_cast<char*>(buffer), static_cast<int>(bytes), 0);

The loop comes from here, where reader.isOpen() is true, but getPayload() is empty:

cppdap/src/session.cpp

Lines 87 to 91 in 6464cd7

while (reader.isOpen()) {
if (auto payload = getPayload()) {
inbox.put(std::move(payload));
}
}

I say sometimes this issue happens because sometimes the session ClosedHandler does execute ... other times, it hangs in this loop forever. Under no circumstance is the session error handler triggered.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions