Loading src/event/epoll.cpp +25 −39 Original line number Diff line number Diff line Loading @@ -342,57 +342,43 @@ namespace netplus { } void CloseEventHandler(int pos, const int tid, ULONG_PTR args) { con* ccon = reinterpret_cast<con*>(_Events[pos].udata); if (!ccon) return; std::lock_guard<std::mutex> global_lock(POLL_HANDLER_MUTEX); // Lock connection first (so nobody is inside Read/Write handler), // then take global map lock later. std::unique_lock<std::mutex> conn_lock(ccon->event_mutex); if (!_Events[pos].data.ptr) { return; } // Cache fd early; after close it may be invalid. int fd = -1; if (ccon->csock) fd = ccon->csock->fd(); con *ccon = reinterpret_cast<con*>(_Events[pos].data.ptr); // 1) Remove from kqueue (ignore ENOENT) if (fd >= 0) { struct kevent kev[2]; EV_SET(&kev[0], fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr); EV_SET(&kev[1], fd, EVFILT_WRITE, EV_DELETE, 0, 0, nullptr); if (!ccon) { return; } std::unique_lock<std::mutex> conn_lock(ccon->event_mutex); // kevent can return -1 even if one filter didn't exist; treat ENOENT as OK. if (kevent(_pollFD, kev, 2, nullptr, 0, nullptr) < 0) { if (errno != ENOENT) { if (ccon->csock) { try { if (epoll_ctl(_pollFD, EPOLL_CTL_DEL, ccon->csock->fd(), nullptr) < 0) { NetException except; char errstr[255]; strerror_r_netplus(errno,errstr,255); except[NetException::Error] << "CloseEventHandler: failed to remove fd from kqueue: " << errstr; // continue cleanup anyway } } except[NetException::Error] << "CloseEventHandler: can't close socket to epoll: " << errstr; throw except; } // 2) Inform application layer (do this before freeing/closing state) try { evconnection->DisconnectEvent(*ccon, tid, args); } catch (...) { // don't let DisconnectEvent prevent cleanup } // 3) Close socket if (ccon->csock) { try { ccon->csock->close(); } catch (...) {} } ccon->csock->close(); // 4) Remove from global map (use cached fd; csock may be gone) { std::lock_guard<std::mutex> global_lock(POLL_HANDLER_MUTEX); if (fd >= 0) CONNECTIONS.erase(fd); } CONNECTIONS.erase(ccon->csock->fd()); // 5) Clear event slot udata so it can’t be reused accidentally _Events[pos].udata = nullptr; } catch (NetException &e) { CONNECTIONS.erase(ccon->csock->fd()); conn_lock.unlock(); throw e; } } // 6) Destroy connection object _Events[pos].data.ptr = nullptr; conn_lock.unlock(); delete ccon; } Loading src/event/kqueue.cpp +42 −30 Original line number Diff line number Diff line Loading @@ -357,45 +357,57 @@ namespace netplus { } void CloseEventHandler(int pos, const int tid, ULONG_PTR args) { std::lock_guard<std::mutex> global_lock(POLL_HANDLER_MUTEX); if (!_Events[pos].udata) { return; } con* ccon = reinterpret_cast<con*>(_Events[pos].udata); if (!ccon) return; if (!ccon) { return; } // Lock connection first (so nobody is inside Read/Write handler), // then take global map lock later. std::unique_lock<std::mutex> conn_lock(ccon->event_mutex); if (ccon->csock) { try { struct kevent setevent = {0}; EV_SET(&setevent, ccon->csock->fd(), EVFILT_READ, EV_DELETE, 0, 0, ccon); // Cache fd early; after close it may be invalid. int fd = -1; if (ccon->csock) fd = ccon->csock->fd(); if (kevent(_pollFD, &setevent, 1, nullptr, 0, nullptr) < 0) { // 1) Remove from kqueue (ignore ENOENT) if (fd >= 0) { struct kevent kev[2]; EV_SET(&kev[0], fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr); EV_SET(&kev[1], fd, EVFILT_WRITE, EV_DELETE, 0, 0, nullptr); // kevent can return -1 even if one filter didn't exist; treat ENOENT as OK. if (kevent(_pollFD, kev, 2, nullptr, 0, nullptr) < 0) { if (errno != ENOENT) { NetException except; char errstr[255]; strerror_r_netplus(errno, errstr, 255); except[NetException::Error] << "CloseEventHandler: failed to remove socket from kqueue: " << errstr; throw except; except[NetException::Error] << "CloseEventHandler: failed to remove fd from kqueue: " << errstr; // continue cleanup anyway } } } // 2) Inform application layer (do this before freeing/closing state) try { evconnection->DisconnectEvent(*ccon, tid, args); } catch (...) { // don't let DisconnectEvent prevent cleanup } ccon->csock->close(); } catch (NetException &e) { conn_lock.unlock(); throw e; // 3) Close socket if (ccon->csock) { try { ccon->csock->close(); } catch (...) {} } // 4) Remove from global map (use cached fd; csock may be gone) { std::lock_guard<std::mutex> global_lock(POLL_HANDLER_MUTEX); if (fd >= 0) CONNECTIONS.erase(fd); } CONNECTIONS.erase(ccon->csock->fd()); // 5) Clear event slot udata so it can’t be reused accidentally _Events[pos].udata = nullptr; // 6) Destroy connection object conn_lock.unlock(); delete ccon; } Loading Loading
src/event/epoll.cpp +25 −39 Original line number Diff line number Diff line Loading @@ -342,57 +342,43 @@ namespace netplus { } void CloseEventHandler(int pos, const int tid, ULONG_PTR args) { con* ccon = reinterpret_cast<con*>(_Events[pos].udata); if (!ccon) return; std::lock_guard<std::mutex> global_lock(POLL_HANDLER_MUTEX); // Lock connection first (so nobody is inside Read/Write handler), // then take global map lock later. std::unique_lock<std::mutex> conn_lock(ccon->event_mutex); if (!_Events[pos].data.ptr) { return; } // Cache fd early; after close it may be invalid. int fd = -1; if (ccon->csock) fd = ccon->csock->fd(); con *ccon = reinterpret_cast<con*>(_Events[pos].data.ptr); // 1) Remove from kqueue (ignore ENOENT) if (fd >= 0) { struct kevent kev[2]; EV_SET(&kev[0], fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr); EV_SET(&kev[1], fd, EVFILT_WRITE, EV_DELETE, 0, 0, nullptr); if (!ccon) { return; } std::unique_lock<std::mutex> conn_lock(ccon->event_mutex); // kevent can return -1 even if one filter didn't exist; treat ENOENT as OK. if (kevent(_pollFD, kev, 2, nullptr, 0, nullptr) < 0) { if (errno != ENOENT) { if (ccon->csock) { try { if (epoll_ctl(_pollFD, EPOLL_CTL_DEL, ccon->csock->fd(), nullptr) < 0) { NetException except; char errstr[255]; strerror_r_netplus(errno,errstr,255); except[NetException::Error] << "CloseEventHandler: failed to remove fd from kqueue: " << errstr; // continue cleanup anyway } } except[NetException::Error] << "CloseEventHandler: can't close socket to epoll: " << errstr; throw except; } // 2) Inform application layer (do this before freeing/closing state) try { evconnection->DisconnectEvent(*ccon, tid, args); } catch (...) { // don't let DisconnectEvent prevent cleanup } // 3) Close socket if (ccon->csock) { try { ccon->csock->close(); } catch (...) {} } ccon->csock->close(); // 4) Remove from global map (use cached fd; csock may be gone) { std::lock_guard<std::mutex> global_lock(POLL_HANDLER_MUTEX); if (fd >= 0) CONNECTIONS.erase(fd); } CONNECTIONS.erase(ccon->csock->fd()); // 5) Clear event slot udata so it can’t be reused accidentally _Events[pos].udata = nullptr; } catch (NetException &e) { CONNECTIONS.erase(ccon->csock->fd()); conn_lock.unlock(); throw e; } } // 6) Destroy connection object _Events[pos].data.ptr = nullptr; conn_lock.unlock(); delete ccon; } Loading
src/event/kqueue.cpp +42 −30 Original line number Diff line number Diff line Loading @@ -357,45 +357,57 @@ namespace netplus { } void CloseEventHandler(int pos, const int tid, ULONG_PTR args) { std::lock_guard<std::mutex> global_lock(POLL_HANDLER_MUTEX); if (!_Events[pos].udata) { return; } con* ccon = reinterpret_cast<con*>(_Events[pos].udata); if (!ccon) return; if (!ccon) { return; } // Lock connection first (so nobody is inside Read/Write handler), // then take global map lock later. std::unique_lock<std::mutex> conn_lock(ccon->event_mutex); if (ccon->csock) { try { struct kevent setevent = {0}; EV_SET(&setevent, ccon->csock->fd(), EVFILT_READ, EV_DELETE, 0, 0, ccon); // Cache fd early; after close it may be invalid. int fd = -1; if (ccon->csock) fd = ccon->csock->fd(); if (kevent(_pollFD, &setevent, 1, nullptr, 0, nullptr) < 0) { // 1) Remove from kqueue (ignore ENOENT) if (fd >= 0) { struct kevent kev[2]; EV_SET(&kev[0], fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr); EV_SET(&kev[1], fd, EVFILT_WRITE, EV_DELETE, 0, 0, nullptr); // kevent can return -1 even if one filter didn't exist; treat ENOENT as OK. if (kevent(_pollFD, kev, 2, nullptr, 0, nullptr) < 0) { if (errno != ENOENT) { NetException except; char errstr[255]; strerror_r_netplus(errno, errstr, 255); except[NetException::Error] << "CloseEventHandler: failed to remove socket from kqueue: " << errstr; throw except; except[NetException::Error] << "CloseEventHandler: failed to remove fd from kqueue: " << errstr; // continue cleanup anyway } } } // 2) Inform application layer (do this before freeing/closing state) try { evconnection->DisconnectEvent(*ccon, tid, args); } catch (...) { // don't let DisconnectEvent prevent cleanup } ccon->csock->close(); } catch (NetException &e) { conn_lock.unlock(); throw e; // 3) Close socket if (ccon->csock) { try { ccon->csock->close(); } catch (...) {} } // 4) Remove from global map (use cached fd; csock may be gone) { std::lock_guard<std::mutex> global_lock(POLL_HANDLER_MUTEX); if (fd >= 0) CONNECTIONS.erase(fd); } CONNECTIONS.erase(ccon->csock->fd()); // 5) Clear event slot udata so it can’t be reused accidentally _Events[pos].udata = nullptr; // 6) Destroy connection object conn_lock.unlock(); delete ccon; } Loading