diff options
Diffstat (limited to 'milkway/mw-timeout-source.c')
-rw-r--r-- | milkway/mw-timeout-source.c | 227 |
1 files changed, 227 insertions, 0 deletions
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); |