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);
193510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        Closure **oldFront = tp->mClosureFront;
194510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        while (oldFront != tp->mClosureRear) {
195510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            Closure **newFront = oldFront;
196510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            if (++newFront == &tp->mClosureArray[tp->mMaxClosures + 1])
197510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten                newFront = tp->mClosureArray;
198510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            Closure *pClosure = *oldFront;
199510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            assert(NULL != pClosure);
200510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            *oldFront = NULL;
201510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            tp->mClosureFront = newFront;
202510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            ok = pthread_mutex_unlock(&tp->mMutex);
203510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            assert(0 == ok);
204510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            free(pClosure);
205510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            ok = pthread_mutex_lock(&tp->mMutex);
206510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            assert(0 == ok);
207510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        }
208d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        ok = pthread_mutex_unlock(&tp->mMutex);
209d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        assert(0 == ok);
210d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        // Note that we can't be sure when mWaitingNotFull will drop to zero
211d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    }
212510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten
213510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    // destroy the mutex and condition variables
2144597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten    if (initialized & INITIALIZED_CONDNOTEMPTY) {
2154597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten        ok = pthread_cond_destroy(&tp->mCondNotEmpty);
2164597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten        assert(0 == ok);
2174597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten    }
2184597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten    if (initialized & INITIALIZED_CONDNOTFULL) {
2194597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten        ok = pthread_cond_destroy(&tp->mCondNotFull);
2204597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten        assert(0 == ok);
2214597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten    }
2224597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten    if (initialized & INITIALIZED_MUTEX) {
2234597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten        ok = pthread_mutex_destroy(&tp->mMutex);
2244597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten        assert(0 == ok);
2254597a7427b697df31d0bbf4c2040806d0c27b6e0Glenn Kasten    }
226d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    tp->mInitialized = INITIALIZED_NONE;
227510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten
228510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    // release the closure circular buffer
229d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    if (tp->mClosureTypical != tp->mClosureArray && NULL != tp->mClosureArray) {
230d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        free(tp->mClosureArray);
231d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        tp->mClosureArray = NULL;
232d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    }
233510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten
234510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    // release the thread pool
235d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    if (tp->mThreadTypical != tp->mThreadArray && NULL != tp->mThreadArray) {
236d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        free(tp->mThreadArray);
237d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        tp->mThreadArray = NULL;
238d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    }
239510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten
240d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten}
241d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten
242d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kastenvoid ThreadPool_deinit(ThreadPool *tp)
243d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten{
244d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    ThreadPool_deinit_internal(tp, tp->mInitialized, tp->mMaxThreads);
245d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten}
246d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten
247cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten// Enqueue a closure to be executed later by a worker thread.
248cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten// Note that this raw interface requires an explicit "kind" and full parameter list.
249cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten// There are convenience methods below that make this easier to use.
25085133c817f6f387cd7d072988a8818f18bb53702Jean-Michel TriviSLresult ThreadPool_add(ThreadPool *tp, ClosureKind kind, ClosureHandler_generic handler,
25185133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        void *context1, void *context2, void *context3, int parameter1, int parameter2)
252d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten{
253d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    assert(NULL != tp);
254510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    assert(NULL != handler);
255510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    Closure *closure = (Closure *) malloc(sizeof(Closure));
256cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    if (NULL == closure) {
257510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        return SL_RESULT_RESOURCE_ERROR;
258cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    }
259cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    closure->mKind = kind;
260ac28eca1df49f581d952ffbda5d3019f7e3b7be6Glenn Kasten    switch (kind) {
26185133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi      case CLOSURE_KIND_PPI:
26285133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        closure->mHandler.mHandler_ppi = (ClosureHandler_ppi)handler;
26385133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        break;
26485133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi      case CLOSURE_KIND_PPII:
26585133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        closure->mHandler.mHandler_ppii = (ClosureHandler_ppii)handler;
26685133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        break;
26785133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi      case CLOSURE_KIND_PIIPP:
26885133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        closure->mHandler.mHandler_piipp = (ClosureHandler_piipp)handler;
26985133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        break;
27085133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi      default:
27185133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        SL_LOGE("ThreadPool_add() invalid closure kind %d", kind);
27285133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        assert(false);
27385133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi    }
274cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    closure->mContext1 = context1;
275cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    closure->mContext2 = context2;
27685133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi    closure->mContext3 = context3;
277cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    closure->mParameter1 = parameter1;
278cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    closure->mParameter2 = parameter2;
279d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    int ok;
280d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    ok = pthread_mutex_lock(&tp->mMutex);
281d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    assert(0 == ok);
282510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    // can't enqueue while thread pool shutting down
283510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    if (tp->mShutdown) {
284510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        ok = pthread_mutex_unlock(&tp->mMutex);
285510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        assert(0 == ok);
286510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        free(closure);
287510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        return SL_RESULT_PRECONDITIONS_VIOLATED;
288510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    }
289d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    for (;;) {
290d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        Closure **oldRear = tp->mClosureRear;
291d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        Closure **newRear = oldRear;
292d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        if (++newRear == &tp->mClosureArray[tp->mMaxClosures + 1])
293d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            newRear = tp->mClosureArray;
294510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        // if closure circular buffer is full, then wait for it to become non-full
295d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        if (newRear == tp->mClosureFront) {
296d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            ++tp->mWaitingNotFull;
297d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            ok = pthread_cond_wait(&tp->mCondNotFull, &tp->mMutex);
298d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            assert(0 == ok);
299510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            // can't enqueue while thread pool shutting down
300d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            if (tp->mShutdown) {
301d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten                assert(0 < tp->mWaitingNotFull);
302d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten                --tp->mWaitingNotFull;
303d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten                ok = pthread_mutex_unlock(&tp->mMutex);
304d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten                assert(0 == ok);
305510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten                free(closure);
306510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten                return SL_RESULT_PRECONDITIONS_VIOLATED;
307d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            }
308d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            continue;
309d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        }
310d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        assert(NULL == *oldRear);
311d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        *oldRear = closure;
312d4b099b83a686a7bb88f5e36c063f5efe623b56dGlenn Kasten        tp->mClosureRear = newRear;
313510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        // if a worker thread was waiting to dequeue, then suggest that it try again
314d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        if (0 < tp->mWaitingNotEmpty) {
315d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            --tp->mWaitingNotEmpty;
316d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            ok = pthread_cond_signal(&tp->mCondNotEmpty);
317d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            assert(0 == ok);
318d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        }
319d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        break;
320d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    }
321d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    ok = pthread_mutex_unlock(&tp->mMutex);
322d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    assert(0 == ok);
323510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten    return SL_RESULT_SUCCESS;
324d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten}
325d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten
326510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten// Called by a worker thread when it is ready to accept the next closure to execute
327d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn KastenClosure *ThreadPool_remove(ThreadPool *tp)
328d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten{
329d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    Closure *pClosure;
330d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    int ok;
331d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    ok = pthread_mutex_lock(&tp->mMutex);
332d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    assert(0 == ok);
333d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    for (;;) {
334d527933b750b107449263fff2c9d870edbfcc520Glenn Kasten        // fail if thread pool is shutting down
335d527933b750b107449263fff2c9d870edbfcc520Glenn Kasten        if (tp->mShutdown) {
336d527933b750b107449263fff2c9d870edbfcc520Glenn Kasten            pClosure = NULL;
337d527933b750b107449263fff2c9d870edbfcc520Glenn Kasten            break;
338d527933b750b107449263fff2c9d870edbfcc520Glenn Kasten        }
339d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        Closure **oldFront = tp->mClosureFront;
340510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        // if closure circular buffer is empty, then wait for it to become non-empty
341d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        if (oldFront == tp->mClosureRear) {
342d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            ++tp->mWaitingNotEmpty;
343d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            ok = pthread_cond_wait(&tp->mCondNotEmpty, &tp->mMutex);
344d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            assert(0 == ok);
345510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten            // try again
346d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            continue;
347d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        }
348510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        // dequeue the closure at front of circular buffer
349d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        Closure **newFront = oldFront;
350d527933b750b107449263fff2c9d870edbfcc520Glenn Kasten        if (++newFront == &tp->mClosureArray[tp->mMaxClosures + 1]) {
351d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            newFront = tp->mClosureArray;
352d527933b750b107449263fff2c9d870edbfcc520Glenn Kasten        }
353d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        pClosure = *oldFront;
354510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        assert(NULL != pClosure);
355510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        *oldFront = NULL;
356d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        tp->mClosureFront = newFront;
357510f3671f716f6835282e4b0fd0275c20e9dadd8Glenn Kasten        // if a client thread was waiting to enqueue, then suggest that it try again
358d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        if (0 < tp->mWaitingNotFull) {
359d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            --tp->mWaitingNotFull;
360d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            ok = pthread_cond_signal(&tp->mCondNotFull);
361d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten            assert(0 == ok);
362d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        }
363d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten        break;
364d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    }
365d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    ok = pthread_mutex_unlock(&tp->mMutex);
366d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    assert(0 == ok);
367d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten    return pClosure;
368d2a7f0d6883a6d3835642e7b282f05ed1c54fe63Glenn Kasten}
369cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten
370cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten// Convenience methods for applications
371cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn KastenSLresult ThreadPool_add_ppi(ThreadPool *tp, ClosureHandler_ppi handler,
372cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten        void *context1, void *context2, int parameter1)
373cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten{
374cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten    // function pointers are the same size so this is a safe cast
37585133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi    return ThreadPool_add(tp, CLOSURE_KIND_PPI, (ClosureHandler_generic) handler,
37685133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            context1, context2, NULL, parameter1, 0);
377cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten}
378cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten
379cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn KastenSLresult ThreadPool_add_ppii(ThreadPool *tp, ClosureHandler_ppii handler,
380cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten        void *context1, void *context2, int parameter1, int parameter2)
381cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten{
38285133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi    // function pointers are the same size so this is a safe cast
38385133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi    return ThreadPool_add(tp, CLOSURE_KIND_PPII, (ClosureHandler_generic) handler,
38485133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            context1, context2, NULL, parameter1, parameter2);
38585133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi}
38685133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi
38785133c817f6f387cd7d072988a8818f18bb53702Jean-Michel TriviSLresult ThreadPool_add_piipp(ThreadPool *tp, ClosureHandler_piipp handler,
38885133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi        void *cntxt1, int param1, int param2, void *cntxt2, void *cntxt3)
38985133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi{
39085133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi    // function pointers are the same size so this is a safe cast
39185133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi    return ThreadPool_add(tp, CLOSURE_KIND_PIIPP, (ClosureHandler_generic) handler,
39285133c817f6f387cd7d072988a8818f18bb53702Jean-Michel Trivi            cntxt1, cntxt2, cntxt3, param1, param2);
393cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0Glenn Kasten}
394