As follow-up to the rework of thread-handling, likewise also the implementation base for locking was switched over from direct usage of POSIX primitives to the portable wrappers available in the C++ standard library. All usages have been reviewed and modernised to prefer λ-functions where possible. With this series of changes, the old threadpool implementation and a lot of further low-level support facilities are not used any more and can be dismantled. Due to the integration efforts spurred by the »Playback Vertical Slice«, several questions of architecture could be decided over the last months. The design of the Scheduler and Engine turned out different than previously anticipated; notably the Scheduler now covers a wider array of functionality, including some asynchronous messaging. This has ramifications for the organisation of work tasks and threads, and leads to a more deterministic memory management. Resource management will be done on a higher level, partially superseding some of the concepts from the early phase of the Lumiera project.
149 lines
4.4 KiB
C++
149 lines
4.4 KiB
C++
/*
|
|
SyncLocking(Test) - check the monitor object based locking
|
|
|
|
Copyright (C) Lumiera.org
|
|
2008, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of
|
|
the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
* *****************************************************/
|
|
|
|
/** @file sync-locking-test.cpp
|
|
** unit test \ref SyncLocking_test
|
|
*/
|
|
|
|
|
|
#include "lib/test/run.hpp"
|
|
|
|
#include "lib/sync.hpp"
|
|
#include "lib/thread.hpp"
|
|
#include "lib/iter-explorer.hpp"
|
|
#include "lib/scoped-collection.hpp"
|
|
|
|
using test::Test;
|
|
using lib::explore;
|
|
using std::this_thread::yield;
|
|
using std::this_thread::sleep_for;
|
|
using std::chrono_literals::operator ""us;
|
|
|
|
|
|
namespace lib {
|
|
namespace test{
|
|
|
|
namespace { // private test classes and data...
|
|
|
|
const uint NUM_THREADS = 200;
|
|
const uint MAX_RAND_SUMMAND = 100;
|
|
|
|
|
|
|
|
/** Helper to verify a contended chain calculation */
|
|
template<class POLICY>
|
|
class Checker
|
|
: public Sync<POLICY>
|
|
{
|
|
size_t hot_sum_{0};
|
|
size_t control_sum_{0};
|
|
|
|
using Lock = typename Sync<POLICY>::Lock;
|
|
|
|
public:
|
|
bool
|
|
verify() ///< verify test values got handled and accounted
|
|
{
|
|
Lock guard{this};
|
|
return 0 < hot_sum_
|
|
and control_sum_ == hot_sum_;
|
|
}
|
|
|
|
uint
|
|
createVal() ///< generating test values, remembering the control sum
|
|
{
|
|
uint val{rand() % MAX_RAND_SUMMAND};
|
|
control_sum_ += val;
|
|
return val;
|
|
}
|
|
|
|
void
|
|
addValues (uint a, uint b) ///< to be called concurrently
|
|
{
|
|
Lock guard{this};
|
|
|
|
hot_sum_ *= 2;
|
|
sleep_for (200us); // force preemption
|
|
hot_sum_ += 2 * (a+b);
|
|
sleep_for (200us);
|
|
hot_sum_ /= 2;
|
|
}
|
|
};
|
|
}// (End) test classes and data....
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************//**
|
|
* @test verify the object monitor provides locking and to prevent
|
|
* data corruption on concurrent modification of shared storage.
|
|
* - use a chained calculation with deliberate sleep state
|
|
* while holding onto an intermediary result
|
|
* - run this calculation contended by a huge number of threads
|
|
* - either use locking or no locking
|
|
* @see sync.happ
|
|
* @see thread.hpp
|
|
*/
|
|
class SyncLocking_test : public Test
|
|
{
|
|
|
|
virtual void
|
|
run (Arg)
|
|
{
|
|
CHECK (can_calc_without_Error<NonrecursiveLock_NoWait>());
|
|
CHECK (can_calc_without_Error<RecursiveLock_NoWait>());
|
|
CHECK (not can_calc_without_Error<sync::NoLocking>());
|
|
}
|
|
|
|
|
|
template<class POLICY>
|
|
bool
|
|
can_calc_without_Error()
|
|
{
|
|
Checker<POLICY> checksum; // shared variable used by multiple threads
|
|
|
|
lib::ScopedCollection<Thread> threads{NUM_THREADS};
|
|
for (uint i=1; i<=NUM_THREADS; ++i)
|
|
threads.emplace ([&checksum, // Note: added values prepared in main thread
|
|
a = checksum.createVal(),
|
|
b = checksum.createVal()]
|
|
{
|
|
checksum.addValues (a,b);
|
|
});
|
|
|
|
while (explore(threads).has_any())
|
|
yield(); // wait for all threads to terminate
|
|
|
|
return checksum.verify();
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/** Register this test class... */
|
|
LAUNCHER (SyncLocking_test, "function common");
|
|
|
|
|
|
|
|
}} // namespace lib::test
|