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) {
119 <<
"Application::testableModeLock(): Thread " << threadName()
120 <<
" tries to obtain lock for " << name;
124 boost::thread::id lastSeen_lastOwner = _lastMutexOwner;
126 auto success = getLockObject().tryLockFor(std::chrono::seconds(30), shared);
127 boost::thread::id currentLastOwner = _lastMutexOwner;
129 if(currentLastOwner != lastSeen_lastOwner) {
130 lastSeen_lastOwner = currentLastOwner;
134 std::cerr <<
"testableModeLock(): Thread " << threadName()
135 <<
" could not obtain lock for at least 30 seconds, presumably because "
136 << threadName(_lastMutexOwner) <<
" [" << pthreadId(_lastMutexOwner)
137 <<
"] does not release it."
139 terminateTestStalled();
144 _lastMutexOwner = boost::this_thread::get_id();
149 <<
"TestableMode::lock(): Thread " << threadName()
150 <<
" obtained lock successfully for " << name;
156 void TestableMode::unlock(
const std::string& name) {
162 <<
"TestableMode::unlock(): Thread " << threadName()
163 <<
" releases lock for " << name;
165 getLockObject().unlock();
170 void TestableMode::step(
bool waitForDeviceInitialisation) {
174 if(_counter == 0 && (!waitForDeviceInitialisation || _deviceInitialisationCounter == 0)) {
175 throw ChimeraTK::logic_error(
"Application::stepApplication() called despite no input was provided "
176 "to the application to process!");
180 size_t oldCounter = 0;
181 auto t0 = std::chrono::steady_clock::now();
183 if(_enableDebug && (oldCounter != _counter)) {
185 <<
"Application::stepApplication(): testableMode.counter = " << _counter;
186 oldCounter = _counter;
188 unlock(
"stepApplication");
189 boost::this_thread::yield();
190 lock(
"stepApplication",
false);
191 if(_counter > 0 || (waitForDeviceInitialisation && _deviceInitialisationCounter > 0)) {
196 if(std::chrono::steady_clock::now() - t0 > std::chrono::seconds(30)) {
199 std::cerr <<
"*** Tests are stalled due to data which has been sent but not received.\n";
201 <<
" The following variables still contain unread values or had data loss due to a queue overflow:\n";
202 for(
auto& pair : _variables) {
203 const auto& variable = pair.second;
204 if(variable.counter > 0) {
205 std::cerr <<
" - " << variable.name <<
" [" << variable.processVariable->getId() <<
"]";
208 if(variable.processVariable->readNonBlocking()) {
209 std::cerr <<
" (unread data in queue)";
212 std::cerr <<
" (data loss)";
215 catch(ChimeraTK::logic_error&) {
219 std::cerr <<
" (data loss)";
224 std::cerr <<
"(end of list)\n";
228 terminateTestStalled();
239 void TestableMode::setThreadName(
const std::string& name) {
240 std::unique_lock<std::mutex> myLock(_threadNamesMutex);
241 _threadNames[boost::this_thread::get_id()] = name;
242 _threadPThreadId[boost::this_thread::get_id()] = gettid();
248 std::string TestableMode::threadName(
const boost::thread::id& threadId) {
249 std::unique_lock<std::mutex> myLock(_threadNamesMutex);
250 if(
auto const& it = _threadNames.find(threadId); it != _threadNames.end()) {
254 return "*UNKNOWN_THREAD*";
259 pid_t TestableMode::pthreadId(
const boost::thread::id& threadId) {
260 std::unique_lock<std::mutex> myLock(_threadNamesMutex);
261 if(
auto const& it = _threadPThreadId.find(threadId); it != _threadPThreadId.end()) {
269 TestableMode::LastMutexOwner::operator boost::thread::id() {
270 std::lock_guard<std::mutex> lk(_mxLastMutexOwner);
271 return _lastMutexOwner;
276 TestableMode::LastMutexOwner& TestableMode::LastMutexOwner::operator=(
const boost::thread::id&
id) {
277 std::lock_guard<std::mutex> lk(_mxLastMutexOwner);
278 _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.