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 30 NativeCallbackThread::NativeCallbackThread(JavaVM *vm) : mvm(vm), mExiting(false), 31 mThread(&NativeCallbackThread::threadLoop, this) { 32 ALOGD("Started native callback thread %p", this); 33 } 34 35 NativeCallbackThread::~NativeCallbackThread() { 36 ALOGV("%s %p", __func__, this); 37 stop(); 38 } 39 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 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 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