/** * Copyright (C) 2017 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. */ #define LOG_TAG "NativeCallbackThread" //#define LOG_NDEBUG 0 #include "NativeCallbackThread.h" #include namespace android { using std::lock_guard; using std::mutex; using std::unique_lock; NativeCallbackThread::NativeCallbackThread(JavaVM *vm) : mvm(vm), mExiting(false), mThread(&NativeCallbackThread::threadLoop, this) { ALOGD("Started native callback thread %p", this); } NativeCallbackThread::~NativeCallbackThread() { ALOGV("%s %p", __func__, this); stop(); } void NativeCallbackThread::threadLoop() { ALOGV("%s", __func__); JNIEnv *env = nullptr; JavaVMAttachArgs aargs = {JNI_VERSION_1_4, "NativeCallbackThread", nullptr}; if (mvm->AttachCurrentThread(&env, &aargs) != JNI_OK || env == nullptr) { ALOGE("Couldn't attach thread"); mExiting = true; return; } while (true) { Task task; { unique_lock lk(mQueueMutex); if (mExiting) break; if (mQueue.empty()) { ALOGV("Waiting for task..."); mQueueCond.wait(lk); if (mExiting) break; if (mQueue.empty()) continue; } task = mQueue.front(); mQueue.pop(); } ALOGV("Executing task..."); task(env); if (env->ExceptionCheck()) { ALOGE("Unexpected exception:"); env->ExceptionDescribe(); env->ExceptionClear(); } } auto res = mvm->DetachCurrentThread(); ALOGE_IF(res != JNI_OK, "Couldn't detach thread"); ALOGV("Native callback thread %p finished", this); ALOGD_IF(!mQueue.empty(), "Skipped execution of %zu tasks", mQueue.size()); } void NativeCallbackThread::enqueue(const Task &task) { lock_guard lk(mQueueMutex); if (mExiting) { ALOGW("Callback thread %p is not serving calls", this); return; } ALOGV("Adding task to the queue..."); mQueue.push(task); mQueueCond.notify_one(); } void NativeCallbackThread::stop() { ALOGV("%s %p", __func__, this); { lock_guard lk(mQueueMutex); if (mExiting) return; mExiting = true; mQueueCond.notify_one(); } if (mThread.get_id() == std::this_thread::get_id()) { // you can't self-join a thread, but it's ok when calling from our sub-task ALOGD("About to stop native callback thread %p", this); mThread.detach(); } else { mThread.join(); ALOGD("Stopped native callback thread %p", this); } } } // namespace android