]> git.puffer.fish Git - mirror/frr.git/commitdiff
lib: allow nonblocking thread_fetch() 377/head
authorQuentin Young <qlyoung@cumulusnetworks.com>
Fri, 28 Apr 2017 22:45:59 +0000 (22:45 +0000)
committerQuentin Young <qlyoung@cumulusnetworks.com>
Sun, 30 Apr 2017 23:06:14 +0000 (23:06 +0000)
This change adds three fields to thread_master and associated code to
use them. The fields are:

 * long selectpoll_timeout

This is a millisecond value that, if nonzero, will override the
internally calculated timeout for select()/poll(). -1 indicates
nonblocking while a positive value indicates the desired timeout in
milliseconds.

 * bool spin

This indicates whether a call to thread_fetch() should result in a loop
until work is available. By default this is set to true, in order to
keep the default behavior. In this case a return value of NULL indicates
that a fatal signal was received in select() or poll(). If it is set to
false, thread_fetch() will return immediately. NULL is then an
acceptable return value if there is no work to be done.

 * bool handle_signals

This indicates whether or not the pthread that owns the thread master
is responsible for handling signals (since this is an MT-unsafe
operation, it is best to have just the root thread do it). It is set to
true by default. Non-root pthreads should set this to false.

Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
lib/thread.c
lib/thread.h

index 6cd3b9676f9d22c58723aaf9cadf9f066c18535c..d4ed5d1a086605796c7dde5f3e4b935ccd2989df 100644 (file)
@@ -376,6 +376,8 @@ thread_master_create (void)
   rv->background = pqueue_create();
   rv->timer->cmp = rv->background->cmp = thread_timer_cmp;
   rv->timer->update = rv->background->update = thread_timer_update;
+  rv->spin = true;
+  rv->handle_signals = true;
 
 #if defined(HAVE_POLL)
   rv->handler.pfdsize = rv->fd_limit;
@@ -696,15 +698,45 @@ static int
 fd_select (struct thread_master *m, int size, thread_fd_set *read, thread_fd_set *write, thread_fd_set *except, struct timeval *timer_wait)
 {
   int num;
+
+  /* If timer_wait is null here, that means either select() or poll() should
+   * block indefinitely, unless the thread_master has overriden it. select()
+   * and poll() differ in the timeout values they interpret as an indefinite
+   * block; select() requires a null pointer, while poll takes a millisecond
+   * value of -1.
+   *
+   * The thread_master owner has the option of overriding the default behavior
+   * by setting ->selectpoll_timeout. If the value is positive, it specifies
+   * the maximum number of milliseconds to wait. If the timeout is -1, it
+   * specifies that we should never wait and always return immediately even if
+   * no event is detected. If the value is zero, the behavior is default.
+   */
+
 #if defined(HAVE_POLL)
-  /* recalc timeout for poll. Attention NULL pointer is no timeout with
-  select, where with poll no timeount is -1 */
   int timeout = -1;
-  if (timer_wait != NULL)
+
+  if (timer_wait != NULL && m->selectpoll_timeout == 0) // use the default value
     timeout = (timer_wait->tv_sec*1000) + (timer_wait->tv_usec/1000);
+  else if (m->selectpoll_timeout > 0) // use the user's timeout
+    timeout = m->selectpoll_timeout;
+  else if (m->selectpoll_timeout < 0) // effect a poll (return immediately)
+    timeout = 0;
 
   num = poll (m->handler.pfds, m->handler.pfdcount + m->handler.pfdcountsnmp, timeout);
 #else
+  struct timeval timeout;
+  if (m->selectpoll_timeout > 0) // use the user's timeout
+  {
+    timeout.tv_sec = m->selectpoll_timeout / 1000;
+    timeout.tv_usec = (m->selectpoll_timeout % 1000) * 1000;
+    timer_wait = &timeout;
+  }
+  else if (m->selectpoll_timeout < 0) // effect a poll (return immediately)
+  {
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 0;
+    timer_wait = &timeout;
+  }
   num = select (size, read, write, except, timer_wait);
 #endif
 
@@ -1232,12 +1264,13 @@ thread_fetch (struct thread_master *m, struct thread *fetch)
   struct timeval *timer_wait = &timer_val;
   struct timeval *timer_wait_bg;
 
-  while (1)
+  do
     {
       int num = 0;
 
       /* Signals pre-empt everything */
-      quagga_sigevent_process ();
+      if (m->handle_signals)
+        quagga_sigevent_process ();
        
       pthread_mutex_lock (&m->mtx);
       /* Drain the ready queue of already scheduled jobs, before scheduling
@@ -1331,7 +1364,10 @@ thread_fetch (struct thread_master *m, struct thread *fetch)
         }
 
       pthread_mutex_unlock (&m->mtx);
-    }
+
+    } while (m->spin);
+
+  return NULL;
 }
 
 unsigned long
index 0cc9841272fcb52196496ca7756d648cf8310277..18fd340ba5d18c41897633b6a2cb6b87fb7095cf 100644 (file)
@@ -85,6 +85,9 @@ struct thread_master
   int fd_limit;
   struct fd_handler handler;
   unsigned long alloc;
+  long selectpoll_timeout;
+  bool spin;
+  bool handle_signals;
   pthread_mutex_t mtx;
 };