diff options
author | Luo Jinghua <sunmoon1997@gmail.com> | 2009-11-01 19:44:10 +0800 |
---|---|---|
committer | Luo Jinghua <sunmoon1997@gmail.com> | 2009-11-01 19:44:10 +0800 |
commit | d51cc574138184f0f4666538f9f4dab37c48814d (patch) | |
tree | d41f424c8e9a2004b966311e75fc9ffe8865d440 | |
parent | 617dc8b75593533656061fcf69b719deb79c2bbe (diff) |
milkway: added timeout source
-rw-r--r-- | milkway/Makefile.am | 8 | ||||
-rw-r--r-- | milkway/mw-timeout-source.c | 227 | ||||
-rw-r--r-- | milkway/mw-timeout-source.h | 62 |
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, ¤t_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, ¤t_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, ¤t_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, ¤t_time); + mw_timeout_set_expiration (self, ¤t_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 |