summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuo Jinghua <sunmoon1997@gmail.com>2009-11-01 19:44:10 +0800
committerLuo Jinghua <sunmoon1997@gmail.com>2009-11-01 19:44:10 +0800
commitd51cc574138184f0f4666538f9f4dab37c48814d (patch)
treed41f424c8e9a2004b966311e75fc9ffe8865d440
parent617dc8b75593533656061fcf69b719deb79c2bbe (diff)
milkway: added timeout source
-rw-r--r--milkway/Makefile.am8
-rw-r--r--milkway/mw-timeout-source.c227
-rw-r--r--milkway/mw-timeout-source.h62
3 files changed, 295 insertions, 2 deletions
diff --git a/milkway/Makefile.am b/milkway/Makefile.am
index ce75f78..25163d1 100644
--- a/milkway/Makefile.am
+++ b/milkway/Makefile.am
@@ -31,7 +31,9 @@ milkway_headers = \
mw-source.h \
mw-poll.h \
mw-poll-imp.h \
- mw-main-context.h
+ mw-main-context.h \
+ mw-main-loop.h \
+ mw-timeout-source.h
libmilkway_la_LDFLAGS = -version-info $(LT_VERSION_INFO) -no-undefined
libmilkway_la_LIBADD = @DEP_LIBS@ $(PTHREAD_LIBS)
@@ -78,7 +80,9 @@ libmilkway_la_SOURCES = \
mw-poll-win32.c \
mw-poll-imp.c \
mw-main-context-private.h \
- mw-main-context.c
+ mw-main-context.c \
+ mw-main-loop.c \
+ mw-timeout-source.c
milkwayincludedir = $(includedir)/milkway/milkway
milkwayinclude_HEADERS = $(milkway_headers)
diff --git a/milkway/mw-timeout-source.c b/milkway/mw-timeout-source.c
new file mode 100644
index 0000000..9790c16
--- /dev/null
+++ b/milkway/mw-timeout-source.c
@@ -0,0 +1,227 @@
+/* Milkway
+ *
+ * Copyright (C) 2008- Luo Jinghua <sunmoon1997@gmail.com>
+ *
+ * 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; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "milkwayint.h"
+#include "milkway/mw-timeout-source.h"
+
+#include <stdlib.h>
+
+#ifndef MIN
+#define MIN(a, b) (a) > (b) ? (b) : (a)
+#endif
+
+#ifndef ABS
+#define ABS(a) (a) > 0 ? (a) : (-(a))
+#endif
+
+static void
+mw_timeout_set_expiration (mw_timeout_source_t *self,
+ mw_timeval_t *current_time)
+{
+ static int timer_perturb = -1;
+ mw_uint_t seconds = self->interval / 1000;
+ mw_uint_t msecs = self->interval - seconds * 1000;
+
+ self->expiration.tv_sec = current_time->tv_sec + seconds;
+ self->expiration.tv_usec = current_time->tv_usec + msecs * 1000;
+ if (self->expiration.tv_usec >= 1000000) {
+ self->expiration.tv_usec -= 1000000;
+ self->expiration.tv_sec++;
+ }
+
+ if (timer_perturb == -1) {
+ /*
+ * we want a per machine/session unique 'random' value; try the dbus
+ * address first, that has a UUID in it. If there is no dbus, use the
+ * hostname for hashing.
+ */
+ const char *session_bus_address = getenv ("DBUS_SESSION_BUS_ADDRESS");
+ if (!session_bus_address)
+ session_bus_address = getenv ("HOSTNAME");
+ if (session_bus_address)
+ timer_perturb = ABS ((int) mw_str_hash ((void*)session_bus_address));
+ else
+ timer_perturb = 0;
+ }
+ if (self->granularity) {
+ int remainder;
+ int gran; /* in usecs */
+ int perturb;
+
+ gran = self->granularity * 1000;
+ perturb = timer_perturb % gran;
+ /*
+ * We want to give each machine a per machine pertubation;
+ * shift time back first, and forward later after the rounding
+ */
+ self->expiration.tv_usec -= perturb;
+ if (self->expiration.tv_usec < 0) {
+ self->expiration.tv_usec += 1000000;
+ self->expiration.tv_sec--;
+ }
+
+ remainder = self->expiration.tv_usec % gran;
+ if (remainder >= gran/4) /* round up */
+ self->expiration.tv_usec += gran;
+ self->expiration.tv_usec -= remainder;
+ /* shift back */
+ self->expiration.tv_usec += perturb;
+
+ /* the rounding may have overflown tv_usec */
+ while (self->expiration.tv_usec > 1000000) {
+ self->expiration.tv_usec -= 1000000;
+ self->expiration.tv_sec++;
+ }
+ }
+}
+
+mw_timeout_source_t*
+mw_timeout_source_init(mw_timeout_source_t *self,
+ int interval,
+ mw_source_func_t func,
+ mw_pointer_t user_data,
+ mw_destroy_func_t destroy_func)
+{
+ if (!mw_source_init(&self->base))
+ return NULL;
+
+ self->interval = interval;
+ self->granularity = 0;
+ self->func = func;
+ self->user_data = user_data;
+ self->destroy_func = destroy_func;
+
+ mw_timeval_clear(&self->expiration);
+ return self;
+}
+
+mw_timeout_source_t*
+mw_timeout_source_new(int interval,
+ mw_source_func_t func,
+ mw_pointer_t user_data,
+ mw_destroy_func_t destroy_func)
+{
+ mw_timeout_source_t *self;
+
+ self = mw_object_alloc(MW_TIMEOUT_SOURCE_TYPE);
+ if (!self)
+ return NULL;
+ return mw_timeout_source_init(self, interval, func,
+ user_data, destroy_func);
+}
+
+static mw_bool_t
+mw_timeout_source_prepare(mw_source_t *super,
+ int *timeout)
+{
+ mw_timeout_source_t* self = (mw_timeout_source_t*)super;
+ mw_timeval_t current_time;
+ long sec, msec;
+
+ mw_source_get_current_time (super, &current_time);
+
+ sec = self->expiration.tv_sec - current_time.tv_sec;
+ msec = (self->expiration.tv_usec - current_time.tv_usec) / 1000;
+
+ /* We do the following in a rather convoluted fashion to deal with
+ * the fact that we don't have an integral type big enough to hold
+ * the difference of two timevals in millseconds.
+ */
+ if (sec < 0 || (sec == 0 && msec < 0)) {
+ msec = 0;
+ } else {
+ long interval_sec = self->interval / 1000;
+ long interval_msec = self->interval % 1000;
+
+ if (msec < 0) {
+ msec += 1000;
+ sec -= 1;
+ }
+
+ if (sec > interval_sec ||
+ (sec == interval_sec && msec > interval_msec)) {
+ /* The system time has been set backwards, so we
+ * reset the expiration time to now + self->interval;
+ * this at least avoids hanging for long periods of time.
+ */
+ mw_timeout_set_expiration (self, &current_time);
+ msec = MIN (INT_MAX, self->interval);
+ } else {
+ msec = MIN (INT_MAX, (mw_uint_t)msec + 1000 * (mw_uint_t)sec);
+ }
+ }
+
+ *timeout = (int)msec;
+ return msec == 0;
+}
+
+static mw_bool_t
+mw_timeout_source_check(mw_source_t *super)
+{
+ mw_timeout_source_t* self = (mw_timeout_source_t*)super;
+ mw_timeval_t current_time;
+
+ mw_source_get_current_time (super, &current_time);
+
+ return ((self->expiration.tv_sec < current_time.tv_sec) ||
+ ((self->expiration.tv_sec == current_time.tv_sec) &&
+ (self->expiration.tv_usec <= current_time.tv_usec)));
+}
+
+static mw_bool_t
+mw_timeout_source_dispatch(mw_source_t *super)
+{
+ mw_timeout_source_t* self = (mw_timeout_source_t*)super;
+
+ if (!self->func)
+ return MW_FALSE;
+
+ if (self->func (super, self->user_data)) {
+ mw_timeval_t current_time;
+
+ mw_source_get_current_time (super, &current_time);
+ mw_timeout_set_expiration (self, &current_time);
+
+ return MW_TRUE;
+ }
+
+ return MW_FALSE;
+}
+
+static void
+mw_timeout_source_finalize(mw_object_t *super)
+{
+ mw_timeout_source_t *self = (mw_timeout_source_t*)super;
+
+ if (self->destroy_func)
+ self->destroy_func(self->user_data);
+ MW_SUPER_FINALIZE(super, MW_TIMEOUT_SOURCE_TYPE)(super);
+}
+
+static void
+mw_timeout_source_type_init(mw_timeout_source_type_t *self)
+{
+ self->base.base.finalize = mw_timeout_source_finalize;
+ self->base.prepare = mw_timeout_source_prepare;
+ self->base.check = mw_timeout_source_check;
+ self->base.dispatch = mw_timeout_source_dispatch;
+}
+
+MW_DEFINE_GET_TYPE(mw_timeout_source, mw_timeout_source_type_t,
+ MW_SOURCE_TYPE, "MWTimeoutSource", 0);
diff --git a/milkway/mw-timeout-source.h b/milkway/mw-timeout-source.h
new file mode 100644
index 0000000..3313e7e
--- /dev/null
+++ b/milkway/mw-timeout-source.h
@@ -0,0 +1,62 @@
+/* Milkway
+ *
+ * Copyright (C) 2008- Luo Jinghua <sunmoon1997@gmail.com>
+ *
+ * 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; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef MW_TIMEOUT_SOURCE_H
+#define MW_TIMEOUT_SOURCE_H
+
+#include <milkway/mw-source.h>
+
+typedef struct _mw_timeout_source_type mw_timeout_source_type_t;
+typedef struct _mw_timeout_source mw_timeout_source_t;
+
+#define MW_TIMEOUT_SOURCE_TYPE mw_timeout_source_get_type()
+
+struct _mw_timeout_source_type {
+ mw_source_type_t base;
+};
+
+struct _mw_timeout_source {
+ mw_source_t base;
+
+ int interval;
+ int granularity;
+ mw_source_func_t func;
+ mw_pointer_t user_data;
+ mw_destroy_func_t destroy_func;
+
+ mw_timeval_t expiration;
+};
+
+mw_public mw_timeout_source_type_t*
+mw_timeout_source_get_type(void);
+
+mw_public mw_timeout_source_t*
+mw_timeout_source_init(mw_timeout_source_t *self,
+ int interval,
+ mw_source_func_t func,
+ mw_pointer_t user_data,
+ mw_destroy_func_t destroy_func);
+
+mw_public mw_timeout_source_t*
+mw_timeout_source_new(int interval,
+ mw_source_func_t func,
+ mw_pointer_t user_data,
+ mw_destroy_func_t destroy_func);
+
+#endif