1/*
2 * Copyright (C) 2011 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 "CallbackProtector.h"
18#include "sllog.h"
19
20#include <media/stagefright/foundation/ADebug.h>
21
22//--------------------------------------------------------------------------------------------------
23namespace android {
24
25
26CallbackProtector::CallbackProtector() : RefBase(),
27        mSafeToEnterCb(true),
28        mCbCount(0)
29#ifdef USE_DEBUG
30        , mCallbackThread((pthread_t) NULL),
31        mCallbackTid(0),
32        mRequesterThread((pthread_t) NULL),
33        mRequesterTid(0)
34#endif
35{
36}
37
38
39CallbackProtector::~CallbackProtector() {
40    Mutex::Autolock _l(mLock);
41    if (mCbCount) {
42        SL_LOGE("Callback protector detected an active callback after destroy");
43    }
44
45}
46
47
48// static
49bool CallbackProtector::enterCbIfOk(const sp<CallbackProtector> &protector) {
50    if (protector != 0) {
51        return protector->enterCb();
52    } else {
53        SL_LOGE("Callback protector is missing");
54        return false;
55    }
56}
57
58
59bool CallbackProtector::enterCb() {
60    Mutex::Autolock _l(mLock);
61    if (mSafeToEnterCb) {
62        mCbCount++;
63#ifdef USE_DEBUG
64        if (mCbCount > 1) {
65            SL_LOGV("Callback protector allowed multiple or nested callback entry: %u", mCbCount);
66        } else {
67            mCallbackThread = pthread_self();
68            mCallbackTid = gettid();
69        }
70#endif
71    } else {
72#ifdef USE_DEBUG
73        SL_LOGV("Callback protector denied callback entry by thread %p tid %d during destroy"
74                " requested by thread %p tid %d",
75                (void *) pthread_self(), gettid(),
76                (void *) mRequesterThread, mRequesterTid);
77#else
78        SL_LOGV("Callback protector denied callback entry during destroy");
79#endif
80    }
81    return mSafeToEnterCb;
82}
83
84
85void CallbackProtector::exitCb() {
86    Mutex::Autolock _l(mLock);
87
88    CHECK(mCbCount > 0);
89    mCbCount--;
90
91    if (mCbCount == 0) {
92        if (!mSafeToEnterCb) {
93#ifdef USE_DEBUG
94            SL_LOGV("Callback protector detected return from callback by thread %p tid %d during"
95                    " destroy requested by thread %p tid %d",
96                    (void *) mCallbackThread, mCallbackTid,
97                    (void *) mRequesterThread, mRequesterTid);
98#else
99            SL_LOGV("Callback protector detected return from callback during destroy");
100#endif
101            mCbExitedCondition.broadcast();
102        }
103#ifdef USE_DEBUG
104        mCallbackThread = (pthread_t) NULL;
105        mCallbackTid = 0;
106#endif
107    }
108}
109
110
111void CallbackProtector::requestCbExitAndWait() {
112    Mutex::Autolock _l(mLock);
113    mSafeToEnterCb = false;
114#ifdef USE_DEBUG
115    mRequesterThread = pthread_self();
116    mRequesterTid = gettid();
117#endif
118    while (mCbCount) {
119#ifdef USE_DEBUG
120        SL_LOGV("Callback protector detected in-progress callback by thread %p tid %d during"
121                " blocking destroy requested by thread %p tid %d",
122                (void *) mCallbackThread, mCallbackTid,
123                (void *) pthread_self(), gettid());
124#else
125        SL_LOGV("Callback protector detected in-progress callback during blocking destroy");
126#endif
127        mCbExitedCondition.wait(mLock);
128    }
129}
130
131
132void CallbackProtector::requestCbExit() {
133    Mutex::Autolock _l(mLock);
134    mSafeToEnterCb = false;
135#ifdef USE_DEBUG
136    mRequesterThread = pthread_self();
137    mRequesterTid = gettid();
138#endif
139    if (mCbCount) {
140#ifdef USE_DEBUG
141        SL_LOGV("Callback protector detected in-progress callback by thread %p tid %d during"
142                " non-blocking destroy requested by thread %p tid %d",
143                (void *) mCallbackThread, mCallbackTid,
144                (void *) pthread_self(), gettid());
145#else
146        SL_LOGV("Callback protector detected in-progress callback during non-blocking destroy");
147#endif
148    }
149}
150
151} // namespace android
152