Mercurial > hg > orthanc-wsi
changeset 154:32a94bbb7d05
merge
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 08 Oct 2018 12:12:39 +0200 |
parents | b798d200ac90 (current diff) a0a3530ff69c (diff) |
children | 1304498491e4 |
files | Framework/Algorithms/PyramidReader.cpp Framework/Algorithms/PyramidReader.h Framework/Algorithms/ReconstructPyramidCommand.cpp Framework/Algorithms/TranscodeTileCommand.cpp Framework/ImageToolbox.cpp Framework/Inputs/SingleLevelDecodedPyramid.cpp Framework/Inputs/SingleLevelDecodedPyramid.h Framework/Jpeg2000Reader.h Framework/MultiThreading/BagOfTasks.h Framework/MultiThreading/ICommand.h Framework/Outputs/InMemoryTiledImage.cpp ViewerPlugin/CMakeLists.txt ViewerPlugin/Plugin.cpp |
diffstat | 23 files changed, 689 insertions(+), 51 deletions(-) [+] |
line wrap: on
line diff
--- a/Applications/ApplicationToolbox.cpp Fri Jul 27 14:06:26 2018 +0200 +++ b/Applications/ApplicationToolbox.cpp Mon Oct 08 12:12:39 2018 +0200 @@ -22,13 +22,14 @@ #include "ApplicationToolbox.h" #include "../Framework/Inputs/OpenSlideLibrary.h" +#include "../Framework/MultiThreading/BagOfTasksProcessor.h" #include <Core/DicomParsing/FromDcmtkBridge.h> #include <Core/HttpClient.h> #include <Core/Logging.h> -#include <Core/MultiThreading/BagOfTasksProcessor.h> #include <Core/OrthancException.h> #include <Core/SystemToolbox.h> +#include <Core/Toolbox.h> #include <boost/filesystem.hpp> #include <boost/lexical_cast.hpp> @@ -63,7 +64,7 @@ void GlobalInitialize() { Orthanc::Logging::Initialize(); - Orthanc::HttpClient::InitializeOpenSsl(); + Orthanc::Toolbox::InitializeOpenSsl(); Orthanc::HttpClient::GlobalInitialize(); Orthanc::FromDcmtkBridge::InitializeDictionary(false /* don't load private dictionary */); assert(DisplayPerformanceWarning()); @@ -74,7 +75,7 @@ { OrthancWSI::OpenSlideLibrary::Finalize(); Orthanc::HttpClient::GlobalFinalize(); - Orthanc::HttpClient::FinalizeOpenSsl(); + Orthanc::Toolbox::FinalizeOpenSsl(); } @@ -279,8 +280,8 @@ if (options.count(OPTION_USERNAME) && options.count(OPTION_PASSWORD)) { - parameters.SetUsername(options[OPTION_USERNAME].as<std::string>()); - parameters.SetPassword(options[OPTION_PASSWORD].as<std::string>()); + parameters.SetCredentials(options[OPTION_USERNAME].as<std::string>(), + options[OPTION_PASSWORD].as<std::string>()); } if (options.count(OPTION_TIMEOUT))
--- a/Applications/ApplicationToolbox.h Fri Jul 27 14:06:26 2018 +0200 +++ b/Applications/ApplicationToolbox.h Mon Oct 08 12:12:39 2018 +0200 @@ -21,7 +21,8 @@ #pragma once -#include <Core/MultiThreading/BagOfTasks.h> +#include "../Framework/MultiThreading/BagOfTasks.h" + #include <Core/WebServiceParameters.h> #include <string>
--- a/Applications/CMakeLists.txt Fri Jul 27 14:06:26 2018 +0200 +++ b/Applications/CMakeLists.txt Mon Oct 08 12:12:39 2018 +0200 @@ -100,14 +100,15 @@ ${ORTHANC_WSI_DIR}/Framework/Inputs/TiledPyramidStatistics.cpp ${ORTHANC_WSI_DIR}/Framework/Jpeg2000Reader.cpp ${ORTHANC_WSI_DIR}/Framework/Jpeg2000Writer.cpp - ${ORTHANC_WSI_DIR}/Framework/Targets/FolderTarget.cpp - ${ORTHANC_WSI_DIR}/Framework/Targets/OrthancTarget.cpp + ${ORTHANC_WSI_DIR}/Framework/MultiThreading/BagOfTasksProcessor.cpp ${ORTHANC_WSI_DIR}/Framework/Outputs/DicomPyramidWriter.cpp ${ORTHANC_WSI_DIR}/Framework/Outputs/HierarchicalTiffWriter.cpp ${ORTHANC_WSI_DIR}/Framework/Outputs/InMemoryTiledImage.cpp ${ORTHANC_WSI_DIR}/Framework/Outputs/MultiframeDicomWriter.cpp ${ORTHANC_WSI_DIR}/Framework/Outputs/PyramidWriterBase.cpp ${ORTHANC_WSI_DIR}/Framework/Outputs/TruncatedPyramidWriter.cpp + ${ORTHANC_WSI_DIR}/Framework/Targets/FolderTarget.cpp + ${ORTHANC_WSI_DIR}/Framework/Targets/OrthancTarget.cpp ) EmbedResources(
--- a/Applications/Dicomizer.cpp Fri Jul 27 14:06:26 2018 +0200 +++ b/Applications/Dicomizer.cpp Mon Oct 08 12:12:39 2018 +0200 @@ -29,12 +29,12 @@ #include "../Framework/Inputs/TiledJpegImage.h" #include "../Framework/Inputs/TiledPngImage.h" #include "../Framework/Inputs/TiledPyramidStatistics.h" +#include "../Framework/MultiThreading/BagOfTasksProcessor.h" #include "../Framework/Outputs/DicomPyramidWriter.h" #include "../Framework/Outputs/TruncatedPyramidWriter.h" #include <Core/DicomParsing/FromDcmtkBridge.h> #include <Core/Logging.h> -#include <Core/MultiThreading/BagOfTasksProcessor.h> #include <Core/OrthancException.h> #include <Core/SystemToolbox.h>
--- a/Framework/Algorithms/PyramidReader.cpp Fri Jul 27 14:06:26 2018 +0200 +++ b/Framework/Algorithms/PyramidReader.cpp Mon Oct 08 12:12:39 2018 +0200 @@ -61,9 +61,10 @@ assert(tileY_ * that_.sourceTileHeight_ < that_.levelHeight_); unsigned int bottom = that_.levelHeight_ - tileY_ * that_.sourceTileHeight_; - Orthanc::ImageAccessor a = decoded_->GetRegion(0, bottom, - that_.sourceTileWidth_, - that_.sourceTileHeight_ - bottom); + Orthanc::ImageAccessor a; + decoded_->GetRegion(a, 0, bottom, + that_.sourceTileWidth_, + that_.sourceTileHeight_ - bottom); ImageToolbox::Set(a, that_.parameters_.GetBackgroundColorRed(), that_.parameters_.GetBackgroundColorGreen(), @@ -77,9 +78,10 @@ assert(tileX_ * that_.sourceTileWidth_ < that_.levelWidth_); unsigned int right = that_.levelWidth_ - tileX_ * that_.sourceTileWidth_; - Orthanc::ImageAccessor a = decoded_->GetRegion(right, 0, - that_.sourceTileWidth_ - right, - that_.sourceTileHeight_); + Orthanc::ImageAccessor a; + decoded_->GetRegion(a, right, 0, + that_.sourceTileWidth_ - right, + that_.sourceTileHeight_); ImageToolbox::Set(a, that_.parameters_.GetBackgroundColorRed(), that_.parameters_.GetBackgroundColorGreen(), @@ -283,37 +285,37 @@ } - Orthanc::ImageAccessor PyramidReader::GetDecodedTile(unsigned int tileX, - unsigned int tileY) + void PyramidReader::GetDecodedTile(Orthanc::ImageAccessor& target, + unsigned int tileX, + unsigned int tileY) { if (tileX * targetTileWidth_ >= levelWidth_ || tileY * targetTileHeight_ >= levelHeight_) { // Accessing a tile out of the source image - return GetOutsideTile(); + GetOutsideTile().GetReadOnlyAccessor(target); } + else + { + SourceTile& source = AccessSourceTile(MapTargetToSourceLocation(tileX, tileY)); + const Orthanc::ImageAccessor& tile = source.GetDecodedTile(); - SourceTile& source = AccessSourceTile(MapTargetToSourceLocation(tileX, tileY)); - const Orthanc::ImageAccessor& tile = source.GetDecodedTile(); + CheckTileSize(tile); - CheckTileSize(tile); - - assert(sourceTileWidth_ % targetTileWidth_ == 0 && - sourceTileHeight_ % targetTileHeight_ == 0); + assert(sourceTileWidth_ % targetTileWidth_ == 0 && + sourceTileHeight_ % targetTileHeight_ == 0); - unsigned int xx = tileX % (sourceTileWidth_ / targetTileWidth_); - unsigned int yy = tileY % (sourceTileHeight_ / targetTileHeight_); - - const uint8_t* bytes = - reinterpret_cast<const uint8_t*>(tile.GetConstRow(yy * targetTileHeight_)) + - GetBytesPerPixel(tile.GetFormat()) * xx * targetTileWidth_; + unsigned int xx = tileX % (sourceTileWidth_ / targetTileWidth_); + unsigned int yy = tileY % (sourceTileHeight_ / targetTileHeight_); - Orthanc::ImageAccessor region; - region.AssignReadOnly(tile.GetFormat(), - targetTileWidth_, - targetTileHeight_, - tile.GetPitch(), bytes); + const uint8_t* bytes = + reinterpret_cast<const uint8_t*>(tile.GetConstRow(yy * targetTileHeight_)) + + GetBytesPerPixel(tile.GetFormat()) * xx * targetTileWidth_; - return region; + target.AssignReadOnly(tile.GetFormat(), + targetTileWidth_, + targetTileHeight_, + tile.GetPitch(), bytes); + } } }
--- a/Framework/Algorithms/PyramidReader.h Fri Jul 27 14:06:26 2018 +0200 +++ b/Framework/Algorithms/PyramidReader.h Mon Oct 08 12:12:39 2018 +0200 @@ -88,7 +88,8 @@ unsigned int tileX, unsigned int tileY); - Orthanc::ImageAccessor GetDecodedTile(unsigned int tileX, - unsigned int tileY); + void GetDecodedTile(Orthanc::ImageAccessor& target, + unsigned int tileX, + unsigned int tileY); }; }
--- a/Framework/Algorithms/ReconstructPyramidCommand.cpp Fri Jul 27 14:06:26 2018 +0200 +++ b/Framework/Algorithms/ReconstructPyramidCommand.cpp Mon Oct 08 12:12:39 2018 +0200 @@ -50,7 +50,8 @@ if (level == 0) { - result.reset(new Orthanc::ImageAccessor(source_.GetDecodedTile(x, y))); + result.reset(new Orthanc::ImageAccessor); + source_.GetDecodedTile(*result, x, y); ImageCompression compression; const std::string* rawTile = source_.GetRawTile(compression, x, y);
--- a/Framework/Algorithms/ReconstructPyramidCommand.h Fri Jul 27 14:06:26 2018 +0200 +++ b/Framework/Algorithms/ReconstructPyramidCommand.h Mon Oct 08 12:12:39 2018 +0200 @@ -23,7 +23,7 @@ #include "PyramidReader.h" #include "../Outputs/IPyramidWriter.h" -#include <Core/MultiThreading/BagOfTasks.h> +#include "../MultiThreading/BagOfTasks.h" namespace OrthancWSI
--- a/Framework/Algorithms/TranscodeTileCommand.cpp Fri Jul 27 14:06:26 2018 +0200 +++ b/Framework/Algorithms/TranscodeTileCommand.cpp Mon Oct 08 12:12:39 2018 +0200 @@ -73,7 +73,8 @@ } else { - Orthanc::ImageAccessor tile = source_.GetDecodedTile(x, y); + Orthanc::ImageAccessor tile; + source_.GetDecodedTile(tile, x, y); // Re-encoding the file target_.EncodeTile(tile, level_, x, y);
--- a/Framework/Algorithms/TranscodeTileCommand.h Fri Jul 27 14:06:26 2018 +0200 +++ b/Framework/Algorithms/TranscodeTileCommand.h Mon Oct 08 12:12:39 2018 +0200 @@ -23,8 +23,7 @@ #include "PyramidReader.h" #include "../Outputs/IPyramidWriter.h" - -#include <Core/MultiThreading/BagOfTasks.h> +#include "../MultiThreading/BagOfTasks.h" namespace OrthancWSI {
--- a/Framework/ImageToolbox.cpp Fri Jul 27 14:06:26 2018 +0200 +++ b/Framework/ImageToolbox.cpp Mon Oct 08 12:12:39 2018 +0200 @@ -68,8 +68,10 @@ unsigned int h = std::min(source.GetHeight(), target.GetHeight() - y); unsigned int w = std::min(source.GetWidth(), target.GetWidth() - x); - Orthanc::ImageAccessor targetRegion = target.GetRegion(x, y, w, h); - Orthanc::ImageAccessor sourceRegion = source.GetRegion(0, 0, w, h); + Orthanc::ImageAccessor targetRegion, sourceRegion; + target.GetRegion(targetRegion, x, y, w, h); + source.GetRegion(sourceRegion, 0, 0, w, h); + Orthanc::ImageProcessing::Copy(targetRegion, sourceRegion); }
--- a/Framework/Inputs/SingleLevelDecodedPyramid.cpp Fri Jul 27 14:06:26 2018 +0200 +++ b/Framework/Inputs/SingleLevelDecodedPyramid.cpp Mon Oct 08 12:12:39 2018 +0200 @@ -32,7 +32,8 @@ unsigned int x, unsigned int y) { - Orthanc::ImageAccessor region = image_.GetRegion(x, y, target.GetWidth(), target.GetHeight()); + Orthanc::ImageAccessor region; + image_.GetRegion(region, x, y, target.GetWidth(), target.GetHeight()); ImageToolbox::Copy(target, region); }
--- a/Framework/Inputs/SingleLevelDecodedPyramid.h Fri Jul 27 14:06:26 2018 +0200 +++ b/Framework/Inputs/SingleLevelDecodedPyramid.h Mon Oct 08 12:12:39 2018 +0200 @@ -35,7 +35,7 @@ protected: void SetImage(const Orthanc::ImageAccessor& image) { - image_ = image; + image.GetReadOnlyAccessor(image_); } virtual void ReadRegion(Orthanc::ImageAccessor& target,
--- a/Framework/Jpeg2000Reader.h Fri Jul 27 14:06:26 2018 +0200 +++ b/Framework/Jpeg2000Reader.h Mon Oct 08 12:12:39 2018 +0200 @@ -33,9 +33,7 @@ Jpeg2000Format_Unknown }; - class Jpeg2000Reader : - public Orthanc::ImageAccessor, - public boost::noncopyable + class Jpeg2000Reader : public Orthanc::ImageAccessor { private: std::auto_ptr<Orthanc::ImageAccessor> image_;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/MultiThreading/BagOfTasks.h Mon Oct 08 12:12:39 2018 +0200 @@ -0,0 +1,72 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ICommand.h" + +#include <list> +#include <cstddef> + +namespace Orthanc +{ + class BagOfTasks : public boost::noncopyable + { + private: + typedef std::list<ICommand*> Tasks; + + Tasks tasks_; + + public: + ~BagOfTasks() + { + for (Tasks::iterator it = tasks_.begin(); it != tasks_.end(); ++it) + { + delete *it; + } + } + + ICommand* Pop() + { + ICommand* task = tasks_.front(); + tasks_.pop_front(); + return task; + } + + void Push(ICommand* task) // Takes ownership + { + if (task != NULL) + { + tasks_.push_back(task); + } + } + + size_t GetSize() const + { + return tasks_.size(); + } + + bool IsEmpty() const + { + return tasks_.empty(); + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/MultiThreading/BagOfTasksProcessor.cpp Mon Oct 08 12:12:39 2018 +0200 @@ -0,0 +1,264 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "BagOfTasksProcessor.h" + +#include <Core/Logging.h> +#include <Core/OrthancException.h> + +#include <stdio.h> + +namespace Orthanc +{ + class BagOfTasksProcessor::Task : public IDynamicObject + { + private: + uint64_t bag_; + std::auto_ptr<ICommand> command_; + + public: + Task(uint64_t bag, + ICommand* command) : + bag_(bag), + command_(command) + { + } + + bool Execute() + { + try + { + return command_->Execute(); + } + catch (OrthancException& e) + { + LOG(ERROR) << "Exception while processing a bag of tasks: " << e.What(); + return false; + } + catch (std::runtime_error& e) + { + LOG(ERROR) << "Runtime exception while processing a bag of tasks: " << e.what(); + return false; + } + catch (...) + { + LOG(ERROR) << "Native exception while processing a bag of tasks"; + return false; + } + } + + uint64_t GetBag() + { + return bag_; + } + }; + + + void BagOfTasksProcessor::SignalProgress(Task& task, + Bag& bag) + { + assert(bag.done_ < bag.size_); + + bag.done_ += 1; + + if (bag.done_ == bag.size_) + { + exitStatus_[task.GetBag()] = (bag.status_ == BagStatus_Running); + bagFinished_.notify_all(); + } + } + + void BagOfTasksProcessor::Worker(BagOfTasksProcessor* that) + { + while (that->continue_) + { + std::auto_ptr<IDynamicObject> obj(that->queue_.Dequeue(100)); + if (obj.get() != NULL) + { + Task& task = *dynamic_cast<Task*>(obj.get()); + + { + boost::mutex::scoped_lock lock(that->mutex_); + + Bags::iterator bag = that->bags_.find(task.GetBag()); + assert(bag != that->bags_.end()); + assert(bag->second.done_ < bag->second.size_); + + if (bag->second.status_ != BagStatus_Running) + { + // Do not execute this task, as its parent bag of tasks + // has failed or is tagged as canceled + that->SignalProgress(task, bag->second); + continue; + } + } + + bool success = task.Execute(); + + { + boost::mutex::scoped_lock lock(that->mutex_); + + Bags::iterator bag = that->bags_.find(task.GetBag()); + assert(bag != that->bags_.end()); + + if (!success) + { + bag->second.status_ = BagStatus_Failed; + } + + that->SignalProgress(task, bag->second); + } + } + } + } + + + void BagOfTasksProcessor::Cancel(int64_t bag) + { + boost::mutex::scoped_lock lock(mutex_); + + Bags::iterator it = bags_.find(bag); + if (it != bags_.end()) + { + it->second.status_ = BagStatus_Canceled; + } + } + + + bool BagOfTasksProcessor::Join(int64_t bag) + { + boost::mutex::scoped_lock lock(mutex_); + + while (continue_) + { + ExitStatus::iterator it = exitStatus_.find(bag); + if (it == exitStatus_.end()) // The bag is still running + { + bagFinished_.wait(lock); + } + else + { + bool status = it->second; + exitStatus_.erase(it); + return status; + } + } + + return false; // The processor is stopping + } + + + float BagOfTasksProcessor::GetProgress(int64_t bag) + { + boost::mutex::scoped_lock lock(mutex_); + + Bags::const_iterator it = bags_.find(bag); + if (it == bags_.end()) + { + // The bag of tasks has finished + return 1.0f; + } + else + { + return (static_cast<float>(it->second.done_) / + static_cast<float>(it->second.size_)); + } + } + + + bool BagOfTasksProcessor::Handle::Join() + { + if (hasJoined_) + { + return status_; + } + else + { + status_ = that_.Join(bag_); + hasJoined_ = true; + return status_; + } + } + + + BagOfTasksProcessor::BagOfTasksProcessor(size_t countThreads) : + countBags_(0), + continue_(true) + { + if (countThreads == 0) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + threads_.resize(countThreads); + + for (size_t i = 0; i < threads_.size(); i++) + { + threads_[i] = new boost::thread(Worker, this); + } + } + + + BagOfTasksProcessor::~BagOfTasksProcessor() + { + continue_ = false; + + bagFinished_.notify_all(); // Wakes up all the pending "Join()" + + for (size_t i = 0; i < threads_.size(); i++) + { + if (threads_[i]) + { + if (threads_[i]->joinable()) + { + threads_[i]->join(); + } + + delete threads_[i]; + threads_[i] = NULL; + } + } + } + + + BagOfTasksProcessor::Handle* BagOfTasksProcessor::Submit(BagOfTasks& tasks) + { + if (tasks.GetSize() == 0) + { + return new Handle(*this, 0, true); + } + + boost::mutex::scoped_lock lock(mutex_); + + uint64_t id = countBags_; + countBags_ += 1; + + Bag bag(tasks.GetSize()); + bags_[id] = bag; + + while (!tasks.IsEmpty()) + { + queue_.Enqueue(new Task(id, tasks.Pop())); + } + + return new Handle(*this, id, false); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/MultiThreading/BagOfTasksProcessor.h Mon Oct 08 12:12:39 2018 +0200 @@ -0,0 +1,139 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "BagOfTasks.h" + +#include <Core/MultiThreading/SharedMessageQueue.h> + +#include <stdint.h> +#include <map> + +namespace Orthanc +{ + class BagOfTasksProcessor : public boost::noncopyable + { + private: + enum BagStatus + { + BagStatus_Running, + BagStatus_Canceled, + BagStatus_Failed + }; + + + struct Bag + { + size_t size_; + size_t done_; + BagStatus status_; + + Bag() : + size_(0), + done_(0), + status_(BagStatus_Failed) + { + } + + explicit Bag(size_t size) : + size_(size), + done_(0), + status_(BagStatus_Running) + { + } + }; + + class Task; + + + typedef std::map<uint64_t, Bag> Bags; + typedef std::map<uint64_t, bool> ExitStatus; + + SharedMessageQueue queue_; + + boost::mutex mutex_; + uint64_t countBags_; + Bags bags_; + std::vector<boost::thread*> threads_; + ExitStatus exitStatus_; + bool continue_; + + boost::condition_variable bagFinished_; + + static void Worker(BagOfTasksProcessor* that); + + void Cancel(int64_t bag); + + bool Join(int64_t bag); + + float GetProgress(int64_t bag); + + void SignalProgress(Task& task, + Bag& bag); + + public: + class Handle : public boost::noncopyable + { + friend class BagOfTasksProcessor; + + private: + BagOfTasksProcessor& that_; + uint64_t bag_; + bool hasJoined_; + bool status_; + + Handle(BagOfTasksProcessor& that, + uint64_t bag, + bool empty) : + that_(that), + bag_(bag), + hasJoined_(empty) + { + } + + public: + ~Handle() + { + Join(); + } + + void Cancel() + { + that_.Cancel(bag_); + } + + bool Join(); + + float GetProgress() + { + return that_.GetProgress(bag_); + } + }; + + + explicit BagOfTasksProcessor(size_t countThreads); + + ~BagOfTasksProcessor(); + + Handle* Submit(BagOfTasks& tasks); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/MultiThreading/ICommand.h Mon Oct 08 12:12:39 2018 +0200 @@ -0,0 +1,37 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include <Core/IDynamicObject.h> + +namespace Orthanc +{ + /** + * This class is the base class for the "Command" design pattern. + * http://en.wikipedia.org/wiki/Command_pattern + **/ + class ICommand : public IDynamicObject + { + public: + virtual bool Execute() = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/MultiThreading/Semaphore.cpp Mon Oct 08 12:12:39 2018 +0200 @@ -0,0 +1,50 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "Semaphore.h" + + +namespace OrthancWSI +{ + Semaphore::Semaphore(unsigned int count) : count_(count) + { + } + + void Semaphore::Release() + { + boost::mutex::scoped_lock lock(mutex_); + + count_++; + condition_.notify_one(); + } + + void Semaphore::Acquire() + { + boost::mutex::scoped_lock lock(mutex_); + + while (count_ == 0) + { + condition_.wait(lock); + } + + count_++; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/MultiThreading/Semaphore.h Mon Oct 08 12:12:39 2018 +0200 @@ -0,0 +1,61 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include <boost/noncopyable.hpp> +#include <boost/thread.hpp> + +namespace OrthancWSI +{ + class Semaphore : public boost::noncopyable + { + private: + unsigned int count_; + boost::mutex mutex_; + boost::condition_variable condition_; + + public: + explicit Semaphore(unsigned int count); + + void Release(); + + void Acquire(); + + class Locker : public boost::noncopyable + { + private: + Semaphore& that_; + + public: + explicit Locker(Semaphore& that) : + that_(that) + { + that_.Acquire(); + } + + ~Locker() + { + that_.Release(); + } + }; + }; +}
--- a/Framework/Outputs/InMemoryTiledImage.cpp Fri Jul 27 14:06:26 2018 +0200 +++ b/Framework/Outputs/InMemoryTiledImage.cpp Mon Oct 08 12:12:39 2018 +0200 @@ -117,7 +117,9 @@ Tiles::const_iterator it = tiles_.find(std::make_pair(tileX, tileY)); if (it != tiles_.end()) { - return new Orthanc::ImageAccessor(*it->second); + std::auto_ptr<Orthanc::ImageAccessor> result(new Orthanc::ImageAccessor); + it->second->GetReadOnlyAccessor(*result); + return result.release(); } else {
--- a/ViewerPlugin/CMakeLists.txt Fri Jul 27 14:06:26 2018 +0200 +++ b/ViewerPlugin/CMakeLists.txt Mon Oct 08 12:12:39 2018 +0200 @@ -154,6 +154,7 @@ ${ORTHANC_WSI_DIR}/Framework/Inputs/PyramidWithRawTiles.cpp ${ORTHANC_WSI_DIR}/Framework/Jpeg2000Reader.cpp ${ORTHANC_WSI_DIR}/Framework/Jpeg2000Writer.cpp + ${ORTHANC_WSI_DIR}/Framework/MultiThreading/Semaphore.cpp ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomDatasetReader.cpp ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomPath.cpp
--- a/ViewerPlugin/Plugin.cpp Fri Jul 27 14:06:26 2018 +0200 +++ b/ViewerPlugin/Plugin.cpp Mon Oct 08 12:12:39 2018 +0200 @@ -23,7 +23,9 @@ #include "DicomPyramidCache.h" #include "../Framework/Jpeg2000Reader.h" +#include "../Framework/MultiThreading/Semaphore.h" +#include <Core/Logging.h> #include <Core/Images/ImageProcessing.h> #include <Core/Images/PngWriter.h> #include <Core/MultiThreading/Semaphore.h> @@ -310,6 +312,8 @@ return -1; } + Orthanc::Logging::Initialize(context); + // Limit the number of PNG transcoders to the number of available // hardware threads (e.g. number of CPUs or cores or // hyperthreading units)