locks.c 11.7 KB
Newer Older
Glenn Kasten's avatar
Glenn Kasten committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "sles_allinclusive.h"
18 19 20 21 22 23 24 25 26 27 28
#include <bionic_pthread.h>


// Use this macro to validate a pthread_t before passing it into __pthread_gettid.
// One of the common reasons for deadlock is trying to lock a mutex for an object
// which has been destroyed (which does memset to 0x00 or 0x55 as the final step).
// To avoid crashing with a SIGSEGV right before we're about to log a deadlock warning,
// we check that the pthread_t is probably valid.  Note that it is theoretically
// possible for something to look like a valid pthread_t but not actually be valid.
// So we might still crash, but only in the case where a deadlock was imminent anyway.
#define LIKELY_VALID(ptr) (((ptr) != (pthread_t) 0) && ((((size_t) (ptr)) & 3) == 0))
Glenn Kasten's avatar
Glenn Kasten committed
29

30

31
/** \brief Exclusively lock an object */
32

Glenn Kasten's avatar
Glenn Kasten committed
33
#ifdef USE_DEBUG
34
void object_lock_exclusive_(IObject *thiz, const char *file, int line)
Glenn Kasten's avatar
Glenn Kasten committed
35 36
{
    int ok;
37
    ok = pthread_mutex_trylock(&thiz->mMutex);
Glenn Kasten's avatar
Glenn Kasten committed
38
    if (0 != ok) {
39 40 41 42
        // not android_atomic_acquire_load because we don't care about relative load/load ordering
        int32_t oldGeneration = thiz->mGeneration;
        // wait up to a total of 250 ms
        static const unsigned backoffs[] = {10, 20, 30, 40, 50, 100};
Glenn Kasten's avatar
Glenn Kasten committed
43 44
        unsigned i = 0;
        for (;;) {
45 46 47
            // the Android version is in ms not timespec, and isn't named pthread_mutex_timedlock_np
            ok = pthread_mutex_lock_timeout_np(&thiz->mMutex, backoffs[i]);
            if (0 == ok) {
Glenn Kasten's avatar
Glenn Kasten committed
48
                break;
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
            }
            if (EBUSY == ok) {
                // this is the expected return value for timeout, and will be handled below
            } else if (EDEADLK == ok) {
                // we don't use the kind of mutex that can return this error, but just in case
                SL_LOGE("%s:%d: recursive lock detected", file, line);
            } else {
                // some other return value
                SL_LOGE("%s:%d: pthread_mutex_lock_timeout_np returned %d", file, line, ok);
            }
            // is anyone else making forward progress?
            int32_t newGeneration = thiz->mGeneration;
            if (newGeneration != oldGeneration) {
                // if we ever see forward progress then lock without timeout (more efficient)
                goto forward_progress;
            }
            // no, then continue trying to lock but with increasing timeouts
Glenn Kasten's avatar
Glenn Kasten committed
66
            if (++i >= (sizeof(backoffs) / sizeof(backoffs[0]))) {
67 68 69 70 71 72 73 74 75 76 77 78
                // the extra block avoids a C++ compiler error about goto past initialization
                {
                    pthread_t me = pthread_self();
                    pthread_t owner = thiz->mOwner;
                    // unlikely, but this could result in a memory fault if owner is corrupt
                    pid_t ownerTid = LIKELY_VALID(owner) ? __pthread_gettid(owner) : -1;
                    SL_LOGW("%s:%d: pthread %p (tid %d) sees object %p was locked by pthread %p"
                            " (tid %d) at %s:%d\n", file, line, *(void **)&me, gettid(), thiz,
                            *(void **)&owner, ownerTid, thiz->mFile, thiz->mLine);
                }
forward_progress:
                // attempt one more time without timeout; maybe this time we will be successful
79
                ok = pthread_mutex_lock(&thiz->mMutex);
Glenn Kasten's avatar
Glenn Kasten committed
80 81 82 83
                assert(0 == ok);
                break;
            }
        }
Glenn Kasten's avatar
Glenn Kasten committed
84
    }
85
    // here if mutex was successfully locked
Glenn Kasten's avatar
Glenn Kasten committed
86 87
    pthread_t zero;
    memset(&zero, 0, sizeof(pthread_t));
88
    if (0 != memcmp(&zero, &thiz->mOwner, sizeof(pthread_t))) {
89 90 91 92 93 94 95
        pthread_t me = pthread_self();
        pthread_t owner = thiz->mOwner;
        pid_t ownerTid = LIKELY_VALID(owner) ? __pthread_gettid(owner) : -1;
        if (pthread_equal(pthread_self(), owner)) {
            SL_LOGE("%s:%d: pthread %p (tid %d) sees object %p was recursively locked by pthread"
                    " %p (tid %d) at %s:%d\n", file, line, *(void **)&me, gettid(), thiz,
                    *(void **)&owner, ownerTid, thiz->mFile, thiz->mLine);
Glenn Kasten's avatar
Glenn Kasten committed
96
        } else {
97 98 99
            SL_LOGE("%s:%d: pthread %p (tid %d) sees object %p was left unlocked in unexpected"
                    " state by pthread %p (tid %d) at %s:%d\n", file, line, *(void **)&me, gettid(),
                    thiz, *(void **)&owner, ownerTid, thiz->mFile, thiz->mLine);
Glenn Kasten's avatar
Glenn Kasten committed
100 101 102
        }
        assert(false);
    }
103 104 105
    thiz->mOwner = pthread_self();
    thiz->mFile = file;
    thiz->mLine = line;
106 107
    // not android_atomic_inc because we are already holding a mutex
    ++thiz->mGeneration;
Glenn Kasten's avatar
Glenn Kasten committed
108 109
}
#else
110
void object_lock_exclusive(IObject *thiz)
Glenn Kasten's avatar
Glenn Kasten committed
111 112
{
    int ok;
113
    ok = pthread_mutex_lock(&thiz->mMutex);
Glenn Kasten's avatar
Glenn Kasten committed
114 115
    assert(0 == ok);
}
Glenn Kasten's avatar
Glenn Kasten committed
116
#endif
Glenn Kasten's avatar
Glenn Kasten committed
117

118

119
/** \brief Exclusively unlock an object and do not report any updates */
120

Glenn Kasten's avatar
Glenn Kasten committed
121
#ifdef USE_DEBUG
122
void object_unlock_exclusive_(IObject *thiz, const char *file, int line)
123
{
124 125 126 127 128 129
    assert(pthread_equal(pthread_self(), thiz->mOwner));
    assert(NULL != thiz->mFile);
    assert(0 != thiz->mLine);
    memset(&thiz->mOwner, 0, sizeof(pthread_t));
    thiz->mFile = file;
    thiz->mLine = line;
Glenn Kasten's avatar
Glenn Kasten committed
130
    int ok;
131
    ok = pthread_mutex_unlock(&thiz->mMutex);
Glenn Kasten's avatar
Glenn Kasten committed
132 133
    assert(0 == ok);
}
134
#else
135
void object_unlock_exclusive(IObject *thiz)
136 137
{
    int ok;
138
    ok = pthread_mutex_unlock(&thiz->mMutex);
139 140 141
    assert(0 == ok);
}
#endif
Glenn Kasten's avatar
Glenn Kasten committed
142

143

144 145 146
/** \brief Exclusively unlock an object and report updates to the specified bit-mask of
 *  attributes
 */
147

148
#ifdef USE_DEBUG
149
void object_unlock_exclusive_attributes_(IObject *thiz, unsigned attributes,
150 151
    const char *file, int line)
#else
152
void object_unlock_exclusive_attributes(IObject *thiz, unsigned attributes)
153
#endif
154
{
Glenn Kasten's avatar
Glenn Kasten committed
155 156

#ifdef USE_DEBUG
157 158 159
    assert(pthread_equal(pthread_self(), thiz->mOwner));
    assert(NULL != thiz->mFile);
    assert(0 != thiz->mLine);
Glenn Kasten's avatar
Glenn Kasten committed
160 161
#endif

162
    int ok;
163

164 165
    // make SL object IDs be contiguous with XA object IDs
    SLuint32 objectID = IObjectToObjectID(thiz);
166 167
    SLuint32 index = objectID;
    if ((XA_OBJECTID_ENGINE <= index) && (index <= XA_OBJECTID_CAMERADEVICE)) {
168
        ;
169 170
    } else if ((SL_OBJECTID_ENGINE <= index) && (index <= SL_OBJECTID_METADATAEXTRACTOR)) {
        index -= SL_OBJECTID_ENGINE - XA_OBJECTID_CAMERADEVICE - 1;
171 172
    } else {
        assert(false);
173
        index = 0;
174 175
    }

176 177 178 179 180 181 182 183 184
    // first synchronously handle updates to attributes here, while object is still locked.
    // This appears to be a loop, but actually typically runs through the loop only once.
    unsigned asynchronous = attributes;
    while (attributes) {
        // this sequence is carefully crafted to be O(1); tread carefully when making changes
        unsigned bit = ctz(attributes);
        // ATTR_INDEX_MAX == next bit position after the last attribute
        assert(ATTR_INDEX_MAX > bit);
        // compute the entry in the handler table using object ID and bit number
185
        AttributeHandler handler = handlerTable[index][bit];
186 187
        if (NULL != handler) {
            asynchronous &= ~(*handler)(thiz);
188
        }
189
        attributes &= ~(1 << bit);
190
    }
191

192 193 194 195 196 197
    // any remaining attributes are handled asynchronously in the sync thread
    if (asynchronous) {
        unsigned oldAttributesMask = thiz->mAttributesMask;
        thiz->mAttributesMask = oldAttributesMask | asynchronous;
        if (oldAttributesMask) {
            asynchronous = ATTR_NONE;
198 199
        }
    }
200

201
#ifdef ANDROID
202 203 204 205
    // FIXME hack to safely handle a post-unlock PrefetchStatus callback and/or AudioTrack::start()
    slPrefetchCallback prefetchCallback = NULL;
    void *prefetchContext = NULL;
    SLuint32 prefetchEvents = SL_PREFETCHEVENT_NONE;
206
    android::sp<android::AudioTrack> audioTrack;
207 208
    if (SL_OBJECTID_AUDIOPLAYER == objectID) {
        CAudioPlayer *ap = (CAudioPlayer *) thiz;
209 210 211 212 213 214 215
        prefetchCallback = ap->mPrefetchStatus.mDeferredPrefetchCallback;
        prefetchContext  = ap->mPrefetchStatus.mDeferredPrefetchContext;
        prefetchEvents   = ap->mPrefetchStatus.mDeferredPrefetchEvents;
        ap->mPrefetchStatus.mDeferredPrefetchCallback = NULL;
        // clearing these next two fields is not required, but avoids stale data during debugging
        ap->mPrefetchStatus.mDeferredPrefetchContext  = NULL;
        ap->mPrefetchStatus.mDeferredPrefetchEvents   = SL_PREFETCHEVENT_NONE;
216 217 218 219 220 221 222
        if (ap->mDeferredStart) {
            audioTrack = ap->mAudioTrack;
            ap->mDeferredStart = false;
        }
    }
#endif

Glenn Kasten's avatar
Glenn Kasten committed
223
#ifdef USE_DEBUG
224 225 226
    memset(&thiz->mOwner, 0, sizeof(pthread_t));
    thiz->mFile = file;
    thiz->mLine = line;
Glenn Kasten's avatar
Glenn Kasten committed
227
#endif
228
    ok = pthread_mutex_unlock(&thiz->mMutex);
229
    assert(0 == ok);
230

231
#ifdef ANDROID
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
    // FIXME call the prefetch status callback while not holding the mutex on AudioPlayer
    if (NULL != prefetchCallback) {
        // note these are synchronous by the application's thread as it is about to return from API
        assert(prefetchEvents != SL_PREFETCHEVENT_NONE);
        CAudioPlayer *ap = (CAudioPlayer *) thiz;
        // spec requires separate callbacks for each event
        if (SL_PREFETCHEVENT_STATUSCHANGE & prefetchEvents) {
            (*prefetchCallback)(&ap->mPrefetchStatus.mItf, prefetchContext,
                    SL_PREFETCHEVENT_STATUSCHANGE);
        }
        if (SL_PREFETCHEVENT_FILLLEVELCHANGE & prefetchEvents) {
            (*prefetchCallback)(&ap->mPrefetchStatus.mItf, prefetchContext,
                    SL_PREFETCHEVENT_FILLLEVELCHANGE);
        }
    }

248 249 250 251 252 253 254
    // call AudioTrack::start() while not holding the mutex on AudioPlayer
    if (audioTrack != 0) {
        audioTrack->start();
        audioTrack.clear();
    }
#endif

255
    // first update to this interface since previous sync
256
    if (ATTR_NONE != asynchronous) {
257
        unsigned id = thiz->mInstanceID;
258 259 260
        if (0 != id) {
            --id;
            assert(MAX_INSTANCE > id);
261
            IEngine *thisEngine = &thiz->mEngine->mEngine;
262
            // FIXME atomic or here
263 264 265 266
            interface_lock_exclusive(thisEngine);
            thisEngine->mChangedMask |= 1 << id;
            interface_unlock_exclusive(thisEngine);
        }
267
    }
268

269 270
}

271

272
/** \brief Wait on the condition variable associated with the object; see pthread_cond_wait */
273

Glenn Kasten's avatar
Glenn Kasten committed
274
#ifdef USE_DEBUG
275
void object_cond_wait_(IObject *thiz, const char *file, int line)
Glenn Kasten's avatar
Glenn Kasten committed
276 277
{
    // note that this will unlock the mutex, so we have to clear the owner
278 279 280 281 282 283
    assert(pthread_equal(pthread_self(), thiz->mOwner));
    assert(NULL != thiz->mFile);
    assert(0 != thiz->mLine);
    memset(&thiz->mOwner, 0, sizeof(pthread_t));
    thiz->mFile = file;
    thiz->mLine = line;
Glenn Kasten's avatar
Glenn Kasten committed
284 285
    // alas we don't know the new owner's identity
    int ok;
286
    ok = pthread_cond_wait(&thiz->mCond, &thiz->mMutex);
Glenn Kasten's avatar
Glenn Kasten committed
287 288
    assert(0 == ok);
    // restore my ownership
289 290 291
    thiz->mOwner = pthread_self();
    thiz->mFile = file;
    thiz->mLine = line;
Glenn Kasten's avatar
Glenn Kasten committed
292 293
}
#else
294
void object_cond_wait(IObject *thiz)
Glenn Kasten's avatar
Glenn Kasten committed
295 296
{
    int ok;
297
    ok = pthread_cond_wait(&thiz->mCond, &thiz->mMutex);
Glenn Kasten's avatar
Glenn Kasten committed
298 299
    assert(0 == ok);
}
Glenn Kasten's avatar
Glenn Kasten committed
300
#endif
Glenn Kasten's avatar
Glenn Kasten committed
301

302

303
/** \brief Signal the condition variable associated with the object; see pthread_cond_signal */
304

305
void object_cond_signal(IObject *thiz)
Glenn Kasten's avatar
Glenn Kasten committed
306 307
{
    int ok;
308
    ok = pthread_cond_signal(&thiz->mCond);
Glenn Kasten's avatar
Glenn Kasten committed
309 310
    assert(0 == ok);
}
Glenn Kasten's avatar
Glenn Kasten committed
311

312

313 314 315
/** \brief Broadcast the condition variable associated with the object;
 *  see pthread_cond_broadcast
 */
316

317
void object_cond_broadcast(IObject *thiz)
Glenn Kasten's avatar
Glenn Kasten committed
318 319
{
    int ok;
320
    ok = pthread_cond_broadcast(&thiz->mCond);
Glenn Kasten's avatar
Glenn Kasten committed
321 322
    assert(0 == ok);
}