lumiera_/tests/library/sync-locking-test.cpp
Ichthyostega 3af6a54219 Library/Application: complete technology switch (closes #1279)
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.
2023-10-16 01:44:04 +02:00

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