lumiera_/src/common/guifacade.cpp
Ichthyostega cb31b145c3 GuiStart: further streamlined the invocation code
due to investigating that Heisenbug, I understand the storage layout
more clearly. It occured to me that there is no reason to copy the
terminationHandler (functor) into an instance variable, since it is
easily possible to keep all of the invocation and error handling
confined within the scope of the run function, i.e. on stack.

So the effective memory layout does not change, but the legibility
of the code is improved, since we're able to remove the dtor and
simplyfy the ctor and avoid most of the member fields.
2017-01-19 23:52:30 +01:00

205 lines
5.9 KiB
C++

/*
GuiFacade - access point for communicating with the Lumiera GTK 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.
* *****************************************************/
#include "gui/guifacade.hpp"
#include "include/gui-notification-facade.h"
#include "lib/sync.hpp"
#include "lib/error.hpp"
#include "lib/depend.hpp"
#include "lib/functor-util.hpp"
#include "common/instancehandle.hpp"
#include <memory>
#include <string>
namespace gui {
using std::string;
using std::unique_ptr;
using lumiera::Subsys;
using lumiera::InstanceHandle;
using lib::Sync;
using lib::RecursiveLock_NoWait;
/** load and start the GUI as a plugin */
struct GuiRunner
: public GuiFacade
{
typedef InstanceHandle<LUMIERA_INTERFACE_INAME(lumieraorg_Gui, 1)> GuiHandle;
GuiHandle theGUI_;
GuiRunner (Subsys::SigTerm terminationHandle)
: theGUI_("lumieraorg_Gui", 1, 1, "lumieraorg_GuiStarterPlugin") // load GuiStarterPlugin
{
ASSERT (theGUI_);
bool res = this->launchUI (terminationHandle);
if (!res || lumiera_error_peek())
throw lumiera::error::Fatal("failed to bring up GUI",lumiera_error());
}
bool launchUI (Subsys::SigTerm& terminationHandle) override
{
return theGUI_->launchUI (reinterpret_cast<void*> (&terminationHandle));
}
};
namespace { // implementation of GUI-"Subsystem" : start GUI through GuiStarterPlugin
unique_ptr<GuiRunner> facade;
class GuiSubsysDescriptor
: public lumiera::Subsys,
public Sync<RecursiveLock_NoWait>
{
operator string () const { return "Lumiera GTK GUI"; }
bool
shouldStart (lumiera::Option& opts) override
{
if (opts.isHeadless() or 0 < opts.getPort())
{
INFO (guifacade, "*not* starting the GUI...");
return false;
}
else
return true;
}
bool
start (lumiera::Option&, Subsys::SigTerm termNotification) override
{
Lock guard (this);
if (facade)
return false; // already started
facade.reset ( // trigger loading of the GuiStarterPlugin...
new GuiRunner (
[=] (string* problemMessage)
{ // will be invoked when the UI thread exits
closeGuiModule();
termNotification(problemMessage);
}));
return true;
}
/**
* @warning there is a possible race here,
* when shutdown is triggered before the GUI was able to open the GuiNotification interface.
* However, the Lumiera thread handling wrapper/framework ensures that a new thread actually
* starts to execute (and picks up the arguments), prior to returning from the thread starting
* function. For this reason, it is rather unlikely this race actually happens in practice,
* since opening the GuiNotification interface is done early, while starting the UI-Bus.
* @todo ////////////////////////////////////////////////////////TICKET #1048 : maybe use Thread.sync (barrier) to work around this?
*/
void
triggerShutdown () noexcept override
{
try { GuiNotification::facade().triggerGuiShutdown ("Application shutdown"); }
ERROR_LOG_AND_IGNORE (guifacade, "trigger shutdown of the GUI");
}
bool
checkRunningState () noexcept override
{
return bool(facade);
}
void
closeGuiModule ()
{
Lock guard (this);
if (!facade)
{
WARN (guifacade, "Termination signal invoked, but GUI is currently closed. "
"Probably this is due to some broken startup logic and should be fixed.");
}
else
facade.reset (0);
}
public:
GuiSubsysDescriptor() { }
~GuiSubsysDescriptor()
{
if (facade)
{
WARN (guifacade, "GUI subsystem terminates, but GuiFacade isn't properly closed. "
"Closing it forcedly; this indicates broken startup logic and should be fixed.");
try { facade.reset (0); }
ERROR_LOG_AND_IGNORE (guifacade, "forcibly closing the GUI");
ENSURE (not lumiera_error_peek());
}
}
};
lib::Depend<GuiSubsysDescriptor> theDescriptor;
} // (End) impl details
/** @internal intended for use by main(). */
lumiera::Subsys&
GuiFacade::getDescriptor()
{
return theDescriptor();
}
bool
GuiFacade::isUp ()
{
return bool(facade);
}
} // namespace gui
/*
// Local Variables:
// mode: C++
// c-file-style: "gnu"
// indent-tabs-mode: nil
// End:
*/