diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30a4e54f4e..a4e92314d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,9 @@ # To retry only on timeout set retry_on: timeout # To retry only on error set retry_on: error # For more information on the retry action see https://github.com/nick-fields/retry + +name: Compile and Testrun + on: pull_request: types: [opened] @@ -231,7 +234,8 @@ jobs: CppUnit::TestCaller.testAccessExpireN, CppUnit::TestCaller.testExpireN, CppUnit::TestCaller.testAccessExpireN, - CppUnit::TestCaller.testPollClosedServer" + CppUnit::TestCaller.testPollClosedServer, + CppUnit::TestCaller.testEncryptDecryptGCM" PWD=`pwd` ctest --output-on-failure -E "(DataMySQL)|(DataODBC)|(PostgreSQL)|(MongoDB)|(Redis)" diff --git a/Crypto/testsuite/Makefile b/Crypto/testsuite/Makefile index 8ec1c27073..c45f61c473 100644 --- a/Crypto/testsuite/Makefile +++ b/Crypto/testsuite/Makefile @@ -16,10 +16,14 @@ else ifeq ($(findstring AIX, $(POCO_CONFIG)), AIX) SYSLIBS += -lssl_a -lcrypto_a -lz -ldl else +ifeq ($(POCO_CONFIG),Darwin) +SYSLIBS += -lssl -lcrypto -lz +else SYSLIBS += -lssl -lcrypto -lz -ldl -endif -endif -endif +endif # Darwin +endif # AIX +endif # QNX +endif # FreeBSD objects = CryptoTestSuite Driver \ CryptoTest DigestEngineTest ECTest \ diff --git a/Foundation/include/Poco/ArchiveStrategy.h b/Foundation/include/Poco/ArchiveStrategy.h index db69354c61..f781279861 100644 --- a/Foundation/include/Poco/ArchiveStrategy.h +++ b/Foundation/include/Poco/ArchiveStrategy.h @@ -23,6 +23,7 @@ #include "Poco/File.h" #include "Poco/DateTimeFormatter.h" #include "Poco/NumberFormatter.h" +#include namespace Poco { @@ -44,7 +45,7 @@ class Foundation_API ArchiveStrategy virtual LogFile* open(LogFile* pFile) = 0; /// Open a new log file and return it. - + virtual LogFile* archive(LogFile* pFile) = 0; /// Renames the given log file for archiving /// and creates and returns a new log file. @@ -61,8 +62,8 @@ class Foundation_API ArchiveStrategy ArchiveStrategy(const ArchiveStrategy&); ArchiveStrategy& operator = (const ArchiveStrategy&); - bool _compress; - ArchiveCompressor* _pCompressor; + std::atomic _compress; + std::atomic _pCompressor; }; diff --git a/Foundation/include/Poco/NumericString.h b/Foundation/include/Poco/NumericString.h index 140619e77f..aefa17b9c8 100644 --- a/Foundation/include/Poco/NumericString.h +++ b/Foundation/include/Poco/NumericString.h @@ -387,7 +387,8 @@ bool intToStr(T value, char fill = ' ', char thSep = 0, bool lowercase = false) - /// Converts integer to string. Standard numeric bases from binary to hexadecimal are supported. + /// Converts signed integer to string. Standard numeric bases from binary to hexadecimal + /// are supported. /// If width is non-zero, it pads the return value with fill character to the specified width. /// When padding is zero character ('0'), it is prepended to the number itself; all other /// paddings are prepended to the formatted result with minus sign or base prefix included @@ -534,8 +535,8 @@ bool intToStr(T value, } -//@ deprecated template +[[deprecated("use intToStr instead")]] bool uIntToStr(T value, unsigned short base, char* result, @@ -579,8 +580,8 @@ bool intToStr (T number, } -//@ deprecated template +[[deprecated("use intToStr instead")]] bool uIntToStr (T number, unsigned short base, std::string& result, diff --git a/Foundation/include/Poco/String.h b/Foundation/include/Poco/String.h index e4157da155..32574ae9d5 100644 --- a/Foundation/include/Poco/String.h +++ b/Foundation/include/Poco/String.h @@ -23,6 +23,11 @@ #include #include +// ignore loop unrolling warnings in this file +#if defined(__clang__) && ((__clang_major__ > 3) || (__clang_major__ == 3 && __clang_minor__ >= 6)) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpass-failed" +#endif namespace Poco { @@ -760,5 +765,8 @@ struct CILess } // namespace Poco +#if defined(__clang__) && ((__clang_major__ > 3) || (__clang_major__ == 3 && __clang_minor__ >= 6)) +# pragma clang diagnostic pop +#endif #endif // Foundation_String_INCLUDED diff --git a/Foundation/include/Poco/Task.h b/Foundation/include/Poco/Task.h index 904dc97ef6..b2c4441a35 100644 --- a/Foundation/include/Poco/Task.h +++ b/Foundation/include/Poco/Task.h @@ -77,6 +77,8 @@ class Foundation_API Task: public Runnable, public RefCountedObject /// A Task's runTask() method should periodically /// call this method and stop whatever it is doing in an /// orderly way when this method returns true. + /// If task is cancelled before it had a chance to run, + /// runTask() will never be called. TaskState state() const; /// Returns the task's current state. @@ -90,9 +92,14 @@ class Foundation_API Task: public Runnable, public RefCountedObject /// be overridden by subclasses. void run(); - /// Calls the task's runTask() method and notifies the owner - /// of the task's start and completion. - + /// If task has not been cancelled prior to this call, it + /// calls the task's runTask() method and notifies the owner of + /// the task's start and completion. + /// If task has been cancelled prior to this call, it only sets + /// the state to TASK_FINISHED and notifies the owner. + + bool hasOwner() const; + /// Returns true iff the task has an owner. protected: bool sleep(long milliseconds); /// Suspends the current thread for the specified @@ -134,7 +141,7 @@ class Foundation_API Task: public Runnable, public RefCountedObject TaskManager* getOwner() const; /// Returns the owner of the task, which may be NULL. - void setState(TaskState state); + TaskState setState(TaskState state); /// Sets the task's state. virtual ~Task(); @@ -145,12 +152,12 @@ class Foundation_API Task: public Runnable, public RefCountedObject Task(const Task&); Task& operator = (const Task&); - std::string _name; - TaskManager* _pOwner; - std::atomic _progress; - std::atomic _state; - Event _cancelEvent; - mutable FastMutex _mutex; + std::string _name; + std::atomic _pOwner; + std::atomic _progress; + std::atomic _state; + Event _cancelEvent; + mutable FastMutex _mutex; friend class TaskManager; }; @@ -185,12 +192,16 @@ inline Task::TaskState Task::state() const inline TaskManager* Task::getOwner() const { - FastMutex::ScopedLock lock(_mutex); - return _pOwner; } +inline bool Task::hasOwner() const +{ + return _pOwner != nullptr; +} + + } // namespace Poco diff --git a/Foundation/include/Poco/TaskManager.h b/Foundation/include/Poco/TaskManager.h index e8f2836f20..6ca0c2f9fc 100644 --- a/Foundation/include/Poco/TaskManager.h +++ b/Foundation/include/Poco/TaskManager.h @@ -65,9 +65,16 @@ class Foundation_API TaskManager ~TaskManager(); /// Destroys the TaskManager. - void start(Task* pTask); + bool start(Task* pTask); /// Starts the given task in a thread obtained - /// from the thread pool. + /// from the thread pool; returns true if successful. + /// + /// If this method returns false, the task was cancelled + /// before it could be started, or it was already running; + /// in any case, a false return means refusal of ownership + /// and indicates that the task pointer may not be valid + /// anymore (it will only be valid if it was duplicated + /// prior to this call). /// /// The TaskManager takes ownership of the Task object /// and deletes it when it is finished. diff --git a/Foundation/src/ActiveThreadPool.cpp b/Foundation/src/ActiveThreadPool.cpp index b7a9170fd7..ed52f6330e 100644 --- a/Foundation/src/ActiveThreadPool.cpp +++ b/Foundation/src/ActiveThreadPool.cpp @@ -28,11 +28,14 @@ namespace Poco { class NewActionNotification: public Notification { public: - NewActionNotification(Thread::Priority priority, Runnable &runnable, std::string name) : - _priority(priority), - _runnable(runnable), - _name(std::move(name)) - { } + using Ptr = AutoPtr; + + NewActionNotification(Thread::Priority priority, Runnable& runnable, const std::string& name) : + _priority(priority), + _runnable(runnable), + _name(name) + { + } ~NewActionNotification() override = default; @@ -41,16 +44,16 @@ class NewActionNotification: public Notification return _runnable; } - Thread::Priority priotity() const + Thread::Priority priority() const { return _priority; } - + const std::string &threadName() const { return _name; } - + std::string threadFullName() const { std::string fullName(_name); @@ -68,8 +71,8 @@ class NewActionNotification: public Notification } private: - Thread::Priority _priority; - Runnable &_runnable; + std::atomic _priority; + Runnable& _runnable; std::string _name; }; @@ -87,13 +90,13 @@ class ActiveThread: public Runnable void run() override; private: - NotificationQueue _pTargetQueue; - std::string _name; - Thread _thread; - Event _targetCompleted; - FastMutex _mutex; - const long JOIN_TIMEOUT = 10000; - std::atomic _needToStop{false}; + NotificationQueue _pTargetQueue; + std::string _name; + Thread _thread; + Event _targetCompleted; + FastMutex _mutex; + const long JOIN_TIMEOUT = 10000; + std::atomic _needToStop{false}; }; @@ -157,16 +160,18 @@ void ActiveThread::release() void ActiveThread::run() { - do { - auto *_pTarget = dynamic_cast(_pTargetQueue.waitDequeueNotification()); - while (_pTarget) + do + { + AutoPtr pN = _pTargetQueue.waitDequeueNotification(); + while (pN) { - Runnable* pTarget = &_pTarget->runnable(); - _thread.setPriority(_pTarget->priotity()); - _thread.setName(_pTarget->name()); + NewActionNotification::Ptr pNAN = pN.cast(); + Runnable& target = pNAN->runnable(); + _thread.setPriority(pNAN->priority()); + _thread.setName(pNAN->name()); try { - pTarget->run(); + target.run(); } catch (Exception& exc) { @@ -180,11 +185,10 @@ void ActiveThread::run() { ErrorHandler::handle(); } - _pTarget->release(); _thread.setName(_name); _thread.setPriority(Thread::PRIO_NORMAL); ThreadLocalStorage::clear(); - _pTarget = dynamic_cast(_pTargetQueue.waitDequeueNotification(1000)); + pN = _pTargetQueue.waitDequeueNotification(1000); } _targetCompleted.set(); } diff --git a/Foundation/src/ArchiveStrategy.cpp b/Foundation/src/ArchiveStrategy.cpp index c8dff0000d..bab2bfa0a9 100644 --- a/Foundation/src/ArchiveStrategy.cpp +++ b/Foundation/src/ArchiveStrategy.cpp @@ -123,7 +123,7 @@ void ArchiveStrategy::moveFile(const std::string& oldPath, const std::string& ne { f.renameTo(newPath); if (!_pCompressor) _pCompressor = new ArchiveCompressor; - _pCompressor->compress(newPath); + _pCompressor.load()->compress(newPath); } } diff --git a/Foundation/src/DirectoryWatcher.cpp b/Foundation/src/DirectoryWatcher.cpp index 6e72d2f4d8..6b2c373f27 100644 --- a/Foundation/src/DirectoryWatcher.cpp +++ b/Foundation/src/DirectoryWatcher.cpp @@ -40,6 +40,7 @@ #endif #include #include +#include namespace Poco { @@ -244,7 +245,7 @@ class WindowsDirectoryWatcherStrategy: public DirectoryWatcherStrategy } private: - HANDLE _hStopped; + std::atomic _hStopped; }; @@ -455,7 +456,7 @@ class BSDDirectoryWatcherStrategy: public DirectoryWatcherStrategy private: int _queueFD; int _dirFD; - bool _stopped; + std::atomic _stopped; }; diff --git a/Foundation/src/NotificationCenter.cpp b/Foundation/src/NotificationCenter.cpp index 9da92f3497..eb7e76cb2e 100644 --- a/Foundation/src/NotificationCenter.cpp +++ b/Foundation/src/NotificationCenter.cpp @@ -29,6 +29,18 @@ NotificationCenter::NotificationCenter() NotificationCenter::~NotificationCenter() { + try + { + Mutex::ScopedLock lock(_mutex); + for (auto& o: _observers) + o->disable(); + + _observers.clear(); + } + catch(...) + { + poco_unexpected(); + } } diff --git a/Foundation/src/Task.cpp b/Foundation/src/Task.cpp index a84c0c2732..3a06c9e4ba 100644 --- a/Foundation/src/Task.cpp +++ b/Foundation/src/Task.cpp @@ -41,7 +41,7 @@ void Task::cancel() _state = TASK_CANCELLING; _cancelEvent.set(); if (_pOwner) - _pOwner->taskCancelled(this); + _pOwner.load()->taskCancelled(this); } @@ -56,27 +56,29 @@ void Task::reset() void Task::run() { TaskManager* pOwner = getOwner(); - if (pOwner) - pOwner->taskStarted(this); - try - { - _state = TASK_RUNNING; - runTask(); - } - catch (Exception& exc) - { - if (pOwner) - pOwner->taskFailed(this, exc); - } - catch (std::exception& exc) + if (_state.exchange(TASK_RUNNING) < TASK_RUNNING) { if (pOwner) - pOwner->taskFailed(this, SystemException("Task::run()", exc.what())); - } - catch (...) - { - if (pOwner) - pOwner->taskFailed(this, SystemException("Task::run(): unknown exception")); + pOwner->taskStarted(this); + try + { + runTask(); + } + catch (Exception& exc) + { + if (pOwner) + pOwner->taskFailed(this, exc); + } + catch (std::exception& exc) + { + if (pOwner) + pOwner->taskFailed(this, SystemException("Task::run()", exc.what())); + } + catch (...) + { + if (pOwner) + pOwner->taskFailed(this, SystemException("Task::run(): unknown exception")); + } } _state = TASK_FINISHED; if (pOwner) pOwner->taskFinished(this); @@ -102,21 +104,20 @@ void Task::setProgress(float progress) { FastMutex::ScopedLock lock(_mutex); if (_pOwner) - _pOwner->taskProgress(this, _progress); + _pOwner.load()->taskProgress(this, _progress); } } void Task::setOwner(TaskManager* pOwner) { - FastMutex::ScopedLock lock(_mutex); _pOwner = pOwner; } -void Task::setState(TaskState state) +Task::TaskState Task::setState(TaskState state) { - _state = state; + return _state.exchange(state); } @@ -127,7 +128,7 @@ void Task::postNotification(Notification* pNf) FastMutex::ScopedLock lock(_mutex); if (_pOwner) - _pOwner->postNotification(pNf); + _pOwner.load()->postNotification(pNf); else if (pNf) pNf->release(); } diff --git a/Foundation/src/TaskManager.cpp b/Foundation/src/TaskManager.cpp index cddf027b69..3daee633fe 100644 --- a/Foundation/src/TaskManager.cpp +++ b/Foundation/src/TaskManager.cpp @@ -48,30 +48,39 @@ TaskManager::TaskManager(ThreadPool& pool): TaskManager::~TaskManager() { + for (auto& pTask: _taskList) + pTask->setOwner(nullptr); + if (_ownPool) delete &_threadPool; } -void TaskManager::start(Task* pTask) +bool TaskManager::start(Task* pTask) { TaskPtr pAutoTask(pTask); // take ownership immediately - pAutoTask->setOwner(this); - pAutoTask->setState(Task::TASK_STARTING); + if (pTask->getOwner()) + throw IllegalStateException("Task already owned by another TaskManager"); - ScopedLockT lock(_mutex); - _taskList.push_back(pAutoTask); - try - { - _threadPool.start(*pAutoTask, pAutoTask->name()); - } - catch (...) + if (pTask->state() == Task::TASK_IDLE) { - // Make sure that we don't act like we own the task since - // we never started it. If we leave the task on our task - // list, the size of the list is incorrect. - _taskList.pop_back(); - throw; + pTask->setOwner(this); + pTask->setState(Task::TASK_STARTING); + try + { + _threadPool.start(*pTask, pTask->name()); + ScopedLockT lock(_mutex); + _taskList.push_back(pAutoTask); + return true; + } + catch (...) + { + pTask->setOwner(nullptr); + throw; + } } + + pTask->setOwner(nullptr); + return false; } @@ -152,6 +161,7 @@ void TaskManager::taskFinished(Task* pTask) { if (*it == pTask) { + pTask->setOwner(nullptr); _taskList.erase(it); break; } diff --git a/Foundation/testsuite/src/FIFOEventTest.cpp b/Foundation/testsuite/src/FIFOEventTest.cpp index 7bc5c0d941..570dc75c13 100644 --- a/Foundation/testsuite/src/FIFOEventTest.cpp +++ b/Foundation/testsuite/src/FIFOEventTest.cpp @@ -18,6 +18,7 @@ #include "Poco/Exception.h" #include "Poco/Stopwatch.h" #include +#include using namespace Poco; @@ -357,12 +358,14 @@ void FIFOEventTest::testAsyncNotifyBenchmark() const int cnt = 10000; int runCount = 1000; const Poco::Int64 allCount = cnt * runCount; + std::vector times; + times.reserve(allCount); + std::vector> vresult; + vresult.reserve(cnt); Poco::Stopwatch sw; - sw.restart(); while (runCount-- > 0) { - std::vector> vresult; - vresult.reserve(cnt); + sw.restart(); for (int i = 0; i < cnt; ++i) { vresult.push_back(simple.notifyAsync(this, i)); @@ -373,12 +376,19 @@ void FIFOEventTest::testAsyncNotifyBenchmark() vresult[i].wait(); assertTrue (vresult[i].data() == (i*2)); } + sw.stop(); + times.push_back(sw.elapsed()/1000); + vresult.clear(); } - sw.stop(); - std::cout << "notify and wait time = " << sw.elapsed() / 1000 << std::endl; + + Poco::UInt64 totTime = std::accumulate(times.begin(), times.end(), 0); + double avgTime = static_cast(totTime)/times.size(); + std::cout << "Total notify/wait time for " << allCount << " runs of " + << cnt << " tasks = " << totTime << "ms (avg/run=" << avgTime << "ms)"; assertTrue (_count == allCount); } + void FIFOEventTest::onVoid(const void* pSender) { _count++; @@ -482,6 +492,6 @@ CppUnit::Test* FIFOEventTest::suite() CppUnit_addTest(pSuite, FIFOEventTest, testExpireReRegister); CppUnit_addTest(pSuite, FIFOEventTest, testOverwriteDelegate); CppUnit_addTest(pSuite, FIFOEventTest, testAsyncNotify); - CppUnit_addTest(pSuite, FIFOEventTest, testAsyncNotifyBenchmark); + //CppUnit_addTest(pSuite, FIFOEventTest, testAsyncNotifyBenchmark); return pSuite; } diff --git a/Foundation/testsuite/src/TaskManagerTest.cpp b/Foundation/testsuite/src/TaskManagerTest.cpp index 070b0ed407..0c94840f40 100644 --- a/Foundation/testsuite/src/TaskManagerTest.cpp +++ b/Foundation/testsuite/src/TaskManagerTest.cpp @@ -22,6 +22,7 @@ #include "Poco/Observer.h" #include "Poco/Exception.h" #include "Poco/AutoPtr.h" +#include using Poco::TaskManager; @@ -51,12 +52,14 @@ namespace public: TestTask(): Task("TestTask"), - _fail(false) + _fail(false), + _started(false) { } void runTask() { + _started = true; _event.wait(); setProgress(0.5); _event.wait(); @@ -78,9 +81,15 @@ namespace _event.set(); } + bool started() const + { + return _started; + } + private: Event _event; - bool _fail; + std::atomic _fail; + std::atomic _started; }; class SimpleTask: public Task @@ -277,6 +286,11 @@ void TaskManagerTest::testFinish() assertTrue (!to.error()); tm.cancelAll(); tm.joinAll(); + tm.removeObserver(Observer(to, &TaskObserver::taskStarted)); + tm.removeObserver(Observer(to, &TaskObserver::taskCancelled)); + tm.removeObserver(Observer(to, &TaskObserver::taskFailed)); + tm.removeObserver(Observer(to, &TaskObserver::taskFinished)); + tm.removeObserver(Observer(to, &TaskObserver::taskProgress)); } @@ -317,6 +331,11 @@ void TaskManagerTest::testCancel() assertTrue (!to.error()); tm.cancelAll(); tm.joinAll(); + tm.removeObserver(Observer(to, &TaskObserver::taskStarted)); + tm.removeObserver(Observer(to, &TaskObserver::taskCancelled)); + tm.removeObserver(Observer(to, &TaskObserver::taskFailed)); + tm.removeObserver(Observer(to, &TaskObserver::taskFinished)); + tm.removeObserver(Observer(to, &TaskObserver::taskProgress)); } @@ -330,7 +349,7 @@ void TaskManagerTest::testError() tm.addObserver(Observer(to, &TaskObserver::taskFinished)); tm.addObserver(Observer(to, &TaskObserver::taskProgress)); AutoPtr pTT = new TestTask; - tm.start(pTT.duplicate()); + assertTrue (tm.start(pTT.duplicate())); while (pTT->state() < Task::TASK_RUNNING) Thread::sleep(50); assertTrue (pTT->progress() == 0); Thread::sleep(200); @@ -356,6 +375,11 @@ void TaskManagerTest::testError() assertTrue (list.empty()); tm.cancelAll(); tm.joinAll(); + tm.removeObserver(Observer(to, &TaskObserver::taskStarted)); + tm.removeObserver(Observer(to, &TaskObserver::taskCancelled)); + tm.removeObserver(Observer(to, &TaskObserver::taskFailed)); + tm.removeObserver(Observer(to, &TaskObserver::taskFinished)); + tm.removeObserver(Observer(to, &TaskObserver::taskProgress)); } @@ -443,12 +467,45 @@ void TaskManagerTest::testCustom() } +void TaskManagerTest::testCancelNoStart() +{ + TaskManager tm; + TaskObserver to; + tm.addObserver(Observer(to, &TaskObserver::taskStarted)); + tm.addObserver(Observer(to, &TaskObserver::taskCancelled)); + tm.addObserver(Observer(to, &TaskObserver::taskFailed)); + tm.addObserver(Observer(to, &TaskObserver::taskFinished)); + tm.addObserver(Observer(to, &TaskObserver::taskProgress)); + AutoPtr pTT = new TestTask; + pTT->cancel(); + assertTrue (pTT->isCancelled()); + assertFalse(tm.start(pTT.duplicate())); + assertTrue (pTT->progress() == 0); + assertTrue (pTT->isCancelled()); + assertFalse (pTT->hasOwner()); + tm.removeObserver(Observer(to, &TaskObserver::taskStarted)); + tm.removeObserver(Observer(to, &TaskObserver::taskCancelled)); + tm.removeObserver(Observer(to, &TaskObserver::taskFailed)); + tm.removeObserver(Observer(to, &TaskObserver::taskFinished)); + tm.removeObserver(Observer(to, &TaskObserver::taskProgress)); +} + + void TaskManagerTest::testMultiTasks() { TaskManager tm; - tm.start(new SimpleTask); - tm.start(new SimpleTask); - tm.start(new SimpleTask); + + AutoPtr pTT1 = new SimpleTask; + AutoPtr pTT2 = new SimpleTask; + AutoPtr pTT3 = new SimpleTask; + + tm.start(pTT1.duplicate()); + tm.start(pTT2.duplicate()); + tm.start(pTT3.duplicate()); + + assertTrue (pTT1->hasOwner()); + assertTrue (pTT2->hasOwner()); + assertTrue (pTT3->hasOwner()); TaskManager::TaskList list = tm.taskList(); assertTrue (list.size() == 3); @@ -457,6 +514,13 @@ void TaskManagerTest::testMultiTasks() while (tm.count() > 0) Thread::sleep(100); assertTrue (tm.count() == 0); tm.joinAll(); + + while (pTT1->state() != Task::TASK_FINISHED) Thread::sleep(50); + assertFalse (pTT1->hasOwner()); + while (pTT2->state() != Task::TASK_FINISHED) Thread::sleep(50); + assertFalse (pTT2->hasOwner()); + while (pTT3->state() != Task::TASK_FINISHED) Thread::sleep(50); + assertFalse (pTT3->hasOwner()); } @@ -510,6 +574,7 @@ CppUnit::Test* TaskManagerTest::suite() CppUnit_addTest(pSuite, TaskManagerTest, testFinish); CppUnit_addTest(pSuite, TaskManagerTest, testCancel); CppUnit_addTest(pSuite, TaskManagerTest, testError); + CppUnit_addTest(pSuite, TaskManagerTest, testCancelNoStart); CppUnit_addTest(pSuite, TaskManagerTest, testMultiTasks); CppUnit_addTest(pSuite, TaskManagerTest, testCustom); CppUnit_addTest(pSuite, TaskManagerTest, testCustomThreadPool); diff --git a/Foundation/testsuite/src/TaskManagerTest.h b/Foundation/testsuite/src/TaskManagerTest.h index 54e9315732..f1fa21e17b 100644 --- a/Foundation/testsuite/src/TaskManagerTest.h +++ b/Foundation/testsuite/src/TaskManagerTest.h @@ -33,6 +33,7 @@ class TaskManagerTest: public CppUnit::TestCase void testFinish(); void testCancel(); void testError(); + void testCancelNoStart(); void testCustom(); void testMultiTasks(); void testCustomThreadPool(); diff --git a/Foundation/testsuite/src/TaskTest.cpp b/Foundation/testsuite/src/TaskTest.cpp index 9e6b67d587..8677430d94 100644 --- a/Foundation/testsuite/src/TaskTest.cpp +++ b/Foundation/testsuite/src/TaskTest.cpp @@ -29,12 +29,14 @@ namespace class TestTask: public Task { public: - TestTask(): Task("TestTask") + TestTask(): Task("TestTask"), + _started(false) { } void runTask() { + _started = true; try { _event.wait(); @@ -73,8 +75,14 @@ namespace } } + bool started() const + { + return _started; + } + private: Event _event; + std::atomic _started; }; } @@ -104,6 +112,20 @@ void TaskTest::testFinish() pTT->cont(); thr.join(); assertTrue (pTT->state() == Task::TASK_FINISHED); + + pTT->reset(); + assertTrue (pTT->progress() == 0); + assertTrue (pTT->state() == Task::TASK_IDLE); + thr.start(*pTT); + assertTrue (pTT->progress() == 0); + pTT->cont(); + while (pTT->progress() != 0.5) Thread::sleep(50); + assertTrue (pTT->state() == Task::TASK_RUNNING); + pTT->cont(); + while (pTT->progress() != 1.0) Thread::sleep(50); + pTT->cont(); + thr.join(); + assertTrue (pTT->state() == Task::TASK_FINISHED); } @@ -142,6 +164,21 @@ void TaskTest::testCancel2() } +void TaskTest::testCancelNoStart() +{ + AutoPtr pTT = new TestTask; + assertTrue (pTT->state() == Task::TASK_IDLE); + pTT->cancel(); + assertTrue (pTT->state() == Task::TASK_CANCELLING); + Thread thr; + thr.start(*pTT); + while (pTT->state() != Task::TASK_FINISHED) + Thread::sleep(50); + assertTrue (pTT->state() == Task::TASK_FINISHED); + assertFalse (pTT->started()); +} + + void TaskTest::setUp() { } @@ -159,6 +196,7 @@ CppUnit::Test* TaskTest::suite() CppUnit_addTest(pSuite, TaskTest, testFinish); CppUnit_addTest(pSuite, TaskTest, testCancel1); CppUnit_addTest(pSuite, TaskTest, testCancel2); + CppUnit_addTest(pSuite, TaskTest, testCancelNoStart); return pSuite; } diff --git a/Foundation/testsuite/src/TaskTest.h b/Foundation/testsuite/src/TaskTest.h index 8da65fc86f..e35972937c 100644 --- a/Foundation/testsuite/src/TaskTest.h +++ b/Foundation/testsuite/src/TaskTest.h @@ -27,6 +27,7 @@ class TaskTest: public CppUnit::TestCase void testFinish(); void testCancel1(); void testCancel2(); + void testCancelNoStart(); void setUp(); void tearDown(); diff --git a/runLibTests.sh b/runLibTests.sh index fb761eb5f7..4acb7755ea 100755 --- a/runLibTests.sh +++ b/runLibTests.sh @@ -7,11 +7,15 @@ # to clean and rebuild a single library, with all of its dependencies, # and run the tests. # -# Usage: ./runLibTests.sh library [address | undefined | thread ] +# Usage: ./runLibTests.sh library [address | undefined | thread] [ | -all | none] # # Example: ./runLibTests.sh Data/SQLite address # (distcleans, rebuilds and runs tests for Data/SQLite with address sanitizer) # +# Known shortcomings (TODO): +# - the script does not check if the library is a dependency of another library +# workaround: run the script for the dependent libraries first +# # g++ does not like empty quoted arguments, but # the shellcheck wants them quoted to remain quiet @@ -20,7 +24,7 @@ path=$1 if [ -z "${path}" ]; then echo "Library not specified" - echo "Usage: $0 path [address | undefined | thread ]" + echo "Usage: $0 path [address | undefined | thread] [ | -all | none]" exit 1 fi @@ -52,13 +56,20 @@ make distclean -C "$basedir"/CppUnit make -s -j4 -C "$basedir"/Foundation $flags make -s -j4 -C "$basedir"/CppUnit $flags +test=$3 +if [ -z "${test}" ]; then + test="-all" +fi + # Foundation requested, build/run tests and exit if [[ "$path" == "$basedir"/"Foundation" ]]; then cd "$path/testsuite/" || exit make -s -j4 -C ./ $flags cd "bin/$OSNAME/$OSARCH/" || exit - ./testrunner -all - ./testrunnerd -all + if [[ "$test" != "none" ]]; then + ./testrunner "${test}" + ./testrunnerd "${test}" + fi echo "$path $flags done." exit 0 fi @@ -72,8 +83,10 @@ do make distclean make -s -j4 -C ./ $flags cd bin/"$OSNAME"/"$OSARCH"/ || exit - ./testrunner -all - ./testrunnerd -all + if [[ "$test" != "none" ]]; then + ./testrunner "${test}" + ./testrunnerd "${test}" + fi echo "$1 $flags done." cd ../../../../ || exit done