Refactoring V: get lifecycle of a class-based monitor correct.
This commit is contained in:
parent
2173698e75
commit
bd2ead37d5
3 changed files with 172 additions and 20 deletions
|
|
@ -27,6 +27,8 @@
|
|||
** being problematic in conjunction with static startup/shutdown, doing so is sometimes
|
||||
** necessary to setup type based dispatcher tables, managing singleton creation etc.
|
||||
**
|
||||
** @note simply using the ClassLock may cause a Monitor object (with a mutex) to be
|
||||
** created at static initialisation and destroyed on application shutdown.
|
||||
** @see singletonfactory.hpp usage example
|
||||
*/
|
||||
|
||||
|
|
@ -39,31 +41,73 @@
|
|||
|
||||
namespace lib {
|
||||
|
||||
namespace { // implementation details
|
||||
|
||||
template<class X>
|
||||
struct NiftyHolder
|
||||
{
|
||||
static uint accessed_;
|
||||
static char content_[sizeof(X)];
|
||||
|
||||
NiftyHolder()
|
||||
{
|
||||
if (!accessed_)
|
||||
new(content_) X();
|
||||
++accessed_;
|
||||
}
|
||||
|
||||
~NiftyHolder()
|
||||
{
|
||||
--accessed_;
|
||||
if (0==accessed_)
|
||||
get().~X();
|
||||
}
|
||||
|
||||
X&
|
||||
get()
|
||||
{
|
||||
X* obj = reinterpret_cast<X*> (&content_);
|
||||
ASSERT (obj, "Logic of Schwartz counter broken.");
|
||||
return *obj;
|
||||
}
|
||||
};
|
||||
|
||||
template<class X>
|
||||
uint NiftyHolder<X>::accessed_;
|
||||
|
||||
template<class X>
|
||||
char NiftyHolder<X>::content_[sizeof(X)];
|
||||
|
||||
} // (End) implementation details
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A synchronisation protection guard employing a lock scoped
|
||||
* to the parameter type as a whole, not an individual instance.
|
||||
* After creating an instance, every other access specifying the same
|
||||
* type is blocked.
|
||||
* @warn beware of recursion when using a nonrecursive Mutex
|
||||
* @see Sync::Lock the usual simple instance-bound variant
|
||||
*/
|
||||
template<class X, class CONF = NonrecursiveLock_NoWait>
|
||||
class ClassLock
|
||||
: public Sync<CONF>::Lock
|
||||
{
|
||||
typedef typename Sync<CONF>::Lock Lock;
|
||||
typedef typename Sync<CONF>::Monitor Monitor;
|
||||
typedef typename sync::Monitor<CONF> Monitor;
|
||||
|
||||
|
||||
static Monitor&
|
||||
getMonitor()
|
||||
Monitor&
|
||||
getPerClassMonitor()
|
||||
{
|
||||
//TODO: a rather obscure race condition is hidden here:
|
||||
//TODO: depending on the build order, the dtor of this static variable may be called, while another thread is still holding an ClassLock.
|
||||
//TODO: An possible solution would be to use an shared_ptr to the Monitor in case of a ClassLock and to protect access with another Mutex.
|
||||
//TODO. But I am really questioning if we can't ignore this case and state: "don't hold a ClassLock when your code maybe still running in shutdown phase!"
|
||||
//TODO: probably best Idea is to detect this situation in DEBUG or ALPHA mode
|
||||
|
||||
static scoped_ptr<Monitor> classMonitor_ (0);
|
||||
if (!classMonitor_) classMonitor_.reset (new Monitor ());
|
||||
return *classMonitor_;
|
||||
static NiftyHolder<Monitor> __used_here;
|
||||
return __used_here.get();
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
ClassLock() : Lock (getMonitor()) {}
|
||||
ClassLock() : Lock (getPerClassMonitor()) {}
|
||||
|
||||
uint use_count() { return NiftyHolder<Monitor>::accessed_; }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -158,11 +158,6 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
PLANNED "Multithread Locking by Monitor" ConcurrencyLocking_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "CustomSharedPtr_test" CustomSharedPtr_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
|
@ -322,6 +317,21 @@ PLANNED "StreamTypeLifecycle_test" StreamTypeLifecycle_test <<END
|
|||
END
|
||||
|
||||
|
||||
TEST "Multithread Locking by Monitor" SyncLocking_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Multithread Locking whole class" SyncClasslock_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Wait/Notify on Object Monitor" SyncWaiting_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "TestOption_test" TestOption_test <<END
|
||||
out: Testing invocation with cmdline: ...
|
||||
out: --> Testgroup=ALL
|
||||
|
|
|
|||
98
tests/lib/sync-classlock-test.cpp
Normal file
98
tests/lib/sync-classlock-test.cpp
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
SyncClasslock(Test) - validate the type-based Monitor 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.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "include/error.hpp"
|
||||
|
||||
#include "lib/sync-classlock.hpp"
|
||||
|
||||
//#include <iostream>
|
||||
|
||||
//using std::cout;
|
||||
using test::Test;
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test {
|
||||
|
||||
namespace { // private test classes and data...
|
||||
|
||||
const uint NUM_INSTANCES = 20; ///< number of probe instances to create
|
||||
|
||||
|
||||
/**
|
||||
* instances of this probe class will be created statically.
|
||||
* They utilise the class-based locking within ctor and dtor
|
||||
*/
|
||||
struct Probe
|
||||
{
|
||||
Probe() { ClassLock<Probe> ctor_lock; }
|
||||
~Probe() { ClassLock<Probe> dtor_lock; }
|
||||
};
|
||||
|
||||
|
||||
} // (End) test classes and data....
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* @test check proper handling of class (not instance)-based Monitor locks.
|
||||
* Because no instance is available in this case, a hidden storage for the
|
||||
* Monitor object needs to be provided in a way safe for use even in the
|
||||
* static startup/shutdown phase. This test validates the associated
|
||||
* refcounting and object creation works as expected. It does \em not
|
||||
* validate the locking functionality as such.
|
||||
*
|
||||
* @see sync.hpp
|
||||
*/
|
||||
class SyncClasslock_test : public Test
|
||||
{
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
static Probe objs[NUM_INSTANCES];
|
||||
|
||||
ClassLock<Probe> get_class_lock;
|
||||
ASSERT ( 1 == get_class_lock.use_count()); // ClassLock<Probe> got created exactly once
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (SyncClasslock_test, "unit common");
|
||||
|
||||
|
||||
|
||||
} // namespace test
|
||||
|
||||
} // namespace lumiera
|
||||
Loading…
Reference in a new issue