Skip to content

Commit 2c5ab9a

Browse files
authored
fix race-condition in sockpool (alibaba#1542)
1 parent c5f6c1b commit 2c5ab9a

1 file changed

Lines changed: 45 additions & 31 deletions

File tree

net/pooled_socket.cpp

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ struct StreamListNode : public intrusive_list_node<StreamListNode> {
113113
};
114114

115115
struct 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 {
188189
public:
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

Comments
 (0)