# HG changeset patch # User Sebastien Jodogne # Date 1786712262 -7200 # Node ID cf1f10a22505a52f5170497a65ac06394f21eff4 # Parent 0a8b8890f26dd188f2fbf868bdcc6792c8339342 added test DataSource.SequentialReader diff -r 0a8b8890f26d -r cf1f10a22505 OrthancFramework/Sources/DataSource/DataSourceSequentialReader.h --- a/OrthancFramework/Sources/DataSource/DataSourceSequentialReader.h Fri Aug 14 13:28:37 2026 +0200 +++ b/OrthancFramework/Sources/DataSource/DataSourceSequentialReader.h Fri Aug 14 14:57:42 2026 +0200 @@ -101,8 +101,10 @@ { } - // Note that "source" will never contain user data, those are handled at the level above - virtual IDynamicObject* Apply(DataSourceAnswer::Item* source) = 0; + // IMPORTANT: The disconnector is in charge of deleting the "source" object. + // If "source" is not properly destructed, deadlock will occur. + // Note that "source" will never contain user data, those are handled at the level above. + virtual IDynamicObject* Apply(DataSourceAnswer::Item* source /* takes ownership */) = 0; }; @@ -126,6 +128,7 @@ void FillWindow(); public: + // IMPORTANT: The "executor" must not be the same as that of "reader", otherwise deadlock will occur DataSourceSequentialReader(const boost::shared_ptr& executor, const boost::shared_ptr& reader, IValueDisconnector* disconnector /* takes ownership */, diff -r 0a8b8890f26d -r cf1f10a22505 OrthancFramework/UnitTestsSources/DataSourceTests.cpp --- a/OrthancFramework/UnitTestsSources/DataSourceTests.cpp Fri Aug 14 13:28:37 2026 +0200 +++ b/OrthancFramework/UnitTestsSources/DataSourceTests.cpp Fri Aug 14 14:57:42 2026 +0200 @@ -28,6 +28,7 @@ #endif #include "../Sources/DataSource/DataSourceReader.h" +#include "../Sources/DataSource/DataSourceSequentialReader.h" #include "../Sources/MultiThreading/SequentialExecutorService.h" #include "../Sources/MultiThreading/ThreadPool.h" #include "../Sources/OrthancException.h" @@ -128,10 +129,32 @@ return 10; } }; + + + class IntegerDisconnector : public DataSourceSequentialReader::IValueDisconnector + { + public: + virtual IDynamicObject* Apply(DataSourceAnswer::Item* source) ORTHANC_OVERRIDE + { + std::unique_ptr protection(source); + + const IntegerIdentifier& id = dynamic_cast(source->GetId()); + const SingleValueObject& value = dynamic_cast&>(*source->GetValue()); + + if (id.GetValue() != value.GetValue()) + { + throw OrthancException(ErrorCode_InternalError); + } + else + { + return new SingleValueObject(value.GetValue()); + } + } + }; } -TEST(DataSource, Test) +TEST(DataSource, ParallelReader) { boost::shared_ptr service(new ThreadPool); service->SetThreadsCount(4); @@ -210,3 +233,47 @@ } } } + + +TEST(DataSource, SequentialReader) +{ + boost::shared_ptr serviceSource(new ThreadPool); + serviceSource->SetLoggingThreadName("SOURCE"); + serviceSource->SetThreadsCount(4); + serviceSource->SetDequeueTimeout(5); // Stop the test fast + serviceSource->Start(); + + boost::shared_ptr serviceSequential(new ThreadPool); + serviceSequential->SetLoggingThreadName("SEQ"); + serviceSequential->SetThreadsCount(1); + serviceSequential->SetDequeueTimeout(5); // Stop the test fast + serviceSequential->Start(); + + { + boost::shared_ptr reader(new DataSourceReader(serviceSource, new IntegerDataSource)); + + DataSourceSequentialReader seq(serviceSequential, reader, new IntegerDisconnector, 4, 0); + + for (int i = 0; i < 10; i++) + { + seq.Submit(new IntegerIdentifier(false, 10 + i, 10 - i)); // Produces out-of-order values + } + + seq.Start(); + ASSERT_THROW(seq.Submit(new IntegerIdentifier(false, 42, 0)), OrthancException); + + for (int i = 0; i < 10; i++) + { + ASSERT_TRUE(seq.HasNext()); + + std::unique_ptr item(seq.Next()); + ASSERT_TRUE(item.get() != NULL); + + const SingleValueObject& value = dynamic_cast&>(item->GetValue()); + ASSERT_EQ(10 + i, value.GetValue()); + } + + ASSERT_FALSE(seq.HasNext()); + ASSERT_THROW(seq.Next(), OrthancException); + } +}