apr-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From wr...@apache.org
Subject svn commit: r654235 - in /apr/apr-util/branches/1.3.x: include/apr_thread_pool.h misc/apr_thread_pool.c
Date Wed, 07 May 2008 19:48:28 GMT
Author: wrowe
Date: Wed May  7 12:48:28 2008
New Revision: 654235

URL: http://svn.apache.org/viewvc?rev=654235&view=rev
Log:

 * The idle thread timeout statistic was being incremented each time an
   idle thread (whether a timeout was set or not) was reaped.  Updated
   the check to only increment when a timeout was set.
 * The test to determine if a thread should go into probation was >=
   when it should have been just > since the idle_cnt has been incremented
   by the time this test is made.

PR: 43876
Submitted by: Joe Mudd <Joe.Mudd sas.com>
Reviewed by: Henry Jen
Backports: 543234

Modified:
    apr/apr-util/branches/1.3.x/include/apr_thread_pool.h
    apr/apr-util/branches/1.3.x/misc/apr_thread_pool.c

Modified: apr/apr-util/branches/1.3.x/include/apr_thread_pool.h
URL: http://svn.apache.org/viewvc/apr/apr-util/branches/1.3.x/include/apr_thread_pool.h?rev=654235&r1=654234&r2=654235&view=diff
==============================================================================
--- apr/apr-util/branches/1.3.x/include/apr_thread_pool.h (original)
+++ apr/apr-util/branches/1.3.x/include/apr_thread_pool.h Wed May  7 12:48:28 2008
@@ -187,6 +187,38 @@
                                                      apr_size_t cnt);
 
 /**
+ * Get number of tasks that have run
+ * @param me The thread pool
+ * @return Number of tasks that have run
+ */
+APU_DECLARE(apr_size_t)
+    apr_thread_pool_tasks_run_count(apr_thread_pool_t * me);
+
+/**
+ * Get high water mark of the number of tasks waiting to run
+ * @param me The thread pool
+ * @return High water mark of tasks waiting to run
+ */
+APU_DECLARE(apr_size_t)
+    apr_thread_pool_tasks_high_count(apr_thread_pool_t * me);
+
+/**
+ * Get high water mark of the number of threads
+ * @param me The thread pool
+ * @return High water mark of threads in thread pool
+ */
+APU_DECLARE(apr_size_t)
+    apr_thread_pool_threads_high_count(apr_thread_pool_t * me);
+
+/**
+ * Get the number of idle threads that were destroyed after timing out
+ * @param me The thread pool
+ * @return Number of idle threads that timed out
+ */
+APU_DECLARE(apr_size_t)
+    apr_thread_pool_threads_idle_timeout_count(apr_thread_pool_t * me);
+
+/**
  * Access function for the maximum number of idle threads
  * @param me The thread pool
  * @return The current maximum number
@@ -203,6 +235,29 @@
                                                        apr_size_t cnt);
 
 /**
+ * Access function for the maximum wait time (in microseconds) of an
+ * idling thread that exceeds the maximum number of idling threads.
+ * A non-zero value allows for the reaping of idling threads to shrink
+ * over time.  Which helps reduce thrashing.
+ * @param me The thread pool
+ * @param timeout The number of microseconds an idle thread should wait
+ * till it reaps itself
+ * @return The original maximum wait time
+ */
+APU_DECLARE(apr_interval_time_t)
+    apr_thread_pool_idle_wait_set(apr_thread_pool_t * me,
+                                  apr_interval_time_t timeout);
+
+/**
+ * Access function for the maximum wait time (in microseconds) of an
+ * idling thread that exceeds the maximum number of idling threads
+ * @param me The thread pool
+ * @return The current maximum wait time
+ */
+APU_DECLARE(apr_interval_time_t)
+    apr_thread_pool_idle_wait_get(apr_thread_pool_t * me);
+
+/**
  * Access function for the maximum number of threads
  * @param me The thread pool
  * @return The current maximum number

Modified: apr/apr-util/branches/1.3.x/misc/apr_thread_pool.c
URL: http://svn.apache.org/viewvc/apr/apr-util/branches/1.3.x/misc/apr_thread_pool.c?rev=654235&r1=654234&r2=654235&view=diff
==============================================================================
--- apr/apr-util/branches/1.3.x/misc/apr_thread_pool.c (original)
+++ apr/apr-util/branches/1.3.x/misc/apr_thread_pool.c Wed May  7 12:48:28 2008
@@ -47,7 +47,7 @@
     APR_RING_ENTRY(apr_thread_list_elt) link;
     apr_thread_t *thd;
     volatile void *current_owner;
-    volatile int stop;
+    volatile enum { TH_RUN, TH_STOP, TH_PROBATION } state;
 };
 
 APR_RING_HEAD(apr_thread_list, apr_thread_list_elt);
@@ -57,11 +57,16 @@
     apr_pool_t *pool;
     volatile apr_size_t thd_max;
     volatile apr_size_t idle_max;
+    volatile apr_interval_time_t idle_wait;
     volatile apr_size_t thd_cnt;
     volatile apr_size_t idle_cnt;
     volatile apr_size_t task_cnt;
     volatile apr_size_t scheduled_task_cnt;
     volatile apr_size_t threshold;
+    volatile apr_size_t tasks_run;
+    volatile apr_size_t tasks_high;
+    volatile apr_size_t thd_high;
+    volatile apr_size_t thd_timed_out;
     struct apr_thread_pool_tasks *tasks;
     struct apr_thread_pool_tasks *scheduled_tasks;
     struct apr_thread_list *busy_thds;
@@ -71,6 +76,7 @@
     apr_thread_cond_t *cond;
     volatile int terminated;
     struct apr_thread_pool_tasks *recycled_tasks;
+    struct apr_thread_list *recycled_thds;
     apr_thread_pool_task_t *task_idx[TASK_PRIORITY_SEGS];
 };
 
@@ -126,7 +132,14 @@
         goto CATCH_ENOMEM;
     }
     APR_RING_INIT(me->idle_thds, apr_thread_list_elt, link);
+    me->recycled_thds = apr_palloc(me->pool, sizeof(*me->recycled_thds));
+    if (!me->recycled_thds) {
+        goto CATCH_ENOMEM;
+    }
+    APR_RING_INIT(me->recycled_thds, apr_thread_list_elt, link);
     me->thd_cnt = me->idle_cnt = me->task_cnt = me->scheduled_task_cnt = 0;
+    me->tasks_run = me->tasks_high = me->thd_high = me->thd_timed_out = 0;
+    me->idle_wait = 0;
     me->terminated = 0;
     for (i = 0; i < TASK_PRIORITY_SEGS; i++) {
         me->task_idx[i] = NULL;
@@ -198,6 +211,32 @@
 }
 
 /*
+ * NOTE: This function is not thread safe by itself. Caller should hold the lock
+ */
+static struct apr_thread_list_elt *elt_new(apr_thread_pool_t * me,
+                                           apr_thread_t * t)
+{
+    struct apr_thread_list_elt *elt;
+
+    if (APR_RING_EMPTY(me->recycled_thds, apr_thread_list_elt, link)) {
+        elt = apr_pcalloc(me->pool, sizeof(*elt));
+        if (NULL == elt) {
+            return NULL;
+        }
+    }
+    else {
+        elt = APR_RING_FIRST(me->recycled_thds);
+        APR_RING_REMOVE(elt, link);
+    }
+
+    APR_RING_ELEM_INIT(elt, link);
+    elt->thd = t;
+    elt->current_owner = NULL;
+    elt->state = TH_RUN;
+    return elt;
+}
+
+/*
  * The worker thread function. Take a task from the queue and perform it if
  * there is any. Otherwise, put itself into the idle thread list and waiting
  * for signal to wake up.
@@ -213,24 +252,24 @@
     apr_interval_time_t wait;
     struct apr_thread_list_elt *elt;
 
-    elt = apr_pcalloc(me->pool, sizeof(*elt));
+    apr_thread_mutex_lock(me->lock);
+    elt = elt_new(me, t);
     if (!elt) {
+        apr_thread_mutex_unlock(me->lock);
         apr_thread_exit(t, APR_ENOMEM);
     }
-    APR_RING_ELEM_INIT(elt, link);
-    elt->thd = t;
-    elt->stop = 0;
 
-    apr_thread_mutex_lock(me->lock);
-    while (!me->terminated && !elt->stop) {
-        /* if not new element, it is awakened from idle */
+    while (!me->terminated && elt->state != TH_STOP) {
+        /* Test if not new element, it is awakened from idle */
         if (APR_RING_NEXT(elt, link) != elt) {
             --me->idle_cnt;
             APR_RING_REMOVE(elt, link);
         }
+
         APR_RING_INSERT_TAIL(me->busy_thds, elt, apr_thread_list_elt, link);
         task = pop_task(me);
         while (NULL != task && !me->terminated) {
+            ++me->tasks_run;
             elt->current_owner = task->owner;
             apr_thread_mutex_unlock(me->lock);
             apr_thread_data_set(task, "apr_thread_pool_task", NULL, t);
@@ -239,28 +278,49 @@
             APR_RING_INSERT_TAIL(me->recycled_tasks, task,
                                  apr_thread_pool_task, link);
             elt->current_owner = NULL;
-            if (elt->stop) {
+            if (TH_STOP == elt->state) {
                 break;
             }
             task = pop_task(me);
         }
         assert(NULL == elt->current_owner);
-        APR_RING_REMOVE(elt, link);
+        if (TH_STOP != elt->state)
+            APR_RING_REMOVE(elt, link);
 
-        /* busy thread been asked to stop, not joinable */
+        /* Test if a busy thread been asked to stop, which is not joinable */
         if ((me->idle_cnt >= me->idle_max
-             && !(me->scheduled_task_cnt && 0 >= me->idle_max))
-            || me->terminated || elt->stop) {
+             && !(me->scheduled_task_cnt && 0 >= me->idle_max)
+             && !me->idle_wait)
+            || me->terminated || elt->state != TH_RUN) {
             --me->thd_cnt;
+            if ((TH_PROBATION == elt->state) && me->idle_wait)
+                ++me->thd_timed_out;
+            APR_RING_INSERT_TAIL(me->recycled_thds, elt,
+                                 apr_thread_list_elt, link);
             apr_thread_mutex_unlock(me->lock);
             apr_thread_detach(t);
             apr_thread_exit(t, APR_SUCCESS);
             return NULL;        /* should not be here, safe net */
         }
 
+        /* busy thread become idle */
         ++me->idle_cnt;
         APR_RING_INSERT_TAIL(me->idle_thds, elt, apr_thread_list_elt, link);
-        wait = (me->scheduled_task_cnt) ? waiting_time(me) : -1;
+
+        /* 
+         * If there is a scheduled task, always scheduled to perform that task.
+         * Since there is no guarantee that current idle threads are scheduled
+         * for next scheduled task.
+         */
+        if (me->scheduled_task_cnt)
+            wait = waiting_time(me);
+        else if (me->idle_cnt > me->idle_max) {
+            wait = me->idle_wait;
+            elt->state = TH_PROBATION;
+        }
+        else
+            wait = -1;
+
         apr_thread_mutex_unlock(me->lock);
         apr_thread_mutex_lock(me->cond_lock);
         if (wait >= 0) {
@@ -324,6 +384,8 @@
             break;
         }
         ++(*me)->thd_cnt;
+        if ((*me)->thd_cnt > (*me)->thd_high)
+            (*me)->thd_high = (*me)->thd_cnt;
         --init_threads;
     }
 
@@ -412,7 +474,7 @@
 }
 
 /*
-*   schedule a task to run in "time" milliseconds. Find the spot in the ring where
+*   schedule a task to run in "time" microseconds. Find the spot in the ring where
 *   the time fits. Adjust the short_time so the thread wakes up when the time is reached.
 */
 static apr_status_t schedule_task(apr_thread_pool_t *me,
@@ -455,6 +517,8 @@
         rv = apr_thread_create(&thd, NULL, thread_pool_func, me, me->pool);
         if (APR_SUCCESS == rv) {
             ++me->thd_cnt;
+            if (me->thd_cnt > me->thd_high)
+                me->thd_high = me->thd_cnt;
         }
     }
     apr_thread_mutex_unlock(me->lock);
@@ -501,11 +565,15 @@
 
   FINAL_EXIT:
     me->task_cnt++;
+    if (me->task_cnt > me->tasks_high)
+        me->tasks_high = me->task_cnt;
     if (0 == me->thd_cnt || (0 == me->idle_cnt && me->thd_cnt < me->thd_max
&&
                              me->task_cnt > me->threshold)) {
         rv = apr_thread_create(&thd, NULL, thread_pool_func, me, me->pool);
         if (APR_SUCCESS == rv) {
             ++me->thd_cnt;
+            if (me->thd_cnt > me->thd_high)
+                me->thd_high = me->thd_cnt;
         }
     }
     apr_thread_mutex_unlock(me->lock);
@@ -671,11 +739,42 @@
     return me->idle_cnt;
 }
 
+APU_DECLARE(apr_size_t)
+    apr_thread_pool_tasks_run_count(apr_thread_pool_t * me)
+{
+    return me->tasks_run;
+}
+
+APU_DECLARE(apr_size_t)
+    apr_thread_pool_tasks_high_count(apr_thread_pool_t * me)
+{
+    return me->tasks_high;
+}
+
+APU_DECLARE(apr_size_t)
+    apr_thread_pool_threads_high_count(apr_thread_pool_t * me)
+{
+    return me->thd_high;
+}
+
+APU_DECLARE(apr_size_t)
+    apr_thread_pool_threads_idle_timeout_count(apr_thread_pool_t * me)
+{
+    return me->thd_timed_out;
+}
+
+
 APU_DECLARE(apr_size_t) apr_thread_pool_idle_max_get(apr_thread_pool_t *me)
 {
     return me->idle_max;
 }
 
+APU_DECLARE(apr_interval_time_t)
+    apr_thread_pool_idle_wait_get(apr_thread_pool_t * me)
+{
+    return me->idle_wait;
+}
+
 /*
  * This function stop extra idle threads to the cnt.
  * @return the number of threads stopped
@@ -709,22 +808,23 @@
         head = APR_RING_NEXT(head, link);
     }
     tail = APR_RING_LAST(thds);
-    APR_RING_UNSPLICE(head, tail, link);
     if (idle) {
+        APR_RING_UNSPLICE(head, tail, link);
         me->idle_cnt = *cnt;
     }
-    apr_thread_mutex_unlock(me->lock);
 
     n_dbg = 0;
     for (elt = head; elt != tail; elt = APR_RING_NEXT(elt, link)) {
-        elt->stop = 1;
+        elt->state = TH_STOP;
         n_dbg++;
     }
-    elt->stop = 1;
+    elt->state = TH_STOP;
     n_dbg++;
     assert(n == n_dbg);
     *cnt = n;
 
+    apr_thread_mutex_unlock(me->lock);
+
     APR_RING_PREV(head, link) = NULL;
     APR_RING_NEXT(tail, link) = NULL;
     return head;
@@ -733,7 +833,7 @@
 static apr_size_t trim_idle_threads(apr_thread_pool_t *me, apr_size_t cnt)
 {
     apr_size_t n_dbg;
-    struct apr_thread_list_elt *elt;
+    struct apr_thread_list_elt *elt, *head, *tail;
     apr_status_t rv;
 
     elt = trim_threads(me, &cnt, 1);
@@ -743,10 +843,17 @@
     apr_thread_mutex_unlock(me->cond_lock);
 
     n_dbg = 0;
-    while (elt) {
-        apr_thread_join(&rv, elt->thd);
-        elt = APR_RING_NEXT(elt, link);
-        ++n_dbg;
+    if (NULL != (head = elt)) {
+        while (elt) {
+            tail = elt;
+            apr_thread_join(&rv, elt->thd);
+            elt = APR_RING_NEXT(elt, link);
+            ++n_dbg;
+        }
+        apr_thread_mutex_lock(me->lock);
+        APR_RING_SPLICE_TAIL(me->recycled_thds, head, tail,
+                             apr_thread_list_elt, link);
+        apr_thread_mutex_unlock(me->lock);
     }
     assert(cnt == n_dbg);
 
@@ -770,6 +877,18 @@
     return cnt;
 }
 
+APU_DECLARE(apr_interval_time_t)
+    apr_thread_pool_idle_wait_set(apr_thread_pool_t * me,
+                                  apr_interval_time_t timeout)
+{
+    apr_interval_time_t oldtime;
+
+    oldtime = me->idle_wait;
+    me->idle_wait = timeout;
+
+    return oldtime;
+}
+
 APU_DECLARE(apr_size_t) apr_thread_pool_thread_max_get(apr_thread_pool_t *me)
 {
     return me->thd_max;
@@ -782,7 +901,7 @@
 APU_DECLARE(apr_size_t) apr_thread_pool_thread_max_set(apr_thread_pool_t *me,
                                                        apr_size_t cnt)
 {
-    apr_size_t n;
+    unsigned int n;
 
     me->thd_max = cnt;
     if (0 == cnt || me->thd_cnt <= cnt) {



Mime
View raw message