-/*
-This file is part of libmspframework
-Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006-2008, 2011 Mikko Rasa, Mikkosoft Productions
Distributed under the LGPL
*/
+
#include <signal.h>
#include <iostream>
+#include <typeinfo>
+#include "../debug/backtrace.h"
+#include "../debug/demangle.h"
+#include "../time/units.h"
+#include "../time/utils.h"
#include "application.h"
-#include "error.h"
+#include "except.h"
using namespace std;
namespace Msp {
-Poller::Slot &Application::add_pollable(Pollable *obj, short events)
-{
- if(!poller_)
- poller_=new Poller;
-
- Poller::Slot &slot=poller_->add_pollable(obj, events);
- // Interrupt a possible poll in progress
-#ifndef WIN32 //XXX
- pthread_kill(main_tid, SIGALRM);
-#endif
- return slot;
-}
-
-EventManager::Event &Application::create_event()
-{
- if(!ev_mgr_)
- ev_mgr_=new EventManager(*this);
-
- return ev_mgr_->create_event();
-}
+Application *Application::app_ = 0;
+Application::Starter *Application::starter_ = 0;
+void *Application::data_ = 0;
-Application::~Application()
-{
- if(poller_)
- delete poller_;
- if(ev_mgr_)
- delete ev_mgr_;
-}
+Application::Application():
+ exit_code(0)
+{ }
/**
Constructs an instance of the registered application class and runs it. If the
This function can only be called once. The global main() function provided by
the library normally does it automatically at program startup.
*/
-int Application::run(int argc, char **argv)
+int Application::run(int argc, char **argv, void *data)
{
- static bool called=false;
+ static bool called = false;
if(called)
{
cerr<<"Trying to call Application::run_app twice!\n";
return 125;
}
- called=true;
+ called = true;
- if(!reg_app_)
+ if(!starter_)
{
- cerr<<"Trying to run with no application class registered!\n";
+ cerr<<"Trying to run with no RegisteredApplication class!\n";
return 126;
}
-#ifndef WIN32 //XXX
- signal(SIGALRM, &sigalrm_);
-#endif
-
+ data_ = data;
+
try
{
- app_=reg_app_->create_app(argc, argv);
+ try
+ {
+ app_ = starter_->create_app(argc, argv);
+ }
+ catch(const UsageError &e)
+ {
+ starter_->usage(e.what(), argv[0], e.get_brief());
+ return 1;
+ }
+
+ int result = app_->main();
+ Application *a = app_;
+ app_ = 0;
+ delete a;
+ return result;
}
- catch(const UsageError &e)
+ catch(const exception &e)
{
- reg_app_->usage(argv[0], e.get_brief());
- return 1;
- }
+ delete app_;
+
+#ifdef WIN32
+ string msg = Debug::demangle(typeid(e).name())+":\n"+e.what();
+ MessageBoxA(0, msg.c_str(), "Uncaught exception", MB_OK|MB_ICONERROR);
+#else
+ cerr<<"An uncaught exception occurred.\n";
+ cerr<<" type: "<<Debug::demangle(typeid(e).name())<<'\n';
+ cerr<<" what(): "<<e.what()<<'\n';
+
+ const Exception *exc = dynamic_cast<const Exception *>(&e);
+ if(exc && !exc->get_backtrace().get_frames().empty())
+ {
+ cerr<<" backtrace:\n";
+ const list<Debug::Backtrace::StackFrame> &frames = exc->get_backtrace().get_frames();
+ for(list<Debug::Backtrace::StackFrame>::const_iterator i=frames.begin(); i!=frames.end(); ++i)
+ cerr<<" "<<*i<<'\n';
+ }
+#endif
- int result=app_->main();
- delete app_;
- return result;
+ return 124;
+ }
}
/**
Prints a message describing the usage of the application. The default version
will blame the programmer for being lazy.
-@param argv0 The value of argv[0], to be used in the message
-@param brief Whether to print a brief or long usage message
+@param reason Why the function was called
+@param argv0 The value of argv[0], to be used in the message
+@param brief Whether to print a brief or long usage message
*/
-void Application::usage(const char *, bool)
+void Application::usage(const char *reason, const char *, bool)
{
+ if(reason)
+ cerr<<"UsageError: "<<reason<<'\n';
cerr<<"The programmer was lazy and didn't write a usage() function for this application.\n";
}
-Application::Application():
- exit_code(0),
- tick_mode_(IDLE),
- poller_(0),
- ev_mgr_(0)
-#ifndef WIN32
- //XXX Figure out how to get the current thread on win32
- ,main_tid(pthread_self())
-#endif
-{ }
-
/**
-Default main loop. Calls tick() periodically if do_ticks is true, otherwise
-just sleeps. A custom main loop should monitor the done member variable and
-return exit_code.
+Default main loop. Calls tick() repeatedly until exit() is called. A custom
+main loop should monitor the done member variable and return exit_code.
*/
int Application::main()
{
- done=false;
+ done = false;
while(!done)
- {
- if(tick_mode_==IDLE)
- {
- if(poller_)
- poller_->poll(0);
- tick();
-#ifdef WIN32
- Sleep(0);
-#else
- sched_yield();
-#endif
- }
- else
- {
- if(poller_)
- poller_->poll(-1);
- else
- {
-#ifdef WIN32
- Sleep(1);
-#else
- timespec ts={1000,0};
- nanosleep(&ts, 0);
-#endif
- }
- if(tick_mode_!=NONE)
- tick();
- }
- }
+ tick();
return exit_code;
}
signal(s, &sighandler_);
}
-void Application::set_tick_mode(TickMode t)
-{
- tick_mode_=t;
-#ifndef WIN32 //XXX
- pthread_kill(main_tid, SIGALRM);
-#endif
-}
-
/**
Causes the application to exit gracefully with the given exit code.
*/
void Application::exit(int c)
{
- done=true;
- exit_code=c;
-#ifndef WIN32 //XXX
- pthread_kill(main_tid, SIGALRM);
-#endif
+ done = true;
+ exit_code = c;
}
+/**
+Static wrapper function to call a member function of the Application instance.
+*/
void Application::sighandler_(int s)
{
app_->sighandler(s);
}
-Application::RegBase::RegBase()
+
+Application::Starter::Starter()
{
- if(reg_app_)
- {
- cerr<<"Warning: registering the application twice\n";
- delete reg_app_;
- }
+ if(starter_)
+ throw InvalidState("Can't create more than one Starter instance");
- reg_app_=this;
+ starter_ = this;
}
-Application *Application::app_=0;
-Application::RegBase *Application::reg_app_=0;
-
} // namespace Msp