# HG changeset patch # User Sebastien Jodogne # Date 1443007987 -7200 # Node ID e15a59a4b4d47fa1afd02bde63961797636926ef # Parent ab5218c18b7a49b6063bcd7a9290d5f628fd9c29 Fix possible deadlock with other plugins in OnChangeCallback() diff -r ab5218c18b7a -r e15a59a4b4d4 NEWS --- a/NEWS Fri Sep 18 17:52:52 2015 +0200 +++ b/NEWS Wed Sep 23 13:33:07 2015 +0200 @@ -4,6 +4,7 @@ => Minimum SDK version: 0.9.4 <= * Fix for old versions of jQuery +* Fix possible deadlock with other plugins in OnChangeCallback() Version 1.2 (2015-08-02) diff -r ab5218c18b7a -r e15a59a4b4d4 Plugin/Plugin.cpp --- a/Plugin/Plugin.cpp Fri Sep 18 17:52:52 2015 +0200 +++ b/Plugin/Plugin.cpp Wed Sep 23 13:33:07 2015 +0200 @@ -42,17 +42,68 @@ #endif +static OrthancPluginContext* context_ = NULL; + + + class CacheContext { private: + class DynamicString : public Orthanc::IDynamicObject + { + private: + std::string value_; + + public: + DynamicString(const char* value) : value_(value) + { + } + + const std::string& GetValue() const + { + return value_; + } + }; + Orthanc::FilesystemStorage storage_; Orthanc::SQLite::Connection db_; std::auto_ptr cache_; std::auto_ptr scheduler_; + Orthanc::SharedMessageQueue newInstances_; + bool stop_; + boost::thread newInstancesThread_; + + + static void NewInstancesThread(CacheContext* cache) + { + while (!cache->stop_) + { + std::auto_ptr obj(cache->newInstances_.Dequeue(100)); + if (obj.get() != NULL) + { + const std::string& instanceId = dynamic_cast(*obj).GetValue(); + printf("[%s]\n", instanceId.c_str()); + + // On the reception of a new instance, precompute its spatial position + cache->GetScheduler().Prefetch(OrthancPlugins::CacheBundle_InstanceInformation, instanceId); + + // Indalidate the parent series of the instance + std::string uri = "/instances/" + std::string(instanceId); + Json::Value instance; + if (OrthancPlugins::GetJsonFromOrthanc(instance, context_, uri)) + { + std::string seriesId = instance["ParentSeries"].asString(); + cache->GetScheduler().Invalidate(OrthancPlugins::CacheBundle_SeriesInformation, seriesId); + } + } + } + } + + public: - CacheContext(const std::string& path) : storage_(path) + CacheContext(const std::string& path) : storage_(path), stop_(false) { boost::filesystem::path p(path); db_.Open((p / "cache.db").string()); @@ -61,10 +112,18 @@ //cache_->SetSanityCheckEnabled(true); // For debug scheduler_.reset(new OrthancPlugins::CacheScheduler(*cache_, 100)); + + newInstancesThread_ = boost::thread(NewInstancesThread, this); } ~CacheContext() { + stop_ = true; + if (newInstancesThread_.joinable()) + { + newInstancesThread_.join(); + } + scheduler_.reset(NULL); cache_.reset(NULL); } @@ -73,10 +132,15 @@ { return *scheduler_; } + + void SignalNewInstance(const char* instanceId) + { + newInstances_.Enqueue(new DynamicString(instanceId)); + } }; -static OrthancPluginContext* context_ = NULL; + static CacheContext* cache_ = NULL; @@ -90,17 +154,7 @@ if (changeType == OrthancPluginChangeType_NewInstance && resourceType == OrthancPluginResourceType_Instance) { - // On the reception of a new instance, precompute its spatial position - cache_->GetScheduler().Prefetch(OrthancPlugins::CacheBundle_InstanceInformation, resourceId); - - // Indalidate the parent series of the instance - std::string uri = "/instances/" + std::string(resourceId); - Json::Value instance; - if (OrthancPlugins::GetJsonFromOrthanc(instance, context_, uri)) - { - std::string seriesId = instance["ParentSeries"].asString(); - cache_->GetScheduler().Invalidate(OrthancPlugins::CacheBundle_SeriesInformation, seriesId); - } + cache_->SignalNewInstance(resourceId); } return RETURN_SUCCESS;