ThreadPool.c revision d527933b750b107449263fff2c9d870edbfcc520
1d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten/*
2d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten * Copyright (C) 2010 The Android Open Source Project
3d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten *
4d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten * Licensed under the Apache License, Version 2.0 (the "License");
5d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten * you may not use this file except in compliance with the License.
6d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten * You may obtain a copy of the License at
7d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten *
8d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten *      http://www.apache.org/licenses/LICENSE-2.0
9d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten *
10d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten * Unless required by applicable law or agreed to in writing, software
11d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten * distributed under the License is distributed on an "AS IS" BASIS,
12d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten * See the License for the specific language governing permissions and
14d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten * limitations under the License.
15d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten */
16d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten
17d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten/* ThreadPool */
18d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten
19d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten#include "sles_allinclusive.h"
20d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten
21d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten// Entry point for each worker thread
22d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten
23d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kastenstatic void *ThreadPool_start(void *context)
24d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten{
25d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    ThreadPool *tp = (ThreadPool *) context;
26d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    assert(NULL != tp);
27d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    for (;;) {
28d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        Closure *pClosure = ThreadPool_remove(tp);
29510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        // closure is NULL when thread pool is being destroyed
30cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten        if (NULL == pClosure) {
31d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            break;
32cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten        }
33cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten        // make a copy of parameters, then free the parameters
34cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten        const Closure closure = *pClosure;
35510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        free(pClosure);
36cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten        // extract parameters and call the right method depending on kind
37cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten        ClosureKind kind = closure.mKind;
38cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten        void *context1 = closure.mContext1;
39cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten        void *context2 = closure.mContext2;
40cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten        int parameter1 = closure.mParameter1;
41cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten        switch (kind) {
4285133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi          case CLOSURE_KIND_PPI:
43cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten            {
4485133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            ClosureHandler_ppi handler_ppi = closure.mHandler.mHandler_ppi;
45cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten            assert(NULL != handler_ppi);
46cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten            (*handler_ppi)(context1, context2, parameter1);
47cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten            }
48cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten            break;
4985133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi          case CLOSURE_KIND_PPII:
50cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten            {
5185133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            ClosureHandler_ppii handler_ppii = closure.mHandler.mHandler_ppii;
52cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten            assert(NULL != handler_ppii);
53cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten            int parameter2 = closure.mParameter2;
54cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten            (*handler_ppii)(context1, context2, parameter1, parameter2);
55cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten            }
56cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten            break;
5785133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi          case CLOSURE_KIND_PIIPP:
5885133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            {
5985133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            ClosureHandler_piipp handler_piipp = closure.mHandler.mHandler_piipp;
6085133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            assert(NULL != handler_piipp);
6185133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            int parameter2 = closure.mParameter2;
6285133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            void *context3 = closure.mContext3;
6385133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            (*handler_piipp)(context1, parameter1, parameter2, context2, context3);
6485133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            }
6585133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            break;
6685133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi          default:
67cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten            SL_LOGE("Unexpected callback kind %d", kind);
68cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten            assert(false);
69cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten            break;
70cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten        }
71d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    }
72d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    return NULL;
73d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten}
74d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten
75d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten#define INITIALIZED_NONE         0
76d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten#define INITIALIZED_MUTEX        1
77d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten#define INITIALIZED_CONDNOTFULL  2
78d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten#define INITIALIZED_CONDNOTEMPTY 4
79d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten#define INITIALIZED_ALL          7
80d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten
81d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kastenstatic void ThreadPool_deinit_internal(ThreadPool *tp, unsigned initialized, unsigned nThreads);
82d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten
83d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten// Initialize a ThreadPool
84d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten// maxClosures defaults to CLOSURE_TYPICAL if 0
85d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten// maxThreads defaults to THREAD_TYPICAL if 0
86d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten
87d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn KastenSLresult ThreadPool_init(ThreadPool *tp, unsigned maxClosures, unsigned maxThreads)
88d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten{
89d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    assert(NULL != tp);
90d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    memset(tp, 0, sizeof(ThreadPool));
91d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    tp->mShutdown = SL_BOOLEAN_FALSE;
92510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    unsigned initialized = INITIALIZED_NONE;    // which objects were successfully initialized
93510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    unsigned nThreads = 0;                      // number of threads successfully created
94510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    int err;
95d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    SLresult result;
96510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten
97510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    // initialize mutex and condition variables
98510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    err = pthread_mutex_init(&tp->mMutex, (const pthread_mutexattr_t *) NULL);
99510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    result = err_to_result(err);
100d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    if (SL_RESULT_SUCCESS != result)
101d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        goto fail;
102d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    initialized |= INITIALIZED_MUTEX;
103510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    err = pthread_cond_init(&tp->mCondNotFull, (const pthread_condattr_t *) NULL);
104510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    result = err_to_result(err);
105d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    if (SL_RESULT_SUCCESS != result)
106d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        goto fail;
107d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    initialized |= INITIALIZED_CONDNOTFULL;
108510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    err = pthread_cond_init(&tp->mCondNotEmpty, (const pthread_condattr_t *) NULL);
109510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    result = err_to_result(err);
110d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    if (SL_RESULT_SUCCESS != result)
111d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        goto fail;
112d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    initialized |= INITIALIZED_CONDNOTEMPTY;
113510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten
114510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    // use default values for parameters, if not specified explicitly
115d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    tp->mWaitingNotFull = 0;
116d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    tp->mWaitingNotEmpty = 0;
117d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    if (0 == maxClosures)
118d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        maxClosures = CLOSURE_TYPICAL;
119d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    tp->mMaxClosures = maxClosures;
120d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    if (0 == maxThreads)
121d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        maxThreads = THREAD_TYPICAL;
122d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    tp->mMaxThreads = maxThreads;
123510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten
124510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    // initialize circular buffer for closures
125d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    if (CLOSURE_TYPICAL >= maxClosures) {
126d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        tp->mClosureArray = tp->mClosureTypical;
127d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    } else {
128d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        tp->mClosureArray = (Closure **) malloc((maxClosures + 1) * sizeof(Closure *));
129d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        if (NULL == tp->mClosureArray) {
130d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            result = SL_RESULT_RESOURCE_ERROR;
131d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            goto fail;
132d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        }
133d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    }
134d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    tp->mClosureFront = tp->mClosureArray;
135d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    tp->mClosureRear = tp->mClosureArray;
136510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten
137510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    // initialize thread pool
138d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    if (THREAD_TYPICAL >= maxThreads) {
139d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        tp->mThreadArray = tp->mThreadTypical;
140d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    } else {
141d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        tp->mThreadArray = (pthread_t *) malloc(maxThreads * sizeof(pthread_t));
142d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        if (NULL == tp->mThreadArray) {
143d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            result = SL_RESULT_RESOURCE_ERROR;
144d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            goto fail;
145d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        }
146d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    }
147d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    unsigned i;
148d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    for (i = 0; i < maxThreads; ++i) {
149510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        int err = pthread_create(&tp->mThreadArray[i], (const pthread_attr_t *) NULL,
150510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            ThreadPool_start, tp);
151510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        result = err_to_result(err);
152d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        if (SL_RESULT_SUCCESS != result)
153d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            goto fail;
154d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        ++nThreads;
155d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    }
156d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    tp->mInitialized = initialized;
157510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten
158510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    // done
159d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    return SL_RESULT_SUCCESS;
160510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten
161510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    // here on any kind of error
162d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kastenfail:
163d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    ThreadPool_deinit_internal(tp, initialized, nThreads);
164d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    return result;
165d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten}
166d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten
167d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kastenstatic void ThreadPool_deinit_internal(ThreadPool *tp, unsigned initialized, unsigned nThreads)
168d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten{
1694597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten    int ok;
1704597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten
171d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    assert(NULL != tp);
172d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    // Destroy all threads
173d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    if (0 < nThreads) {
174d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        assert(INITIALIZED_ALL == initialized);
175d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        ok = pthread_mutex_lock(&tp->mMutex);
176d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        assert(0 == ok);
177d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        tp->mShutdown = SL_BOOLEAN_TRUE;
178d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        ok = pthread_cond_broadcast(&tp->mCondNotEmpty);
179d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        assert(0 == ok);
180d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        ok = pthread_cond_broadcast(&tp->mCondNotFull);
181d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        assert(0 == ok);
182d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        ok = pthread_mutex_unlock(&tp->mMutex);
183d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        assert(0 == ok);
184d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        unsigned i;
185d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        for (i = 0; i < nThreads; ++i) {
186d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            ok = pthread_join(tp->mThreadArray[i], (void **) NULL);
187d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            assert(ok == 0);
188d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        }
189510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten
190510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        // Empty out the circular buffer of closures
191d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        ok = pthread_mutex_lock(&tp->mMutex);
192d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        assert(0 == ok);
193d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        assert(0 == tp->mWaitingNotEmpty);
194510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        Closure **oldFront = tp->mClosureFront;
195510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        while (oldFront != tp->mClosureRear) {
196510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            Closure **newFront = oldFront;
197510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            if (++newFront == &tp->mClosureArray[tp->mMaxClosures + 1])
198510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten                newFront = tp->mClosureArray;
199510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            Closure *pClosure = *oldFront;
200510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            assert(NULL != pClosure);
201510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            *oldFront = NULL;
202510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            tp->mClosureFront = newFront;
203510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            ok = pthread_mutex_unlock(&tp->mMutex);
204510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            assert(0 == ok);
205510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            free(pClosure);
206510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            ok = pthread_mutex_lock(&tp->mMutex);
207510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            assert(0 == ok);
208510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        }
209d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        ok = pthread_mutex_unlock(&tp->mMutex);
210d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        assert(0 == ok);
211d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        // Note that we can't be sure when mWaitingNotFull will drop to zero
212d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    }
213510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten
214510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    // destroy the mutex and condition variables
2154597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten    if (initialized & INITIALIZED_CONDNOTEMPTY) {
2164597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten        ok = pthread_cond_destroy(&tp->mCondNotEmpty);
2174597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten        assert(0 == ok);
2184597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten    }
2194597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten    if (initialized & INITIALIZED_CONDNOTFULL) {
2204597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten        ok = pthread_cond_destroy(&tp->mCondNotFull);
2214597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten        assert(0 == ok);
2224597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten    }
2234597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten    if (initialized & INITIALIZED_MUTEX) {
2244597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten        ok = pthread_mutex_destroy(&tp->mMutex);
2254597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten        assert(0 == ok);
2264597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten    }
227d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    tp->mInitialized = INITIALIZED_NONE;
228510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten
229510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    // release the closure circular buffer
230d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    if (tp->mClosureTypical != tp->mClosureArray && NULL != tp->mClosureArray) {
231d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        free(tp->mClosureArray);
232d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        tp->mClosureArray = NULL;
233d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    }
234510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten
235510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    // release the thread pool
236d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    if (tp->mThreadTypical != tp->mThreadArray && NULL != tp->mThreadArray) {
237d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        free(tp->mThreadArray);
238d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        tp->mThreadArray = NULL;
239d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    }
240510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten
241d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten}
242d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten
243d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kastenvoid ThreadPool_deinit(ThreadPool *tp)
244d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten{
245d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    ThreadPool_deinit_internal(tp, tp->mInitialized, tp->mMaxThreads);
246d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten}
247d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten
248cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten// Enqueue a closure to be executed later by a worker thread.
249cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten// Note that this raw interface requires an explicit "kind" and full parameter list.
250cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten// There are convenience methods below that make this easier to use.
25185133c817f6f387cd7d072988a8818f18bb53702Jean-Michel TriviSLresult ThreadPool_add(ThreadPool *tp, ClosureKind kind, ClosureHandler_generic handler,
25285133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        void *context1, void *context2, void *context3, int parameter1, int parameter2)
253d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten{
254d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    assert(NULL != tp);
255510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    assert(NULL != handler);
256510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    Closure *closure = (Closure *) malloc(sizeof(Closure));
257cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    if (NULL == closure) {
258510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        return SL_RESULT_RESOURCE_ERROR;
259cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    }
260cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    closure->mKind = kind;
26185133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi    switch(kind) {
26285133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi      case CLOSURE_KIND_PPI:
26385133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        closure->mHandler.mHandler_ppi = (ClosureHandler_ppi)handler;
26485133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        break;
26585133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi      case CLOSURE_KIND_PPII:
26685133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        closure->mHandler.mHandler_ppii = (ClosureHandler_ppii)handler;
26785133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        break;
26885133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi      case CLOSURE_KIND_PIIPP:
26985133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        closure->mHandler.mHandler_piipp = (ClosureHandler_piipp)handler;
27085133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        break;
27185133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi      default:
27285133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        SL_LOGE("ThreadPool_add() invalid closure kind %d", kind);
27385133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        assert(false);
27485133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi    }
275cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    closure->mContext1 = context1;
276cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    closure->mContext2 = context2;
27785133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi    closure->mContext3 = context3;
278cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    closure->mParameter1 = parameter1;
279cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    closure->mParameter2 = parameter2;
280d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    int ok;
281d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    ok = pthread_mutex_lock(&tp->mMutex);
282d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    assert(0 == ok);
283510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    // can't enqueue while thread pool shutting down
284510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    if (tp->mShutdown) {
285510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        ok = pthread_mutex_unlock(&tp->mMutex);
286510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        assert(0 == ok);
287510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        free(closure);
288510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        return SL_RESULT_PRECONDITIONS_VIOLATED;
289510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    }
290d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    for (;;) {
291d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        Closure **oldRear = tp->mClosureRear;
292d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        Closure **newRear = oldRear;
293d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        if (++newRear == &tp->mClosureArray[tp->mMaxClosures + 1])
294d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            newRear = tp->mClosureArray;
295510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        // if closure circular buffer is full, then wait for it to become non-full
296d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        if (newRear == tp->mClosureFront) {
297d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            ++tp->mWaitingNotFull;
298d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            ok = pthread_cond_wait(&tp->mCondNotFull, &tp->mMutex);
299d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            assert(0 == ok);
300510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            // can't enqueue while thread pool shutting down
301d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            if (tp->mShutdown) {
302d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten                assert(0 < tp->mWaitingNotFull);
303d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten                --tp->mWaitingNotFull;
304d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten                ok = pthread_mutex_unlock(&tp->mMutex);
305d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten                assert(0 == ok);
306510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten                free(closure);
307510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten                return SL_RESULT_PRECONDITIONS_VIOLATED;
308d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            }
309d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            continue;
310d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        }
311d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        assert(NULL == *oldRear);
312d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        *oldRear = closure;
313d4b099b83a686a7bb88f5e36c063f5efe623b56dGlenn Kasten        tp->mClosureRear = newRear;
314510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        // if a worker thread was waiting to dequeue, then suggest that it try again
315d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        if (0 < tp->mWaitingNotEmpty) {
316d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            --tp->mWaitingNotEmpty;
317d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            ok = pthread_cond_signal(&tp->mCondNotEmpty);
318d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            assert(0 == ok);
319d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        }
320d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        break;
321d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    }
322d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    ok = pthread_mutex_unlock(&tp->mMutex);
323d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    assert(0 == ok);
324510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    return SL_RESULT_SUCCESS;
325d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten}
326d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten
327510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten// Called by a worker thread when it is ready to accept the next closure to execute
328d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn KastenClosure *ThreadPool_remove(ThreadPool *tp)
329d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten{
330d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    Closure *pClosure;
331d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    int ok;
332d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    ok = pthread_mutex_lock(&tp->mMutex);
333d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    assert(0 == ok);
334d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    for (;;) {
335d527933b750b107449263fff2c9d870edbfcc520Glenn Kasten        // fail if thread pool is shutting down
336d527933b750b107449263fff2c9d870edbfcc520Glenn Kasten        if (tp->mShutdown) {
337d527933b750b107449263fff2c9d870edbfcc520Glenn Kasten            pClosure = NULL;
338d527933b750b107449263fff2c9d870edbfcc520Glenn Kasten            break;
339d527933b750b107449263fff2c9d870edbfcc520Glenn Kasten        }
340d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        Closure **oldFront = tp->mClosureFront;
341510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        // if closure circular buffer is empty, then wait for it to become non-empty
342d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        if (oldFront == tp->mClosureRear) {
343d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            ++tp->mWaitingNotEmpty;
344d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            ok = pthread_cond_wait(&tp->mCondNotEmpty, &tp->mMutex);
345d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            assert(0 == ok);
346510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            // try again
347d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            continue;
348d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        }
349510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        // dequeue the closure at front of circular buffer
350d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        Closure **newFront = oldFront;
351d527933b750b107449263fff2c9d870edbfcc520Glenn Kasten        if (++newFront == &tp->mClosureArray[tp->mMaxClosures + 1]) {
352d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            newFront = tp->mClosureArray;
353d527933b750b107449263fff2c9d870edbfcc520Glenn Kasten        }
354d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        pClosure = *oldFront;
355510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        assert(NULL != pClosure);
356510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        *oldFront = NULL;
357d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        tp->mClosureFront = newFront;
358510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        // if a client thread was waiting to enqueue, then suggest that it try again
359d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        if (0 < tp->mWaitingNotFull) {
360d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            --tp->mWaitingNotFull;
361d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            ok = pthread_cond_signal(&tp->mCondNotFull);
362d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            assert(0 == ok);
363d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        }
364d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        break;
365d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    }
366d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    ok = pthread_mutex_unlock(&tp->mMutex);
367d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    assert(0 == ok);
368d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    return pClosure;
369d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten}
370cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten
371cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten// Convenience methods for applications
372cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn KastenSLresult ThreadPool_add_ppi(ThreadPool *tp, ClosureHandler_ppi handler,
373cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten        void *context1, void *context2, int parameter1)
374cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten{
375cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    // function pointers are the same size so this is a safe cast
37685133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi    return ThreadPool_add(tp, CLOSURE_KIND_PPI, (ClosureHandler_generic) handler,
37785133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            context1, context2, NULL, parameter1, 0);
378cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten}
379cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten
380cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn KastenSLresult ThreadPool_add_ppii(ThreadPool *tp, ClosureHandler_ppii handler,
381cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten        void *context1, void *context2, int parameter1, int parameter2)
382cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten{
38385133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi    // function pointers are the same size so this is a safe cast
38485133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi    return ThreadPool_add(tp, CLOSURE_KIND_PPII, (ClosureHandler_generic) handler,
38585133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            context1, context2, NULL, parameter1, parameter2);
38685133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi}
38785133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi
38885133c817f6f387cd7d072988a8818f18bb53702Jean-Michel TriviSLresult ThreadPool_add_piipp(ThreadPool *tp, ClosureHandler_piipp handler,
38985133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        void *cntxt1, int param1, int param2, void *cntxt2, void *cntxt3)
39085133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi{
39185133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi    // function pointers are the same size so this is a safe cast
39285133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi    return ThreadPool_add(tp, CLOSURE_KIND_PIIPP, (ClosureHandler_generic) handler,
39385133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            cntxt1, cntxt2, cntxt3, param1, param2);
394cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten}
395