Refactoring V: get lifecycle of a class-based monitor correct.

This commit is contained in:
Fischlurch 2008-12-26 05:44:49 +01:00
parent 2173698e75
commit bd2ead37d5
3 changed files with 172 additions and 20 deletions

View file

@ -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_; }
};

View file

@ -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

View 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