4307/8/9/10 data races (#4312)

* fix(NumericString): properly mark uIntToString deprecated #4304

* dev(runLibtests): allow to specify test to run

* fix(NotificationCenter): data race #4307

* fix(DirectoryWatcher): data race #4308

* fix(ArchiveStrategy): data race #4309

* fix(ActiveThread): data race #4310

* fix(Task): Cancelled Task shouldn't start running #4311 (WIP)

* fix(String): ignore clang loop unrolling warnings

* fix(TaskManager): task ownership #4311

* chore(FIFOEventTest): fix unused var warning; disable benchmark in test

* fix(Task): remove unnecessary mutex (and prevent cyclic locking reported by TSAN)

* fix(CryptoTest): disable testEncryptDecryptGCM

* fix(ci): typo

* fix(NotificationCenter): disable and clear observers in dtor (#4307)

---------

Co-authored-by: Matej Kenda <matejken@gmail.com>
This commit is contained in:
Aleksandar Fabijanic 2023-12-09 21:16:24 +01:00 committed by GitHub
parent 35e1490b26
commit 1e90f64bbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 303 additions and 111 deletions

View File

@ -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<ExpireLRUCacheTest>.testAccessExpireN,
CppUnit::TestCaller<UniqueExpireLRUCacheTest>.testExpireN,
CppUnit::TestCaller<ExpireLRUCacheTest>.testAccessExpireN,
CppUnit::TestCaller<PollSetTest>.testPollClosedServer"
CppUnit::TestCaller<PollSetTest>.testPollClosedServer,
CppUnit::TestCaller<CryptoTest>.testEncryptDecryptGCM"
PWD=`pwd`
ctest --output-on-failure -E "(DataMySQL)|(DataODBC)|(PostgreSQL)|(MongoDB)|(Redis)"

View File

@ -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 \

View File

@ -23,6 +23,7 @@
#include "Poco/File.h"
#include "Poco/DateTimeFormatter.h"
#include "Poco/NumberFormatter.h"
#include <atomic>
namespace Poco {
@ -44,7 +45,7 @@ public:
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 @@ private:
ArchiveStrategy(const ArchiveStrategy&);
ArchiveStrategy& operator = (const ArchiveStrategy&);
bool _compress;
ArchiveCompressor* _pCompressor;
std::atomic<bool> _compress;
std::atomic<ArchiveCompressor*> _pCompressor;
};

View File

@ -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 <typename T>
[[deprecated("use intToStr instead")]]
bool uIntToStr(T value,
unsigned short base,
char* result,
@ -579,8 +580,8 @@ bool intToStr (T number,
}
//@ deprecated
template <typename T>
[[deprecated("use intToStr instead")]]
bool uIntToStr (T number,
unsigned short base,
std::string& result,

View File

@ -23,6 +23,11 @@
#include <cstring>
#include <algorithm>
// 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

View File

@ -77,6 +77,8 @@ public:
/// 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 @@ public:
/// 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 @@ protected:
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 @@ private:
Task(const Task&);
Task& operator = (const Task&);
std::string _name;
TaskManager* _pOwner;
std::atomic<float> _progress;
std::atomic<TaskState> _state;
Event _cancelEvent;
mutable FastMutex _mutex;
std::string _name;
std::atomic<TaskManager*> _pOwner;
std::atomic<float> _progress;
std::atomic<TaskState> _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

View File

@ -65,9 +65,16 @@ public:
~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.

View File

@ -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>;
NewActionNotification(Thread::Priority priority, Runnable& runnable, const std::string& name) :
_priority(priority),
_runnable(runnable),
_name(name)
{
}
~NewActionNotification() override = default;
@ -41,16 +44,16 @@ public:
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 @@ public:
}
private:
Thread::Priority _priority;
Runnable &_runnable;
std::atomic<Thread::Priority> _priority;
Runnable& _runnable;
std::string _name;
};
@ -87,13 +90,13 @@ public:
void run() override;
private:
NotificationQueue _pTargetQueue;
std::string _name;
Thread _thread;
Event _targetCompleted;
FastMutex _mutex;
const long JOIN_TIMEOUT = 10000;
std::atomic<bool> _needToStop{false};
NotificationQueue _pTargetQueue;
std::string _name;
Thread _thread;
Event _targetCompleted;
FastMutex _mutex;
const long JOIN_TIMEOUT = 10000;
std::atomic<bool> _needToStop{false};
};
@ -157,16 +160,18 @@ void ActiveThread::release()
void ActiveThread::run()
{
do {
auto *_pTarget = dynamic_cast<NewActionNotification*>(_pTargetQueue.waitDequeueNotification());
while (_pTarget)
do
{
AutoPtr<Notification> pN = _pTargetQueue.waitDequeueNotification();
while (pN)
{
Runnable* pTarget = &_pTarget->runnable();
_thread.setPriority(_pTarget->priotity());
_thread.setName(_pTarget->name());
NewActionNotification::Ptr pNAN = pN.cast<NewActionNotification>();
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<NewActionNotification*>(_pTargetQueue.waitDequeueNotification(1000));
pN = _pTargetQueue.waitDequeueNotification(1000);
}
_targetCompleted.set();
}

View File

@ -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);
}
}

View File

@ -40,6 +40,7 @@
#endif
#include <algorithm>
#include <map>
#include <atomic>
namespace Poco {
@ -244,7 +245,7 @@ public:
}
private:
HANDLE _hStopped;
std::atomic<HANDLE> _hStopped;
};
@ -455,7 +456,7 @@ public:
private:
int _queueFD;
int _dirFD;
bool _stopped;
std::atomic<bool> _stopped;
};

View File

@ -29,6 +29,18 @@ NotificationCenter::NotificationCenter()
NotificationCenter::~NotificationCenter()
{
try
{
Mutex::ScopedLock lock(_mutex);
for (auto& o: _observers)
o->disable();
_observers.clear();
}
catch(...)
{
poco_unexpected();
}
}

View File

@ -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 (_state.exchange(TASK_RUNNING) < TASK_RUNNING)
{
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"));
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();
}

View File

@ -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
if (pTask->state() == Task::TASK_IDLE)
{
_threadPool.start(*pAutoTask, pAutoTask->name());
}
catch (...)
{
// 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;
}

View File

@ -18,6 +18,7 @@
#include "Poco/Exception.h"
#include "Poco/Stopwatch.h"
#include <iostream>
#include <numeric>
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<int> times;
times.reserve(allCount);
std::vector<Poco::ActiveResult<int>> vresult;
vresult.reserve(cnt);
Poco::Stopwatch sw;
sw.restart();
while (runCount-- > 0)
{
std::vector<Poco::ActiveResult<int>> 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<double>(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;
}

View File

@ -22,6 +22,7 @@
#include "Poco/Observer.h"
#include "Poco/Exception.h"
#include "Poco/AutoPtr.h"
#include <iostream>
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<bool> _fail;
std::atomic<bool> _started;
};
class SimpleTask: public Task
@ -277,6 +286,11 @@ void TaskManagerTest::testFinish()
assertTrue (!to.error());
tm.cancelAll();
tm.joinAll();
tm.removeObserver(Observer<TaskObserver, TaskStartedNotification>(to, &TaskObserver::taskStarted));
tm.removeObserver(Observer<TaskObserver, TaskCancelledNotification>(to, &TaskObserver::taskCancelled));
tm.removeObserver(Observer<TaskObserver, TaskFailedNotification>(to, &TaskObserver::taskFailed));
tm.removeObserver(Observer<TaskObserver, TaskFinishedNotification>(to, &TaskObserver::taskFinished));
tm.removeObserver(Observer<TaskObserver, TaskProgressNotification>(to, &TaskObserver::taskProgress));
}
@ -317,6 +331,11 @@ void TaskManagerTest::testCancel()
assertTrue (!to.error());
tm.cancelAll();
tm.joinAll();
tm.removeObserver(Observer<TaskObserver, TaskStartedNotification>(to, &TaskObserver::taskStarted));
tm.removeObserver(Observer<TaskObserver, TaskCancelledNotification>(to, &TaskObserver::taskCancelled));
tm.removeObserver(Observer<TaskObserver, TaskFailedNotification>(to, &TaskObserver::taskFailed));
tm.removeObserver(Observer<TaskObserver, TaskFinishedNotification>(to, &TaskObserver::taskFinished));
tm.removeObserver(Observer<TaskObserver, TaskProgressNotification>(to, &TaskObserver::taskProgress));
}
@ -330,7 +349,7 @@ void TaskManagerTest::testError()
tm.addObserver(Observer<TaskObserver, TaskFinishedNotification>(to, &TaskObserver::taskFinished));
tm.addObserver(Observer<TaskObserver, TaskProgressNotification>(to, &TaskObserver::taskProgress));
AutoPtr<TestTask> 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<TaskObserver, TaskStartedNotification>(to, &TaskObserver::taskStarted));
tm.removeObserver(Observer<TaskObserver, TaskCancelledNotification>(to, &TaskObserver::taskCancelled));
tm.removeObserver(Observer<TaskObserver, TaskFailedNotification>(to, &TaskObserver::taskFailed));
tm.removeObserver(Observer<TaskObserver, TaskFinishedNotification>(to, &TaskObserver::taskFinished));
tm.removeObserver(Observer<TaskObserver, TaskProgressNotification>(to, &TaskObserver::taskProgress));
}
@ -443,12 +467,45 @@ void TaskManagerTest::testCustom()
}
void TaskManagerTest::testCancelNoStart()
{
TaskManager tm;
TaskObserver to;
tm.addObserver(Observer<TaskObserver, TaskStartedNotification>(to, &TaskObserver::taskStarted));
tm.addObserver(Observer<TaskObserver, TaskCancelledNotification>(to, &TaskObserver::taskCancelled));
tm.addObserver(Observer<TaskObserver, TaskFailedNotification>(to, &TaskObserver::taskFailed));
tm.addObserver(Observer<TaskObserver, TaskFinishedNotification>(to, &TaskObserver::taskFinished));
tm.addObserver(Observer<TaskObserver, TaskProgressNotification>(to, &TaskObserver::taskProgress));
AutoPtr<TestTask> 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<TaskObserver, TaskStartedNotification>(to, &TaskObserver::taskStarted));
tm.removeObserver(Observer<TaskObserver, TaskCancelledNotification>(to, &TaskObserver::taskCancelled));
tm.removeObserver(Observer<TaskObserver, TaskFailedNotification>(to, &TaskObserver::taskFailed));
tm.removeObserver(Observer<TaskObserver, TaskFinishedNotification>(to, &TaskObserver::taskFinished));
tm.removeObserver(Observer<TaskObserver, TaskProgressNotification>(to, &TaskObserver::taskProgress));
}
void TaskManagerTest::testMultiTasks()
{
TaskManager tm;
tm.start(new SimpleTask);
tm.start(new SimpleTask);
tm.start(new SimpleTask);
AutoPtr<SimpleTask> pTT1 = new SimpleTask;
AutoPtr<SimpleTask> pTT2 = new SimpleTask;
AutoPtr<SimpleTask> 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);

View File

@ -33,6 +33,7 @@ public:
void testFinish();
void testCancel();
void testError();
void testCancelNoStart();
void testCustom();
void testMultiTasks();
void testCustomThreadPool();

View File

@ -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<bool> _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<TestTask> 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;
}

View File

@ -27,6 +27,7 @@ public:
void testFinish();
void testCancel1();
void testCancel2();
void testCancelNoStart();
void setUp();
void tearDown();

View File

@ -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] [<test> | -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] [<test> | -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