• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2017 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 #define LOG_TAG "NativeCallbackThread"
18 //#define LOG_NDEBUG 0
19 
20 #include "NativeCallbackThread.h"
21 
22 #include <utils/Log.h>
23 
24 namespace android {
25 
26 using std::lock_guard;
27 using std::mutex;
28 using std::unique_lock;
29 
NativeCallbackThread(JavaVM * vm)30 NativeCallbackThread::NativeCallbackThread(JavaVM *vm) : mvm(vm), mExiting(false),
31         mThread(&NativeCallbackThread::threadLoop, this) {
32     ALOGD("Started native callback thread %p", this);
33 }
34 
~NativeCallbackThread()35 NativeCallbackThread::~NativeCallbackThread() {
36     ALOGV("%s %p", __func__, this);
37     stop();
38 }
39 
threadLoop()40 void NativeCallbackThread::threadLoop() {
41     ALOGV("%s", __func__);
42 
43     JNIEnv *env = nullptr;
44     JavaVMAttachArgs aargs = {JNI_VERSION_1_4, "NativeCallbackThread", nullptr};
45     if (mvm->AttachCurrentThread(&env, &aargs) != JNI_OK || env == nullptr) {
46         ALOGE("Couldn't attach thread");
47         mExiting = true;
48         return;
49     }
50 
51     while (true) {
52         Task task;
53         {
54             unique_lock<mutex> lk(mQueueMutex);
55 
56             if (mExiting) break;
57             if (mQueue.empty()) {
58                 ALOGV("Waiting for task...");
59                 mQueueCond.wait(lk);
60                 if (mExiting) break;
61                 if (mQueue.empty()) continue;
62             }
63 
64             task = mQueue.front();
65             mQueue.pop();
66         }
67 
68         ALOGV("Executing task...");
69         task(env);
70         if (env->ExceptionCheck()) {
71             ALOGE("Unexpected exception:");
72             env->ExceptionDescribe();
73             env->ExceptionClear();
74         }
75     }
76 
77     auto res = mvm->DetachCurrentThread();
78     ALOGE_IF(res != JNI_OK, "Couldn't detach thread");
79 
80     ALOGV("Native callback thread %p finished", this);
81     ALOGD_IF(!mQueue.empty(), "Skipped execution of %zu tasks", mQueue.size());
82 }
83 
enqueue(const Task & task)84 void NativeCallbackThread::enqueue(const Task &task) {
85     lock_guard<mutex> lk(mQueueMutex);
86 
87     if (mExiting) {
88         ALOGW("Callback thread %p is not serving calls", this);
89         return;
90     }
91 
92     ALOGV("Adding task to the queue...");
93     mQueue.push(task);
94     mQueueCond.notify_one();
95 }
96 
stop()97 void NativeCallbackThread::stop() {
98     ALOGV("%s %p", __func__, this);
99 
100     {
101         lock_guard<mutex> lk(mQueueMutex);
102 
103         if (mExiting) return;
104 
105         mExiting = true;
106         mQueueCond.notify_one();
107     }
108 
109     if (mThread.get_id() == std::this_thread::get_id()) {
110         // you can't self-join a thread, but it's ok when calling from our sub-task
111         ALOGD("About to stop native callback thread %p", this);
112         mThread.detach();
113     } else {
114         mThread.join();
115         ALOGD("Stopped native callback thread %p", this);
116     }
117 }
118 
119 } // namespace android
120