You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
336 lines
12 KiB
336 lines
12 KiB
/*
|
|
* 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"
|
|
|
|
|
|
// Use this macro to validate a pthread_t before passing it into pthread_gettid_np.
|
|
// 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))
|
|
|
|
|
|
/** \brief Exclusively lock an object */
|
|
|
|
#ifdef USE_DEBUG
|
|
|
|
void init_time_spec(timespec* ts, long delta) {
|
|
clock_gettime(CLOCK_REALTIME, ts);
|
|
ts->tv_nsec += delta;
|
|
|
|
if (ts->tv_nsec >= 1000000000L) {
|
|
ts->tv_sec++;
|
|
ts->tv_nsec -= 1000000000L;
|
|
}
|
|
}
|
|
|
|
void object_lock_exclusive_(IObject *thiz, const char *file, int line)
|
|
{
|
|
int ok;
|
|
ok = pthread_mutex_trylock(&thiz->mMutex);
|
|
if (0 != ok) {
|
|
// 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 long nanoBackoffs[] = {
|
|
10 * 1000000, 20 * 1000000, 30 * 1000000, 40 * 1000000, 50 * 1000000, 100 * 1000000};
|
|
unsigned i = 0;
|
|
timespec ts;
|
|
memset(&ts, 0, sizeof(timespec));
|
|
for (;;) {
|
|
init_time_spec(&ts, nanoBackoffs[i]);
|
|
ok = pthread_mutex_timedlock(&thiz->mMutex, &ts);
|
|
if (0 == ok) {
|
|
break;
|
|
}
|
|
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
|
|
if (++i >= (sizeof(nanoBackoffs) / sizeof(nanoBackoffs[0]))) {
|
|
// 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_np(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
|
|
ok = pthread_mutex_lock(&thiz->mMutex);
|
|
assert(0 == ok);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// here if mutex was successfully locked
|
|
pthread_t zero;
|
|
memset(&zero, 0, sizeof(pthread_t));
|
|
if (0 != memcmp(&zero, &thiz->mOwner, sizeof(pthread_t))) {
|
|
pthread_t me = pthread_self();
|
|
pthread_t owner = thiz->mOwner;
|
|
pid_t ownerTid = LIKELY_VALID(owner) ? pthread_gettid_np(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);
|
|
} else {
|
|
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);
|
|
}
|
|
assert(false);
|
|
}
|
|
thiz->mOwner = pthread_self();
|
|
thiz->mFile = file;
|
|
thiz->mLine = line;
|
|
// not android_atomic_inc because we are already holding a mutex
|
|
++thiz->mGeneration;
|
|
}
|
|
#else
|
|
void object_lock_exclusive(IObject *thiz)
|
|
{
|
|
int ok;
|
|
ok = pthread_mutex_lock(&thiz->mMutex);
|
|
assert(0 == ok);
|
|
}
|
|
#endif
|
|
|
|
|
|
/** \brief Exclusively unlock an object and do not report any updates */
|
|
|
|
#ifdef USE_DEBUG
|
|
void object_unlock_exclusive_(IObject *thiz, const char *file, int line)
|
|
{
|
|
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;
|
|
int ok;
|
|
ok = pthread_mutex_unlock(&thiz->mMutex);
|
|
assert(0 == ok);
|
|
}
|
|
#else
|
|
void object_unlock_exclusive(IObject *thiz)
|
|
{
|
|
int ok;
|
|
ok = pthread_mutex_unlock(&thiz->mMutex);
|
|
assert(0 == ok);
|
|
}
|
|
#endif
|
|
|
|
|
|
/** \brief Exclusively unlock an object and report updates to the specified bit-mask of
|
|
* attributes
|
|
*/
|
|
|
|
#ifdef USE_DEBUG
|
|
void object_unlock_exclusive_attributes_(IObject *thiz, unsigned attributes,
|
|
const char *file, int line)
|
|
#else
|
|
void object_unlock_exclusive_attributes(IObject *thiz, unsigned attributes)
|
|
#endif
|
|
{
|
|
|
|
#ifdef USE_DEBUG
|
|
assert(pthread_equal(pthread_self(), thiz->mOwner));
|
|
assert(NULL != thiz->mFile);
|
|
assert(0 != thiz->mLine);
|
|
#endif
|
|
|
|
int ok;
|
|
|
|
// make SL object IDs be contiguous with XA object IDs
|
|
SLuint32 objectID = IObjectToObjectID(thiz);
|
|
SLuint32 index = objectID;
|
|
if ((XA_OBJECTID_ENGINE <= index) && (index <= XA_OBJECTID_CAMERADEVICE)) {
|
|
;
|
|
} else if ((SL_OBJECTID_ENGINE <= index) && (index <= SL_OBJECTID_METADATAEXTRACTOR)) {
|
|
index -= SL_OBJECTID_ENGINE - XA_OBJECTID_CAMERADEVICE - 1;
|
|
} else {
|
|
assert(false);
|
|
index = 0;
|
|
}
|
|
|
|
// 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
|
|
AttributeHandler handler = handlerTable[index][bit];
|
|
if (NULL != handler) {
|
|
asynchronous &= ~(*handler)(thiz);
|
|
}
|
|
attributes &= ~(1 << bit);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
#ifdef ANDROID
|
|
// 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;
|
|
android::sp<android::AudioTrack> audioTrack;
|
|
if (SL_OBJECTID_AUDIOPLAYER == objectID) {
|
|
CAudioPlayer *ap = (CAudioPlayer *) thiz;
|
|
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;
|
|
if (ap->mDeferredStart) {
|
|
audioTrack = ap->mTrackPlayer->mAudioTrack;
|
|
ap->mDeferredStart = false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_DEBUG
|
|
memset(&thiz->mOwner, 0, sizeof(pthread_t));
|
|
thiz->mFile = file;
|
|
thiz->mLine = line;
|
|
#endif
|
|
ok = pthread_mutex_unlock(&thiz->mMutex);
|
|
assert(0 == ok);
|
|
|
|
#ifdef ANDROID
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
// call AudioTrack::start() while not holding the mutex on AudioPlayer
|
|
if (audioTrack != 0) {
|
|
audioTrack->start();
|
|
audioTrack.clear();
|
|
}
|
|
#endif
|
|
|
|
// first update to this interface since previous sync
|
|
if (ATTR_NONE != asynchronous) {
|
|
unsigned id = thiz->mInstanceID;
|
|
if (0 != id) {
|
|
--id;
|
|
assert(MAX_INSTANCE > id);
|
|
IEngine *thisEngine = &thiz->mEngine->mEngine;
|
|
// FIXME atomic or here
|
|
interface_lock_exclusive(thisEngine);
|
|
thisEngine->mChangedMask |= 1 << id;
|
|
interface_unlock_exclusive(thisEngine);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/** \brief Wait on the condition variable associated with the object; see pthread_cond_wait */
|
|
|
|
#ifdef USE_DEBUG
|
|
void object_cond_wait_(IObject *thiz, const char *file, int line)
|
|
{
|
|
// note that this will unlock the mutex, so we have to clear the owner
|
|
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;
|
|
// alas we don't know the new owner's identity
|
|
int ok;
|
|
ok = pthread_cond_wait(&thiz->mCond, &thiz->mMutex);
|
|
assert(0 == ok);
|
|
// restore my ownership
|
|
thiz->mOwner = pthread_self();
|
|
thiz->mFile = file;
|
|
thiz->mLine = line;
|
|
}
|
|
#else
|
|
void object_cond_wait(IObject *thiz)
|
|
{
|
|
int ok;
|
|
ok = pthread_cond_wait(&thiz->mCond, &thiz->mMutex);
|
|
assert(0 == ok);
|
|
}
|
|
#endif
|
|
|
|
|
|
/** \brief Signal the condition variable associated with the object; see pthread_cond_signal */
|
|
|
|
void object_cond_signal(IObject *thiz)
|
|
{
|
|
int ok;
|
|
ok = pthread_cond_signal(&thiz->mCond);
|
|
assert(0 == ok);
|
|
}
|
|
|
|
|
|
/** \brief Broadcast the condition variable associated with the object;
|
|
* see pthread_cond_broadcast
|
|
*/
|
|
|
|
void object_cond_broadcast(IObject *thiz)
|
|
{
|
|
int ok;
|
|
ok = pthread_cond_broadcast(&thiz->mCond);
|
|
assert(0 == ok);
|
|
}
|