mirror of
https://github.com/pocoproject/poco.git
synced 2024-12-12 10:13:51 +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 timeout set retry_on: timeout
|
||||||
# To retry only on error set retry_on: error
|
# To retry only on error set retry_on: error
|
||||||
# For more information on the retry action see https://github.com/nick-fields/retry
|
# For more information on the retry action see https://github.com/nick-fields/retry
|
||||||
|
|
||||||
|
name: Compile and Testrun
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened]
|
types: [opened]
|
||||||
@ -231,7 +234,8 @@ jobs:
|
|||||||
CppUnit::TestCaller<ExpireLRUCacheTest>.testAccessExpireN,
|
CppUnit::TestCaller<ExpireLRUCacheTest>.testAccessExpireN,
|
||||||
CppUnit::TestCaller<UniqueExpireLRUCacheTest>.testExpireN,
|
CppUnit::TestCaller<UniqueExpireLRUCacheTest>.testExpireN,
|
||||||
CppUnit::TestCaller<ExpireLRUCacheTest>.testAccessExpireN,
|
CppUnit::TestCaller<ExpireLRUCacheTest>.testAccessExpireN,
|
||||||
CppUnit::TestCaller<PollSetTest>.testPollClosedServer"
|
CppUnit::TestCaller<PollSetTest>.testPollClosedServer,
|
||||||
|
CppUnit::TestCaller<CryptoTest>.testEncryptDecryptGCM"
|
||||||
PWD=`pwd`
|
PWD=`pwd`
|
||||||
ctest --output-on-failure -E "(DataMySQL)|(DataODBC)|(PostgreSQL)|(MongoDB)|(Redis)"
|
ctest --output-on-failure -E "(DataMySQL)|(DataODBC)|(PostgreSQL)|(MongoDB)|(Redis)"
|
||||||
|
|
||||||
|
@ -16,10 +16,14 @@ else
|
|||||||
ifeq ($(findstring AIX, $(POCO_CONFIG)), AIX)
|
ifeq ($(findstring AIX, $(POCO_CONFIG)), AIX)
|
||||||
SYSLIBS += -lssl_a -lcrypto_a -lz -ldl
|
SYSLIBS += -lssl_a -lcrypto_a -lz -ldl
|
||||||
else
|
else
|
||||||
|
ifeq ($(POCO_CONFIG),Darwin)
|
||||||
|
SYSLIBS += -lssl -lcrypto -lz
|
||||||
|
else
|
||||||
SYSLIBS += -lssl -lcrypto -lz -ldl
|
SYSLIBS += -lssl -lcrypto -lz -ldl
|
||||||
endif
|
endif # Darwin
|
||||||
endif
|
endif # AIX
|
||||||
endif
|
endif # QNX
|
||||||
|
endif # FreeBSD
|
||||||
|
|
||||||
objects = CryptoTestSuite Driver \
|
objects = CryptoTestSuite Driver \
|
||||||
CryptoTest DigestEngineTest ECTest \
|
CryptoTest DigestEngineTest ECTest \
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "Poco/File.h"
|
#include "Poco/File.h"
|
||||||
#include "Poco/DateTimeFormatter.h"
|
#include "Poco/DateTimeFormatter.h"
|
||||||
#include "Poco/NumberFormatter.h"
|
#include "Poco/NumberFormatter.h"
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
|
||||||
namespace Poco {
|
namespace Poco {
|
||||||
@ -61,8 +62,8 @@ private:
|
|||||||
ArchiveStrategy(const ArchiveStrategy&);
|
ArchiveStrategy(const ArchiveStrategy&);
|
||||||
ArchiveStrategy& operator = (const ArchiveStrategy&);
|
ArchiveStrategy& operator = (const ArchiveStrategy&);
|
||||||
|
|
||||||
bool _compress;
|
std::atomic<bool> _compress;
|
||||||
ArchiveCompressor* _pCompressor;
|
std::atomic<ArchiveCompressor*> _pCompressor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -387,7 +387,8 @@ bool intToStr(T value,
|
|||||||
char fill = ' ',
|
char fill = ' ',
|
||||||
char thSep = 0,
|
char thSep = 0,
|
||||||
bool lowercase = false)
|
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.
|
/// 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
|
/// 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
|
/// 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>
|
template <typename T>
|
||||||
|
[[deprecated("use intToStr instead")]]
|
||||||
bool uIntToStr(T value,
|
bool uIntToStr(T value,
|
||||||
unsigned short base,
|
unsigned short base,
|
||||||
char* result,
|
char* result,
|
||||||
@ -579,8 +580,8 @@ bool intToStr (T number,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//@ deprecated
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
[[deprecated("use intToStr instead")]]
|
||||||
bool uIntToStr (T number,
|
bool uIntToStr (T number,
|
||||||
unsigned short base,
|
unsigned short base,
|
||||||
std::string& result,
|
std::string& result,
|
||||||
|
@ -23,6 +23,11 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <algorithm>
|
#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 {
|
namespace Poco {
|
||||||
|
|
||||||
@ -760,5 +765,8 @@ struct CILess
|
|||||||
|
|
||||||
} // namespace Poco
|
} // namespace Poco
|
||||||
|
|
||||||
|
#if defined(__clang__) && ((__clang_major__ > 3) || (__clang_major__ == 3 && __clang_minor__ >= 6))
|
||||||
|
# pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // Foundation_String_INCLUDED
|
#endif // Foundation_String_INCLUDED
|
||||||
|
@ -77,6 +77,8 @@ public:
|
|||||||
/// A Task's runTask() method should periodically
|
/// A Task's runTask() method should periodically
|
||||||
/// call this method and stop whatever it is doing in an
|
/// call this method and stop whatever it is doing in an
|
||||||
/// orderly way when this method returns true.
|
/// 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;
|
TaskState state() const;
|
||||||
/// Returns the task's current state.
|
/// Returns the task's current state.
|
||||||
@ -90,9 +92,14 @@ public:
|
|||||||
/// be overridden by subclasses.
|
/// be overridden by subclasses.
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
/// Calls the task's runTask() method and notifies the owner
|
/// If task has not been cancelled prior to this call, it
|
||||||
/// of the task's start and completion.
|
/// 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:
|
protected:
|
||||||
bool sleep(long milliseconds);
|
bool sleep(long milliseconds);
|
||||||
/// Suspends the current thread for the specified
|
/// Suspends the current thread for the specified
|
||||||
@ -134,7 +141,7 @@ protected:
|
|||||||
TaskManager* getOwner() const;
|
TaskManager* getOwner() const;
|
||||||
/// Returns the owner of the task, which may be NULL.
|
/// Returns the owner of the task, which may be NULL.
|
||||||
|
|
||||||
void setState(TaskState state);
|
TaskState setState(TaskState state);
|
||||||
/// Sets the task's state.
|
/// Sets the task's state.
|
||||||
|
|
||||||
virtual ~Task();
|
virtual ~Task();
|
||||||
@ -146,7 +153,7 @@ private:
|
|||||||
Task& operator = (const Task&);
|
Task& operator = (const Task&);
|
||||||
|
|
||||||
std::string _name;
|
std::string _name;
|
||||||
TaskManager* _pOwner;
|
std::atomic<TaskManager*> _pOwner;
|
||||||
std::atomic<float> _progress;
|
std::atomic<float> _progress;
|
||||||
std::atomic<TaskState> _state;
|
std::atomic<TaskState> _state;
|
||||||
Event _cancelEvent;
|
Event _cancelEvent;
|
||||||
@ -185,12 +192,16 @@ inline Task::TaskState Task::state() const
|
|||||||
|
|
||||||
inline TaskManager* Task::getOwner() const
|
inline TaskManager* Task::getOwner() const
|
||||||
{
|
{
|
||||||
FastMutex::ScopedLock lock(_mutex);
|
|
||||||
|
|
||||||
return _pOwner;
|
return _pOwner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline bool Task::hasOwner() const
|
||||||
|
{
|
||||||
|
return _pOwner != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace Poco
|
} // namespace Poco
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,9 +65,16 @@ public:
|
|||||||
~TaskManager();
|
~TaskManager();
|
||||||
/// Destroys the TaskManager.
|
/// Destroys the TaskManager.
|
||||||
|
|
||||||
void start(Task* pTask);
|
bool start(Task* pTask);
|
||||||
/// Starts the given task in a thread obtained
|
/// 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
|
/// The TaskManager takes ownership of the Task object
|
||||||
/// and deletes it when it is finished.
|
/// and deletes it when it is finished.
|
||||||
|
@ -28,11 +28,14 @@ namespace Poco {
|
|||||||
class NewActionNotification: public Notification
|
class NewActionNotification: public Notification
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NewActionNotification(Thread::Priority priority, Runnable &runnable, std::string name) :
|
using Ptr = AutoPtr<NewActionNotification>;
|
||||||
|
|
||||||
|
NewActionNotification(Thread::Priority priority, Runnable& runnable, const std::string& name) :
|
||||||
_priority(priority),
|
_priority(priority),
|
||||||
_runnable(runnable),
|
_runnable(runnable),
|
||||||
_name(std::move(name))
|
_name(name)
|
||||||
{ }
|
{
|
||||||
|
}
|
||||||
|
|
||||||
~NewActionNotification() override = default;
|
~NewActionNotification() override = default;
|
||||||
|
|
||||||
@ -41,7 +44,7 @@ public:
|
|||||||
return _runnable;
|
return _runnable;
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread::Priority priotity() const
|
Thread::Priority priority() const
|
||||||
{
|
{
|
||||||
return _priority;
|
return _priority;
|
||||||
}
|
}
|
||||||
@ -68,8 +71,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Thread::Priority _priority;
|
std::atomic<Thread::Priority> _priority;
|
||||||
Runnable &_runnable;
|
Runnable& _runnable;
|
||||||
std::string _name;
|
std::string _name;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -157,16 +160,18 @@ void ActiveThread::release()
|
|||||||
|
|
||||||
void ActiveThread::run()
|
void ActiveThread::run()
|
||||||
{
|
{
|
||||||
do {
|
do
|
||||||
auto *_pTarget = dynamic_cast<NewActionNotification*>(_pTargetQueue.waitDequeueNotification());
|
|
||||||
while (_pTarget)
|
|
||||||
{
|
{
|
||||||
Runnable* pTarget = &_pTarget->runnable();
|
AutoPtr<Notification> pN = _pTargetQueue.waitDequeueNotification();
|
||||||
_thread.setPriority(_pTarget->priotity());
|
while (pN)
|
||||||
_thread.setName(_pTarget->name());
|
{
|
||||||
|
NewActionNotification::Ptr pNAN = pN.cast<NewActionNotification>();
|
||||||
|
Runnable& target = pNAN->runnable();
|
||||||
|
_thread.setPriority(pNAN->priority());
|
||||||
|
_thread.setName(pNAN->name());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
pTarget->run();
|
target.run();
|
||||||
}
|
}
|
||||||
catch (Exception& exc)
|
catch (Exception& exc)
|
||||||
{
|
{
|
||||||
@ -180,11 +185,10 @@ void ActiveThread::run()
|
|||||||
{
|
{
|
||||||
ErrorHandler::handle();
|
ErrorHandler::handle();
|
||||||
}
|
}
|
||||||
_pTarget->release();
|
|
||||||
_thread.setName(_name);
|
_thread.setName(_name);
|
||||||
_thread.setPriority(Thread::PRIO_NORMAL);
|
_thread.setPriority(Thread::PRIO_NORMAL);
|
||||||
ThreadLocalStorage::clear();
|
ThreadLocalStorage::clear();
|
||||||
_pTarget = dynamic_cast<NewActionNotification*>(_pTargetQueue.waitDequeueNotification(1000));
|
pN = _pTargetQueue.waitDequeueNotification(1000);
|
||||||
}
|
}
|
||||||
_targetCompleted.set();
|
_targetCompleted.set();
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ void ArchiveStrategy::moveFile(const std::string& oldPath, const std::string& ne
|
|||||||
{
|
{
|
||||||
f.renameTo(newPath);
|
f.renameTo(newPath);
|
||||||
if (!_pCompressor) _pCompressor = new ArchiveCompressor;
|
if (!_pCompressor) _pCompressor = new ArchiveCompressor;
|
||||||
_pCompressor->compress(newPath);
|
_pCompressor.load()->compress(newPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
|
||||||
namespace Poco {
|
namespace Poco {
|
||||||
@ -244,7 +245,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HANDLE _hStopped;
|
std::atomic<HANDLE> _hStopped;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -455,7 +456,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
int _queueFD;
|
int _queueFD;
|
||||||
int _dirFD;
|
int _dirFD;
|
||||||
bool _stopped;
|
std::atomic<bool> _stopped;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,6 +29,18 @@ NotificationCenter::NotificationCenter()
|
|||||||
|
|
||||||
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;
|
_state = TASK_CANCELLING;
|
||||||
_cancelEvent.set();
|
_cancelEvent.set();
|
||||||
if (_pOwner)
|
if (_pOwner)
|
||||||
_pOwner->taskCancelled(this);
|
_pOwner.load()->taskCancelled(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -56,11 +56,12 @@ void Task::reset()
|
|||||||
void Task::run()
|
void Task::run()
|
||||||
{
|
{
|
||||||
TaskManager* pOwner = getOwner();
|
TaskManager* pOwner = getOwner();
|
||||||
|
if (_state.exchange(TASK_RUNNING) < TASK_RUNNING)
|
||||||
|
{
|
||||||
if (pOwner)
|
if (pOwner)
|
||||||
pOwner->taskStarted(this);
|
pOwner->taskStarted(this);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_state = TASK_RUNNING;
|
|
||||||
runTask();
|
runTask();
|
||||||
}
|
}
|
||||||
catch (Exception& exc)
|
catch (Exception& exc)
|
||||||
@ -78,6 +79,7 @@ void Task::run()
|
|||||||
if (pOwner)
|
if (pOwner)
|
||||||
pOwner->taskFailed(this, SystemException("Task::run(): unknown exception"));
|
pOwner->taskFailed(this, SystemException("Task::run(): unknown exception"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_state = TASK_FINISHED;
|
_state = TASK_FINISHED;
|
||||||
if (pOwner) pOwner->taskFinished(this);
|
if (pOwner) pOwner->taskFinished(this);
|
||||||
}
|
}
|
||||||
@ -102,21 +104,20 @@ void Task::setProgress(float progress)
|
|||||||
{
|
{
|
||||||
FastMutex::ScopedLock lock(_mutex);
|
FastMutex::ScopedLock lock(_mutex);
|
||||||
if (_pOwner)
|
if (_pOwner)
|
||||||
_pOwner->taskProgress(this, _progress);
|
_pOwner.load()->taskProgress(this, _progress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Task::setOwner(TaskManager* pOwner)
|
void Task::setOwner(TaskManager* pOwner)
|
||||||
{
|
{
|
||||||
FastMutex::ScopedLock lock(_mutex);
|
|
||||||
_pOwner = pOwner;
|
_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);
|
FastMutex::ScopedLock lock(_mutex);
|
||||||
|
|
||||||
if (_pOwner)
|
if (_pOwner)
|
||||||
_pOwner->postNotification(pNf);
|
_pOwner.load()->postNotification(pNf);
|
||||||
else if (pNf)
|
else if (pNf)
|
||||||
pNf->release();
|
pNf->release();
|
||||||
}
|
}
|
||||||
|
@ -48,30 +48,39 @@ TaskManager::TaskManager(ThreadPool& pool):
|
|||||||
|
|
||||||
TaskManager::~TaskManager()
|
TaskManager::~TaskManager()
|
||||||
{
|
{
|
||||||
|
for (auto& pTask: _taskList)
|
||||||
|
pTask->setOwner(nullptr);
|
||||||
|
|
||||||
if (_ownPool) delete &_threadPool;
|
if (_ownPool) delete &_threadPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TaskManager::start(Task* pTask)
|
bool TaskManager::start(Task* pTask)
|
||||||
{
|
{
|
||||||
TaskPtr pAutoTask(pTask); // take ownership immediately
|
TaskPtr pAutoTask(pTask); // take ownership immediately
|
||||||
pAutoTask->setOwner(this);
|
if (pTask->getOwner())
|
||||||
pAutoTask->setState(Task::TASK_STARTING);
|
throw IllegalStateException("Task already owned by another TaskManager");
|
||||||
|
|
||||||
ScopedLockT lock(_mutex);
|
if (pTask->state() == Task::TASK_IDLE)
|
||||||
_taskList.push_back(pAutoTask);
|
{
|
||||||
|
pTask->setOwner(this);
|
||||||
|
pTask->setState(Task::TASK_STARTING);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_threadPool.start(*pAutoTask, pAutoTask->name());
|
_threadPool.start(*pTask, pTask->name());
|
||||||
|
ScopedLockT lock(_mutex);
|
||||||
|
_taskList.push_back(pAutoTask);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
// Make sure that we don't act like we own the task since
|
pTask->setOwner(nullptr);
|
||||||
// we never started it. If we leave the task on our task
|
|
||||||
// list, the size of the list is incorrect.
|
|
||||||
_taskList.pop_back();
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pTask->setOwner(nullptr);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -152,6 +161,7 @@ void TaskManager::taskFinished(Task* pTask)
|
|||||||
{
|
{
|
||||||
if (*it == pTask)
|
if (*it == pTask)
|
||||||
{
|
{
|
||||||
|
pTask->setOwner(nullptr);
|
||||||
_taskList.erase(it);
|
_taskList.erase(it);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "Poco/Exception.h"
|
#include "Poco/Exception.h"
|
||||||
#include "Poco/Stopwatch.h"
|
#include "Poco/Stopwatch.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
|
||||||
using namespace Poco;
|
using namespace Poco;
|
||||||
@ -357,12 +358,14 @@ void FIFOEventTest::testAsyncNotifyBenchmark()
|
|||||||
const int cnt = 10000;
|
const int cnt = 10000;
|
||||||
int runCount = 1000;
|
int runCount = 1000;
|
||||||
const Poco::Int64 allCount = cnt * runCount;
|
const Poco::Int64 allCount = cnt * runCount;
|
||||||
Poco::Stopwatch sw;
|
std::vector<int> times;
|
||||||
sw.restart();
|
times.reserve(allCount);
|
||||||
while (runCount-- > 0)
|
|
||||||
{
|
|
||||||
std::vector<Poco::ActiveResult<int>> vresult;
|
std::vector<Poco::ActiveResult<int>> vresult;
|
||||||
vresult.reserve(cnt);
|
vresult.reserve(cnt);
|
||||||
|
Poco::Stopwatch sw;
|
||||||
|
while (runCount-- > 0)
|
||||||
|
{
|
||||||
|
sw.restart();
|
||||||
for (int i = 0; i < cnt; ++i)
|
for (int i = 0; i < cnt; ++i)
|
||||||
{
|
{
|
||||||
vresult.push_back(simple.notifyAsync(this, i));
|
vresult.push_back(simple.notifyAsync(this, i));
|
||||||
@ -373,12 +376,19 @@ void FIFOEventTest::testAsyncNotifyBenchmark()
|
|||||||
vresult[i].wait();
|
vresult[i].wait();
|
||||||
assertTrue (vresult[i].data() == (i*2));
|
assertTrue (vresult[i].data() == (i*2));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
sw.stop();
|
sw.stop();
|
||||||
std::cout << "notify and wait time = " << sw.elapsed() / 1000 << std::endl;
|
times.push_back(sw.elapsed()/1000);
|
||||||
|
vresult.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
assertTrue (_count == allCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void FIFOEventTest::onVoid(const void* pSender)
|
void FIFOEventTest::onVoid(const void* pSender)
|
||||||
{
|
{
|
||||||
_count++;
|
_count++;
|
||||||
@ -482,6 +492,6 @@ CppUnit::Test* FIFOEventTest::suite()
|
|||||||
CppUnit_addTest(pSuite, FIFOEventTest, testExpireReRegister);
|
CppUnit_addTest(pSuite, FIFOEventTest, testExpireReRegister);
|
||||||
CppUnit_addTest(pSuite, FIFOEventTest, testOverwriteDelegate);
|
CppUnit_addTest(pSuite, FIFOEventTest, testOverwriteDelegate);
|
||||||
CppUnit_addTest(pSuite, FIFOEventTest, testAsyncNotify);
|
CppUnit_addTest(pSuite, FIFOEventTest, testAsyncNotify);
|
||||||
CppUnit_addTest(pSuite, FIFOEventTest, testAsyncNotifyBenchmark);
|
//CppUnit_addTest(pSuite, FIFOEventTest, testAsyncNotifyBenchmark);
|
||||||
return pSuite;
|
return pSuite;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "Poco/Observer.h"
|
#include "Poco/Observer.h"
|
||||||
#include "Poco/Exception.h"
|
#include "Poco/Exception.h"
|
||||||
#include "Poco/AutoPtr.h"
|
#include "Poco/AutoPtr.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
using Poco::TaskManager;
|
using Poco::TaskManager;
|
||||||
@ -51,12 +52,14 @@ namespace
|
|||||||
public:
|
public:
|
||||||
TestTask():
|
TestTask():
|
||||||
Task("TestTask"),
|
Task("TestTask"),
|
||||||
_fail(false)
|
_fail(false),
|
||||||
|
_started(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void runTask()
|
void runTask()
|
||||||
{
|
{
|
||||||
|
_started = true;
|
||||||
_event.wait();
|
_event.wait();
|
||||||
setProgress(0.5);
|
setProgress(0.5);
|
||||||
_event.wait();
|
_event.wait();
|
||||||
@ -78,9 +81,15 @@ namespace
|
|||||||
_event.set();
|
_event.set();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool started() const
|
||||||
|
{
|
||||||
|
return _started;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Event _event;
|
Event _event;
|
||||||
bool _fail;
|
std::atomic<bool> _fail;
|
||||||
|
std::atomic<bool> _started;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SimpleTask: public Task
|
class SimpleTask: public Task
|
||||||
@ -277,6 +286,11 @@ void TaskManagerTest::testFinish()
|
|||||||
assertTrue (!to.error());
|
assertTrue (!to.error());
|
||||||
tm.cancelAll();
|
tm.cancelAll();
|
||||||
tm.joinAll();
|
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());
|
assertTrue (!to.error());
|
||||||
tm.cancelAll();
|
tm.cancelAll();
|
||||||
tm.joinAll();
|
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, TaskFinishedNotification>(to, &TaskObserver::taskFinished));
|
||||||
tm.addObserver(Observer<TaskObserver, TaskProgressNotification>(to, &TaskObserver::taskProgress));
|
tm.addObserver(Observer<TaskObserver, TaskProgressNotification>(to, &TaskObserver::taskProgress));
|
||||||
AutoPtr<TestTask> pTT = new TestTask;
|
AutoPtr<TestTask> pTT = new TestTask;
|
||||||
tm.start(pTT.duplicate());
|
assertTrue (tm.start(pTT.duplicate()));
|
||||||
while (pTT->state() < Task::TASK_RUNNING) Thread::sleep(50);
|
while (pTT->state() < Task::TASK_RUNNING) Thread::sleep(50);
|
||||||
assertTrue (pTT->progress() == 0);
|
assertTrue (pTT->progress() == 0);
|
||||||
Thread::sleep(200);
|
Thread::sleep(200);
|
||||||
@ -356,6 +375,11 @@ void TaskManagerTest::testError()
|
|||||||
assertTrue (list.empty());
|
assertTrue (list.empty());
|
||||||
tm.cancelAll();
|
tm.cancelAll();
|
||||||
tm.joinAll();
|
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()
|
void TaskManagerTest::testMultiTasks()
|
||||||
{
|
{
|
||||||
TaskManager tm;
|
TaskManager tm;
|
||||||
tm.start(new SimpleTask);
|
|
||||||
tm.start(new SimpleTask);
|
AutoPtr<SimpleTask> pTT1 = new SimpleTask;
|
||||||
tm.start(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();
|
TaskManager::TaskList list = tm.taskList();
|
||||||
assertTrue (list.size() == 3);
|
assertTrue (list.size() == 3);
|
||||||
@ -457,6 +514,13 @@ void TaskManagerTest::testMultiTasks()
|
|||||||
while (tm.count() > 0) Thread::sleep(100);
|
while (tm.count() > 0) Thread::sleep(100);
|
||||||
assertTrue (tm.count() == 0);
|
assertTrue (tm.count() == 0);
|
||||||
tm.joinAll();
|
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, testFinish);
|
||||||
CppUnit_addTest(pSuite, TaskManagerTest, testCancel);
|
CppUnit_addTest(pSuite, TaskManagerTest, testCancel);
|
||||||
CppUnit_addTest(pSuite, TaskManagerTest, testError);
|
CppUnit_addTest(pSuite, TaskManagerTest, testError);
|
||||||
|
CppUnit_addTest(pSuite, TaskManagerTest, testCancelNoStart);
|
||||||
CppUnit_addTest(pSuite, TaskManagerTest, testMultiTasks);
|
CppUnit_addTest(pSuite, TaskManagerTest, testMultiTasks);
|
||||||
CppUnit_addTest(pSuite, TaskManagerTest, testCustom);
|
CppUnit_addTest(pSuite, TaskManagerTest, testCustom);
|
||||||
CppUnit_addTest(pSuite, TaskManagerTest, testCustomThreadPool);
|
CppUnit_addTest(pSuite, TaskManagerTest, testCustomThreadPool);
|
||||||
|
@ -33,6 +33,7 @@ public:
|
|||||||
void testFinish();
|
void testFinish();
|
||||||
void testCancel();
|
void testCancel();
|
||||||
void testError();
|
void testError();
|
||||||
|
void testCancelNoStart();
|
||||||
void testCustom();
|
void testCustom();
|
||||||
void testMultiTasks();
|
void testMultiTasks();
|
||||||
void testCustomThreadPool();
|
void testCustomThreadPool();
|
||||||
|
@ -29,12 +29,14 @@ namespace
|
|||||||
class TestTask: public Task
|
class TestTask: public Task
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TestTask(): Task("TestTask")
|
TestTask(): Task("TestTask"),
|
||||||
|
_started(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void runTask()
|
void runTask()
|
||||||
{
|
{
|
||||||
|
_started = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_event.wait();
|
_event.wait();
|
||||||
@ -73,8 +75,14 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool started() const
|
||||||
|
{
|
||||||
|
return _started;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Event _event;
|
Event _event;
|
||||||
|
std::atomic<bool> _started;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +112,20 @@ void TaskTest::testFinish()
|
|||||||
pTT->cont();
|
pTT->cont();
|
||||||
thr.join();
|
thr.join();
|
||||||
assertTrue (pTT->state() == Task::TASK_FINISHED);
|
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()
|
void TaskTest::setUp()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -159,6 +196,7 @@ CppUnit::Test* TaskTest::suite()
|
|||||||
CppUnit_addTest(pSuite, TaskTest, testFinish);
|
CppUnit_addTest(pSuite, TaskTest, testFinish);
|
||||||
CppUnit_addTest(pSuite, TaskTest, testCancel1);
|
CppUnit_addTest(pSuite, TaskTest, testCancel1);
|
||||||
CppUnit_addTest(pSuite, TaskTest, testCancel2);
|
CppUnit_addTest(pSuite, TaskTest, testCancel2);
|
||||||
|
CppUnit_addTest(pSuite, TaskTest, testCancelNoStart);
|
||||||
|
|
||||||
return pSuite;
|
return pSuite;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ public:
|
|||||||
void testFinish();
|
void testFinish();
|
||||||
void testCancel1();
|
void testCancel1();
|
||||||
void testCancel2();
|
void testCancel2();
|
||||||
|
void testCancelNoStart();
|
||||||
|
|
||||||
void setUp();
|
void setUp();
|
||||||
void tearDown();
|
void tearDown();
|
||||||
|
@ -7,11 +7,15 @@
|
|||||||
# to clean and rebuild a single library, with all of its dependencies,
|
# to clean and rebuild a single library, with all of its dependencies,
|
||||||
# and run the tests.
|
# 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
|
# Example: ./runLibTests.sh Data/SQLite address
|
||||||
# (distcleans, rebuilds and runs tests for Data/SQLite with address sanitizer)
|
# (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
|
# g++ does not like empty quoted arguments, but
|
||||||
# the shellcheck wants them quoted to remain quiet
|
# the shellcheck wants them quoted to remain quiet
|
||||||
@ -20,7 +24,7 @@
|
|||||||
path=$1
|
path=$1
|
||||||
if [ -z "${path}" ]; then
|
if [ -z "${path}" ]; then
|
||||||
echo "Library not specified"
|
echo "Library not specified"
|
||||||
echo "Usage: $0 path [address | undefined | thread ]"
|
echo "Usage: $0 path [address | undefined | thread] [<test> | -all | none]"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -52,13 +56,20 @@ make distclean -C "$basedir"/CppUnit
|
|||||||
make -s -j4 -C "$basedir"/Foundation $flags
|
make -s -j4 -C "$basedir"/Foundation $flags
|
||||||
make -s -j4 -C "$basedir"/CppUnit $flags
|
make -s -j4 -C "$basedir"/CppUnit $flags
|
||||||
|
|
||||||
|
test=$3
|
||||||
|
if [ -z "${test}" ]; then
|
||||||
|
test="-all"
|
||||||
|
fi
|
||||||
|
|
||||||
# Foundation requested, build/run tests and exit
|
# Foundation requested, build/run tests and exit
|
||||||
if [[ "$path" == "$basedir"/"Foundation" ]]; then
|
if [[ "$path" == "$basedir"/"Foundation" ]]; then
|
||||||
cd "$path/testsuite/" || exit
|
cd "$path/testsuite/" || exit
|
||||||
make -s -j4 -C ./ $flags
|
make -s -j4 -C ./ $flags
|
||||||
cd "bin/$OSNAME/$OSARCH/" || exit
|
cd "bin/$OSNAME/$OSARCH/" || exit
|
||||||
./testrunner -all
|
if [[ "$test" != "none" ]]; then
|
||||||
./testrunnerd -all
|
./testrunner "${test}"
|
||||||
|
./testrunnerd "${test}"
|
||||||
|
fi
|
||||||
echo "$path $flags done."
|
echo "$path $flags done."
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
@ -72,8 +83,10 @@ do
|
|||||||
make distclean
|
make distclean
|
||||||
make -s -j4 -C ./ $flags
|
make -s -j4 -C ./ $flags
|
||||||
cd bin/"$OSNAME"/"$OSARCH"/ || exit
|
cd bin/"$OSNAME"/"$OSARCH"/ || exit
|
||||||
./testrunner -all
|
if [[ "$test" != "none" ]]; then
|
||||||
./testrunnerd -all
|
./testrunner "${test}"
|
||||||
|
./testrunnerd "${test}"
|
||||||
|
fi
|
||||||
echo "$1 $flags done."
|
echo "$1 $flags done."
|
||||||
cd ../../../../ || exit
|
cd ../../../../ || exit
|
||||||
done
|
done
|
||||||
|
Loading…
Reference in New Issue
Block a user