mirror of
https://github.com/pocoproject/poco.git
synced 2024-12-13 10:32:57 +01:00
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:
parent
35e1490b26
commit
1e90f64bbf
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@ -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)"
|
||||
|
||||
|
@ -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 \
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -29,6 +29,18 @@ NotificationCenter::NotificationCenter()
|
||||
|
||||
NotificationCenter::~NotificationCenter()
|
||||
{
|
||||
try
|
||||
{
|
||||
Mutex::ScopedLock lock(_mutex);
|
||||
for (auto& o: _observers)
|
||||
o->disable();
|
||||
|
||||
_observers.clear();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
poco_unexpected();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -33,6 +33,7 @@ public:
|
||||
void testFinish();
|
||||
void testCancel();
|
||||
void testError();
|
||||
void testCancelNoStart();
|
||||
void testCustom();
|
||||
void testMultiTasks();
|
||||
void testCustomThreadPool();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ public:
|
||||
void testFinish();
|
||||
void testCancel1();
|
||||
void testCancel2();
|
||||
void testCancelNoStart();
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user