summaryrefslogtreecommitdiffstats
path: root/gmcop/giomanager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gmcop/giomanager.cc')
-rw-r--r--gmcop/giomanager.cc506
1 files changed, 506 insertions, 0 deletions
diff --git a/gmcop/giomanager.cc b/gmcop/giomanager.cc
new file mode 100644
index 0000000..2518008
--- /dev/null
+++ b/gmcop/giomanager.cc
@@ -0,0 +1,506 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+
+#include "giomanager.h"
+#include "notification.h"
+#include "debug.h"
+#include "dispatcher.h"
+#include <glib.h>
+
+using namespace Arts;
+using namespace std;
+
+/*
+ * Fallback for the case where we should perform blocking
+ */
+namespace Arts {
+class GIOManagerBlocking : public StdIOManager {
+public:
+ void setLevel(int newLevel) { level = newLevel; }
+};
+}
+
+namespace Arts {
+ struct GIOManagerSource
+ {
+ GSource source;
+ GIOManager *ioManager;
+ };
+ struct GIOWatch
+ {
+ GPollFD gpollfd; /* <- must be the first data member */
+
+ /* GPollFD =
+ gint fd;
+ gushort events;
+ gushort revents;
+ */
+ GIOWatch(GIOManagerSource *source, int fd, int types, IONotify *notify)
+ : types(types), registered(false), notify(notify), source(source)
+ {
+ gpollfd.fd = fd;
+ setTypes(types);
+ }
+ void setTypes(int types)
+ {
+ this->types = types;
+ gpollfd.events = gpollfd.revents = 0;
+
+ if(types & IOType::read)
+ gpollfd.events |= G_IO_IN | G_IO_HUP;
+ if(types & IOType::write)
+ gpollfd.events |= G_IO_OUT;
+ if(types & IOType::except)
+ gpollfd.events |= G_IO_ERR;
+
+ }
+ /* prepares source for running in event loop level "level"
+ * removes source unconditionally if level == -1
+ */
+ void prepare(int level)
+ {
+ gpollfd.revents = 0;
+
+ bool shouldRegister = true;
+ if(level != 1 && (types & IOType::reentrant) == 0)
+ shouldRegister = false;
+ if(level == -1)
+ shouldRegister = false;
+
+ if(shouldRegister == registered)
+ return;
+
+ if(shouldRegister)
+ {
+ g_source_add_poll(&source->source, &this->gpollfd);
+ }
+ else
+ {
+ g_source_remove_poll(&source->source, &this->gpollfd);
+ }
+
+ registered = shouldRegister;
+ }
+ int check()
+ {
+ int result = 0;
+
+ if(gpollfd.revents & (G_IO_IN | G_IO_HUP))
+ result |= IOType::read;
+ if(gpollfd.revents & G_IO_OUT)
+ result |= IOType::write;
+ if(gpollfd.revents & G_IO_ERR)
+ result |= IOType::except;
+
+ return result;
+ }
+ void destroy()
+ {
+ /* TODO: if active do this, else do that */
+ delete this;
+ }
+ ~GIOWatch()
+ {
+ prepare(-1);
+ }
+
+ int types;
+ bool registered;
+ IONotify *notify;
+ GIOManagerSource *source;
+ };
+
+class GIOTimeWatch {
+ int milliseconds;
+ TimeNotify *_notify;
+ timeval nextNotify;
+ bool active, destroyed;
+public:
+ GIOTimeWatch(int milliseconds, TimeNotify *notify)
+ : milliseconds(milliseconds), _notify(notify),
+ active(false),destroyed(false)
+ {
+ gettimeofday(&nextNotify,0);
+
+ nextNotify.tv_usec += (milliseconds%1000)*1000;
+ nextNotify.tv_sec += (milliseconds/1000)+(nextNotify.tv_usec/1000000);
+ nextNotify.tv_usec %= 1000000;
+ }
+ int msUntilNextNotify(const timeval& now)
+ {
+ int result = (nextNotify.tv_sec - now.tv_sec)*1000
+ + (nextNotify.tv_usec - now.tv_usec)/1000;
+
+ if(result < 0) result = 0;
+ return result;
+ }
+ void advance(const timeval& currentTime)
+ {
+ active = true;
+ while(msUntilNextNotify(currentTime) == 0)
+ {
+ nextNotify.tv_usec += (milliseconds%1000)*1000;
+ nextNotify.tv_sec += (milliseconds/1000)
+ +(nextNotify.tv_usec/1000000);
+ nextNotify.tv_usec %= 1000000;
+
+ _notify->notifyTime();
+
+ if(destroyed)
+ {
+ delete this;
+ return;
+ }
+ }
+ active = false;
+ }
+ void destroy()
+ {
+ if(active)
+ {
+ destroyed = true;
+ }
+ else
+ {
+ delete this;
+ }
+ }
+ TimeNotify *notify()
+ {
+ return _notify;
+ }
+};
+
+static gboolean GIOManager_prepare(GSource *source, gint *timeout)
+{
+ return((GIOManagerSource *)source)->ioManager->prepare(timeout);
+}
+
+static gboolean GIOManager_check(GSource *source)
+{
+ return((GIOManagerSource *)source)->ioManager->check();
+}
+
+static gboolean GIOManager_dispatch(GSource *source, GSourceFunc callback,
+ gpointer user_data)
+{
+ return((GIOManagerSource *)source)->ioManager->dispatch(callback,user_data);
+}
+
+
+}
+
+GIOManager::GIOManager(GMainContext *context)
+ : level(0), context(context)
+{
+ static GSourceFuncs funcs =
+ {
+ GIOManager_prepare,
+ GIOManager_check,
+ GIOManager_dispatch,
+ 0
+ };
+
+ source = (GIOManagerSource *)g_source_new(&funcs, sizeof(GIOManagerSource));
+ source->ioManager = this;
+ g_source_set_can_recurse(&source->source, true);
+ g_source_attach(&source->source, context);
+
+ gioManagerBlocking = new GIOManagerBlocking();
+ _blocking = true;
+ fileDescriptorsNeedRecheck = false;
+}
+
+GIOManager::~GIOManager()
+{
+ g_source_unref(&source->source);
+ delete gioManagerBlocking;
+}
+
+void GIOManager::processOneEvent(bool blocking)
+{
+ if(_blocking)
+ {
+ level++;
+ if(level == 1)
+ Dispatcher::lock();
+
+ /*
+ * we explicitly take the level to gioManagerBlocking, so that it
+ * will process reentrant watchFDs only
+ */
+ fileDescriptorsNeedRecheck = true;
+ gioManagerBlocking->setLevel(level);
+ gioManagerBlocking->processOneEvent(blocking);
+
+ if(level == 1)
+ Dispatcher::unlock();
+ level--;
+ }
+ else
+ {
+ g_main_context_iteration(context, blocking);
+ }
+}
+
+void GIOManager::setBlocking(bool blocking)
+{
+ _blocking = blocking;
+}
+
+void GIOManager::run()
+{
+ arts_warning("GIOManager::run not implemented yet");
+}
+
+void GIOManager::terminate()
+{
+ arts_warning("GIOManager::terminate not implemented yet");
+}
+
+void GIOManager::watchFD(int fd, int types, IONotify * notify)
+{
+ fdList.push_back(new GIOWatch(source, fd, types, notify));
+
+ if(types & IOType::reentrant)
+ gioManagerBlocking->watchFD(fd, types, notify);
+}
+
+void GIOManager::remove(IONotify *notify, int types)
+{
+ list<GIOWatch *>::iterator i;
+
+ i = fdList.begin();
+ while(i != fdList.end())
+ {
+ GIOWatch *w = *i;
+
+ if(w->notify == notify)
+ {
+ int newTypes = w->types & (~types);
+
+ if(newTypes)
+ {
+ w->setTypes(newTypes);
+ }
+ else
+ {
+ w->destroy();
+ fdList.erase(i);
+ i = fdList.begin();
+ }
+ }
+ else i++;
+ }
+ gioManagerBlocking->remove(notify, types);
+}
+
+void GIOManager::addTimer(int milliseconds, TimeNotify *notify)
+{
+ timeList.push_back(new GIOTimeWatch(milliseconds,notify));
+}
+
+void GIOManager::removeTimer(TimeNotify *notify)
+{
+ list<GIOTimeWatch *>::iterator i;
+
+ i = timeList.begin();
+ while(i != timeList.end())
+ {
+ GIOTimeWatch *w = *i;
+
+ if(w->notify() == notify)
+ {
+ w->destroy();
+ timeList.erase(i);
+ i = timeList.begin();
+ }
+ else i++;
+ }
+}
+
+gboolean GIOManager::prepare(gint *timeout)
+{
+ *timeout = 10000;
+
+ level++;
+
+ if(level == 1)
+ Dispatcher::lock();
+
+ /* handle timers - only at level 1 */
+ if(level == 1 && timeList.size())
+ {
+ struct timeval currenttime;
+ gettimeofday(&currenttime,0);
+
+ list<GIOTimeWatch *>::iterator ti;
+
+ ti = timeList.begin();
+ while(ti != timeList.end())
+ {
+ GIOTimeWatch *w = *ti++;
+ int ms = w->msUntilNextNotify(currenttime);
+
+ if(ms < *timeout) *timeout = ms;
+ }
+ }
+
+ list<GIOWatch *>::iterator i;
+ for(i = fdList.begin(); i != fdList.end(); i++)
+ {
+ GIOWatch *w = *i;
+ w->prepare(level);
+ }
+ fileDescriptorsNeedRecheck = false;
+
+ if(level == 1 && NotificationManager::the()->pending())
+ *timeout = 0;
+
+ if(level == 1)
+ Dispatcher::unlock();
+
+ level--;
+
+ return (*timeout == 0);
+}
+
+gboolean GIOManager::check()
+{
+ gboolean result = false;
+ level++;
+
+ if(level == 1)
+ Dispatcher::lock();
+
+ /*
+ * handle timers - only at level 1
+ */
+ if(level == 1 && timeList.size())
+ {
+ struct timeval currenttime;
+ gettimeofday(&currenttime,0);
+
+ list<GIOTimeWatch *>::iterator ti;
+
+ ti = timeList.begin();
+ while(ti != timeList.end() && !result)
+ {
+ GIOTimeWatch *w = *ti++;
+ if(w->msUntilNextNotify(currenttime) == 0)
+ result = true;
+ }
+ }
+
+ /*
+ * handle filedescriptors
+ */
+ list<GIOWatch *>::iterator i;
+ for(i = fdList.begin(); i != fdList.end(); i++) {
+ GIOWatch *w = *i;
+ int match = w->check();
+
+ if((w->types & IOType::reentrant) == 0 && level != 1)
+ {
+ arts_assert(match == 0);
+ }
+ if(match)
+ {
+ result = true;
+ }
+ }
+ fileDescriptorsNeedRecheck = false;
+
+ /*
+ * check for notifications
+ */
+ if(level == 1 && NotificationManager::the()->pending())
+ result = true;
+
+ if(level == 1)
+ Dispatcher::unlock();
+
+ level--;
+
+ return result;
+}
+
+gboolean GIOManager::dispatch(GSourceFunc /* func */, gpointer /* user_data */)
+{
+ bool done = false;
+
+ level++;
+
+ if(level == 1)
+ Dispatcher::lock();
+
+ // notifications not carried out reentrant
+ if(!done && level == 1 && NotificationManager::the()->pending())
+ {
+ NotificationManager::the()->run();
+ done = true;
+ }
+
+ // handle filedescriptor things
+ if(!done && !fileDescriptorsNeedRecheck)
+ {
+ list<GIOWatch *>::iterator i;
+ for(i = fdList.begin(); i != fdList.end(); i++) {
+ GIOWatch *w = *i;
+ int match = w->check();
+
+ if((w->types & IOType::reentrant) == 0 && level != 1)
+ {
+ arts_assert(match == 0);
+ }
+ if(match)
+ {
+ w->notify->notifyIO(w->gpollfd.fd,match);
+ done = true;
+ break;
+ }
+ }
+ }
+
+ // handle timers - only at level 1
+ if(!done && level == 1 && timeList.size())
+ {
+ struct timeval currenttime;
+ gettimeofday(&currenttime,0);
+
+ list<GIOTimeWatch *>::iterator ti;
+
+ ti = timeList.begin();
+ while(ti != timeList.end())
+ {
+ GIOTimeWatch *w = *ti++;
+ w->advance(currenttime);
+ }
+ }
+
+ if(level == 1)
+ Dispatcher::unlock();
+ level--;
+
+ return true;
+}