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