9namespace ChimeraTK::detail {
11 std::shared_timed_mutex TestableMode::_mutex;
12 std::shared_mutex TestableMode::_mutex2;
16 void TestableMode::enable() {
19 lock(
"enableTestableMode",
false);
24 bool TestableMode::Lock::tryLockFor(std::chrono::seconds timeout,
bool shared) {
28 _ownsLock = _mutex.try_lock_shared_for(timeout);
30 _mutex2.lock_shared();
34 _ownsLock = _mutex.try_lock_for(timeout);
43 void TestableMode::Lock::unlock() {
47 _mutex2.unlock_shared();
48 _mutex.unlock_shared();
58 TestableMode::Lock::~Lock() {
66 TestableMode::Lock& TestableMode::getLockObject() {
72 thread_local Lock myLock;
78 bool TestableMode::testLock()
const {
82 return getLockObject().ownsLock();
89 void terminateTestStalled() {
92 std::cerr <<
"*** Test stalled. Continue anyway since running in debugger." << std::endl;
96 struct TestStalled : std::exception {
97 [[nodiscard]]
const char* what() const noexcept
override {
return "Test stalled."; }
110 void TestableMode::lock(
const std::string& name,
bool shared) {
118 <<
"Application::testableModeLock(): Thread " << threadName() <<
" tries to obtain lock for " << name;
121 boost::thread::id lastSeen_lastOwner = _lastMutexOwner;
123 auto success = getLockObject().tryLockFor(std::chrono::seconds(30), shared);
124 boost::thread::id currentLastOwner = _lastMutexOwner;
126 if(currentLastOwner != lastSeen_lastOwner) {
127 lastSeen_lastOwner = currentLastOwner;
131 std::cerr <<
"testableModeLock(): Thread " << threadName()
132 <<
" could not obtain lock for at least 30 seconds, presumably because "
133 << threadName(_lastMutexOwner) <<
" [" << pthreadId(_lastMutexOwner)
134 <<
"] does not release it."
136 terminateTestStalled();
141 _lastMutexOwner = boost::this_thread::get_id();
145 <<
"TestableMode::lock(): Thread " << threadName() <<
" obtained lock successfully for " << name;
150 void TestableMode::unlock(
const std::string& name) {
155 <<
"TestableMode::unlock(): Thread " << threadName() <<
" releases lock for " << name;
156 getLockObject().unlock();
161 void TestableMode::step(
bool waitForDeviceInitialisation) {
165 if(_counter == 0 && (!waitForDeviceInitialisation || _deviceInitialisationCounter == 0)) {
166 throw ChimeraTK::logic_error(
"Application::stepApplication() called despite no input was provided "
167 "to the application to process!");
171 size_t oldCounter = 0;
172 auto t0 = std::chrono::steady_clock::now();
174 if((oldCounter != _counter)) {
176 <<
"Application::stepApplication(): testableMode.counter = " << _counter;
177 oldCounter = _counter;
179 unlock(
"stepApplication");
180 boost::this_thread::yield();
181 lock(
"stepApplication",
false);
182 if(_counter > 0 || (waitForDeviceInitialisation && _deviceInitialisationCounter > 0)) {
187 if(std::chrono::steady_clock::now() - t0 > std::chrono::seconds(30)) {
190 std::cerr <<
"*** Tests are stalled due to data which has been sent but not received.\n";
192 <<
" The following variables still contain unread values or had data loss due to a queue overflow:\n";
193 for(
auto& pair : _variables) {
194 const auto& variable = pair.second;
195 if(variable.counter > 0) {
196 std::cerr <<
" - " << variable.name <<
" [" << variable.processVariable->getId() <<
"]";
199 if(variable.processVariable->readNonBlocking()) {
200 std::cerr <<
" (unread data in queue)";
203 std::cerr <<
" (data loss)";
206 catch(ChimeraTK::logic_error&) {
210 std::cerr <<
" (data loss)";
215 std::cerr <<
"(end of list)\n";
219 terminateTestStalled();
230 void TestableMode::setThreadName(
const std::string& name) {
231 std::unique_lock<std::mutex> myLock(_threadNamesMutex);
232 _threadNames[boost::this_thread::get_id()] = name;
233 _threadPThreadId[boost::this_thread::get_id()] = gettid();
239 std::string TestableMode::threadName(
const boost::thread::id& threadId) {
240 std::unique_lock<std::mutex> myLock(_threadNamesMutex);
241 if(
auto const& it = _threadNames.find(threadId); it != _threadNames.end()) {
245 return "*UNKNOWN_THREAD*";
250 pid_t TestableMode::pthreadId(
const boost::thread::id& threadId) {
251 std::unique_lock<std::mutex> myLock(_threadNamesMutex);
252 if(
auto const& it = _threadPThreadId.find(threadId); it != _threadPThreadId.end()) {
260 TestableMode::LastMutexOwner::operator boost::thread::id() {
261 std::lock_guard<std::mutex> lk(_mxLastMutexOwner);
262 return _lastMutexOwner;
267 TestableMode::LastMutexOwner& TestableMode::LastMutexOwner::operator=(
const boost::thread::id&
id) {
268 std::lock_guard<std::mutex> lk(_mxLastMutexOwner);
269 _lastMutexOwner = id;
static Application & getInstance()
Obtain instance of the application.
detail::CircularDependencyDetector _circularDependencyDetector
void setThreadName(const std::string &name)
Set name of the current thread.
bool isBeingDebugged()
Checks whether the current process is being debugged.
Logger::StreamProxy logger(Logger::Severity severity, std::string context)
Convenience function to obtain the logger stream.