@@ -113,6 +113,7 @@ struct StreamListNode : public intrusive_list_node<StreamListNode> {
113113};
114114
115115struct StreamListHead : public StreamListNode {
116+ StreamListHead* _key_next = this ;
116117 // total # of sockets, including those in use, not including the head
117118 uint32_t _refcnt = 0 ;
118119 uint16_t _key_len;
@@ -188,6 +189,7 @@ class TCPSocketPool : public ISocketPool {
188189public:
189190 SocketPoolArgs args;
190191 std::unordered_set<StreamList, Hash, Equal> sockmap;
192+ StreamListHead *_key_head = nullptr ;
191193 CascadingEventEngine* ev = new_default_cascading_engine();
192194 join_handle* collector = thread_enable_join(
193195 thread_create11 (&TCPSocketPool::collect, this ));
@@ -279,12 +281,18 @@ class TCPSocketPool : public ISocketPool {
279281 ISocketStream* connect (std::string_view key, TempDelegate<ISocketStream*> connector) override {
280282 auto ret = sockmap.emplace (key);
281283 auto head = ret.first ->head ();
284+ if (ret.second ) { // new entry, prepend to key list
285+ head->_key_next = _key_head;
286+ _key_head = head;
287+ }
282288 ISocketStream* stream;
283289 if (head->single ()) {
290+ head->_refcnt ++;
284291 stream = connector ();
285- if (!stream)
292+ if (!stream) {
293+ head->_refcnt --;
286294 return nullptr ;
287- head-> _refcnt ++;
295+ }
288296 if (args.enable_tcp_keepalive )
289297 set_keepalive (stream);
290298 } else {
@@ -341,50 +349,56 @@ class TCPSocketPool : public ISocketPool {
341349 }
342350
343351 Timeout check_expire_heartbeat () {
344- intrusive_list<StreamListNode> freelist;
345- auto pre_delete = [&](StreamListHead* head, StreamListNode* ptr) {
346- assert (head->_refcnt > 0 );
347- head->_refcnt --;
348- auto next = ptr->remove_from_list ();
349- freelist.push_back (ptr);
350- return next;
351- };
352352 Timeout near_expire (args.expiration );
353- // uint64_t near_expire = sat_add(now, args.expiration), exp;
354- for (auto it = sockmap.begin (); it != sockmap.end ();) {
355- assert (!it->empty ());
356- auto head = it->head ();
357- auto ptr = head->next ();
358- while (ptr != head) {
353+ for (auto h = _key_head; h; h = h->_key_next ) {
354+ // Expire timed-out nodes
355+ auto ptr = h->next ();
356+ while (ptr != h) {
359357 if (now < ptr->timeout .expiration ()) {
360358 near_expire = std::min (near_expire, ptr->timeout );
361359 break ;
362360 }
363- ptr = pre_delete (head, ptr);
361+ auto next = ptr->remove_from_list ();
362+ rm_watch (ptr);
363+ assert (h->_refcnt > 0 );
364+ h->_refcnt --;
365+ delete ptr;
366+ ptr = next;
364367 }
368+ // Heartbeat remaining nodes
365369 if (args.heartbeater ) {
366- while (ptr != head) {
367- #ifdef _WIN32 // a fd (handle) can not be added to multiple
368- int fd = rm_watch (ptr); // IOCPs, while epoll, iouring, kqueue can.
369- #endif
370+ while (ptr != h) {
371+ // Take node out of the list so connect() cannot grab it
372+ // while the heartbeater yields (I/O).
373+ auto next = ptr->remove_from_list ();
374+ rm_watch (ptr);
370375 int ret = args.heartbeater (ptr->stream .get ());
371376 if (ret == 0 ) {
372- #ifdef _WIN32
373- add_watch (fd, ptr);
374- #endif
375- ptr = ptr->next ();
377+ h->insert_before (ptr);
378+ add_watch (ptr->stream ->get_underlay_fd (), ptr);
379+ ptr = next;
376380 } else {
377- ptr = pre_delete (head, ptr);
381+ assert (h->_refcnt > 0 );
382+ h->_refcnt --;
383+ delete ptr;
384+ ptr = next;
378385 }
379386 }
380387 }
381- if (head->_refcnt ) ++it;
382- else it = sockmap.erase (it);
383388 }
384- for (auto node : freelist) {
385- rm_watch (node);
389+ // Erase empty entries, unlinking from key list as we go
390+ auto *prev_next = &_key_head;
391+ for (auto h = _key_head; h; ) {
392+ if (h->_refcnt ) {
393+ prev_next = &h->_key_next ;
394+ h = h->_key_next ;
395+ } else {
396+ auto next = h->_key_next ;
397+ *prev_next = next;
398+ sockmap.erase (h->key ()); // frees h via StreamList destructor
399+ h = next;
400+ }
386401 }
387- freelist.delete_all ();
388402 assert (near_expire.expiration () > now);
389403 return near_expire;
390404 }
0 commit comments