Loading src/event/kqueue.cpp +73 −79 Original line number Diff line number Diff line Loading @@ -172,21 +172,16 @@ namespace netplus { void ConnectEventHandler(int pos, const int tid, ULONG_PTR args) { NetException exception; // DRAIN THE LISTEN QUEUE: Loop to accept all waiting connections. while (true) { // The ccon unique_ptr scope is inside the loop, ensuring it is destroyed // if setup fails before being released into the CONNECTIONS map. // 1) Create connection object (HttpRequest etc.) std::unique_ptr<con> ccon; // 1. Create the base connection object evconnection->CreateConnection(ccon); if (!ccon) { // Should not happen, but exit if connection wrapper creation failed std::cerr << "ConnectEventHandler: CreateConnection returned null\n"; return; } // 2. Initialize the socket wrapper based on server type // 2) Create the right socket if (_ServerSocket->_Type == sockettype::TCP) { ccon->csock = std::make_unique<tcp>(-1); } else if (_ServerSocket->_Type == sockettype::UDP) { Loading @@ -194,74 +189,73 @@ void ConnectEventHandler(int pos, const int tid, ULONG_PTR args) { } else if (_ServerSocket->_Type == sockettype::SSL) { netplus::ssl* srv = static_cast<netplus::ssl*>(_ServerSocket); ccon->csock = std::make_unique<ssl>(srv->_cert, -1); } else { exception[NetException::Error] << "ConnectEventHandler: unsupported socket type"; throw exception; } // If no supported socket type was initialized, skip this iteration/exit function if (!ccon->csock) { // Since we don't know how other types are handled, we stop the loop // in a production environment, this should log a critical error. std::cerr << "ConnectEventHandler: Unsupported socket type on server." << std::endl; return; exception[NetException::Error] << "ConnectEventHandler: failed to create client socket"; throw exception; } try{ // 3. Attempt to accept the new connection // 3) Accept _ServerSocket->accept(ccon->csock); }catch(NetException &e){ // Check for the non-fatal error (EAGAIN/EWOULDBLOCK) wrapped in NetException::Note. if (e.getErrorType()==NetException::Note) { // This means the listen queue is empty. Break the accept loop. return; } // Handle fatal accept errors (e.g., resource exhaustion) char errstr[255]; strerror_r_netplus(errno,errstr,255); std::cerr << "ConnectEventHandler: Failed to accept connection: " << errstr << std::endl; return; } // --- SETUP ACCEPTED CONNECTION (The rest of the original logic) --- // Set accepted socket to non-blocking // 4) Nonblocking try { ccon->csock->setFlag(O_NONBLOCK, 1); } catch (NetException& e) { // Accept succeeded but nonblock failed -> close immediately try { ccon->csock->close(); } catch (...) {} throw; } // Log IP // 5) Optional log try { std::string ip; ccon->csock->getAddress(ip); std::cout << "Connected: " << ip << std::endl; } catch (...) { // ignore logging failures } ccon->lasteventime = time(nullptr); std::lock_guard<std::mutex> global_lock(POLL_HANDLER_MUTEX); // Register client socket with kqueue for read events struct kevent setevent {}; setevent.udata = ccon.get(); // EV_ONESHOT is used, so we must re-register after every R/W operation EV_SET(&setevent, ccon->csock->fd(), EVFILT_READ, EV_ADD | EV_ONESHOT,0,0,ccon.get()); int estate = kevent(_pollFD, &setevent, 1, nullptr, 0, nullptr); if ( estate < 0 ) { // IMPORTANT: kqueue stores the udata pointer; it must remain valid. // We'll only release ownership once the socket is registered successfully. con* raw = ccon.get(); int fd = ccon->csock->fd(); // 6) Register initial READ (oneshot) // Use EV_CLEAR if you want edge-trigger-ish behavior. // If you prefer level-trigger: omit EV_CLEAR. struct kevent kev; EV_SET(&kev, fd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT /* | EV_CLEAR */, 0, 0, raw); if (kevent(_pollFD, &kev, 1, nullptr, 0, nullptr) < 0) { char errstr[255]; strerror_r_netplus(errno, errstr, 255); exception[NetException::Error] << "ConnectEventHandler: can't add socket to kqueue: " << errstr; // Throw error to close the worker thread throw exception; } // Call user-defined connection event handler evconnection ->ConnectEvent(*ccon,tid,args); // 7) Inform protocol layer once (do NOT call repeatedly in write handler) evconnection->ConnectEvent(*raw, tid, args); // Release the unique_ptr and insert the raw pointer into the global map // 8) Publish to global map and release ownership { std::lock_guard<std::mutex> clock(ccon->event_mutex),global_lock(POLL_HANDLER_MUTEX); CONNECTIONS.emplace(ccon->csock->fd(),ccon.release()); std::lock_guard<std::mutex> glk(POLL_HANDLER_MUTEX); CONNECTIONS.emplace(fd, raw); } // Loop continues to accept the next connection from the queue } ccon.release(); // ownership transferred to event loop lifetime management } void ReadEventHandler(int pos, const int tid, ULONG_PTR args) { Loading Loading
src/event/kqueue.cpp +73 −79 Original line number Diff line number Diff line Loading @@ -172,21 +172,16 @@ namespace netplus { void ConnectEventHandler(int pos, const int tid, ULONG_PTR args) { NetException exception; // DRAIN THE LISTEN QUEUE: Loop to accept all waiting connections. while (true) { // The ccon unique_ptr scope is inside the loop, ensuring it is destroyed // if setup fails before being released into the CONNECTIONS map. // 1) Create connection object (HttpRequest etc.) std::unique_ptr<con> ccon; // 1. Create the base connection object evconnection->CreateConnection(ccon); if (!ccon) { // Should not happen, but exit if connection wrapper creation failed std::cerr << "ConnectEventHandler: CreateConnection returned null\n"; return; } // 2. Initialize the socket wrapper based on server type // 2) Create the right socket if (_ServerSocket->_Type == sockettype::TCP) { ccon->csock = std::make_unique<tcp>(-1); } else if (_ServerSocket->_Type == sockettype::UDP) { Loading @@ -194,74 +189,73 @@ void ConnectEventHandler(int pos, const int tid, ULONG_PTR args) { } else if (_ServerSocket->_Type == sockettype::SSL) { netplus::ssl* srv = static_cast<netplus::ssl*>(_ServerSocket); ccon->csock = std::make_unique<ssl>(srv->_cert, -1); } else { exception[NetException::Error] << "ConnectEventHandler: unsupported socket type"; throw exception; } // If no supported socket type was initialized, skip this iteration/exit function if (!ccon->csock) { // Since we don't know how other types are handled, we stop the loop // in a production environment, this should log a critical error. std::cerr << "ConnectEventHandler: Unsupported socket type on server." << std::endl; return; exception[NetException::Error] << "ConnectEventHandler: failed to create client socket"; throw exception; } try{ // 3. Attempt to accept the new connection // 3) Accept _ServerSocket->accept(ccon->csock); }catch(NetException &e){ // Check for the non-fatal error (EAGAIN/EWOULDBLOCK) wrapped in NetException::Note. if (e.getErrorType()==NetException::Note) { // This means the listen queue is empty. Break the accept loop. return; } // Handle fatal accept errors (e.g., resource exhaustion) char errstr[255]; strerror_r_netplus(errno,errstr,255); std::cerr << "ConnectEventHandler: Failed to accept connection: " << errstr << std::endl; return; } // --- SETUP ACCEPTED CONNECTION (The rest of the original logic) --- // Set accepted socket to non-blocking // 4) Nonblocking try { ccon->csock->setFlag(O_NONBLOCK, 1); } catch (NetException& e) { // Accept succeeded but nonblock failed -> close immediately try { ccon->csock->close(); } catch (...) {} throw; } // Log IP // 5) Optional log try { std::string ip; ccon->csock->getAddress(ip); std::cout << "Connected: " << ip << std::endl; } catch (...) { // ignore logging failures } ccon->lasteventime = time(nullptr); std::lock_guard<std::mutex> global_lock(POLL_HANDLER_MUTEX); // Register client socket with kqueue for read events struct kevent setevent {}; setevent.udata = ccon.get(); // EV_ONESHOT is used, so we must re-register after every R/W operation EV_SET(&setevent, ccon->csock->fd(), EVFILT_READ, EV_ADD | EV_ONESHOT,0,0,ccon.get()); int estate = kevent(_pollFD, &setevent, 1, nullptr, 0, nullptr); if ( estate < 0 ) { // IMPORTANT: kqueue stores the udata pointer; it must remain valid. // We'll only release ownership once the socket is registered successfully. con* raw = ccon.get(); int fd = ccon->csock->fd(); // 6) Register initial READ (oneshot) // Use EV_CLEAR if you want edge-trigger-ish behavior. // If you prefer level-trigger: omit EV_CLEAR. struct kevent kev; EV_SET(&kev, fd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT /* | EV_CLEAR */, 0, 0, raw); if (kevent(_pollFD, &kev, 1, nullptr, 0, nullptr) < 0) { char errstr[255]; strerror_r_netplus(errno, errstr, 255); exception[NetException::Error] << "ConnectEventHandler: can't add socket to kqueue: " << errstr; // Throw error to close the worker thread throw exception; } // Call user-defined connection event handler evconnection ->ConnectEvent(*ccon,tid,args); // 7) Inform protocol layer once (do NOT call repeatedly in write handler) evconnection->ConnectEvent(*raw, tid, args); // Release the unique_ptr and insert the raw pointer into the global map // 8) Publish to global map and release ownership { std::lock_guard<std::mutex> clock(ccon->event_mutex),global_lock(POLL_HANDLER_MUTEX); CONNECTIONS.emplace(ccon->csock->fd(),ccon.release()); std::lock_guard<std::mutex> glk(POLL_HANDLER_MUTEX); CONNECTIONS.emplace(fd, raw); } // Loop continues to accept the next connection from the queue } ccon.release(); // ownership transferred to event loop lifetime management } void ReadEventHandler(int pos, const int tid, ULONG_PTR args) { Loading