blob: 29ddab1d9ac5441d458bed7885b71708f0b18dfa [file] [log] [blame]
/*
* Copyright (C) 2022 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.
*/
/**
* The CameraService watchdog is used to help detect bad states in the
* Camera HAL. The threadloop uses cycle counters, assigned to each calling
* thread, to monitor the elapsing time and kills the process when the
* expected duration has exceeded.
* Notes on multi-threaded behaviors:
* - The threadloop is blocked/paused when there are no calls being
* monitored.
* - The start and stop functions handle simultaneous call monitoring
* and single call monitoring differently. See function documentation for
* more details.
*/
#pragma once
#include <chrono>
#include <thread>
#include <time.h>
#include <utils/Thread.h>
#include <utils/Log.h>
#include <unordered_map>
// Used to wrap the call of interest in start and stop calls
#define WATCH(toMonitor) watchThread([&]() { return toMonitor;}, gettid())
#define WATCH_CUSTOM_TIMER(toMonitor, cycles, cycleLength) \
watchThread([&]() { return toMonitor;}, gettid(), cycles, cycleLength);
// Default cycles and cycle length values used to calculate permitted elapsed time
const static size_t kMaxCycles = 100;
const static uint32_t kCycleLengthMs = 100;
namespace android {
class CameraServiceWatchdog : public Thread {
public:
explicit CameraServiceWatchdog() : mPause(true), mMaxCycles(kMaxCycles),
mCycleLengthMs(kCycleLengthMs) {};
explicit CameraServiceWatchdog (size_t maxCycles, uint32_t cycleLengthMs) :
mPause(true), mMaxCycles(maxCycles), mCycleLengthMs(cycleLengthMs) {};
virtual ~CameraServiceWatchdog() {};
virtual void requestExit();
/** Used to wrap monitored calls in start and stop functions using custom timer values */
template<typename T>
auto watchThread(T func, uint32_t tid, uint32_t cycles, uint32_t cycleLength) {
decltype(func()) res;
if (cycles != mMaxCycles || cycleLength != mCycleLengthMs) {
// Create another instance of the watchdog to prevent disruption
// of timer for current monitored calls
sp<CameraServiceWatchdog> tempWatchdog =
new CameraServiceWatchdog(cycles, cycleLength);
tempWatchdog->run("CameraServiceWatchdog");
res = tempWatchdog->watchThread(func, tid);
tempWatchdog->requestExit();
tempWatchdog.clear();
} else {
// If custom timer values are equivalent to set class timer values, use
// current thread
res = watchThread(func, tid);
}
return res;
}
/** Used to wrap monitored calls in start and stop functions using class timer values */
template<typename T>
auto watchThread(T func, uint32_t tid) {
start(tid);
auto res = func();
stop(tid);
return res;
}
private:
/**
* Start adds a cycle counter for the calling thread. When threadloop is blocked/paused,
* start() unblocks and starts the watchdog
*/
void start(uint32_t tid);
/**
* If there are no calls left to be monitored, stop blocks/pauses threadloop
* otherwise stop() erases the cycle counter to end watchdog for the calling thread
*/
void stop(uint32_t tid);
virtual bool threadLoop();
Mutex mWatchdogLock; // Lock for condition variable
Condition mWatchdogCondition; // Condition variable for stop/start
bool mPause; // True if thread is currently paused
uint32_t mMaxCycles; // Max cycles
uint32_t mCycleLengthMs; // Length of time elapsed per cycle
std::unordered_map<uint32_t, uint32_t> tidToCycleCounterMap; // Thread Id to cycle counter map
};
} // namespace android