Merge branch 'master' of git.lumiera.org:/git/LUMIERA into join

Conflicts:

	src/gui/Makefile.am
This commit is contained in:
Joel Holdsworth 2009-01-11 00:15:01 +00:00
commit fade287745
66 changed files with 1876 additions and 461 deletions

View file

@ -61,6 +61,6 @@ noinst_HEADERS += \
$(liblumieracommon_la_srcdir)/subsys.hpp \
$(liblumieracommon_la_srcdir)/appstate.hpp \
$(liblumieracommon_la_srcdir)/option.hpp \
$(liblumieracommon_la_srcdir)/subsystemrunner.hpp \
$(liblumieracommon_la_srcdir)/subsystem-runner.hpp \
$(liblumieracommon_la_srcdir)/instancehandle.hpp

View file

@ -25,7 +25,7 @@
#include "include/lifecycle.h"
#include "common/appstate.hpp"
#include "lib/lifecycleregistry.hpp"
#include "common/subsystemrunner.hpp"
#include "common/subsystem-runner.hpp"
extern "C" {
#include "common/config_interface.h"

View file

@ -23,9 +23,11 @@
#include "gui/guifacade.hpp"
#include "include/guinotificationfacade.h"
#include "lib/sync.hpp"
#include "lib/error.hpp"
#include "lib/singleton.hpp"
#include "lib/functorutil.hpp"
#include "lib/thread-wrapper.hpp"
#include "common/instancehandle.hpp"
#include <boost/scoped_ptr.hpp>
@ -42,6 +44,9 @@ namespace gui {
using lumiera::Subsys;
using lumiera::InstanceHandle;
using util::dispatchSequenced;
using lib::Sync;
using lib::RecursiveLock_NoWait;
using lib::Thread;
@ -57,17 +62,22 @@ namespace gui {
: theGUI_("lumieraorg_Gui", 1, 1, "lumieraorg_GuiStarterPlugin") // load GuiStarterPlugin
{
ASSERT (theGUI_);
if (!kickOff (terminationHandle))
Thread ("GUI-Main", bind (&GuiRunner::kickOff, this, terminationHandle));
if (lumiera_error_peek())
throw lumiera::error::Fatal("failed to bring up GUI",lumiera_error());
///////////////////////////////////////////////////////TODO: just a test to verify the GuiNotification facade is properly opened
GuiNotification::facade().displayInfo("Test-Notification message pushed to GUI!!!!");
///////////////////////////////////////////////////////TODO: just a test to verify the GuiNotification facade is properly opened
}
~GuiRunner () { }
bool kickOff (Subsys::SigTerm& terminationHandle)
void kickOff (Subsys::SigTerm& terminationHandle)
{
return theGUI_->kickOff (reinterpret_cast<void*> (&terminationHandle))
&& !lumiera_error_peek();
theGUI_->kickOff (reinterpret_cast<void*> (&terminationHandle));
}
};
@ -78,8 +88,10 @@ namespace gui {
scoped_ptr<GuiRunner> facade (0);
class GuiSubsysDescriptor
: public lumiera::Subsys
: public lumiera::Subsys,
public Sync<RecursiveLock_NoWait>
{
operator string () const { return "Lumiera GTK GUI"; }
@ -98,7 +110,7 @@ namespace gui {
bool
start (lumiera::Option&, Subsys::SigTerm termination)
{
//Lock guard (*this);
Lock guard (this);
if (facade) return false; // already started
facade.reset (
@ -119,14 +131,14 @@ namespace gui {
bool
checkRunningState () throw()
{
//Lock guard (*this);
return (facade);
}
void
closeGuiModule (lumiera::Error *)
closeGuiModule (std::string *)
{
//Lock guard (*this);
Lock guard (this);
if (!facade)
{
WARN (operate, "Termination signal invoked, but GUI is currently closed. "

View file

@ -31,7 +31,9 @@
**
** @see gui::GuiFacade usage example
** @see interface.h
** @see interfaceproxy.cpp
** @see interfaceproxy.hpp (more explanations)
** @see interfaceproxy.cpp (Implementation of the proxies)
**
*/
@ -40,6 +42,8 @@
#include "include/nobugcfg.h"
#include "lib/error.hpp"
#include "include/interfaceproxy.hpp"
extern "C" {
#include "common/interface.h"
@ -47,7 +51,6 @@ extern "C" {
}
#include <boost/noncopyable.hpp>
//#include <boost/scoped_ptr.hpp>
#include <string>
@ -55,25 +58,36 @@ extern "C" {
namespace lumiera {
using std::string;
// using boost::scoped_ptr;
template<class I, class FA>
class InstanceHandle;
namespace { // implementation details
void
throwIfError()
{
if (lumiera_error_peek())
throw lumiera::error::Config("failed to open interface or plugin.",lumiera_error());
}
/** takes a bunch of instance definitions, as typically created
* when defining interfaces for external use, and registers them
/** takes a (single) instance definitions, as typically created
* when defining interfaces for external use, and registers it
* with the InterfaceSystem. Then uses the data found in the
* \em first descriptor to open an instance handle.
* \em given instance descriptor to open an instance handle.
* @throws error::Config when the registration process fails
*/
LumieraInterface
register_and_open (LumieraInterface* descriptors)
register_and_open (LumieraInterface descriptor)
{
if (!descriptors) return NULL;
lumiera_interfaceregistry_bulkregister_interfaces (descriptors, NULL);
LumieraInterface masterI = descriptors[0];
return lumiera_interface_open (masterI->interface,
masterI->version,
masterI->size,
masterI->name);
if (!descriptor) return NULL;
lumiera_interfaceregistry_register_interface (descriptor, NULL);
throwIfError();
return lumiera_interface_open (descriptor->interface,
descriptor->version,
descriptor->size,
descriptor->name);
}
/** do a lookup within the interfaceregistry
@ -87,8 +101,62 @@ namespace lumiera {
ifa->version,
ifa->name));
}
} // (End) impl details
namespace facade {
/**
* @internal Helper/Adapter for establishing a link
* between an InstanceHandle and a facade interface,
* which is going to be implemented through the given
* interface/plugin. This way, creating the InstanceHandle
* automatically creates a lumiera::facade::Proxy, to route
* any facade calls through the interface/plugin. Similarly,
* when destroying the InstanceHandle, the proxy will be closed.
*/
template<class I, class FA>
struct Link
: boost::noncopyable
{
typedef InstanceHandle<I,FA> IH;
Link (IH const& iha) { facade::openProxy(iha); }
~Link() { facade::closeProxy<IH>(); }
FA&
operator() (IH const&) const
{
return facade::Accessor<FA>()();
}
};
/**
* @internal when the InstanceHandle isn't associated with a
* facade interface, then this specialisation switches
* the facade::Link into "NOP" mode.
*/
template<class I>
struct Link<I,I>
: boost::noncopyable
{
typedef InstanceHandle<I,I> IH;
Link (IH const&) { /* NOP */ }
~Link() { /* NOP */ }
I&
operator() (IH const& handle) const
{
return handle.get();
}
};
} // namespace facade (impl details)
@ -96,8 +164,11 @@ namespace lumiera {
* Handle tracking the registration of an interface, deregistering it on deletion.
* Depending on which flavour of the ctor is used, either (bulk) registration of interfaces
* or plugin loading is triggered. The interface type is defined by type parameter.
* @todo when provided with the type of an facade interface class, care for enabling/disabling
* access through the facade proxy singleton when opening/closing the registration.
* Additionally, choosing a facade interface as second type parameter causes installation
* of a proxy, which implements the facade by routing calls through the basic interface
* represented by this handle. This proxy will be "closed" automatically when this
* InstanceHandle goes out of scope. Of course, the proxy needs to be implemented
* somewhere, typically in interfaceproxy.cpp
*/
template< class I ///< fully mangled name of the interface type
, class FA = I ///< facade interface type to be used by clients
@ -105,8 +176,9 @@ namespace lumiera {
class InstanceHandle
: private boost::noncopyable
{
LumieraInterface* desc_;
LumieraInterface desc_;
I* instance_;
facade::Link<I,FA> facadeLink_;
typedef InstanceHandle<I,FA> _ThisType;
@ -119,37 +191,42 @@ namespace lumiera {
* @param impName unmangled name of the instance (implementation)
*/
InstanceHandle (string const& iName, uint version, size_t minminor, string const& impName)
: desc_(0),
instance_(reinterpret_cast<I*>
: desc_(0)
, instance_(reinterpret_cast<I*>
(lumiera_interface_open (iName.c_str(), version, minminor, impName.c_str())))
{ }
, facadeLink_(*this)
{
throwIfError();
}
/** Set up an InstanceHandle managing the
* registration and deregistration of interface(s).
* Should be placed at the service providing side.
* @param descriptors zero terminated array of interface descriptors,
* usually available through lumiera_plugin_interfaces()
* @param a (single) interface descriptor, which can be created with
* LUMIERA_INTERFACE_INSTANCE and referred to by LUMIERA_INTERFACE_REF
*/
InstanceHandle (LumieraInterface* descriptors)
: desc_(descriptors),
instance_(reinterpret_cast<I*> (register_and_open (desc_)))
{ }
InstanceHandle (LumieraInterface descriptor)
: desc_(descriptor)
, instance_(reinterpret_cast<I*> (register_and_open (desc_)))
, facadeLink_(*this)
{
throwIfError();
}
~InstanceHandle ()
{
lumiera_interface_close (&instance_->interface_header_);
if (desc_)
lumiera_interfaceregistry_bulkremove_interfaces (desc_);
lumiera_interfaceregistry_remove_interface (desc_);
}
/** act as smart pointer providing access through the facade.
* @todo implement the case where the Facade differs from I
* @note we don't provide operator* */
FA * operator-> () const { return accessFacade(); }
FA * operator-> () const { return &(facadeLink_(*this)); }
/** directly access the instance via the CLI interface */
/** directly access the instance via the CL interface */
I& get () const { ENSURE(instance_); return *instance_; }
@ -164,13 +241,6 @@ namespace lumiera {
private:
FA *
accessFacade() const
{
ENSURE (instance_);
return static_cast<FA *> (instance_); /////////////////TODO: actually handle the case when the facade differs from the interface by using the proxy
}
bool
isValid() const
{

View file

@ -21,65 +21,118 @@
* *****************************************************/
#include "lib/singletonsubclass.hpp"
#include "include/guinotificationfacade.h"
#include "lib/util.hpp"
#include "include/interfaceproxy.hpp"
#include "common/instancehandle.hpp"
#include "lib/error.hpp"
extern "C" {
#include "common/interface.h"
}
#include "lib/util.hpp"
using util::cStr;
namespace singleton = lumiera::singleton;
#include "include/guinotificationfacade.h"
namespace gui {
class GuiNotificationInterfaceProxy
: public GuiNotification
{
LUMIERA_INTERFACE_INAME(lumieraorg_GuiNotification, 1) * interface_;
GuiNotificationInterfaceProxy ()
{
interface_ = LUMIERA_INTERFACE_OPEN (lumieraorg_GuiNotification, 1, 2, lumieraorg_GuiNotificationFacade);
if (!interface_)
throw lumiera::error::State ("unable to access GuiNotificationFacade");
}
friend class singleton::StaticCreate<GuiNotificationInterfaceProxy>;
/* ======== forwarding through interface ========== */
void
displayInfo (string const& text)
{
interface_->displayInfo (cStr(text));
}
void
triggerGuiShutdown (string const& cause)
{
interface_->triggerGuiShutdown (cStr(cause));
}
};
namespace {
singleton::UseSubclass<GuiNotificationInterfaceProxy> typeinfo_proxyInstance_to_create;
}
/** storage for the facade proxy factory used by client code to invoke through the interface */
lumiera::SingletonSub<GuiNotification> GuiNotification::facade (typeinfo_proxyInstance_to_create);
///////////////////////////////////////TODO: this solution is not correct, because it doesn't detect when the interface is shut down!
lumiera::facade::Accessor<GuiNotification> GuiNotification::facade;
} // namespace gui
namespace lumiera {
namespace facade {
LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade interface currently not accessible");
template<class IHA>
class Holder;
template<class FA, class I>
class Holder<InstanceHandle<I,FA> >
: Accessor<FA>,
protected FA
{
protected:
typedef InstanceHandle<I,FA> IHandle;
typedef Holder<IHandle> THolder;
typedef Proxy<IHandle> TProxy;
typedef Accessor<FA> Access;
I& _i_;
Holder (IHandle const& iha)
: _i_(iha.get())
{ }
public:
static TProxy& open(IHandle const& iha)
{
static char buff[sizeof(TProxy)];
TProxy* p = new(buff) TProxy(iha);
Access::implProxy_ = p;
return *p;
}
static void close()
{
if (!Access::implProxy_) return;
TProxy* p = static_cast<TProxy*> (Access::implProxy_);
Access::implProxy_ = 0;
p->~TProxy();
}
};
template<class FA>
FA* Accessor<FA>::implProxy_;
template<class IHA>
void
openProxy (IHA const& iha)
{
Proxy<IHA>::open(iha);
}
template<class IHA>
void
closeProxy ()
{
Proxy<IHA>::close();
}
typedef InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_GuiNotification, 1)
, gui::GuiNotification
> Handle_GuiNotification;
template<>
class Proxy<Handle_GuiNotification>
: public Holder<Handle_GuiNotification>
{
//----Proxy-Implementation-of-GuiNotification--------
void displayInfo (string const& text) { _i_.displayInfo (cStr(text)); }
void triggerGuiShutdown (string const& cause) { _i_.triggerGuiShutdown (cStr(cause)); }
public:
Proxy (IHandle const& iha) : THolder(iha) {}
};
template void openProxy<Handle_GuiNotification> (Handle_GuiNotification const&);
template void closeProxy<Handle_GuiNotification> (void);
} // namespace facade
} // namespace lumiera

View file

@ -23,19 +23,12 @@
#include "common/subsys.hpp"
#include "lib/error.hpp"
//#include "lib/util.hpp"
//using util::isnil;
//using util::cStr;
namespace lumiera {
Subsys::~Subsys() { }
@ -43,7 +36,6 @@ namespace lumiera {
Subsys&
Subsys::depends (Subsys& prereq)
{
TODO ("anything else to care when defining a dependency on the prerequisite subsystem??");/////////////////////TODO
prereq_.push_back(&prereq);
return *this;
}
@ -53,7 +45,6 @@ namespace lumiera {
bool
Subsys::isRunning()
{
//Lock guard (this);
return checkRunningState();
}

View file

@ -65,12 +65,13 @@ namespace lumiera {
* Dependencies and lifecycle of a partially independent Subsystem of the Application.
* Using such descriptors, AppState as activated from main() is able to pull up,
* maintain and shut down the primary parts of the Application.
* @note synchronisation is up to the implementor.
*/
class Subsys
: private noncopyable
{
public:
typedef function<void(Error*)> SigTerm;
typedef function<void(string*)> SigTerm;
virtual ~Subsys();
@ -82,7 +83,8 @@ namespace lumiera {
* required for running this subsystem */
Subsys& depends (Subsys& prereq);
/** @return true if Up */
/** @return true if Up
* @warning must not block nor throw. */
bool isRunning();

View file

@ -1,5 +1,5 @@
/*
SUBSYSTEMRUNNER.hpp - helper for controlling execution of several dependant subsystems
SUBSYSTEMRUNNER.hpp - helper for controlling execution of several dependent subsystems
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
@ -31,6 +31,7 @@
#include <tr1/functional>
#include <vector>
#include <string>
namespace lumiera {
@ -39,13 +40,22 @@ namespace lumiera {
using std::tr1::function;
using std::tr1::placeholders::_1;
using std::vector;
using std::string;
using util::cStr;
using util::isnil;
using util::and_all;
using util::for_each;
using util::removeall;
using lib::Sync;
using lib::RecursiveLock_Waitable;
namespace {
/** limit waiting for subsystem shutdown in case of
* an emergency shutdown to max 2 seconds */
const uint EMERGENCYTIMEOUT = 2000;
function<bool(Subsys*)>
isRunning() {
return bind (&Subsys::isRunning, _1);
@ -76,10 +86,9 @@ namespace lumiera {
* Usually, the startup process is conducted from one (main) thread, which enters
* a blocking wait() after starting the subsystems. Awakened by some termination
* signal from one of the subsystems, termination of any remaining subsystems
* will be triggered. The wait() function returns after shutdown of all subsystems,
* will be triggered. The #wait() function returns after shutdown of all subsystems,
* signalling an emergency exit (caused by an exception) with its return value.
*
* @todo implement an object monitor primitive based on a mutex and a condition. Inherit from this privately and create local instances of the embedded Lock class to activate the locking and wait/notify
* @todo maybe use my refArray (see builder) to use Subsys& instead of Subsys* ??
*
* @see lumiera::AppState
@ -87,7 +96,7 @@ namespace lumiera {
* @see main.cpp
*/
class SubsystemRunner
// : Sync
: public Sync<RecursiveLock_Waitable>
{
Option& opts_;
volatile bool emergency_;
@ -109,48 +118,57 @@ namespace lumiera {
void
maybeRun (Subsys& susy)
{
//Lock guard (this);
Lock guard (this);
if (!susy.isRunning() && susy.shouldStart (opts_))
triggerStartup (&susy);
if (susy.isRunning())
running_.push_back (&susy);
}
void
shutdownAll ()
{
//Lock guard (this);
Lock guard (this);
for_each (running_, killIt_);
}
void
triggerEmergency (bool cond)
{
Lock guard (this);
if (cond) emergency_= true;
}
bool
wait ()
{
//Lock(this).wait (&SubsystemRunner::allDead);
Lock wait_blocking(this, &SubsystemRunner::allDead);
return isEmergencyExit();
}
bool isEmergencyExit () { return emergency_; }
void triggerEmergency (bool cond) { emergency_ |= cond; }
private:
bool isEmergencyExit () { return emergency_; }
void
triggerStartup (Subsys* susy)
{
ASSERT (susy);
INFO (operate, "Starting subsystem \"%s\"", cStr(*susy));
if (susy->isRunning()) return;
INFO (operate, "Triggering startup of subsystem \"%s\"", cStr(*susy));
for_each (susy->getPrerequisites(), start_);
bool started = susy->start (opts_, bind (&SubsystemRunner::sigTerm, this, susy, _1));
if (started && !susy->isRunning())
if (started)
{
throw error::Logic("Subsystem "+string(*susy)+" failed to start");
if (susy->isRunning())
running_.push_back (susy); // now responsible for managing the started subsystem
else
throw error::Logic("Subsystem "+string(*susy)+" failed to start");
}
if (!and_all (susy->getPrerequisites(), isRunning() ))
{
susy->triggerShutdown();
@ -158,23 +176,29 @@ namespace lumiera {
} }
void
sigTerm (Subsys* susy, Error* problem) ///< called from subsystem on termination
sigTerm (Subsys* susy, string* problem) ///< called from subsystem on termination
{
ASSERT (susy);
//Lock guard (this);
triggerEmergency(problem);
Lock sync (this);
triggerEmergency(!isnil (problem));
INFO (operate, "Subsystem '%s' terminated.", cStr(*susy));
WARN_IF (!isnil(problem), operate, "Irregular shutdown caused by: %s", cStr(*problem));
ERROR_IF (susy->isRunning(), lumiera, "Subsystem '%s' signals termination, "
"without resetting running state", cStr(*susy));
removeall (running_, susy);
shutdownAll();
//guard.notify();
sync.notify();
}
bool
allDead ()
{
if (isEmergencyExit())
; //Lock(this).setTimeout(EMERGENCYTIMEOUT);
{
Lock sync(this);
if (!sync.isTimedWait())
sync.setTimeout(EMERGENCYTIMEOUT);
}
return isnil (running_); // end wait if no running subsystem left
}

View file

@ -64,7 +64,7 @@ pkglib_LTLIBRARIES += gtk_gui.la
gtk_gui_la_CFLAGS = $(AM_CFLAGS) -std=gnu99 -Wall -Wextra -Werror
gtk_gui_la_CXXFLAGS = $(AM_CXXFLAGS) -Wall -Wextra
gtk_gui_la_CPPFLAGS = $(AM_CPPFLAGS) -DLUMIERA_PLUGIN -I$(top_srcdir)/src/
gtk_gui_la_CPPFLAGS = $(AM_CPPFLAGS) $(LUMIERA_GUI_CFLAGS) -DLUMIERA_PLUGIN -I$(top_srcdir)/src/
gtk_gui_la_LDFLAGS = -module -avoid-version -no-undefined -rpath /dev/null -shrext .lum
gtk_gui_la_LIBADD = \
@ -72,10 +72,11 @@ gtk_gui_la_LIBADD = \
liblumierabackend.la \
liblumieraproc.la \
liblumiera.la \
libgui.la \
$(LUMIERA_GUI_LIBS) \
$(NOBUGMT_LUMIERA_LIBS)
gtk_gui_la_SOURCES = \
gtk_gui_la_SOURCES = \
$(lumigui_srcdir)/guistart.cpp
@ -94,18 +95,18 @@ libgui_la_CPPFLAGS = $(AM_CPPFLAGS) \
$(LUMIERA_GUI_CFLAGS)
libgui_la_SOURCES = \
$(lumigui_srcdir)/guinotificationfacade.cpp \
$(lumigui_srcdir)/notification-service.cpp \
$(lumigui_srcdir)/window-manager.cpp \
$(lumigui_srcdir)/window-manager.hpp \
$(lumigui_srcdir)/workspace/actions.cpp \
$(lumigui_srcdir)/workspace/actions.hpp \
$(lumigui_srcdir)/workspace/workspace-window.cpp \
$(lumigui_srcdir)/workspace/workspace-window.hpp \
$(lumigui_srcdir)/workspace/workspace-window.cpp \
$(lumigui_srcdir)/workspace/workspace-window.hpp \
$(lumigui_srcdir)/dialogs/dialog.hpp \
$(lumigui_srcdir)/dialogs/render.cpp \
$(lumigui_srcdir)/dialogs/render.hpp \
$(lumigui_srcdir)/dialogs/preferences-dialog.cpp \
$(lumigui_srcdir)/dialogs/preferences-dialog.hpp \
$(lumigui_srcdir)/dialogs/preferences-dialog.cpp \
$(lumigui_srcdir)/dialogs/preferences-dialog.hpp \
$(lumigui_srcdir)/dialogs/name-chooser.cpp \
$(lumigui_srcdir)/dialogs/name-chooser.hpp \
$(lumigui_srcdir)/panels/panel.cpp \
@ -152,14 +153,14 @@ libgui_la_SOURCES = \
$(lumigui_srcdir)/model/track.hpp \
$(lumigui_srcdir)/model/clip-track.cpp \
$(lumigui_srcdir)/model/clip-track.hpp \
$(lumigui_srcdir)/model/parent-track.cpp \
$(lumigui_srcdir)/model/parent-track.hpp \
$(lumigui_srcdir)/model/parent-track.cpp \
$(lumigui_srcdir)/model/parent-track.hpp \
$(lumigui_srcdir)/model/group-track.cpp \
$(lumigui_srcdir)/model/group-track.hpp \
$(lumigui_srcdir)/model/sequence.cpp \
$(lumigui_srcdir)/model/sequence.hpp \
$(lumigui_srcdir)/model/clip.cpp \
$(lumigui_srcdir)/model/clip.hpp \
$(lumigui_srcdir)/model/clip.cpp \
$(lumigui_srcdir)/model/clip.hpp \
$(lumigui_srcdir)/output/displayer.cpp \
$(lumigui_srcdir)/output/displayer.hpp \
$(lumigui_srcdir)/output/gdkdisplayer.cpp \

View file

@ -47,20 +47,14 @@ using namespace gui::model;
GtkLumiera the_application;
int
main (int argc, char *argv[])
{
return the_application.main(argc, argv);
}
namespace gui {
int
void
GtkLumiera::main(int argc, char *argv[])
{
NOBUG_INIT;
Main kit(argc, argv);
@ -75,9 +69,9 @@ GtkLumiera::main(int argc, char *argv[])
kit.run(main_window);
return 0;
}
Glib::ustring
GtkLumiera::get_home_data_path()
{
@ -109,3 +103,14 @@ const int GtkLumiera::AppAuthorCount = 4;
} // namespace gui
/**
* Run the Lumiera GTK GUI as standalone application without backend.
*/
int
main (int argc, char *argv[])
{
NOBUG_INIT;
gui::application().main(argc, argv);
return 0;
}

View file

@ -29,7 +29,7 @@
#define GTK_LUMIERA_HPP
#include <gtkmm.h>
#include <nobug.h>
#include <nobug.h> // need to include this after gtkmm.h, because types.h from GTK tries to shaddow the ERROR macro from windows, which kills NoBug's ERROR macro
#include <vector>
#include <boost/utility.hpp>
#include <boost/optional.hpp>
@ -69,7 +69,7 @@ namespace gui {
class GtkLumiera : private boost::noncopyable
{
public:
int main(int argc, char *argv[]);
void main(int argc, char *argv[]);
static Glib::ustring get_home_data_path();

View file

@ -91,7 +91,7 @@ namespace gui {
* @internal this function is invoked automatically during the GUI
* loading and startup process. Don't call it manually.
*/
virtual bool kickOff (lumiera::Subsys::SigTerm&) =0;
virtual void kickOff (lumiera::Subsys::SigTerm&) =0;
protected:
@ -100,7 +100,7 @@ namespace gui {
/** interface of the GuiStarterPlugin */
LUMIERA_INTERFACE_DECLARE (lumieraorg_Gui, 1,
LUMIERA_INTERFACE_SLOT (bool, kickOff, (void*))
LUMIERA_INTERFACE_SLOT (void, kickOff, (void*))
);

View file

@ -22,28 +22,36 @@
* *****************************************************/
/** @file guistart.cpp
** Start up the Lumiera GTK GUI when loading it as dynamic module.
** This plugin is linked together with the Lumiera GUI code; when loaded as
** Lumiera plugin, it allows to kick off the main GUI thread and thus to bring up
** the GUI. The loading and shutdown process is carried out by gui::GuiFacade and
** Lumiera plugin, it allows to kick off the GTK main event loop and thus to bring
** up the GUI. The loading and shutdown process is carried out by gui::GuiFacade and
** controlled by lumiera::AppState, which in turn is activated by Lumiera main().
**
** After successfully loading this module, a call to #kickOff is expected to be
** issued, passing a termination signal (callback) to be executed when the GUI
** terminates. This call returns immediately, after spawning off the main thread
** and setting up the termination callback accordingly. Additionally, it cares
** for opening the primary "business" interface of the GUI, i.e. the interface
** gui::GuiNotification.
** terminates. The \c kickOff() call remains blocked within the main GTK event loop;
** thus typically this call should be issued within a separate dedicated GUI thread.
** Especially, the gui::GuiRunner will ensure this to happen.
**
** Prior to entering the GTK event loop, all primary "business" interface of the GUI
** will be opened (currently as of 1/09 this is the interface gui::GuiNotification.)
**
** @see lumiera::AppState
** @see gui::GuiFacade
** @see guifacade.cpp
** @see ///////////////////////////////////TODO: add link to the gui main routine here!
** @see gui::GtkLumiera#main the GTK GUI main
*/
#include <locale> // need to include this to prevent errors when libintl.h defines textdomain (because gtk-lumiera removes the def when ENABLE_NLS isn't defined)
#include "gui/gtk-lumiera.hpp" // need to include this before nobugcfg.h, because types.h from GTK tries to shaddow the ERROR macro from windows, which kills nobug's ERROR macro
#include "include/nobugcfg.h"
#include "lib/error.hpp"
#include "gui/guifacade.hpp"
#include "gui/notification-service.hpp"
#include "common/subsys.hpp"
#include "lib/singleton.hpp"
@ -54,12 +62,9 @@ extern "C" {
#include <string>
using std::string;
#include <iostream> /////////////TODO
using std::cout; //////////////TODO
using lumiera::Subsys;
using gui::LUMIERA_INTERFACE_INAME(lumieraorg_Gui, 1);
@ -69,28 +74,61 @@ namespace gui {
namespace { // implementation details
/**
* Implement the necessary steps for starting up the GUI main thread
* Implement the necessary steps for actually making the Lumiera Gui available.
* Open the business interface(s) and start up the GTK GUI main event loop.
*/
struct GuiFacadeImpl
: public GuiFacade
struct GuiLifecycle
{
string error_;
Subsys::SigTerm& reportOnTermination_;
NotificationService activateNotificationService_;
bool kickOff (Subsys::SigTerm& terminationHandle)
GuiLifecycle (Subsys::SigTerm& terminationHandler)
: reportOnTermination_(terminationHandler)
, activateNotificationService_() // opens the GuiNotification facade interface
{ }
~GuiLifecycle ()
{
cout << " *** Ha Ha Ha\n"
<< " this is the GuiStarterPlugin speaking!\n"
<< " now, the Lumiera GUI should be spawned....\n"
<< " but actually nothing happens!!!!!!!!!!!!!!\n\n";
terminationHandle(0); // signal immediate shutdown without error
return true;
reportOnTermination_(&error_); // inform main thread that the GUI has been shut down.
}
void
run ()
{
try
{
int argc =0;
char *argv[] = {}; // dummy command line for GTK
gui::application().main(argc, argv); // execute the GTK Event Loop
if (!lumiera_error_peek())
return;
}
catch (lumiera::Error& problem)
{
error_ = problem.what();
lumiera_error(); // clear error flag
return;
}
catch (...){ }
error_ = "unexpected error terminated the GUI.";
return;
}
};
lumiera::Singleton<GuiFacadeImpl> facade_;
} // (End) impl details
void
kickOff (Subsys::SigTerm& reportTermination)
{
GuiLifecycle(reportTermination).run();
}
} // namespace gui
@ -179,10 +217,9 @@ extern "C" { /* ================== define an lumieraorg_Gui instance ===========
, NULL /* on open */
, NULL /* on close */
, LUMIERA_INTERFACE_INLINE (kickOff, "\255\142\006\244\057\170\152\312\301\372\220\323\230\026\200\065",
bool, (void* termSig),
void, (void* termSig),
{
return gui::facade_().kickOff (
*reinterpret_cast<Subsys::SigTerm *> (termSig));
gui::kickOff (*reinterpret_cast<Subsys::SigTerm *> (termSig));
}
)
)

View file

@ -1,5 +1,5 @@
/*
GuiNotificationFacade - access point for pushing informations into the GUI
NotificationService - public service allowing to push informations into the GUI
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
@ -21,7 +21,7 @@
* *****************************************************/
#include "include/guinotificationfacade.h"
#include "gui/notification-service.hpp"
#include "lib/singleton.hpp"
#include "include/nobugcfg.h"
#include "lib/util.hpp"
@ -33,35 +33,33 @@ extern "C" {
#include <string>
namespace gui {
using std::string;
using util::cStr;
void
NotificationService::displayInfo (string const& text)
{
INFO (operate, "@GUI: display '%s' as notification message.", cStr(text));
////////////////////////TODO actually push the information to the GUI
}
void
NotificationService::triggerGuiShutdown (string const& cause)
{
NOTICE (operate, "@GUI: shutdown triggered with explanation '%s'....", cStr(cause));
TODO ("actually request a shutdown from the GUI");
}
namespace { // facade implementation details
struct GuiNotificationFacade
: public GuiNotification
{
void
displayInfo (string const& text)
{
INFO (operate, "@GUI: display '%s' as notification message.", cStr(text));
}
void
triggerGuiShutdown (string const& cause)
{
NOTICE (operate, "@GUI: shutdown triggered with explanation '%s'....", cStr(cause));
}
};
lumiera::Singleton<GuiNotificationFacade> _facade;
/* ================== define an lumieraorg_GuiNotification instance ======================= */
LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0
@ -131,6 +129,16 @@ namespace gui {
);
using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE;
typedef lib::SingletonRef<GuiNotification>::Accessor InstanceRef;
InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual GuiNotification implementation...
LUMIERA_INTERFACE_INSTANCE (lumieraorg_GuiNotification, 1
,lumieraorg_GuiNotificationFacade
, LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_GuiNotificationFacade_descriptor)
@ -138,15 +146,37 @@ namespace gui {
, NULL /* on close */
, LUMIERA_INTERFACE_INLINE (displayInfo, "\366\075\213\163\207\040\221\233\010\366\174\374\317\126\331\205",
void, (const char* text),
{ return _facade().displayInfo(text); }
{
if (!_instance) lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE);
else
_instance->displayInfo(text);
}
)
, LUMIERA_INTERFACE_INLINE (triggerGuiShutdown, "\267\043\244\065\107\314\370\175\063\330\264\257\302\146\326\303",
void, (const char* cause),
{ return _facade().triggerGuiShutdown(cause); }
{
if (!_instance) lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE);
else
_instance->triggerGuiShutdown(cause);
}
)
);
} // (END) facade implementation details
NotificationService::NotificationService ()
: implInstance_(this,_instance),
serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_GuiNotification, 1,lumieraorg_GuiNotificationFacade))
{
INFO (operate, "GuiNotification Facade opened.");
}
} // namespace gui

View file

@ -0,0 +1,91 @@
/*
NOTIFICATION-SERVICE.hpp - public service allowing to push informations into the GUI
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 notification-service.hpp
** A public service provided by the GUI, implementing the gui::GuiNotification facade interface.
** The purpose of this service is to push state update and notification of events from the lower
** layers into the Lumiera GUI. Typically, this happens asynchronously and triggered by events
** within the lower layers.
**
** This service is the implementation of a layer separation facade interface. Clients should use
** gui::GuiNotification#facade to access this service. This header defines the interface used
** to \em provide this service, not to access it.
**
** @see gui::GuiFacade
** @see guifacade.cpp starting this service
*/
#ifndef GUI_NOTIFICATION_SERVICE_H
#define GUI_NOTIFICATION_SERVICE_H
#include "include/guinotificationfacade.h"
#include "common/instancehandle.hpp"
#include "lib/singleton-ref.hpp"
namespace gui {
/******************************************************
* Actual implementation of the GuiNotification service
* within the Lumiera GTK GUI. Creating an instance of
* this class automatically registers the interface
* with the Lumiera Interface/Plugin system and creates
* a forwarding proxy within the application core to
* route calls through this interface.
*
* @todo the ctor of this class should take references
* to any internal service providers within the
* GUI which are needed to implement the service.
*/
class NotificationService
: public GuiNotification
{
/* === Implementation of the Facade Interface === */
void displayInfo (string const& text);
void triggerGuiShutdown (string const& cause);
/* === Interface Lifecycle === */
typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_GuiNotification, 1)
, GuiNotification
> ServiceInstanceHandle;
lib::SingletonRef<GuiNotification> implInstance_;
ServiceInstanceHandle serviceInstance_;
public:
NotificationService();
};
} // namespace gui
#endif

View file

@ -20,16 +20,13 @@
*/
/** @file guifacade.hpp
** Interface for the GUI loader and for accessing the GUI interface from the
** lower layers of Lumiera. While part of the public interface of the Lumiera GUI,
** the implementation of this facility is part of the core application (and not
** contained within the GUI dynamic module), because it's job is to load and
** activate this module and to startup the GUI.
/** @file guinotificationfacade.hpp
** Main public Interface of the Lumiera GUI. While generally speaking, the GUI
** controls the application and thus acts on its own, it exposes some services
** usable by scripts or the two lower layers. The main purpose of these services
** is to push informations and status updates into the GUI.
**
** @see lumiera::AppState
** @see lumiera::Option
** @see guifacade.cpp
** @see gui::GuiFacade
** @see main.cpp
*/
@ -40,7 +37,7 @@
#ifdef __cplusplus /* ============== C++ Interface ================= */
#include "lib/singletonsubclass.hpp"
#include "include/interfaceproxy.hpp"
#include <string>
@ -63,7 +60,7 @@ namespace gui {
class GuiNotification
{
public:
static lumiera::SingletonSub<GuiNotification> facade;
static lumiera::facade::Accessor<GuiNotification> facade;
/** push a user visible notification text */
virtual void displayInfo (string const& text) =0;
@ -83,7 +80,7 @@ namespace gui {
extern "C" {
#endif /* =========================== CLI Interface ================= */
#endif /* =========================== CL Interface ===================== */
#include "common/interface.h"

View file

@ -0,0 +1,126 @@
/*
INTERFACEPROXY - definition of forwarding proxies for the facade interfaces
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 interfaceproxy.hpp
** Facade Interfaces Lifecycle. Communication between the Layers within Lumiera
** usually is routed through <b>Layer Separation Interfaces</b>. These are comprised
** of a Facade interface and a equivalent rendering as C Language interface defined
** with the help of the Interface/Plugin system. But in order to be able to actually
** access a service via this Facade, you need an instance of the interface.
**
** lumiera::facade::Proxy and InstanceHandle together are used to create such an concrete
** instance of the Facade interface. It is implemented such as to route each call
** through the corresponding C Language function defined in the Interface/Plugin system.
** Typically there is another subclass of the Facade interfaces sitting "on the other side"
** of the interface barrier and actually implementing the functionality. The template
** facade::Accessor can be thought of as a factory creating such a proxy instance of the
** facade interface for the client code to use. Typically, in instance of the \em factory
** is embedded (as a static functor member object) right within the otherwise abstract
** facade interface, this way allowing the client code to write e.g. \c XYZInterface::facade()
** to yield a reference to a proxy object implementing \c XYZInterface.
**
** Interface Lifecycle
**
** Instances of an Interface are either directly provided by some facility within the core,
** or they are loaded from a shared module (plugin). In either case this means the interface
** isn't accessible all the time, rather it comes up at a defined point in the application
** lifecycle and similarly will be shut down deliberately at some point. Beyond this time
** window of availability, any access through the proxy factory throws an lumiera::error::State.
** Any sort of dependency management is outside the scope of the InstanceHandle (for the core
** services, it is handled by the dependency of subsystems, while the plugin loader cares
** for dependency issues regarding loadable modules, thereby building on the deployment
** descriptors.
**
** For the Layer separation interfaces, the process of loading and opening is abstracted as
** an InstanceHandle object. When creating such an InstanceHandle using the appropriate
** template and ctor parameters, in addition to the registration with the Interface/Plugin
** system, the corresponding facade::Proxy factory is addressed and "opened" by creating
** the right proxy object instance. Similarly, when the InstanceHandle object goes out
** of scope, prior to detaching from the Interface/Proxy system, the corresponding
** lumiera::facade::Accessor factory is "closed", which additionally means destroying
** the proxy object instance and switching any further access to throwing and exception.
**
** While client code just includes the interface header (including interfaceproxy.hpp
** in turn), there needs to be an actual implementation of each proxy object located in
** some translation unit. The usual place is interfaceproxy.cpp, which gets linked into
** \c liblumieracommon.so and contains actual specialisations and literal forwarding
** code <i>for each individual facade.</i>
**
** @see interface.h
** @see plugin.h
** @see lumiera::Subsys
** @see guinotification.h usage example (facade interface)
** @see guinotificationfacade.cpp corresponding implementation within the GUI
*/
#ifndef LUMIERA_INTERFACE_PROXY_H
#define LUMIERA_INTERFACE_PROXY_H
#include "lib/error.hpp"
namespace lumiera {
namespace facade {
/** error-ID for accessing a (currently) closed facade */
LUMIERA_ERROR_DECLARE(FACADE_LIFECYCLE);
/*********************************************************************
*
*/
template<class FA>
class Accessor
{
protected:
static FA* implProxy_;
public:
FA&
operator() ()
{
if (implProxy_)
return *implProxy_;
else
throw error::State("Facade interface currently closed.");
}
};
template<class IHA>
void openProxy (IHA const&);
template<class IHA>
void closeProxy ();
template<class IHA>
class Proxy;
} // namespace facade
} // namespace lumiera
#endif

View file

@ -23,7 +23,7 @@
/** @file symbol.hpp
** WIP placeholder definition for a planned Symbol datatype.
**
** @todo the (currently just planned as of 11/08) rules based configuration
** @todo for the (currently just planned as of 11/08) rules based configuration
** in the Proc-Layer a explicit Symbol datatype will probably very helpful.
** For now we just a typedef is sufficient. A real Symbol datatype should
** - be definable by string constant
@ -41,11 +41,14 @@
#define LUMIERA_SYMBOL_H
#include <string>
namespace lumiera {
typedef const char * const Symbol; //TODO define a real Symbol class, i.e. same literal string==same pointer,
typedef const char * const Symbol; ///< Token or Atom with distinct identity @todo define a real Symbol class, i.e. same literal string==same pointer,
typedef const std::string Literal; ///< inline string literal @todo improve interaction with Symbol

View file

@ -28,6 +28,7 @@ liblumiera_la_SOURCES = \
$(liblumiera_la_srcdir)/rwlock.c \
$(liblumiera_la_srcdir)/condition.c \
$(liblumiera_la_srcdir)/reccondition.c \
$(liblumiera_la_srcdir)/threads.c \
$(liblumiera_la_srcdir)/luid.c \
$(liblumiera_la_srcdir)/safeclib.c \
$(liblumiera_la_srcdir)/psplay.c \
@ -52,6 +53,7 @@ noinst_HEADERS += \
$(liblumiera_la_srcdir)/rwlock.h \
$(liblumiera_la_srcdir)/condition.h \
$(liblumiera_la_srcdir)/reccondition.h \
$(liblumiera_la_srcdir)/threads.h \
$(liblumiera_la_srcdir)/luid.h \
$(liblumiera_la_srcdir)/safeclib.h \
$(liblumiera_la_srcdir)/psplay.h \

View file

@ -24,6 +24,7 @@
#include "lib/allocationcluster.hpp"
#include "lib/error.hpp"
#include "lib/util.hpp"
#include "lib/sync.hpp"
using util::isnil;
@ -38,9 +39,9 @@ namespace lib {
* successful ctor call of the object being allocated. Allocations and commits
* can be assumed to come in pairs, thus if an allocation immediately follows
* another one (without commit), the previous allocation can be considered
* a failure and can be dropped silently. After an allocation has succeeds
* a failure and can be dropped silently. After an allocation succeeds
* (i.e. was committed), the MemoryManager is in charge for the lifecycle
* of the object within the allocated spaces and has to guarantee calling
* of the object within the allocated space and has to guarantee calling
* it's dtor, either on shutdown or on explicit #purge() -- the type info
* structure handed in on initialisation provides a means for invoking
* the dtor without actually knowing the object's type.
@ -55,6 +56,7 @@ namespace lib {
* on real-world measurements, not guessing.
*/
class AllocationCluster::MemoryManager
: public Sync<RecursiveLock_NoWait>
{
typedef std::vector<char*> MemTable;
TypeInfo type_;
@ -81,7 +83,7 @@ namespace lib {
void
AllocationCluster::MemoryManager::reset (TypeInfo info)
{
ClassLock<MemoryManager> guard();
Lock instance();
if (0 < mem_.size()) purge();
type_ = info;
@ -96,7 +98,7 @@ namespace lib {
void
AllocationCluster::MemoryManager::purge()
{
ClassLock<MemoryManager> guard();
Lock instance();
REQUIRE (type_.killIt, "we need a deleter function");
REQUIRE (0 < type_.allocSize, "allocation size unknown");
@ -120,7 +122,7 @@ namespace lib {
inline void*
AllocationCluster::MemoryManager::allocate()
{
ClassLock<MemoryManager> guard();
Lock instance();
REQUIRE (0 < type_.allocSize);
REQUIRE (top_ <= mem_.size());
@ -140,7 +142,7 @@ namespace lib {
inline void
AllocationCluster::MemoryManager::commit (void* pendingAlloc)
{
ClassLock<MemoryManager> guard();
Lock instance();
REQUIRE (pendingAlloc);
ASSERT (top_ < mem_.size());
@ -174,8 +176,8 @@ namespace lib {
AllocationCluster::~AllocationCluster() throw()
{
try
{
ClassLock<AllocationCluster> guard();
{ // avoiding a per-instance lock for now.
ClassLock<AllocationCluster> guard(); // (see note in the class description)
TRACE (memory, "shutting down AllocationCluster");
for (size_t i = typeHandlers_.size(); 0 < i; --i)
@ -213,8 +215,8 @@ namespace lib {
{
ASSERT (0 < slot);
{
ClassLock<AllocationCluster> guard(); /////TODO: decide tradeoff: lock just the instance, or lock the AllocationCluster class?
{ // avoiding a per-instance lock for now.
ClassLock<AllocationCluster> guard(); // (see note in the class description)
if (slot > typeHandlers_.size())
typeHandlers_.resize(slot);

View file

@ -76,6 +76,12 @@ namespace lib {
* the object families are to be discarded. Currently
* they are just purged in reverse order defined by
* the first request for allocating a certain type.
* @todo should we use an per-instance lock? We can't avoid
* the class-wide lock, unless also the type-ID registration
* is done on a per-instance base. AllocationCluster is intended
* to be used within the builder, which executes in a dedicated
* thread. Thus I doubt lock contention could be a problem and
* we can avoid using a mutex per instance. Re-evaluate this!
*/
class AllocationCluster
: boost::noncopyable

View file

@ -39,14 +39,11 @@ using util::contains;
using util::isnil;
namespace lumiera
{
namespace query
{
namespace // local definitions
{
namespace lumiera {
namespace query {
namespace { // local definitions
typedef boost::function<bool(string::value_type)> ChPredicate;
ChPredicate is_alpha = boost::algorithm::is_alpha();
@ -92,8 +89,8 @@ namespace lumiera
/** (preliminary) helper: instead of really parsing and evaluating the terms,
* just do a regular expression match to extract the literal argument
* behind the given predicate symbol. e.g calling
* queryID ("stream", "id(abc), stream(mpeg)")
* yields "mpeg"
* \code extractID ("stream", "id(abc), stream(mpeg)") \endcode
* yields \c "mpeg"
*/
const string
extractID (Symbol sym, const string& termString)

131
src/lib/singleton-ref.hpp Normal file
View file

@ -0,0 +1,131 @@
/*
SINGLETON-REF.hpp - helper template providing singleton-like access for implementation code
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 singleton-ref.hpp
** Helper for singleton-kind access without managing object creation and lifecycle.
** A typical usage scenario is when implementing C Language Interfaces without any
** canonical access to some "this" pointer.
**
** @see gui::NotificationService usage example
*/
#ifndef LIB_SINGLETON_REF_H
#define LIB_SINGLETON_REF_H
#include "lib/error.hpp"
#include <boost/noncopyable.hpp>
namespace lib {
namespace singleton {
/***************************************
* Detail/Policy class specifying
* how the SingletonRef can be accessed
*/
template<class TY>
class AccessAsReference
: boost::noncopyable
{
TY* obj_;
typedef AccessAsReference<TY> _ThisType;
public:
void
open (TY* instance)
{
ASSERT (!obj_, "Lifecycle error");
obj_ = instance;
}
void
close ()
{
ASSERT (obj_, "Lifecycle error");
obj_ = 0;
}
TY*
operator-> () const
{
if (!obj_)
throw lumiera::error::State("Target currently not available.");
return obj_;
}
typedef void* _ThisType::*unspecified_bool_type;
/** implicit conversion to "bool" */
operator unspecified_bool_type() const // never throws
{
return obj_? &_ThisType::obj_ : 0;
}
bool operator! () const { return !obj_; }
};
} // namespace Singleton
/*************************************************************
* Helper template providing singleton access without managing
* object creation and lifecycle.
* @param TY the actual type to be made accessible
* @param B a base class to inherit from; defaults to noncopyable
* @param Accessor how to implement the static instance access
*/
template< class TY
, class B = boost::noncopyable
, template<class> class Access = singleton::AccessAsReference
>
struct SingletonRef
: public B
{
typedef Access<TY> Accessor;
Accessor& accessor_;
SingletonRef(TY * instance, Accessor& acc)
: accessor_(acc)
{
accessor_.open (instance);
}
~SingletonRef()
{
accessor_.close ();
}
};
} // namespace lib
#endif

View file

@ -25,7 +25,7 @@
** This configuration header just pulls in some other implementation headers in
** the right order. The basic class template for creating singletons resides in
** singletonfactory.hpp, besides we need policy classes defining how to create
** the singleton objects, how to manage lifecycle and multithreading. Finally,
** the singleton objects and how to manage singleton lifecycle. Finally,
** we want to preconfigure singleton factories for some important facilities;
** e.g. sometimes we want to include a hook for injecting Test Mock instances.
**

View file

@ -37,31 +37,40 @@ This code is heavily inspired by
#include "lib/singletonpolicies.hpp" // several Policies usable together with SingletonFactory
#include "lib/util.hpp"
#include "include/nobugcfg.h"
#include "lib/util.hpp"
#include "lib/sync-classlock.hpp"
namespace lumiera
{
namespace lumiera {
/**
* A configurable Template for implementing Singletons.
* Actually this is a Functor object, which could be placed into a static field
* Actually this is a Factory object, which could be placed into a static field
* of the Singleton (target) class or used directly.
* @note internally uses static fields, so all functor instances share pInstance_
* @note internally uses static fields, so all factory instances share pInstance_
* @note there is an ongoing discussion regarding Double Checked Locking pattern,
* which in this case boils down to the question: does \c pthread_mutex_lock/unlock
* constitute a memory barrier, such as to force any memory writes done within
* the singleton ctor to be flushed and visible to other threads when releasing
* the lock? To my understanding, the answer is yes. See
* http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap04.html#tag_04_10
* @param SI the class of the Singleton instance
* @param Create policy defining how to create/destroy the instance
* @oaram Life policy defining how to manage Singleton Lifecycle
*/
template
< class SI // the class of the Singleton instance
, template <class> class Create = singleton::StaticCreate // how to create/destroy the instance
, template <class> class Life = singleton::AutoDestroy // how to manage Singleton Lifecycle
, template <class> class Threading = singleton::IgnoreThreadsafety //TODO use Multithreaded!!!
< class SI
, template <class> class Create = singleton::StaticCreate
, template <class> class Life = singleton::AutoDestroy
>
class SingletonFactory
{
typedef typename Threading<SI>::VolatileType SType;
typedef typename Threading<SI>::Lock ThreadLock;
static SType* pInstance_;
typedef SI* volatile PType;
typedef lib::ClassLock<SI> ThreadLock;
static PType pInstance_;
static bool isDead_;
public:
@ -110,21 +119,19 @@ namespace lumiera
template
< class SI,
template <class> class C,
template <class> class L,
template <class> class T
template <class> class L
>
typename SingletonFactory<SI,C,L,T>::SType*
SingletonFactory<SI,C,L,T>::pInstance_;
typename SingletonFactory<SI,C,L>::PType
SingletonFactory<SI,C,L>::pInstance_;
template
< class SI,
template <class> class C,
template <class> class L,
template <class> class T
template <class> class L
>
bool SingletonFactory<SI,C,L,T>::isDead_;
bool SingletonFactory<SI,C,L>::isDead_;
///// TODO: get rid of the static fields?
///// is tricky because of invoking the destructors. If we rely on instance vars,
@ -132,6 +139,6 @@ namespace lumiera
///// destructors of static objects at shutdown.
///// It seems this would either cost us much of the flexibility or get complicated
///// to a point where we could as well implement our own Dependency Injection Manager.
} // namespace lumiera
#endif

View file

@ -35,7 +35,6 @@ This code is heavily inspired by
#define LUMIERA_SINGLETONPOLICIES_H
#include "lib/error.hpp"
#include "lib/sync-classlock.hpp"
#include <vector>
@ -84,7 +83,7 @@ namespace lumiera {
typedef void (*DelFunc)(void);
using std::vector;
/**
* Policy relying on the compiler/runtime system for Singleton Lifecycle
*/
@ -98,7 +97,8 @@ namespace lumiera {
* several Singletons, we need to memorise all registered
* deleter functions for calling them at shutdown.
*/
static void scheduleDelete (DelFunc kill_the_singleton)
static void
scheduleDelete (DelFunc kill_the_singleton)
{
class DeleteTrigger
{
@ -122,36 +122,15 @@ namespace lumiera {
finally.schedule (kill_the_singleton);
}
static void onDeadReference ()
static void
onDeadReference ()
{
throw error::Logic ("Trying to access the a Singleton instance that has "
"already been released or finished its lifecycle.");
}
};
/**
* Policy for handling multithreaded access to the singleton instance
* @todo actually implement this policy using the Lumiera databackend.
*/
template<class S>
struct Multithreaded
{
typedef volatile S VolatileType;
typedef lib::ClassLock<S> Lock;
};
/**
* Policy just ignoring thread safety
*/
template<class S>
struct IgnoreThreadsafety
{
typedef S VolatileType;
struct Lock {};
};
} // namespace singleton

View file

@ -64,8 +64,8 @@ namespace lumiera
/* Forward declarations of all Classes we want to specialise the template */
/* ********************************************************************** */
namespace test
{
namespace test {
class TestSingletonO;
using lumiera::Singleton;

View file

@ -21,14 +21,14 @@
*/
/** @file singletonsubclass.hpp
** Spezialized SingletonFactory creating sublcasses of the nominal type.
** Specialised SingletonFactory creating subclasses of the nominal type.
** The rationale is to be able to defer the decision what type to create
** down to the point where the singleton factory is actualy created.
** down to the point where the singleton factory is actually created.
** Thus the code using the singleton need not know the implementation
** class, but nevertheless gets an non-virtual access function to the
** singleton instance (which can be inlined), and the compiler is
** still able to spot type errors. Maybe someone knows a less
** contrieved solution fulfilling the same criteria....?
** contrived solution fulfilling the same criteria....?
**
** @see configrules.cpp usage example
** @see SingletonSubclass_test
@ -50,8 +50,8 @@ namespace lumiera {
using boost::scoped_ptr;
namespace singleton
{
namespace singleton {
/**
* Helper template to use the general policy classes of the lumiera::Singleton,
* but change the way they are parametrised on-the-fly.
@ -63,7 +63,7 @@ namespace lumiera {
struct Link
{
virtual ~Link() {}
virtual I* create () = 0; ///< @note compiler will check the actual type is assignable...
virtual I* create () = 0; ///< @note compiler will check if the actual type is assignable...
virtual void destroy (I* pSi) = 0;
};
@ -123,13 +123,11 @@ namespace lumiera {
< class SI // the class to use as Interface for the Singleton
, template <class> class Create = singleton::StaticCreate // how to create/destroy the instance
, template <class> class Life = singleton::AutoDestroy // how to manage Singleton Lifecycle
, template <class> class Threading = singleton::IgnoreThreadsafety //TODO use Multithreaded!!!
>
class SingletonSubclassFactory
: public SingletonFactory< SI
, singleton::Adapter<Create,SI>::template Adapted
, Life
, Threading
>
{
public:

View file

@ -74,11 +74,15 @@
extern "C" {
#include "lib/mutex.h"
#include "lib/condition.h"
#include "lib/reccondition.h"
}
#include <boost/noncopyable.hpp>
#include <pthread.h>
#include <cerrno>
#include <ctime>
using boost::noncopyable;
namespace lib {
@ -106,7 +110,7 @@ namespace lib {
: public lumiera_mutex
{
protected:
Wrapped_LumieraRecMutex() { lumiera_recmutex_init (this, "Obj.Monitor ExclMutex", &NOBUG_FLAG(sync)); }
Wrapped_LumieraRecMutex() { lumiera_recmutex_init (this, "Obj.Monitor RecMutex", &NOBUG_FLAG(sync)); }
~Wrapped_LumieraRecMutex() { lumiera_mutex_destroy (this, &NOBUG_FLAG(sync)); }
//------------------Resource-Tracking------
@ -120,7 +124,7 @@ namespace lib {
: public lumiera_condition
{
protected:
Wrapped_LumieraExcCond() { lumiera_condition_init (this, "Obj.Monitor Condition", &NOBUG_FLAG(sync) ); }
Wrapped_LumieraExcCond() { lumiera_condition_init (this, "Obj.Monitor ExclCondition", &NOBUG_FLAG(sync) ); }
~Wrapped_LumieraExcCond() { lumiera_condition_destroy (this, &NOBUG_FLAG(sync) ); }
//------------------Resource-Tracking------
@ -131,11 +135,11 @@ namespace lib {
struct Wrapped_LumieraRecCond
: public lumiera_condition //////////////////////////////////////////TODO use correct implementation here!
: public lumiera_reccondition
{
protected:
Wrapped_LumieraRecCond() { lumiera_condition_init (this, "Obj.Monitor Condition", &NOBUG_FLAG(sync) ); } ////////TODO
~Wrapped_LumieraRecCond() { lumiera_condition_destroy (this, &NOBUG_FLAG(sync) ); }
Wrapped_LumieraRecCond() { lumiera_reccondition_init (this, "Obj.Monitor RecCondition", &NOBUG_FLAG(sync) ); } ////////TODO
~Wrapped_LumieraRecCond() { lumiera_reccondition_destroy (this, &NOBUG_FLAG(sync) ); }
//------------------Resource-Tracking------
void __may_block() { TODO ("Record we may block on mutex"); }
@ -157,6 +161,11 @@ namespace lib {
using MTX::__enter;
using MTX::__leave;
~Mutex () { }
Mutex () { }
Mutex (const Mutex&); ///< noncopyable...
const Mutex& operator= (const Mutex&);
public:
void
acquire()
@ -282,6 +291,8 @@ namespace lib {
/**
* Object Monitor for synchronisation and waiting.
* Implemented by a (wrapped) set of sync primitives,
* which are default constructible and noncopyable.
*/
template<class IMPL>
class Monitor
@ -293,6 +304,11 @@ namespace lib {
Monitor() {}
~Monitor() {}
/** allow copy, without interfering with the identity of IMPL */
Monitor (Monitor const& ref) : IMPL(), timeout_(ref.timeout_) { }
const Monitor& operator= (Monitor const& ref) { timeout_ = ref.timeout_; }
void acquireLock() { IMPL::acquire(); }
void releaseLock() { IMPL::release(); }
@ -370,13 +386,13 @@ namespace lib {
public:
class Lock
: private noncopyable
{
Monitor& mon_;
public:
template<class X>
Lock(X* it) : mon_(getMonitor(it)){ mon_.acquireLock(); }
Lock(Monitor& m) : mon_(m) { mon_.acquireLock(); }
~Lock() { mon_.releaseLock(); }
void notify() { mon_.signal(false);}
@ -385,11 +401,12 @@ namespace lib {
template<typename C>
bool wait (C& cond, ulong time=0) { return mon_.wait(cond,time);}
bool isTimedWait() { return mon_.isTimedWait(); }
/** convenience shortcut:
* Locks and immediately enters wait state,
* observing a condition defined as member function.
*/
* observing a condition defined as member function. */
template<class X>
Lock(X* it, bool (X::*method)(void))
: mon_(getMonitor(it))
@ -397,6 +414,11 @@ namespace lib {
mon_.acquireLock();
mon_.wait(*it,method);
}
protected:
/** for creating a ClassLock */
Lock(Monitor& m) : mon_(m)
{ mon_.acquireLock(); }
};

View file

@ -31,15 +31,14 @@
#include <boost/scoped_ptr.hpp>
namespace lumiera
{
namespace test
{
namespace lumiera {
namespace test {
using boost::scoped_ptr;
/**
* Special SingletonFactory allowing to inject some instance of the Singleton
* class, thus shaddowing "the" (default) Singleton instance temporarily.
* class, thus shadowing "the" (default) Singleton instance temporarily.
* This allows installing a Mock Subclass of the Singleton for running tests,
* while the Singleton can be used as usual in production code.
* @note we use the default policies or SingletonFactory

117
src/lib/thread-wrapper.hpp Normal file
View file

@ -0,0 +1,117 @@
/*
THREADWRAPPER.hpp - thin convenience wrapper for starting lumiera threads
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.
*/
#ifndef LIB_THREADWRAPPER_H
#define LIB_THREADWRAPPER_H
#include "include/nobugcfg.h"
#include "lib/sync.hpp"
extern "C" {
#include "lib/threads.h"
}
#include <tr1/functional>
#include <boost/noncopyable.hpp>
namespace lib {
using std::tr1::bind;
using std::tr1::function;
using lumiera::Literal;
/**
* A thin convenience wrapper for dealing with threads,
* as implemented by the backend (on top of pthread).
* Using this wrapper...
* - helps with passing data to the function executed in the new thread
* - allows to bind to various kinds of functions including member functions
* - supports integrating with an existing object monitor based lock (planned)
* The new thread starts immediately within the ctor; after returning, the new
* thread has already copied the arguments and indeed actively started to run.
*
* @note this class is \em not a thread handle. Within Lumiera, we do all of
* our thread management such as to avoid using global thread handles.
* If some cooperation between threads is needed, this should be done
* in a implementation private way, e.g. by sharing a condition var.
*
* @todo Ichthyo started this wrapper 12/08 while our own thread handling
* was just being shaped. It may well be possible that such a wrapper
* is superfluous in the final application. Re-evaluate this!
*/
class Thread
: public Sync<NonrecursiveLock_Waitable>,
boost::noncopyable
{
volatile bool started_;
typedef function<void(void)> Operation;
Operation const& operation_;
static void
run (void* arg)
{
ASSERT (arg);
Thread* startingWrapper = reinterpret_cast<Thread*>(arg);
Operation _doIt_(startingWrapper->operation_);
{
Lock sync(startingWrapper);
startingWrapper->started_ = true;
sync.notify(); // handshake signalling we've gotten the parameter
}
_doIt_(); // execute the actual operation in the new thread
}
public:
Thread (Literal& purpose, Operation const& operation, struct nobug_flag *logging_flag = &NOBUG_FLAG(operate))
: started_(false),
operation_(operation)
{
Lock sync(this);
LumieraThread res =
lumiera_thread_run ( LUMIERA_THREAD_INTERACTIVE
, &run // invoking the run helper and..
, this // passing this start context as parameter
, 0 // no condition variable provided (for now...)
, purpose.c_str()
, logging_flag
);
if (!res)
throw lumiera::error::State("failed to create new thread.");
// make sure the new thread had the opportunity to take the Operation
// prior to leaving and thereby possibly destroying this local context
sync.wait (started_);
}
};
} // namespace lib
#endif

View file

@ -23,7 +23,7 @@
//TODO: Lumiera header includes//
#include "backend/threads.h"
#include "lib/threads.h"
//TODO: internal/static forward declarations//
@ -98,10 +98,11 @@ lumiera_thread_run (enum lumiera_thread_class kind,
pthread_mutex_lock (&threads_mutex);
pthread_t dummy;
pthread_create (&dummy, &attrs, pthread_runner, &thread);
int error = pthread_create (&dummy, &attrs, pthread_runner, &thread);
pthread_mutex_unlock (&threads_mutex);
if (error) return 0; /////TODO temporary addition by Ichthyo; probably we'll set lumiera_error?
return (LumieraThread) 1;
}

View file

@ -60,6 +60,13 @@ namespace util {
return !pContainer || pContainer->empty();
}
template <class CONT>
inline bool
isnil (CONT* pContainer)
{
return !pContainer || pContainer->empty();
}
inline bool
isnil (const char* pCStr)
{

View file

@ -105,6 +105,8 @@ namespace lumiera {
* For each possible call entry point via some subclass of the visitable hierarchy,
* we maintain a dispatcher table to keep track of all concrete tool implementations
* able to receive and process calls on objects of this subclass.
* @param TAR the concrete target (subclass) type within the visitable hierarchy
* @param TOOL the overall tool family (base class of all concrete tools)
*/
template<class TAR, class TOOL>
class Dispatcher
@ -140,7 +142,7 @@ namespace lumiera {
void
accomodate (size_t index)
{
ClassLock<Dispatcher> guard();
ClassLock<Dispatcher> guard(); // note: this lock is also used for the singleton!
if (index > table_.size())
table_.resize (index); // performance bottleneck?? TODO: measure the real impact!
}

View file

@ -42,8 +42,8 @@
namespace asset
{
namespace asset {
class Proc;
class ProcFactory;
@ -92,7 +92,7 @@ namespace asset
/**
* Factory specialized for createing Processor Asset objects.
* Factory specialised for creating Processor Asset objects.
*/
class ProcFactory : public lumiera::Factory<asset::Proc>
{

View file

@ -82,8 +82,8 @@ namespace asset
/** Factory method for Structural Asset instances.
* First tries to relove the asset by issuing an capability query.
* If unsuccessfull, use some internally specialized ctor call.
* First tries to resolve the asset by issuing an capability query.
* If unsuccessful, use some internally specialised ctor call.
* @todo work out the struct asset naming scheme!
* @return an Struct smart ptr linked to the internally registered smart ptr
* created as a side effect of calling the concrete Struct subclass ctor.
@ -109,7 +109,7 @@ namespace asset
/** Factory method for creating Pipes explicitly.
* Normalizes pipe- and streamID, then retrieves the
* Normalises pipe- and streamID, then retrieves the
* default processing pattern (ProcPatt) for this streamID.
* The Pipe ctor will fill out the shortDesc and longDesc
* automatically, based on pipeID and streamID (and they
@ -144,8 +144,7 @@ namespace asset
#include "proc/asset/pipe.hpp"
namespace asset
{
namespace asset {
template P<Pipe> StructFactory::operator() (const Query<Pipe>& query);
template P<Track> StructFactory::operator() (const Query<Track>& query);

View file

@ -49,8 +49,8 @@
namespace asset
{
namespace asset {
using std::string;
using std::wstring;
using boost::scoped_ptr;
@ -108,7 +108,7 @@ namespace asset
/**
* Factory specialized for createing Structural Asset objects.
* Factory specialised for creating Structural Asset objects.
*/
class StructFactory : public lumiera::Factory<asset::Struct>
{

View file

@ -79,7 +79,7 @@ namespace asset
/**
* Implementation deatils, esp. concerning how configuration
* Implementation details, especially concerning how configuration
* queries are resolved and when to create new objects automatically.
* @todo better use a general struct traits class, esp.for creating the Ident
*/

View file

@ -183,16 +183,19 @@ namespace asset {
}
void
recursive_call (AssetManager* instance, PAsset& pA)
{
instance->remove (pA->getID());
}
function<void(PAsset&)>
detach_child_recursively () ///< @return a functor recursively invoking remove(child)
{
return bind( &recursive_call, &AssetManager::instance(), _1 );
namespace { // details implementing AssetManager::remove
void
recursive_call (AssetManager* instance, PAsset& pA)
{
instance->remove (pA->getID());
}
function<void(PAsset&)>
detach_child_recursively () ///< @return a functor recursively invoking remove(child)
{
return bind( &recursive_call, &AssetManager::instance(), _1 );
}
}
/**

View file

@ -33,18 +33,11 @@ namespace proc {
/*********************************************************************
* Global access point for loading and starting up the Lumiera GTK GUI
* and for defining the public interface(s) for addressing the GUI
* from Backend or Proc-Layer.
* Global access point for the services implemented by the Proc-Layer.
*
* If running Lumiera with a GUI is required (the default case),
* it is loaded as dynamic module, thus defining the interface(s)
* for any further access. After successfully loading and starting
* the GUI, this gui::Facade is wired internally with this interface
* such as to allow transparent access from within the core. This
* startup sequence includes providing the GUI with similar facade
* access via interface handles for communication with Backend and
* Proc-Layer.
* @todo this is a dummy placeholder as of 1/2009. Currently, there
* is only implementation-level code within the Proc-Layer and
* the interfaces need to be worked out.
*
*/
struct Facade

View file

@ -42,7 +42,7 @@ namespace mobject {
/** initialize the most basic internal defaults. */
/** initialise the most basic internal defaults. */
DefsManager::DefsManager () throw()
: defsRegistry(new DefsRegistry)
{
@ -142,11 +142,10 @@ namespace mobject {
#include "proc/asset/track.hpp"
#include "proc/mobject/session/track.hpp"
namespace mobject
{
namespace session
{
namespace mobject {
namespace session {
using asset::Pipe;
using asset::PPipe;
using asset::ProcPatt;
@ -164,7 +163,8 @@ namespace mobject
template bool DefsManager::define (const PPipe&, const Query<Pipe>&);
template bool DefsManager::forget (const PPipe&);
} // namespace mobject::session
} // namespace mobject

View file

@ -44,8 +44,8 @@ namespace mobject {
/**
* Organize a collection of preconfigured default objects.
* For various kinds of objects we can tweek the default parametrisation
* Organise a collection of preconfigured default objects.
* For various kinds of objects we can tweak the default parametrisation
* as part of the general session configuration. A ref to an instance of
* this class is accessible through the current session and can be used
* to fill in parts of the configuration of new objects, if the user
@ -88,10 +88,10 @@ namespace mobject {
template<class TAR>
P<TAR> create (const lumiera::Query<TAR>&);
/** register the given object as default, after ensuring it fulfills the
/** register the given object as default, after ensuring it fulfils the
* query. The latter may cause some properties of the object to be set,
* trigger creation of additional objects, and may fail altogether.
* @return true if query was successfull and object is registered as default
* @return true if query was successful and object is registered as default
* @note only a weak ref to the object is stored
*/
template<class TAR>

View file

@ -25,7 +25,7 @@
** A piece of implementation code factored out into a separate header (include).
** Only used in defsmanager.cpp and for the unit tests. We can't place it into
** a separate compilation unit, because defsmanager.cpp defines some explicit
** template instantiaton, which cause the different Slots of the DefsrRegistry#table_
** template instantiation, which cause the different Slots of the DefsrRegistry#table_
** to be filled with data and defaults for the specific Types.
**
** @see mobject::session::DefsManager
@ -80,7 +80,7 @@ namespace mobject {
};
/** we maintain an independent defaults registry
* for every participating kind of objects */
* for every participating kind of object. */
typedef std::vector< P<TableEntry> > Table;
@ -180,8 +180,8 @@ namespace mobject {
/**
* @internal Helper for organizing preconfigured default objects.
* Maintaines a collection of objects known or encountered as "default"
* @internal Helper for organising preconfigured default objects.
* Maintains a collection of objects known or encountered as "default"
* for a given type. This collection is ordered by "degree of constriction",
* which is implemented by counting the number of predicates in the query
* used to define or identify each object.
@ -206,17 +206,17 @@ namespace mobject {
II p,i,e;
P<TAR> next, ptr;
Iter (II from, II to) ///< just ennumerates the given range
Iter (II from, II to) ///< just enumerates the given range
: p(from), i(from), e(to)
{
if (i!=e) ++i; // p is next to be tested, i always one ahead
operator++ ();
}
Iter (II match, II from, II to) ///< returns direct match first, then ennumerates
Iter (II match, II from, II to) ///< returns direct match first, then enumerates
: p(match), i(from), e(to)
{
operator++ (); // init to first element (or to null if emty)
operator++ (); // init to first element (or to null if empty)
}
P<TAR> findNext () throw()
@ -233,7 +233,7 @@ namespace mobject {
public:
P<TAR> operator* () { return ptr; }
bool hasNext () { return next || findNext(); }
bool hasNext () { return next || findNext(); }
Iter operator++ (int) { Iter tmp=*this; operator++(); return tmp; }
Iter& operator++ ()
{
@ -243,12 +243,12 @@ namespace mobject {
}
};
/** find a sequence of "default" objects possibliy matching the query.
/** find a sequence of "default" objects possibly matching the query.
* If there was a registration for some object of the given kind with
* the \em same query, this one will be first in the sequence. Besides,
* the sequence will yield all still existing registered "default" objects
* of this kind, ordered ascending by "degree of constriction", i.e. starting
* with the object registered togehter with the shortest query.
* with the object registered together with the shortest query.
* @return a forward input iterator yielding this sequence
* @note none of the queries will be evaluated (we're just counting predicates)
*/
@ -265,7 +265,7 @@ namespace mobject {
typename Registry::iterator end = registry.end();
if (pos==end)
return Iter<TAR> (registry.begin(), end); // just ennumerate contents
return Iter<TAR> (registry.begin(), end); // just enumerate contents
else
return Iter<TAR> (pos, registry.begin(), end); // start with direct match
}

View file

@ -180,38 +180,38 @@ END
TEST "Factory_test" Factory_test 5 <<END
out: ctor TargetObj(5) successfull
out: ctor TargetObj(5) successful
out: now the smart-ptr has refcount=3
out: .....TargetObj(5): data="*****", array[5]={0,1,2,3,4,}
out: dtor ~TargetObj(5) successfull
out: dtor ~TargetObj(5) successful
END
TEST "Factory_special_test" Factory_special_test 5 <<END
out: checkPlacement--------
out: ctor TargetObj(5) successfull
out: ctor TargetObj(5) successful
out: created 3 shared_ptrs to Object placed in static buffer.
out: .....TargetObj(5): data="*****", array[5]={0,1,2,3,4,}
out: dtor ~TargetObj(5) successfull
out: ctor TargetObj(6) successfull
out: dtor ~TargetObj(5) successful
out: ctor TargetObj(6) successful
out: created 4 shared_ptrs to Object placed in static buffer.
out: dtor ~TargetObj(6) successfull
out: dtor ~TargetObj(6) successful
out: checkPrivate--------
out: ctor TargetObj(5) successfull
out: ctor TargetObj(5) successful
out: created 3 shared_ptrs to paranoid Object.
out: .....TargetObj(5): data="*****", array[5]={0,1,2,3,4,}
out: dtor ~TargetObj(5) successfull
out: dtor ~TargetObj(5) successful
out: checkMalloc--------
out: ctor TargetObj(7) successfull
out: ctor TargetObj(7) successful
out: created auto_ptr to malloc-ed Object.
out: .....TargetObj(7): data="*******", array[7]={0,1,2,3,4,5,6,}
out: dtor ~TargetObj(7) successfull
out: dtor ~TargetObj(7) successful
out: checkPImpl--------
out: ctor TargetObj(12) successfull
out: ctor TargetObj(12) successful
out: created auto_ptr to Interface Object.
out: .....ImplObj::funky() called
out: .....TargetObj(12): data="************", array[12]={0,1,2,3,4,5,6,7,8,9,10,11,}
out: dtor ~TargetObj(12) successfull
out: dtor ~TargetObj(12) successful
END
@ -251,10 +251,10 @@ END
TEST "SingletonSubclass_test" SingletonSubclass_test 13 <<END
out: using the Singleton should create TargetObj(13)...
out: ctor TargetObj(13) successfull
out: ctor TargetObj(13) successful
out: calling a non-static method on the Singleton-Implementation
out: .....TargetObj(13): data="*************", array[13]={0,1,2,3,4,5,6,7,8,9,10,11,12,}
out: dtor ~TargetObj(13) successfull
out: dtor ~TargetObj(13) successful
END
@ -297,15 +297,15 @@ END
TEST "Singleton_test" Singleton_test 23 <<END
out: testing TargetObj(23) as Singleton(statically allocated)
out: ctor TargetObj(23) successfull
out: ctor TargetObj(23) successful
out: calling a non-static method on the Singleton instance
out: .....TargetObj(23): data="***********************", array[23]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,}
out: testing TargetObj(24) as Singleton(heap allocated)
out: ctor TargetObj(24) successfull
out: ctor TargetObj(24) successful
out: calling a non-static method on the Singleton instance
out: .....TargetObj(24): data="************************", array[24]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,}
out: dtor ~TargetObj(23) successfull
out: dtor ~TargetObj(24) successfull
out: dtor ~TargetObj(23) successful
out: dtor ~TargetObj(24) successful
END
@ -332,6 +332,21 @@ return: 0
END
TEST "Create 20 Threads passing context" ThreadWrapper_test <<END
return: 0
END
TEST "Starting and stopping subsystems" SubsystemRunner_test <<END
out: -----singleSubsys_complete_cycle-----
out: -----singleSubsys_start_failure-----
out: -----singleSubsys_emegency_exit-----
out: -----dependentSubsys_complete_cycle-----
out: -----dependentSubsys_start_failure-----
return: 0
END
TEST "TestOption_test" TestOption_test <<END
out: Testing invocation with cmdline: ...
out: --> Testgroup=ALL

View file

@ -9,7 +9,7 @@ END
TEST "QueryUtils_test" QueryUtils_test normalizeID <<END
out: ..original : a A AA dufte 1a _1 A_A BÄH White space §&Ω%€GΩ%€ar Ω baäääääge!!!!! :
out: normalized : a a aA dufte o1a o_1 a_A bH o white_space gar_bage :
out: normalised : a a aA dufte o1a o_1 a_A bH o white_space gar_bage :
END

View file

@ -59,9 +59,11 @@ test_lib_SOURCES = \
$(testlib_srcdir)/singletontestmocktest.cpp \
$(testlib_srcdir)/streamtypebasicstest.cpp \
$(testlib_srcdir)/streamtypelifecycletest.cpp \
$(testlib_srcdir)/subsystem-runner-test.cpp \
$(testlib_srcdir)/sync-classlock-test.cpp \
$(testlib_srcdir)/sync-locking-test.cpp \
$(testlib_srcdir)/sync-waiting-test.cpp \
$(testlib_srcdir)/thread-wrapper-test.cpp \
$(testlib_srcdir)/test/cmdlinewrappertest.cpp \
$(testlib_srcdir)/test/testoptiontest.cpp \
$(testlib_srcdir)/vectortransfertest.cpp \

View file

@ -31,10 +31,9 @@
using std::string;
namespace asset
{
namespace test
{
namespace asset {
namespace test{
using lumiera::P;
using std::tr1::shared_ptr;
using std::tr1::weak_ptr;
@ -71,7 +70,7 @@ namespace asset
class CustomSharedPtr_test : public Test
{
virtual void
run (Arg arg)
run (Arg)
{
check_refcounting ();
check_shared_ownership ();

View file

@ -1,5 +1,5 @@
/*
Exceptionhandlin(Test) - throwing and catching our exception type
ExceptionError(Test) - throwing and catching our exception type
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
@ -41,13 +41,11 @@ using std::cout;
namespace lumiera
{
namespace test
{
namespace lumiera {
namespace test {
/** local specific error-constant for use in the
* construcor of the nested SpecificError class.
* constructor of the nested SpecificError class.
*/
LUMIERA_ERROR_DEFINE(LIFE_AND_UNIVERSE, "and everything?");
LUMIERA_ERROR_DEFINE(DERIVED, "convoluted exception");
@ -91,17 +89,17 @@ namespace lumiera
/** @test simply throw some exception and pass context info */
void throwSpecial (string _) { throw SpecificError(); }
void throwDerived (string _) { throw DerivedError(); }
void throwSpecial (string ) { throw SpecificError(); }
void throwDerived (string ) { throw DerivedError(); }
void throwFatal (string _) { throw error::Fatal(_); }
void throwInvalid (string _) { throw error::Invalid(_); }
void throwExternal(string _) { throw error::External(_); }
void throwRuntime (string _) { throw std::runtime_error(_); }
void throwExceptn (string _) { throw std::exception(); }
void throwExceptn (string ) { throw std::exception(); }
/** @test catching, repackaging and rethrowing of errors.
* This feature is important for passing exceptions transparentely
* This feature is important for passing exceptions transparently
* over several layers. The nested operation will throw an error::External,
* which we are able to catch because it is derived from std::exception.
* We don't need to know the exact type, but we can classify the error situation
@ -126,7 +124,7 @@ namespace lumiera
try { nestedThrower (msg); }
catch (Error& e)
{
cout << "2nd intermediate handler caught: " << e.what()
cout << "2nd intermediate handler caught: " << e.what()
<< "....will rethrow as error::Config\n";
throw error::Config (e);
}
@ -172,15 +170,15 @@ namespace lumiera
ASSERT (err2.rootCause() == err1.what());
ASSERT (err3.rootCause() == err1.what());
ASSERT (err4.rootCause() == err1.what());
ASSERT (err5.rootCause() == rerr.what());
ASSERT (err6.rootCause() == rerr.what());
}
/** @test terminate the Application by throwing an undclared exception.
/** @test terminate the Application by throwing an undeclared exception.
* this should result in the global unknown() handler to be called,
* so usually it will terminate the testrun.
* so usually it will terminate the test run.
* @note inside error.hpp, an initialisation hook has been installed into
* AppState, causing our own unknown() handler to be installed and
* invoked, which gives additional diagnostics.*/
@ -214,22 +212,22 @@ namespace lumiera
(this->*funky) (context);
}
catch (SpecificError& e) { cout << "caught: " << e.what() << "..the answer is: " << e.revealIt() << "\n"; }
catch (error::Logic& e) { cout << "caught error::Logic: " << e.what() << "\n"; }
catch (error::Invalid&e) { cout << "caught error::Invalid: " << e.what() << "\n"; }
catch (Error& e) { cout << "caught lumiera::Error: " << e.what() << "\n"; }
catch (runtime_error& e) { cout << "caught std::runtime_error: " << e.what() << "\n"; }
catch (SpecificError& e) { cout << "caught: " << e.what() << "..the answer is: " << e.revealIt() << "\n"; }
catch (error::Logic& e) { cout << "caught error::Logic: " << e.what() << "\n"; }
catch (error::Invalid&e) { cout << "caught error::Invalid: " << e.what() << "\n"; }
catch (Error& e) { cout << "caught lumiera::Error: " << e.what() << "\n"; }
catch (runtime_error& e) { cout << "caught std::runtime_error: " << e.what() << "\n"; }
catch (exception& e) { cout << "caught std::exception. (unspecific)" << "\n"; }
catch (...) { cout << "caught an unknown exception\n"; }
}
};
/** register this test class... */
LAUNCHER (ExceptionError_test, "function common");
/** register this test class... */
LAUNCHER (ExceptionError_test, "function common");
} // namespace test
} // namespace util

View file

@ -58,7 +58,7 @@ namespace lumiera
class LifeCycle_test : public Test
{
virtual void
run (Arg arg)
run (Arg)
{
ASSERT (basicInit, "the basic-init callback hasn't been invoked automatically");
ASSERT (1 == basicInit, "the basic-init callback has been invoked more than once");

View file

@ -78,7 +78,8 @@ namespace lumiera
*/
class TypeList_test : public Test
{
virtual void run(Arg arg)
void
run (Arg)
{
AssembledClass wow_me_has_numbers;

View file

@ -44,12 +44,10 @@ using std::cout;
namespace lumiera
{
namespace query
{
namespace test
{
namespace lumiera {
namespace query {
namespace test{
struct Thing
{
@ -59,7 +57,7 @@ namespace lumiera
/************************************************************************
* @test check the various small helpers and utilities we utilize
* @test check the various small helpers and utilities we utilise
* for dealing with ConfigQuery
*/
class QueryUtils_test : public Test
@ -88,7 +86,7 @@ namespace lumiera
/** @test sanitizing and normalizing various tokens */
/** @test sanitising and normalising various tokens */
void
check_normalizeID ()
{
@ -101,7 +99,7 @@ namespace lumiera
for_each (tokens, bind ( &normalizeID, _1 ));
cout << "normalized : " << tokens << " :\n";
cout << "normalised : " << tokens << " :\n";
}
@ -115,7 +113,7 @@ namespace lumiera
ASSERT ("tok" == extractID ("pred", "pred(tok), pred(tux)." ));
ASSERT ("tok" == extractID ("pred", "other(xyz) pred(tok) pred(tux)" ));
ASSERT ("tok" == extractID ("pred", "some( pred(tok)" ));
ASSERT (isnil (extractID ("pred", "pred (tok)")));
ASSERT (isnil (extractID ("pred", "pred tok)" )));
ASSERT (isnil (extractID ("pred", "pred(tok " )));
@ -127,14 +125,14 @@ namespace lumiera
void
check_removeTerm ()
{
// successfull-----Symbol---input-string----------------------extracted------remaining-------------
// successful------Symbol---input-string----------------------extracted------remaining-------------
ASSERT_removeTerm ("pred", "pred(tok).", "pred(tok)", "." );
ASSERT_removeTerm ("pred", " pred( tok )", "pred(tok)", " " );
ASSERT_removeTerm ("pred", "pred(tok), pred(tux).", "pred(tok)", "pred(tux)." );
ASSERT_removeTerm ("pred", "other(xyz) pred(tok) pred(tux)", "pred(tok)", "other(xyz) pred(tux)" );
ASSERT_removeTerm ("pred", "some( pred(tok)", "pred(tok)", "some( " );
// not successfull
// not successful
ASSERT_removeTerm ("pred", "pred (tok", "", "pred (tok" );
ASSERT_removeTerm ("pred", "pred tok)", "", "pred tok)" );
ASSERT_removeTerm ("pred", "pred(tok", "", "pred(tok" );
@ -149,7 +147,7 @@ namespace lumiera
/** @test counting of predicates in a quiery
/** @test counting of predicates in a query
* (currently 4/08 regexp based...)
*/
void
@ -167,7 +165,7 @@ namespace lumiera
} // namespace test
} // namespace query
} // namespace lumiera

View file

@ -67,7 +67,7 @@ namespace util
class RemoveFromSet_test : public Test
{
virtual void
run (Arg arg)
run (Arg)
{
test_remove (" nothing ");
test_remove ("0");

View file

@ -58,7 +58,7 @@ namespace lib {
{
virtual void
run (Arg arg)
run (Arg)
{
cout << "checking ScopedHolder<Dummy>...\n";

View file

@ -101,7 +101,7 @@ namespace lib {
{
virtual void
run (Arg arg)
run (Arg)
{
cout << "checking ScopedHolder<Dummy>...\n";

View file

@ -0,0 +1,435 @@
/*
SubsystemRunner(Test) - validate starting and stopping of dependent subsystems
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 "common/subsys.hpp"
#include "common/subsystem-runner.hpp"
#include "common/option.hpp"
#include "include/symbol.hpp"
#include "lib/thread-wrapper.hpp"
#include "lib/error.hpp"
#include "lib/query.hpp"
#include "lib/util.hpp"
#include "lib/sync.hpp"
#include <tr1/functional>
#include <iostream>
using std::cout;
using std::tr1::bind;
using util::isnil;
using util::cStr;
using test::Test;
using lib::Sync;
using lib::RecursiveLock_Waitable;
using lib::Thread;
namespace lumiera {
namespace test {
namespace { // private test classes and data...
using query::extractID;
/** limit for the randomly selected duration of
* subsystem's running phase (milliseconds) */
const uint MAX_RUNNING_TIME_ms = 80;
const uint MIN_RUNNING_TIME_ms = 10;
/** the "running" subsystem checks for a
* shutdown request every XX milliseconds */
const uint TICK_DURATION_ms = 5;
/** dummy options just to be ignored */
util::Cmdline dummyArgs ("");
lumiera::Option dummyOpt (dummyArgs);
/** marker for simulated failure exceptions */
LUMIERA_ERROR_DEFINE( TEST, "simulated failure.");
/**
* A simulated "Lumiera Subsystem".
* It is capable of starting a separate thread,
* which may terminate regularly after a random
* time, or may fail in various ways.
* The behaviour is controlled by a number of
* definitions, given at construction in
* logic predicate notation.
*/
class MockSys
: public lumiera::Subsys,
public Sync<RecursiveLock_Waitable>
{
Literal id_;
Literal spec_;
volatile bool isUp_;
volatile bool didRun_;
volatile bool termRequest_;
int running_duration_;
bool
shouldStart (lumiera::Option&)
{
Literal startSpec (extractID ("start",spec_));
return "true" ==startSpec
|| "fail" ==startSpec
|| "throw"==startSpec;
}
bool
start (lumiera::Option&, Subsys::SigTerm termination)
{
REQUIRE (!isUp_, "attempt to start %s twice!", cStr(*this));
Lock guard (this);
Literal startSpec (extractID ("start",spec_));
ASSERT (!isnil (startSpec));
if ("true"==startSpec) //----simulate successful subsystem start
{
Thread (id_, bind (&MockSys::run, this, termination)); /////TODO: the thread description should be rather "Test: "+string(*this), but this requires to implement the class Literal as planned
isUp_ = true;
}
else
if ("fail"==startSpec) //----not starting, incorrectly reporting success
return true;
else
if ("throw"==startSpec) //---starting flounders
throw error::Fatal("simulated failure to start the subsystem", LUMIERA_ERROR_TEST);
return isUp_;
}
void
triggerShutdown () throw()
{
// note: *not* locking here...
termRequest_ = true;
INFO (test, "triggerShutdown() --> %s....", cStr(*this));
}
bool
checkRunningState () throw()
{
// note: *not* locking here...
return isUp_;
}
void
run (Subsys::SigTerm termination) ///< simulates a "running" subsystem
{
Literal runSpec (extractID ("run",spec_));
ASSERT (!isnil (runSpec));
if ("false"!=runSpec) //----actually enter running state for some time
{
didRun_ = true;
running_duration_ = MIN_RUNNING_TIME_ms;
running_duration_ += (rand() % (MAX_RUNNING_TIME_ms - MIN_RUNNING_TIME_ms));
INFO (test, "thread %s now running....", cStr(*this));
Lock wait_blocking (this, &MockSys::tick);
}
Error problemIndicator("simulated Problem killing a subsystem",LUMIERA_ERROR_TEST);
lumiera_error(); // reset error state....
// Note: in real life this actually
// would be an catched exception!
string problemReport (problemIndicator.what());
{
Lock guard (this);
isUp_ = false;
INFO (test, "thread %s terminates.", cStr(*this));
termination ("true"==runSpec? 0 : &problemReport);
} }
bool
tick () ///< simulates async termination, either on request or by timing
{
Lock sync(this);
if (!sync.isTimedWait())
{
sync.setTimeout(TICK_DURATION_ms);
running_duration_ += TICK_DURATION_ms;
}
running_duration_ -= TICK_DURATION_ms;
return termRequest_ || running_duration_ <= 0;
}
public:
MockSys(Literal id, Literal spec)
: id_(id),
spec_(spec),
isUp_(false),
didRun_(false),
termRequest_(false),
running_duration_(0)
{ }
~MockSys() { }
operator string () const { return "MockSys(\""+id_+"\")"; }
friend inline ostream&
operator<< (ostream& os, MockSys const& mosi) { return os << string(mosi); }
bool didRun () const { return didRun_; }
};
} // (End) test classes and data....
/**************************************************************************
* @test managing start and stop of several dependent "subsystems"
* under various conditions. Using mock-subsystems, which actually
* spawn a thread and finish by themselves and generally behave sane.
* For each such MockSys, we can define a behaviour pattern, e.g.
* weather the start succeeds and if the run terminates with error.
*
* @see lumiera::Subsys
* @see lumiera::SubsystemRunner
* @see lumiera::AppState
* @see main.cpp
*/
class SubsystemRunner_test : public Test
{
virtual void
run (Arg)
{
singleSubsys_complete_cycle();
singleSubsys_start_failure();
singleSubsys_emegency_exit();
dependentSubsys_complete_cycle();
dependentSubsys_start_failure();
}
void
singleSubsys_complete_cycle()
{
cout << "-----singleSubsys_complete_cycle-----\n";
MockSys unit ("one", "start(true), run(true).");
SubsystemRunner runner(dummyOpt);
REQUIRE (!unit.isRunning());
REQUIRE (!unit.didRun());
runner.maybeRun (unit);
bool emergency = runner.wait();
ASSERT (!emergency);
ASSERT (!unit.isRunning());
ASSERT (unit.didRun());
}
void
singleSubsys_start_failure()
{
cout << "-----singleSubsys_start_failure-----\n";
MockSys unit1 ("U1", "start(false), run(false).");
MockSys unit2 ("U2", "start(throw), run(false).");
MockSys unit3 ("U3", "start(fail), run(false)."); // simulates incorrect behaviour
MockSys unit4 ("U4", "start(true), run(false).");
SubsystemRunner runner(dummyOpt);
runner.maybeRun (unit1); // this one doesn't start at all, which isn't considered an error
try
{
runner.maybeRun (unit2);
NOTREACHED;
}
catch (lumiera::Error&)
{
ASSERT (lumiera_error() == LUMIERA_ERROR_TEST);
}
try
{
runner.maybeRun (unit3);
NOTREACHED;
}
catch (lumiera::Error&)
{
ASSERT (lumiera_error() == error::LUMIERA_ERROR_LOGIC); // incorrect behaviour trapped
}
try
{
runner.maybeRun (unit4);
}
catch (lumiera::Error&)
{
ASSERT (lumiera_error() == error::LUMIERA_ERROR_LOGIC); // detected that the subsystem didn't come up
} // (due to the way the test subsystem is written,
// this may not always be detected, because there
// is a short time window where isUp_==true )
runner.wait();
ASSERT (!unit1.isRunning());
ASSERT (!unit2.isRunning());
ASSERT (!unit3.isRunning());
ASSERT (!unit4.isRunning());
ASSERT (!unit1.didRun());
ASSERT (!unit2.didRun());
ASSERT (!unit3.didRun());
ASSERT (!unit4.didRun());
}
void
singleSubsys_emegency_exit()
{
cout << "-----singleSubsys_emegency_exit-----\n";
MockSys unit ("one", "start(true), run(fail).");
SubsystemRunner runner(dummyOpt);
runner.maybeRun (unit);
bool emergency = runner.wait();
ASSERT (emergency); // emergency state got propagated
ASSERT (!unit.isRunning());
ASSERT (unit.didRun());
}
void
dependentSubsys_complete_cycle()
{
cout << "-----dependentSubsys_complete_cycle-----\n";
MockSys unit1 ("U1", "start(true), run(true).");
MockSys unit2 ("U2", "start(true), run(true).");
MockSys unit3 ("U3", "start(true), run(true).");
MockSys unit4 ("U4", "start(true), run(true).");
unit2.depends (unit1);
unit4.depends (unit3);
unit4.depends (unit1);
unit3.depends (unit2);
SubsystemRunner runner(dummyOpt);
runner.maybeRun (unit4);
ASSERT (unit1.isRunning());
ASSERT (unit2.isRunning());
ASSERT (unit3.isRunning());
ASSERT (unit4.isRunning());
bool emergency = runner.wait();
ASSERT (!emergency);
ASSERT (!unit1.isRunning());
ASSERT (!unit2.isRunning());
ASSERT (!unit3.isRunning());
ASSERT (!unit4.isRunning());
ASSERT (unit1.didRun());
ASSERT (unit2.didRun());
ASSERT (unit3.didRun());
ASSERT (unit4.didRun());
}
void
dependentSubsys_start_failure()
{
cout << "-----dependentSubsys_start_failure-----\n";
MockSys unit1 ("U1", "start(true), run(true).");
MockSys unit2 ("U2", "start(true), run(true).");
MockSys unit3 ("U3", "start(false),run(false)."); // note
MockSys unit4 ("U4", "start(true), run(true).");
unit2.depends (unit1);
unit4.depends (unit3);
unit4.depends (unit1);
unit3.depends (unit2);
SubsystemRunner runner(dummyOpt);
try
{
runner.maybeRun (unit4);
NOTREACHED;
}
catch (lumiera::Error&)
{
ASSERT (lumiera_error() == error::LUMIERA_ERROR_LOGIC); // failure to bring up prerequisites is detected
}
ASSERT ( unit1.isRunning());
ASSERT ( unit2.isRunning());
ASSERT (!unit3.isRunning());
// shutdown has been triggered for unit4, but may require some time
bool emergency = runner.wait();
ASSERT (!emergency); // no problems with the subsystems actually running...
ASSERT (!unit1.isRunning());
ASSERT (!unit2.isRunning());
ASSERT (!unit3.isRunning());
ASSERT (!unit4.isRunning());
ASSERT ( unit1.didRun());
ASSERT ( unit2.didRun());
ASSERT (!unit3.didRun());
// can't say for sure if unit4 actually did run
}
};
/** Register this test class... */
LAUNCHER (SubsystemRunner_test, "function common");
} // namespace test
} // namespace lumiera

View file

@ -95,4 +95,4 @@ namespace lib {
} // namespace test
} // namespace lumiera
} // namespace lib

View file

@ -206,4 +206,4 @@ namespace lib {
} // namespace test
} // namespace lumiera
} // namespace lib

View file

@ -0,0 +1,140 @@
/*
SyncTimedwait(Test) - check the monitor object based timed condition wait
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 "lib/error.hpp"
#include "lib/sync.hpp"
#include <iostream>
using std::cout;
using test::Test;
namespace lib {
namespace test {
namespace { // private test classes and data...
const uint WAIT_mSec = 200; ///< milliseconds to wait before timeout
} // (End) test classes and data....
/********************************************************************************
* @test timeout feature on condition wait as provided by pthread and accessible
* via the object monitor based locking/waiting mechanism. Without creating
* multiple threads, we engage into a blocking wait, which aborts due to
* setting a timeout. (Note it is discouraged to use the timed wait feature;
* when possible, you should prefer relying on the Lumiera scheduler)
*
* @see SyncWaiting_test
* @see sync::Timeout
* @see sync.hpp
*/
class SyncTimedwait_test
: public Test,
Sync<RecursiveLock_Waitable>
{
friend class Lock; // allows inheriting privately from Sync
virtual void
run (Arg)
{
checkTimeoutStruct();
Lock block(this, &SyncTimedwait_test::neverHappens);
cout << "back from LaLaLand, alive and thriving!\n";
ASSERT (block.isTimedWait());
}
bool
neverHappens() ///< the "condition test" used for waiting....
{
Lock currentLock(this); // get the Lock recursively
if (!currentLock.isTimedWait()) // right from within the condition test:
currentLock.setTimeout(WAIT_mSec); // switch waiting mode to timed wait and set timeout
return false;
}
void
checkTimeoutStruct()
{
sync::Timeout tout;
ASSERT (!tout);
ASSERT (0 == tout.tv_sec);
ASSERT (0 == tout.tv_nsec);
tout.setOffset (0);
ASSERT (!tout);
ASSERT (0 == tout.tv_sec);
ASSERT (0 == tout.tv_nsec);
timespec ref;
clock_gettime(CLOCK_REALTIME, &ref);
tout.setOffset (1);
ASSERT (tout);
ASSERT (0 < tout.tv_sec);
ASSERT (ref.tv_sec <= tout.tv_sec);
ASSERT (ref.tv_nsec <= 1000000 + tout.tv_nsec || ref.tv_nsec > 1000000000-100000);
clock_gettime(CLOCK_REALTIME, &ref);
tout.setOffset (1000);
ASSERT (tout);
if (ref.tv_nsec!=0) // should have gotten an overflow to the seconds part
{
ASSERT (ref.tv_sec <= 2 + tout.tv_sec );
ASSERT ((ref.tv_nsec + 1000000 * 999) % 1000000000
<= tout.tv_nsec);
}
}
};
/** Register this test class... */
LAUNCHER (SyncTimedwait_test, "unit common");
} // namespace test
} // namespace lib

View file

@ -185,4 +185,4 @@ namespace lib {
} // namespace test
} // namespace lumiera
} // namespace lib

View file

@ -46,7 +46,8 @@ namespace util
/** @test for util::Cmdline, wrapping various example cmdlines */
class CmdlineWrapper_test : public Test
{
virtual void run (Arg arg)
void
run (Arg)
{
testLine("");
testLine("\n\t ");

View file

@ -41,7 +41,8 @@ namespace test
*/
class TestOption_test : public Test
{
virtual void run(Arg arg)
void
run (Arg)
{
noOptions();
help();
@ -53,6 +54,7 @@ namespace test
additionalCmd2();
}
/** @test performs the actual test for the option parser test::TestOption */
void doIt (const string cmdline)
{

View file

@ -73,7 +73,7 @@ namespace lumiera
{
for (uint i=0; i<cnt_; ++i)
heapArray_[i] = lexical_cast<string>(i);
cout << format("ctor TargetObj(%i) successfull\n") % cnt_;
cout << format("ctor TargetObj(%i) successful\n") % cnt_;
}
@ -82,7 +82,7 @@ namespace lumiera
{
delete heapData_;
delete[] heapArray_;
cout << format("dtor ~TargetObj(%i) successfull\n") % cnt_;
cout << format("dtor ~TargetObj(%i) successful\n") % cnt_;
}

View file

@ -0,0 +1,117 @@
/*
ThreadWrapper(Test) - starting threads and passing context
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/symbol.hpp"
#include "lib/thread-wrapper.hpp"
#include "lib/sync.hpp"
#include <tr1/functional>
using std::tr1::bind;
using test::Test;
namespace lib {
namespace test {
namespace { // private test classes and data...
ulong sum;
ulong checksum;
const uint NUM_THREADS = 20;
const uint MAX_RAND_SUMMAND = 100;
uint
createVal() ///< generating test values, remembering the sum
{
uint val(rand() % MAX_RAND_SUMMAND);
checksum += val;
return val;
}
struct TestThread : Thread
{
TestThread()
: Thread("test Thread creation",
bind (&TestThread::theOperation, this, createVal(), createVal()))
{ } // note the binding (functor object) is passed as anonymous temporary
void
theOperation (uint a, uint b) ///< the actual operation running in a separate thread
{
Lock sync(this); // *not* a recursive lock, because parent unlocks prior to invoking the operation
sum += (a+b);
}
};
} // (End) test classes and data....
/**************************************************************************
* @test use the Lumiera backend to create some new threads, utilising the
* lumiera::Thread wrapper for binding to an arbitrary operation
* and passing the appropriate context.
*
* @see lib::Thread
* @see threads.h
*/
class ThreadWrapper_test : public Test
{
virtual void
run (Arg)
{
sum = checksum = 0;
TestThread instances[NUM_THREADS] SIDEEFFECT;
ASSERT (0 < sum);
ASSERT (sum==checksum);
}
};
/** Register this test class... */
LAUNCHER (ThreadWrapper_test, "function common");
} // namespace test
} // namespace lib

View file

@ -62,6 +62,7 @@ namespace lib {
*/
TransDummy (const TransDummy& o)
: Dummy()
{
TRACE (test, "COPY-ctor TransDummy( ref=%x ) --> this=%x", &o,this);
ASSERT (!o, "protocol violation: real copy operations inhibited");
@ -113,18 +114,17 @@ namespace lib {
/**********************************************************************************
* @test ScopedHolder and ScopedPtrHolder are initially empty and copyable.
* After taking ownership, they prohibit copy operations, manage the
* lifecycle of the contained object and provide smart-ptr like access.
* A series of identical tests is conducted both with the ScopedPtrHolder
* (the contained objects are heap allocated but managed by the holder)
* and with the ScopedHolder (objects placed inline)
* @test growing (re-allocating) a vector with noncopyable objects, with the
* help of a special Allocator and a custom \c transfer_control operation
* provided by the contained objects. The idea is to allow some special
* copy-operations for the purpose of re-allocations within the vector,
* without requiring the object to be really copyable.
*/
class VectorTransfer_test : public Test
{
virtual void
run (Arg arg)
run (Arg)
{
cout << "\n..setup table space for 2 elements\n";
TransDummyVector table;

View file

@ -97,7 +97,7 @@ namespace lumiera
template<class TOOLImpl>
static Tag&
get (TOOLImpl* const concreteTool=0)
get (TOOLImpl* const =0) // param used to pass type info
{
// we have a race condition here...
Tag& t = TagTypeRegistry<TOOL,TOOLImpl>::tag;
@ -410,7 +410,7 @@ namespace lumiera
*/
class VisitingTool_concept : public Test
{
virtual void run(Arg arg)
virtual void run(Arg)
{
known_visitor_known_class();
visitor_not_visiting_some_class();