This is an automated email from the ASF dual-hosted git repository.

cliffjansen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/qpid-proton.git


The following commit(s) were added to refs/heads/main by this push:
     new c6db2b0  PROTON-2483: epoll proactor TSAN race - ensure task locks not 
held when calling pni_timer_set()
c6db2b0 is described below

commit c6db2b089c6c40c46b5e37a701164bbce3f5ebf9
Author: Cliff Jansen <[email protected]>
AuthorDate: Wed Jan 12 13:00:14 2022 -0800

    PROTON-2483: epoll proactor TSAN race - ensure task locks not held when 
calling pni_timer_set()
---
 c/src/proactor/epoll-internal.h |  3 ++-
 c/src/proactor/epoll.c          | 36 ++++++++++++++++++++++++++++--------
 c/src/proactor/epoll_timer.c    | 14 +++++++++-----
 3 files changed, 39 insertions(+), 14 deletions(-)

diff --git a/c/src/proactor/epoll-internal.h b/c/src/proactor/epoll-internal.h
index 984724f..8db12a1 100644
--- a/c/src/proactor/epoll-internal.h
+++ b/c/src/proactor/epoll-internal.h
@@ -157,6 +157,7 @@ struct pn_proactor_t {
   bool need_timeout;
   bool timeout_set; /* timeout has been set by user and not yet cancelled or 
generated event */
   bool timeout_processed;  /* timeout event dispatched in the most recent 
event batch */
+  pmutex timeout_mutex;
   int task_count;
 
   // ready list subsystem
@@ -391,7 +392,7 @@ void pni_raw_connection_done(praw_connection_t *rc);
 
 pni_timer_t *pni_timer(pni_timer_manager_t *tm, pconnection_t *c);
 void pni_timer_free(pni_timer_t *timer);
-void pni_timer_set(pni_timer_t *timer, uint64_t deadline);
+bool pni_timer_set(pni_timer_t *timer, uint64_t deadline);
 bool pni_timer_manager_init(pni_timer_manager_t *tm);
 void pni_timer_manager_finalize(pni_timer_manager_t *tm);
 pn_event_batch_t *pni_timer_manager_process(pni_timer_manager_t *tm, bool 
timeout, bool sched_ready);
diff --git a/c/src/proactor/epoll.c b/c/src/proactor/epoll.c
index 3784d05..d2f394a 100644
--- a/c/src/proactor/epoll.c
+++ b/c/src/proactor/epoll.c
@@ -835,8 +835,8 @@ static void pconnection_final_free(pconnection_t *pc) {
   pmutex_finalize(&pc->rearm_mutex);
   pn_condition_free(pc->disconnect_condition);
   pn_connection_driver_destroy(&pc->driver);
-  task_finalize(&pc->task);
   pni_timer_free(pc->timer);
+  task_finalize(&pc->task);
   free(pc);
 }
 
@@ -1453,7 +1453,8 @@ static void pconnection_tick(pconnection_t *pc) {
       lock(&pc->task.mutex);
       pc->expected_timeout = next;
       unlock(&pc->task.mutex);
-      pni_timer_set(pc->timer, next);
+      if (pni_timer_set(pc->timer, next))
+        notify_poller(pc->task.proactor);
     }
   }
 }
@@ -1957,6 +1958,7 @@ pn_proactor_t *pn_proactor() {
   pmutex_init(&p->eventfd_mutex);
   pmutex_init(&p->sched_mutex);
   pmutex_init(&p->tslot_mutex);
+  pmutex_init(&p->timeout_mutex);
 
   if ((p->epollfd = epoll_create(1)) >= 0) {
     if ((p->eventfd = eventfd(0, EFD_NONBLOCK)) >= 0) {
@@ -1979,6 +1981,7 @@ pn_proactor_t *pn_proactor() {
   if (p->eventfd >= 0) close(p->eventfd);
   if (p->interruptfd >= 0) close(p->interruptfd);
   pni_timer_manager_finalize(&p->timer_manager);
+  pmutex_finalize(&p->timeout_mutex);
   pmutex_finalize(&p->tslot_mutex);
   pmutex_finalize(&p->sched_mutex);
   pmutex_finalize(&p->eventfd_mutex);
@@ -2014,6 +2017,7 @@ void pn_proactor_free(pn_proactor_t *p) {
 
   pni_timer_manager_finalize(&p->timer_manager);
   pn_collector_free(p->collector);
+  pmutex_finalize(&p->timeout_mutex);
   pmutex_finalize(&p->tslot_mutex);
   pmutex_finalize(&p->sched_mutex);
   pmutex_finalize(&p->eventfd_mutex);
@@ -2768,26 +2772,42 @@ void pn_proactor_interrupt(pn_proactor_t *p) {
 
 void pn_proactor_set_timeout(pn_proactor_t *p, pn_millis_t t) {
   bool notify = false;
+  lock(&p->timeout_mutex);
+
   lock(&p->task.mutex);
   p->timeout_set = true;
   if (t == 0) {
-    pni_timer_set(p->timer, 0);
     p->need_timeout = true;
-    notify = schedule(&p->task);
-  } else {
-    pni_timer_set(p->timer, t + pn_proactor_now_64());
+    if (schedule(&p->task))
+      notify = true;
   }
   unlock(&p->task.mutex);
+
+  if (t == 0) {
+    if (pni_timer_set(p->timer, 0))
+      notify = true;
+  } else {
+    if (pni_timer_set(p->timer, t + pn_proactor_now_64()))
+      notify = true;
+  }
+  unlock(&p->timeout_mutex);
   if (notify) notify_poller(p);
 }
 
 void pn_proactor_cancel_timeout(pn_proactor_t *p) {
+  bool notify = false;
+  lock(&p->timeout_mutex);
+
   lock(&p->task.mutex);
   p->timeout_set = false;
   p->need_timeout = false;
-  pni_timer_set(p->timer, 0);
-  bool notify = schedule_if_inactive(p);
+  if (schedule_if_inactive(p))
+    notify = true;
   unlock(&p->task.mutex);
+
+  if (pni_timer_set(p->timer, 0))
+    notify = true;
+  unlock(&p->timeout_mutex);
   if (notify) notify_poller(p);
 }
 
diff --git a/c/src/proactor/epoll_timer.c b/c/src/proactor/epoll_timer.c
index c154881..4a68ce1 100644
--- a/c/src/proactor/epoll_timer.c
+++ b/c/src/proactor/epoll_timer.c
@@ -153,7 +153,9 @@ pni_timer_t *pni_timer(pni_timer_manager_t *tm, 
pconnection_t *c) {
 void pni_timer_free(pni_timer_t *timer) {
   timer_deadline_t *td = timer->timer_deadline;
   bool can_free_td = false;
-  if (td) pni_timer_set(timer, 0);
+  bool notify = false;
+  if (td)
+    notify = pni_timer_set(timer, 0);
   pni_timer_manager_t *tm = timer->manager;
   lock(&tm->task.mutex);
   lock(&tm->deletion_mutex);
@@ -165,6 +167,8 @@ void pni_timer_free(pni_timer_t *timer) {
   }
   unlock(&tm->deletion_mutex);
   unlock(&tm->task.mutex);
+  if (notify)
+    notify_poller(tm->task.proactor);
   if (can_free_td) {
     timer_deadline_t_free(td);
   }
@@ -253,14 +257,15 @@ static bool adjust_deadline(pni_timer_manager_t *tm) {
 // Call without task lock or timer_manager lock.
 // Calls for connection timers are generated in the proactor and serialized 
per connection.
 // Calls for the proactor timer can come from arbitrary user threads.
-void pni_timer_set(pni_timer_t *timer, uint64_t deadline) {
+// Caller must call notify_poller() if true returned.
+bool pni_timer_set(pni_timer_t *timer, uint64_t deadline) {
   pni_timer_manager_t *tm = timer->manager;
   bool notify = false;
 
   lock(&tm->task.mutex);
   if (deadline == timer->deadline) {
     unlock(&tm->task.mutex);
-    return;  // No change.
+    return false;  // No change.
   }
 
   if (timer == tm->proactor_timer) {
@@ -293,8 +298,7 @@ void pni_timer_set(pni_timer_t *timer, uint64_t deadline) {
     notify = adjust_deadline(tm);
   unlock(&tm->task.mutex);
 
-  if (notify)
-    notify_poller(tm->task.proactor);
+  return notify;
 }
 
 pn_event_batch_t *pni_timer_manager_process(pni_timer_manager_t *tm, bool 
timeout, bool sched_ready) {

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to