1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "sles_allinclusive.h"
18
19
20// Use this macro to validate a pthread_t before passing it into pthread_gettid_np.
21// One of the common reasons for deadlock is trying to lock a mutex for an object
22// which has been destroyed (which does memset to 0x00 or 0x55 as the final step).
23// To avoid crashing with a SIGSEGV right before we're about to log a deadlock warning,
24// we check that the pthread_t is probably valid.  Note that it is theoretically
25// possible for something to look like a valid pthread_t but not actually be valid.
26// So we might still crash, but only in the case where a deadlock was imminent anyway.
27#define LIKELY_VALID(ptr) (((ptr) != (pthread_t) 0) && ((((size_t) (ptr)) & 3) == 0))
28
29
30/** \brief Exclusively lock an object */
31
32#ifdef USE_DEBUG
33
34void init_time_spec(timespec* ts, long delta) {
35    clock_gettime(CLOCK_REALTIME, ts);
36    ts->tv_nsec += delta;
37
38    if (ts->tv_nsec >= 1000000000L) {
39        ts->tv_sec++;
40        ts->tv_nsec -= 1000000000L;
41    }
42}
43
44void object_lock_exclusive_(IObject *thiz, const char *file, int line)
45{
46    int ok;
47    ok = pthread_mutex_trylock(&thiz->mMutex);
48    if (0 != ok) {
49        // not android_atomic_acquire_load because we don't care about relative load/load ordering
50        int32_t oldGeneration = thiz->mGeneration;
51        // wait up to a total of 250 ms
52        static const long nanoBackoffs[] = {
53            10 * 1000000, 20 * 1000000, 30 * 1000000, 40 * 1000000, 50 * 1000000, 100 * 1000000};
54        unsigned i = 0;
55        timespec ts;
56        memset(&ts, 0, sizeof(timespec));
57        for (;;) {
58            init_time_spec(&ts, nanoBackoffs[i]);
59            ok = pthread_mutex_timedlock(&thiz->mMutex, &ts);
60            if (0 == ok) {
61                break;
62            }
63            if (EBUSY == ok) {
64                // this is the expected return value for timeout, and will be handled below
65            } else if (EDEADLK == ok) {
66                // we don't use the kind of mutex that can return this error, but just in case
67                SL_LOGE("%s:%d: recursive lock detected", file, line);
68            } else {
69                // some other return value
70                SL_LOGE("%s:%d: pthread_mutex_lock_timeout_np returned %d", file, line, ok);
71            }
72            // is anyone else making forward progress?
73            int32_t newGeneration = thiz->mGeneration;
74            if (newGeneration != oldGeneration) {
75                // if we ever see forward progress then lock without timeout (more efficient)
76                goto forward_progress;
77            }
78            // no, then continue trying to lock but with increasing timeouts
79            if (++i >= (sizeof(nanoBackoffs) / sizeof(nanoBackoffs[0]))) {
80                // the extra block avoids a C++ compiler error about goto past initialization
81                {
82                    pthread_t me = pthread_self();
83                    pthread_t owner = thiz->mOwner;
84                    // unlikely, but this could result in a memory fault if owner is corrupt
85                    pid_t ownerTid = LIKELY_VALID(owner) ? pthread_gettid_np(owner) : -1;
86                    SL_LOGW("%s:%d: pthread %p (tid %d) sees object %p was locked by pthread %p"
87                            " (tid %d) at %s:%d\n", file, line, *(void **)&me, gettid(), thiz,
88                            *(void **)&owner, ownerTid, thiz->mFile, thiz->mLine);
89                }
90forward_progress:
91                // attempt one more time without timeout; maybe this time we will be successful
92                ok = pthread_mutex_lock(&thiz->mMutex);
93                assert(0 == ok);
94                break;
95            }
96        }
97    }
98    // here if mutex was successfully locked
99    pthread_t zero;
100    memset(&zero, 0, sizeof(pthread_t));
101    if (0 != memcmp(&zero, &thiz->mOwner, sizeof(pthread_t))) {
102        pthread_t me = pthread_self();
103        pthread_t owner = thiz->mOwner;
104        pid_t ownerTid = LIKELY_VALID(owner) ? pthread_gettid_np(owner) : -1;
105        if (pthread_equal(pthread_self(), owner)) {
106            SL_LOGE("%s:%d: pthread %p (tid %d) sees object %p was recursively locked by pthread"
107                    " %p (tid %d) at %s:%d\n", file, line, *(void **)&me, gettid(), thiz,
108                    *(void **)&owner, ownerTid, thiz->mFile, thiz->mLine);
109        } else {
110            SL_LOGE("%s:%d: pthread %p (tid %d) sees object %p was left unlocked in unexpected"
111                    " state by pthread %p (tid %d) at %s:%d\n", file, line, *(void **)&me, gettid(),
112                    thiz, *(void **)&owner, ownerTid, thiz->mFile, thiz->mLine);
113        }
114        assert(false);
115    }
116    thiz->mOwner = pthread_self();
117    thiz->mFile = file;
118    thiz->mLine = line;
119    // not android_atomic_inc because we are already holding a mutex
120    ++thiz->mGeneration;
121}
122#else
123void object_lock_exclusive(IObject *thiz)
124{
125    int ok;
126    ok = pthread_mutex_lock(&thiz->mMutex);
127    assert(0 == ok);
128}
129#endif
130
131
132/** \brief Exclusively unlock an object and do not report any updates */
133
134#ifdef USE_DEBUG
135void object_unlock_exclusive_(IObject *thiz, const char *file, int line)
136{
137    assert(pthread_equal(pthread_self(), thiz->mOwner));
138    assert(NULL != thiz->mFile);
139    assert(0 != thiz->mLine);
140    memset(&thiz->mOwner, 0, sizeof(pthread_t));
141    thiz->mFile = file;
142    thiz->mLine = line;
143    int ok;
144    ok = pthread_mutex_unlock(&thiz->mMutex);
145    assert(0 == ok);
146}
147#else
148void object_unlock_exclusive(IObject *thiz)
149{
150    int ok;
151    ok = pthread_mutex_unlock(&thiz->mMutex);
152    assert(0 == ok);
153}
154#endif
155
156
157/** \brief Exclusively unlock an object and report updates to the specified bit-mask of
158 *  attributes
159 */
160
161#ifdef USE_DEBUG
162void object_unlock_exclusive_attributes_(IObject *thiz, unsigned attributes,
163    const char *file, int line)
164#else
165void object_unlock_exclusive_attributes(IObject *thiz, unsigned attributes)
166#endif
167{
168
169#ifdef USE_DEBUG
170    assert(pthread_equal(pthread_self(), thiz->mOwner));
171    assert(NULL != thiz->mFile);
172    assert(0 != thiz->mLine);
173#endif
174
175    int ok;
176
177    // make SL object IDs be contiguous with XA object IDs
178    SLuint32 objectID = IObjectToObjectID(thiz);
179    SLuint32 index = objectID;
180    if ((XA_OBJECTID_ENGINE <= index) && (index <= XA_OBJECTID_CAMERADEVICE)) {
181        ;
182    } else if ((SL_OBJECTID_ENGINE <= index) && (index <= SL_OBJECTID_METADATAEXTRACTOR)) {
183        index -= SL_OBJECTID_ENGINE - XA_OBJECTID_CAMERADEVICE - 1;
184    } else {
185        assert(false);
186        index = 0;
187    }
188
189    // first synchronously handle updates to attributes here, while object is still locked.
190    // This appears to be a loop, but actually typically runs through the loop only once.
191    unsigned asynchronous = attributes;
192    while (attributes) {
193        // this sequence is carefully crafted to be O(1); tread carefully when making changes
194        unsigned bit = ctz(attributes);
195        // ATTR_INDEX_MAX == next bit position after the last attribute
196        assert(ATTR_INDEX_MAX > bit);
197        // compute the entry in the handler table using object ID and bit number
198        AttributeHandler handler = handlerTable[index][bit];
199        if (NULL != handler) {
200            asynchronous &= ~(*handler)(thiz);
201        }
202        attributes &= ~(1 << bit);
203    }
204
205    // any remaining attributes are handled asynchronously in the sync thread
206    if (asynchronous) {
207        unsigned oldAttributesMask = thiz->mAttributesMask;
208        thiz->mAttributesMask = oldAttributesMask | asynchronous;
209        if (oldAttributesMask) {
210            asynchronous = ATTR_NONE;
211        }
212    }
213
214#ifdef ANDROID
215    // FIXME hack to safely handle a post-unlock PrefetchStatus callback and/or AudioTrack::start()
216    slPrefetchCallback prefetchCallback = NULL;
217    void *prefetchContext = NULL;
218    SLuint32 prefetchEvents = SL_PREFETCHEVENT_NONE;
219    android::sp<android::AudioTrack> audioTrack;
220    if (SL_OBJECTID_AUDIOPLAYER == objectID) {
221        CAudioPlayer *ap = (CAudioPlayer *) thiz;
222        prefetchCallback = ap->mPrefetchStatus.mDeferredPrefetchCallback;
223        prefetchContext  = ap->mPrefetchStatus.mDeferredPrefetchContext;
224        prefetchEvents   = ap->mPrefetchStatus.mDeferredPrefetchEvents;
225        ap->mPrefetchStatus.mDeferredPrefetchCallback = NULL;
226        // clearing these next two fields is not required, but avoids stale data during debugging
227        ap->mPrefetchStatus.mDeferredPrefetchContext  = NULL;
228        ap->mPrefetchStatus.mDeferredPrefetchEvents   = SL_PREFETCHEVENT_NONE;
229        if (ap->mDeferredStart) {
230            audioTrack = ap->mTrackPlayer->mAudioTrack;
231            ap->mDeferredStart = false;
232        }
233    }
234#endif
235
236#ifdef USE_DEBUG
237    memset(&thiz->mOwner, 0, sizeof(pthread_t));
238    thiz->mFile = file;
239    thiz->mLine = line;
240#endif
241    ok = pthread_mutex_unlock(&thiz->mMutex);
242    assert(0 == ok);
243
244#ifdef ANDROID
245    // FIXME call the prefetch status callback while not holding the mutex on AudioPlayer
246    if (NULL != prefetchCallback) {
247        // note these are synchronous by the application's thread as it is about to return from API
248        assert(prefetchEvents != SL_PREFETCHEVENT_NONE);
249        CAudioPlayer *ap = (CAudioPlayer *) thiz;
250        // spec requires separate callbacks for each event
251        if (SL_PREFETCHEVENT_STATUSCHANGE & prefetchEvents) {
252            (*prefetchCallback)(&ap->mPrefetchStatus.mItf, prefetchContext,
253                    SL_PREFETCHEVENT_STATUSCHANGE);
254        }
255        if (SL_PREFETCHEVENT_FILLLEVELCHANGE & prefetchEvents) {
256            (*prefetchCallback)(&ap->mPrefetchStatus.mItf, prefetchContext,
257                    SL_PREFETCHEVENT_FILLLEVELCHANGE);
258        }
259    }
260
261    // call AudioTrack::start() while not holding the mutex on AudioPlayer
262    if (audioTrack != 0) {
263        audioTrack->start();
264        audioTrack.clear();
265    }
266#endif
267
268    // first update to this interface since previous sync
269    if (ATTR_NONE != asynchronous) {
270        unsigned id = thiz->mInstanceID;
271        if (0 != id) {
272            --id;
273            assert(MAX_INSTANCE > id);
274            IEngine *thisEngine = &thiz->mEngine->mEngine;
275            // FIXME atomic or here
276            interface_lock_exclusive(thisEngine);
277            thisEngine->mChangedMask |= 1 << id;
278            interface_unlock_exclusive(thisEngine);
279        }
280    }
281
282}
283
284
285/** \brief Wait on the condition variable associated with the object; see pthread_cond_wait */
286
287#ifdef USE_DEBUG
288void object_cond_wait_(IObject *thiz, const char *file, int line)
289{
290    // note that this will unlock the mutex, so we have to clear the owner
291    assert(pthread_equal(pthread_self(), thiz->mOwner));
292    assert(NULL != thiz->mFile);
293    assert(0 != thiz->mLine);
294    memset(&thiz->mOwner, 0, sizeof(pthread_t));
295    thiz->mFile = file;
296    thiz->mLine = line;
297    // alas we don't know the new owner's identity
298    int ok;
299    ok = pthread_cond_wait(&thiz->mCond, &thiz->mMutex);
300    assert(0 == ok);
301    // restore my ownership
302    thiz->mOwner = pthread_self();
303    thiz->mFile = file;
304    thiz->mLine = line;
305}
306#else
307void object_cond_wait(IObject *thiz)
308{
309    int ok;
310    ok = pthread_cond_wait(&thiz->mCond, &thiz->mMutex);
311    assert(0 == ok);
312}
313#endif
314
315
316/** \brief Signal the condition variable associated with the object; see pthread_cond_signal */
317
318void object_cond_signal(IObject *thiz)
319{
320    int ok;
321    ok = pthread_cond_signal(&thiz->mCond);
322    assert(0 == ok);
323}
324
325
326/** \brief Broadcast the condition variable associated with the object;
327 *  see pthread_cond_broadcast
328 */
329
330void object_cond_broadcast(IObject *thiz)
331{
332    int ok;
333    ok = pthread_cond_broadcast(&thiz->mCond);
334    assert(0 == ok);
335}
336