12namespace ChimeraTK::detail {
14 void CircularDependencyDetector::registerDependencyWait(VariableNetworkNode& node) {
16 std::unique_lock<std::mutex> lock(_mutex);
18 auto* dependent =
dynamic_cast<Module*
>(node.getOwningModule())->findApplicationModule();
22 _awaitedNodes[dependent] = node;
25 auto feedingModule = node.getModel().visit(Model::returnApplicationModule, Model::keepPvAccess,
26 Model::keepApplicationModules, Model::adjacentInSearch,
Model::returnFirstHit(Model::ApplicationModuleProxy{}));
27 if(!feedingModule.isValid()) {
30 Module* dependency = &feedingModule.getApplicationModule();
34 if(dependent == dependency) {
39 _awaitedVariables[dependent] = node.getQualifiedName();
40 _waitMap[dependent] = dependency;
43 auto* depdep = dependency;
44 while(_waitMap.find(depdep) != _waitMap.end()) {
45 auto* depdep_prev = depdep;
46 depdep = _waitMap[depdep];
47 if(depdep == dependent) {
50 for(
size_t i = 0; i < 1000; ++i) {
57 if(_waitMap.find(depdep_prev) == _waitMap.end() || _waitMap[depdep] != dependent) {
62 std::cerr <<
"*** Circular dependency of ApplicationModules found while waiting for initial values!"
64 std::cerr << std::endl;
66 std::cerr << dependent->getQualifiedNameWithType() <<
" waits for " << node.getQualifiedName()
67 <<
" from:" << std::endl;
68 auto* depdep2 = dependency;
69 while(_waitMap.find(depdep2) != _waitMap.end()) {
70 auto waitsFor = _awaitedVariables[depdep2];
71 std::cerr << depdep2->getQualifiedNameWithType();
72 if(depdep2 == dependent) {
75 depdep2 = _waitMap[depdep2];
76 std::cerr <<
" waits for " << waitsFor <<
" from:" << std::endl;
78 std::cerr <<
"." << std::endl;
80 std::cerr << std::endl;
82 <<
"Please provide an initial value in the prepare() function of one of the involved ApplicationModules!"
85 throw ChimeraTK::logic_error(
"Circular dependency of ApplicationModules while waiting for initial values");
96 void CircularDependencyDetector::printWaiters(std::ostream& stream) {
97 if(_waitMap.empty()) {
100 stream <<
"The following modules are still waiting for initial values:" << std::endl;
101 for(
auto& waiters : _waitMap) {
102 stream << waiters.first->getQualifiedNameWithType() <<
" waits for " << _awaitedVariables[waiters.first]
103 <<
" from " << waiters.second->getQualifiedNameWithType() << std::endl;
105 stream <<
"(end of list)" << std::endl;
110 void CircularDependencyDetector::unregisterDependencyWait(VariableNetworkNode& node) {
112 std::lock_guard<std::mutex> lock(_mutex);
113 auto* mod =
dynamic_cast<Module*
>(node.getOwningModule())->findApplicationModule();
115 _awaitedVariables.erase(mod);
116 _awaitedNodes.erase(mod);
121 void CircularDependencyDetector::startDetectBlockedModules() {
122 _thread = boost::thread([
this] { detectBlockedModules(); });
127 void CircularDependencyDetector::detectBlockedModules() {
133 boost::this_thread::sleep_for(boost::chrono::seconds(60));
136 bool allModulesEnteredMainLoop =
true;
137 for(
auto* module : app.getSubmoduleListRecursive()) {
141 if(
module->hasReachedTestableMode()) {
146 allModulesEnteredMainLoop =
false;
149 std::lock_guard<std::mutex> lock(_mutex);
153 if(_awaitedNodes.find(module) == _awaitedNodes.end()) {
157 auto* appModule =
dynamic_cast<ApplicationModule*
>(
module);
161 <<
"found non-application module: " <<
module->getQualifiedNameWithType() << std::endl;
166 std::set<Module*> searchedModules;
167 std::function<void(
const VariableNetworkNode& node)> iterativeSearch = [&](
const VariableNetworkNode& node) {
168 auto visitor = [&](
auto proxy) {
171 auto* feedingAppModule =
dynamic_cast<ApplicationModule*
>(&proxy.getApplicationModule());
172 if(feedingAppModule->hasReachedTestableMode()) {
176 if(_modulesWeHaveWarnedAbout.find(feedingAppModule) == _modulesWeHaveWarnedAbout.end()) {
177 _modulesWeHaveWarnedAbout.insert(feedingAppModule);
179 <<
"Note: ApplicationModule " << appModule->getQualifiedNameWithType() <<
" is waiting for an "
180 <<
"initial value, because " << feedingAppModule->getQualifiedNameWithType()
181 <<
" has not yet sent one on " << node.getModel().getFullyQualifiedPath() << std::endl;
185 if(_awaitedNodes.find(feedingAppModule) == _awaitedNodes.end()) {
191 bool notYetSearched = searchedModules.insert(feedingAppModule).second;
193 iterativeSearch(_awaitedNodes.at(feedingAppModule));
196 if(_modulesWeHaveWarnedAbout.find(feedingAppModule) == _modulesWeHaveWarnedAbout.end()) {
197 _modulesWeHaveWarnedAbout.insert(feedingAppModule);
199 <<
"Note: ApplicationModule " << appModule->getQualifiedNameWithType()
200 <<
" is directly or indirectly waiting for an initial value from "
201 << feedingAppModule->getQualifiedNameWithType()
202 <<
", which in turn is waiting for an initial value on input "
203 << node.getModel().getFullyQualifiedPath() << std::endl;
209 const auto& deviceName = proxy.getAliasOrCdd();
210 if(_devicesWeHaveWarnedAbout.find(deviceName) == _devicesWeHaveWarnedAbout.end()) {
211 _devicesWeHaveWarnedAbout.insert(deviceName);
213 myLog <<
"Note: Still waiting for device " << deviceName <<
" to come up";
215 std::unique_lock dmLock(dm->_errorMutex);
216 if(dm->_deviceHasError) {
217 myLog <<
" (" << std::string(dm->_deviceError._message) <<
")";
225 <<
"At least one ApplicationModule " << appModule->getQualifiedNameWithType() <<
" is waiting for an "
226 <<
"initial value from an unexpected source."
228 <<
"This is probably a BUG in the ChimeraTK framework.";
232 node.getModel().visit(visitor, Model::keepPvAccess, Model::adjacentInSearch);
234 iterativeSearch(_awaitedNodes.at(appModule));
238 if(allModulesEnteredMainLoop) {
247 _awaitedVariables.clear();
248 _awaitedNodes.clear();
249 _modulesWeHaveWarnedAbout.clear();
250 _devicesWeHaveWarnedAbout.clear();
251 _otherThingsWeHaveWarnedAbout.clear();
256 void CircularDependencyDetector::terminate() {
257 if(_thread.joinable()) {
265 CircularDependencyDetector::~CircularDependencyDetector() {
266 assert(!_thread.joinable());
static void registerThread(const std::string &name)
Register the thread in the application system and give it a name.
std::map< std::string, boost::shared_ptr< DeviceManager > > _deviceManagerMap
Map of DeviceManagers.
static Application & getInstance()
Obtain instance of the application.
constexpr ReturnFirstHitWithValue< void > returnFirstHit()
Stop the search after the first hit and return.
constexpr bool isApplicationModule(const PROPERTY_OR_PROXY &)
constexpr bool isDeviceModule(const PROPERTY_OR_PROXY &)
Logger::StreamProxy logger(Logger::Severity severity, std::string context)
Convenience function to obtain the logger stream.