pthreadpool: Undo put_job when returning error
authorChristof Schmitt <cs@samba.org>
Tue, 28 Nov 2017 17:59:06 +0000 (10:59 -0700)
committerChristof Schmitt <cs@samba.org>
Fri, 8 Dec 2017 08:43:12 +0000 (09:43 +0100)
When an error is returned to the caller of pthreadpool_add_job, the job
should not be kept in the internal job array. Otherwise the caller might
free the data structure and a later worker thread would still reference
it.

When it is not possible to create a single worker thread, the system
might be out of resources or hitting a configured limit. In this case
fall back to calling the job function synchronously instead of raising
the error to the caller and possibly back to the SMB client.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=13170

Signed-off-by: Christof Schmitt <cs@samba.org>
Reviewed-by: Volker Lendecke <vl@samba.org>
lib/pthreadpool/pthreadpool.c

index 4e1e5d41fcaca71b7b27ca4f4e762d615cdd083b..309aba988667d5d9105ea8b81416a36ff14853f2 100644 (file)
@@ -429,6 +429,11 @@ static bool pthreadpool_put_job(struct pthreadpool *p,
        return true;
 }
 
        return true;
 }
 
+static void pthreadpool_undo_put_job(struct pthreadpool *p)
+{
+       p->num_jobs -= 1;
+}
+
 static void *pthreadpool_server(void *arg)
 {
        struct pthreadpool *pool = (struct pthreadpool *)arg;
 static void *pthreadpool_server(void *arg)
 {
        struct pthreadpool *pool = (struct pthreadpool *)arg;
@@ -599,6 +604,9 @@ int pthreadpool_add_job(struct pthreadpool *pool, int job_id,
                 * We have idle threads, wake one.
                 */
                res = pthread_cond_signal(&pool->condvar);
                 * We have idle threads, wake one.
                 */
                res = pthread_cond_signal(&pool->condvar);
+               if (res != 0) {
+                       pthreadpool_undo_put_job(pool);
+               }
                pthread_mutex_unlock(&pool->mutex);
                return res;
        }
                pthread_mutex_unlock(&pool->mutex);
                return res;
        }
@@ -614,8 +622,24 @@ int pthreadpool_add_job(struct pthreadpool *pool, int job_id,
 
        res = pthreadpool_create_thread(pool);
        if (res != 0) {
 
        res = pthreadpool_create_thread(pool);
        if (res != 0) {
-               pthread_mutex_unlock(&pool->mutex);
-               return res;
+               if (pool->num_threads == 0) {
+                       /*
+                        * No thread could be created to run job,
+                        * fallback to sync call.
+                        */
+                       pthreadpool_undo_put_job(pool);
+                       pthread_mutex_unlock(&pool->mutex);
+
+                       fn(private_data);
+                       return pool->signal_fn(job_id, fn, private_data,
+                                              pool->signal_fn_private_data);
+               }
+
+               /*
+                * At least one thread is still available, let
+                * that one run the queued job.
+                */
+               res = 0;
        }
 
        pthread_mutex_unlock(&pool->mutex);
        }
 
        pthread_mutex_unlock(&pool->mutex);