449 lines
15 KiB
C++
449 lines
15 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef __VTS_HAL_HIDL_TARGET_CALLBACK_BASE_H
|
|
#define __VTS_HAL_HIDL_TARGET_CALLBACK_BASE_H
|
|
|
|
#include <chrono>
|
|
#include <condition_variable>
|
|
#include <iostream>
|
|
#include <mutex>
|
|
#include <queue>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
|
|
using namespace ::std;
|
|
using namespace ::std::chrono;
|
|
|
|
constexpr char kVtsHalHidlTargetCallbackDefaultName[] =
|
|
"VtsHalHidlTargetCallbackDefaultName";
|
|
constexpr milliseconds DEFAULT_CALLBACK_WAIT_TIMEOUT_INITIAL = minutes(1);
|
|
|
|
namespace testing {
|
|
|
|
/*
|
|
* VTS target side test template for callback.
|
|
*
|
|
* Providing wait and notify for callback functionality.
|
|
*
|
|
* A typical usage looks like this:
|
|
*
|
|
* class CallbackArgs {
|
|
* ArgType1 arg1;
|
|
* ArgType2 arg2;
|
|
* }
|
|
*
|
|
* class MyCallback
|
|
* : public ::testing::VtsHalHidlTargetCallbackBase<>,
|
|
* public CallbackInterface {
|
|
* public:
|
|
* CallbackApi1(ArgType1 arg1) {
|
|
* CallbackArgs data;
|
|
* data.arg1 = arg1;
|
|
* NotifyFromCallback("CallbackApi1", data);
|
|
* }
|
|
*
|
|
* CallbackApi2(ArgType2 arg2) {
|
|
* CallbackArgs data;
|
|
* data.arg1 = arg1;
|
|
* NotifyFromCallback("CallbackApi2", data);
|
|
* }
|
|
* }
|
|
*
|
|
* Test(MyTest) {
|
|
* CallApi1();
|
|
* CallApi2();
|
|
* auto result = cb_.WaitForCallback("CallbackApi1");
|
|
* // cb_ as an instance of MyCallback, result is an instance of
|
|
* // ::testing::VtsHalHidlTargetCallbackBase::WaitForCallbackResult
|
|
* EXPECT_TRUE(result.no_timeout); // Check wait did not time out
|
|
* EXPECT_TRUE(result.args); // Check CallbackArgs is received (not
|
|
* nullptr). This is optional.
|
|
* // Here check value of args using the pointer result.args;
|
|
* result = cb_.WaitForCallback("CallbackApi2");
|
|
* EXPECT_TRUE(result.no_timeout);
|
|
* // Here check value of args using the pointer result.args;
|
|
*
|
|
* // Additionally. a test can wait for one of multiple callbacks.
|
|
* // In this case, wait will return when any of the callbacks in the provided
|
|
* // name list is called.
|
|
* result = cb_.WaitForCallbackAny(<vector_of_string>)
|
|
* // When vector_of_string is not provided, all callback functions will
|
|
* // be monitored. The name of callback function that was invoked
|
|
* // is stored in result.name
|
|
* }
|
|
*
|
|
* Note type of CallbackArgsTemplateClass is same across the class, which means
|
|
* all WaitForCallback method will return the same data type.
|
|
*/
|
|
template <class CallbackArgsTemplateClass>
|
|
class VtsHalHidlTargetCallbackBase {
|
|
public:
|
|
struct WaitForCallbackResult {
|
|
WaitForCallbackResult()
|
|
: no_timeout(false),
|
|
args(shared_ptr<CallbackArgsTemplateClass>(nullptr)),
|
|
name("") {}
|
|
|
|
// Whether the wait timed out
|
|
bool no_timeout;
|
|
// Arguments data from callback functions. Defaults to nullptr.
|
|
shared_ptr<CallbackArgsTemplateClass> args;
|
|
// Name of the callback. Defaults to empty string.
|
|
string name;
|
|
};
|
|
|
|
VtsHalHidlTargetCallbackBase()
|
|
: cb_default_wait_timeout_(DEFAULT_CALLBACK_WAIT_TIMEOUT_INITIAL) {}
|
|
|
|
virtual ~VtsHalHidlTargetCallbackBase() {
|
|
for (auto it : cb_lock_map_) {
|
|
delete it.second;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Wait for a callback function in a test.
|
|
* Returns a WaitForCallbackResult object containing wait results.
|
|
* If callback_function_name is not provided, a default name will be used.
|
|
* Timeout defaults to -1 milliseconds. Negative timeout means use to
|
|
* use the time out set for the callback or default callback wait time out.
|
|
*/
|
|
WaitForCallbackResult WaitForCallback(
|
|
const string& callback_function_name =
|
|
kVtsHalHidlTargetCallbackDefaultName,
|
|
milliseconds timeout = milliseconds(-1)) {
|
|
return GetCallbackLock(callback_function_name)->WaitForCallback(timeout);
|
|
}
|
|
|
|
/*
|
|
* Wait for any of the callback functions specified.
|
|
* Returns a WaitForCallbackResult object containing wait results.
|
|
* If callback_function_names is not provided, all callback functions will
|
|
* be monitored, and the list of callback functions will be updated
|
|
* dynamically during run time.
|
|
* If timeout_any is not provided, the shortest timeout from the function
|
|
* list will be used.
|
|
*/
|
|
WaitForCallbackResult WaitForCallbackAny(
|
|
const vector<string>& callback_function_names = vector<string>(),
|
|
milliseconds timeout_any = milliseconds(-1)) {
|
|
unique_lock<mutex> lock(cb_wait_any_mtx_);
|
|
|
|
auto start_time = steady_clock::now();
|
|
|
|
WaitForCallbackResult res = PeekCallbackLocks(callback_function_names);
|
|
while (!res.no_timeout) {
|
|
auto expiration =
|
|
GetWaitAnyTimeout(callback_function_names, start_time, timeout_any);
|
|
auto status = cb_wait_any_cv_.wait_until(lock, expiration);
|
|
if (status == cv_status::timeout) {
|
|
cerr << "Timed out waiting for callback functions." << endl;
|
|
break;
|
|
}
|
|
res = PeekCallbackLocks(callback_function_names);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Notify a waiting test when a callback is invoked.
|
|
* If callback_function_name is not provided, a default name will be used.
|
|
*/
|
|
void NotifyFromCallback(const string& callback_function_name =
|
|
kVtsHalHidlTargetCallbackDefaultName) {
|
|
unique_lock<mutex> lock(cb_wait_any_mtx_);
|
|
GetCallbackLock(callback_function_name)->NotifyFromCallback();
|
|
cb_wait_any_cv_.notify_one();
|
|
}
|
|
|
|
/*
|
|
* Notify a waiting test with data when a callback is invoked.
|
|
*/
|
|
void NotifyFromCallback(const CallbackArgsTemplateClass& data) {
|
|
NotifyFromCallback(kVtsHalHidlTargetCallbackDefaultName, data);
|
|
}
|
|
|
|
/*
|
|
* Notify a waiting test with data when a callback is invoked.
|
|
* If callback_function_name is not provided, a default name will be used.
|
|
*/
|
|
void NotifyFromCallback(const string& callback_function_name,
|
|
const CallbackArgsTemplateClass& data) {
|
|
unique_lock<mutex> lock(cb_wait_any_mtx_);
|
|
GetCallbackLock(callback_function_name)->NotifyFromCallback(data);
|
|
cb_wait_any_cv_.notify_one();
|
|
}
|
|
|
|
/*
|
|
* Clear lock and data for a callback function.
|
|
* This function is optional.
|
|
*/
|
|
void ClearForCallback(const string& callback_function_name =
|
|
kVtsHalHidlTargetCallbackDefaultName) {
|
|
GetCallbackLock(callback_function_name, true);
|
|
}
|
|
|
|
/*
|
|
* Get wait timeout for a specific callback function.
|
|
* If callback_function_name is not provided, a default name will be used.
|
|
*/
|
|
milliseconds GetWaitTimeout(const string& callback_function_name =
|
|
kVtsHalHidlTargetCallbackDefaultName) {
|
|
return GetCallbackLock(callback_function_name)->GetWaitTimeout();
|
|
}
|
|
|
|
/*
|
|
* Set wait timeout for a specific callback function.
|
|
* To set a default timeout (not for the default function name),
|
|
* use SetWaitTimeoutDefault. default function name callback timeout will
|
|
* also be set by SetWaitTimeoutDefault.
|
|
*/
|
|
void SetWaitTimeout(const string& callback_function_name,
|
|
milliseconds timeout) {
|
|
GetCallbackLock(callback_function_name)->SetWaitTimeout(timeout);
|
|
}
|
|
|
|
/*
|
|
* Get default wait timeout for a callback function.
|
|
* The default timeout is valid for all callback function names that
|
|
* have not been specified a timeout value, including default function name.
|
|
*/
|
|
milliseconds GetWaitTimeoutDefault() { return cb_default_wait_timeout_; }
|
|
|
|
/*
|
|
* Set default wait timeout for a callback function.
|
|
* The default timeout is valid for all callback function names that
|
|
* have not been specified a timeout value, including default function name.
|
|
*/
|
|
void SetWaitTimeoutDefault(milliseconds timeout) {
|
|
cb_default_wait_timeout_ = timeout;
|
|
}
|
|
|
|
private:
|
|
/*
|
|
* A utility class to store semaphore and data for a callback name.
|
|
*/
|
|
class CallbackLock {
|
|
public:
|
|
CallbackLock(VtsHalHidlTargetCallbackBase& parent, const string& name)
|
|
: wait_count_(0),
|
|
parent_(parent),
|
|
timeout_(milliseconds(-1)),
|
|
name_(name) {}
|
|
|
|
/*
|
|
* Wait for represented callback function.
|
|
* Timeout defaults to -1 milliseconds. Negative timeout means use to
|
|
* use the time out set for the callback or default callback wait time out.
|
|
*/
|
|
WaitForCallbackResult WaitForCallback(
|
|
milliseconds timeout = milliseconds(-1),
|
|
bool no_wait_blocking = false) {
|
|
return Wait(timeout, no_wait_blocking);
|
|
}
|
|
|
|
/*
|
|
* Wait for represented callback function.
|
|
* Timeout defaults to -1 milliseconds. Negative timeout means use to
|
|
* use the time out set for the callback or default callback wait time out.
|
|
*/
|
|
WaitForCallbackResult WaitForCallback(bool no_wait_blocking) {
|
|
return Wait(milliseconds(-1), no_wait_blocking);
|
|
}
|
|
|
|
/* Notify from represented callback function. */
|
|
void NotifyFromCallback() {
|
|
unique_lock<mutex> lock(wait_mtx_);
|
|
Notify();
|
|
}
|
|
|
|
/* Notify from represented callback function with data. */
|
|
void NotifyFromCallback(const CallbackArgsTemplateClass& data) {
|
|
unique_lock<mutex> wait_lock(wait_mtx_);
|
|
arg_data_.push(make_shared<CallbackArgsTemplateClass>(data));
|
|
Notify();
|
|
}
|
|
|
|
/* Set wait timeout for represented callback function. */
|
|
void SetWaitTimeout(milliseconds timeout) { timeout_ = timeout; }
|
|
|
|
/* Get wait timeout for represented callback function. */
|
|
milliseconds GetWaitTimeout() {
|
|
if (timeout_ < milliseconds(0)) {
|
|
return parent_.GetWaitTimeoutDefault();
|
|
}
|
|
return timeout_;
|
|
}
|
|
|
|
private:
|
|
/*
|
|
* Wait for represented callback function in a test.
|
|
* Returns a WaitForCallbackResult object containing wait results.
|
|
* Timeout defaults to -1 milliseconds. Negative timeout means use to
|
|
* use the time out set for the callback or default callback wait time out.
|
|
*/
|
|
WaitForCallbackResult Wait(milliseconds timeout, bool no_wait_blocking) {
|
|
unique_lock<mutex> lock(wait_mtx_);
|
|
WaitForCallbackResult res;
|
|
res.name = name_;
|
|
if (!no_wait_blocking) {
|
|
if (timeout < milliseconds(0)) {
|
|
timeout = GetWaitTimeout();
|
|
}
|
|
auto expiration = steady_clock::now() + timeout;
|
|
while (wait_count_ == 0) {
|
|
auto status = wait_cv_.wait_until(lock, expiration);
|
|
if (status == cv_status::timeout) {
|
|
cerr << "Timed out waiting for callback" << endl;
|
|
return res;
|
|
}
|
|
}
|
|
} else if (!wait_count_) {
|
|
return res;
|
|
}
|
|
|
|
wait_count_--;
|
|
res.no_timeout = true;
|
|
if (!arg_data_.empty()) {
|
|
res.args = arg_data_.front();
|
|
arg_data_.pop();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* Notify from represented callback function. */
|
|
void Notify() {
|
|
wait_count_++;
|
|
wait_cv_.notify_one();
|
|
}
|
|
|
|
// Mutex for protecting operations on wait count and conditional variable
|
|
mutex wait_mtx_;
|
|
// Conditional variable for callback wait and notify
|
|
condition_variable wait_cv_;
|
|
// Count for callback conditional variable
|
|
unsigned int wait_count_;
|
|
// A queue of callback arg data
|
|
queue<shared_ptr<CallbackArgsTemplateClass>> arg_data_;
|
|
// Pointer to parent class
|
|
VtsHalHidlTargetCallbackBase& parent_;
|
|
// Wait time out
|
|
milliseconds timeout_;
|
|
// Name of the represented callback function
|
|
string name_;
|
|
};
|
|
|
|
/*
|
|
* Get CallbackLock object using callback function name.
|
|
* If callback_function_name is not provided, a default name will be used.
|
|
* If callback_function_name does not exists in map yet, a new CallbackLock
|
|
* object will be created.
|
|
* If auto_clear is true, the old CallbackLock will be deleted.
|
|
*/
|
|
CallbackLock* GetCallbackLock(const string& callback_function_name,
|
|
bool auto_clear = false) {
|
|
unique_lock<mutex> lock(cb_lock_map_mtx_);
|
|
auto found = cb_lock_map_.find(callback_function_name);
|
|
if (found == cb_lock_map_.end()) {
|
|
CallbackLock* result = new CallbackLock(*this, callback_function_name);
|
|
cb_lock_map_.insert({callback_function_name, result});
|
|
return result;
|
|
} else {
|
|
if (auto_clear) {
|
|
delete (found->second);
|
|
found->second = new CallbackLock(*this, callback_function_name);
|
|
}
|
|
return found->second;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get wait timeout for a list of function names.
|
|
* If timeout_any is not negative, start_time + timeout_any will be returned.
|
|
* Otherwise, the shortest timeout from the list will be returned.
|
|
*/
|
|
steady_clock::time_point GetWaitAnyTimeout(
|
|
const vector<string>& callback_function_names,
|
|
steady_clock::time_point start_time, milliseconds timeout_any) {
|
|
if (timeout_any >= milliseconds(0)) {
|
|
return start_time + timeout_any;
|
|
}
|
|
|
|
auto locks = GetWaitAnyCallbackLocks(callback_function_names);
|
|
|
|
auto timeout_min = steady_clock::duration::max();
|
|
for (auto lock : locks) {
|
|
auto timeout = lock->GetWaitTimeout();
|
|
if (timeout < timeout_min) {
|
|
timeout_min = timeout;
|
|
}
|
|
}
|
|
|
|
return start_time + timeout_min;
|
|
}
|
|
|
|
/*
|
|
* Get a list of CallbackLock pointers from provided function name list.
|
|
*/
|
|
vector<CallbackLock*> GetWaitAnyCallbackLocks(
|
|
const vector<string>& callback_function_names) {
|
|
vector<CallbackLock*> res;
|
|
if (callback_function_names.empty()) {
|
|
for (auto const& it : cb_lock_map_) {
|
|
res.push_back(it.second);
|
|
}
|
|
} else {
|
|
for (auto const& name : callback_function_names) {
|
|
res.push_back(GetCallbackLock(name));
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Peek into the list of callback locks to check whether any of the
|
|
* callback functions has been called.
|
|
*/
|
|
WaitForCallbackResult PeekCallbackLocks(
|
|
const vector<string>& callback_function_names) {
|
|
auto locks = GetWaitAnyCallbackLocks(callback_function_names);
|
|
for (auto lock : locks) {
|
|
auto test = lock->WaitForCallback(true);
|
|
if (test.no_timeout) {
|
|
return test;
|
|
}
|
|
}
|
|
WaitForCallbackResult res;
|
|
return res;
|
|
}
|
|
|
|
// A map of function name and CallbackLock object pointers
|
|
unordered_map<string, CallbackLock*> cb_lock_map_;
|
|
// Mutex for protecting operations on lock map
|
|
mutex cb_lock_map_mtx_;
|
|
// Mutex for protecting waiting any callback
|
|
mutex cb_wait_any_mtx_;
|
|
// Default wait timeout
|
|
milliseconds cb_default_wait_timeout_;
|
|
// Conditional variable for any callback notify
|
|
condition_variable cb_wait_any_cv_;
|
|
};
|
|
|
|
} // namespace testing
|
|
|
|
#endif // __VTS_HAL_HIDL_TARGET_CALLBACK_BASE_H
|