| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Asterisk -- An open source telephony toolkit. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2008, Digium, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Russell Bryant <russell@digium.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * See http://www.asterisk.org for more information about
 | 
					
						
							|  |  |  |  * the Asterisk project. Please do not directly contact | 
					
						
							|  |  |  |  * any of the maintainers of this project for assistance; | 
					
						
							|  |  |  |  * the project provides a web site, mailing lists and IRC | 
					
						
							|  |  |  |  * channels for your use. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software, distributed under the terms of | 
					
						
							|  |  |  |  * the GNU General Public License Version 2. See the LICENSE file | 
					
						
							|  |  |  |  * at the top of the source tree. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-29 16:15:30 +00:00
										 |  |  | /*!
 | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  |  * \file | 
					
						
							|  |  |  |  * \author Russell Bryant <russell@digium.com> | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2009-05-29 16:15:30 +00:00
										 |  |  |  * \brief pthread timing interface | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-14 20:13:06 +00:00
										 |  |  | /*** MODULEINFO
 | 
					
						
							|  |  |  | 	<support_level>extended</support_level> | 
					
						
							|  |  |  |  ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <math.h>
 | 
					
						
							|  |  |  | #include <sys/select.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "asterisk/module.h"
 | 
					
						
							|  |  |  | #include "asterisk/timing.h"
 | 
					
						
							|  |  |  | #include "asterisk/utils.h"
 | 
					
						
							|  |  |  | #include "asterisk/astobj2.h"
 | 
					
						
							|  |  |  | #include "asterisk/time.h"
 | 
					
						
							|  |  |  | #include "asterisk/lock.h"
 | 
					
						
							| 
									
										
										
										
											2010-07-21 16:15:00 +00:00
										 |  |  | #include "asterisk/poll-compat.h"
 | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void *timing_funcs_handle; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int pthread_timer_open(void); | 
					
						
							|  |  |  | static void pthread_timer_close(int handle); | 
					
						
							|  |  |  | static int pthread_timer_set_rate(int handle, unsigned int rate); | 
					
						
							|  |  |  | static void pthread_timer_ack(int handle, unsigned int quantity); | 
					
						
							|  |  |  | static int pthread_timer_enable_continuous(int handle); | 
					
						
							|  |  |  | static int pthread_timer_disable_continuous(int handle); | 
					
						
							| 
									
										
										
										
											2009-02-17 21:22:40 +00:00
										 |  |  | static enum ast_timer_event pthread_timer_get_event(int handle); | 
					
						
							| 
									
										
										
										
											2008-06-26 15:37:01 +00:00
										 |  |  | static unsigned int pthread_timer_get_max_rate(int handle); | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-17 21:22:40 +00:00
										 |  |  | static struct ast_timing_interface pthread_timing = { | 
					
						
							|  |  |  | 	.name = "pthread", | 
					
						
							|  |  |  | 	.priority = 0, /* use this as a last resort */ | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 	.timer_open = pthread_timer_open, | 
					
						
							|  |  |  | 	.timer_close = pthread_timer_close, | 
					
						
							|  |  |  | 	.timer_set_rate = pthread_timer_set_rate, | 
					
						
							|  |  |  | 	.timer_ack = pthread_timer_ack, | 
					
						
							|  |  |  | 	.timer_enable_continuous = pthread_timer_enable_continuous, | 
					
						
							|  |  |  | 	.timer_disable_continuous = pthread_timer_disable_continuous, | 
					
						
							|  |  |  | 	.timer_get_event = pthread_timer_get_event, | 
					
						
							| 
									
										
										
										
											2008-06-26 15:37:01 +00:00
										 |  |  | 	.timer_get_max_rate = pthread_timer_get_max_rate, | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:30:49 +00:00
										 |  |  | /* 1 tick / 10 ms */ | 
					
						
							|  |  |  | #define MAX_RATE 100
 | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | static struct ao2_container *pthread_timers; | 
					
						
							|  |  |  | #define PTHREAD_TIMER_BUCKETS 563
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum { | 
					
						
							|  |  |  | 	PIPE_READ =  0, | 
					
						
							|  |  |  | 	PIPE_WRITE = 1 | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum pthread_timer_state { | 
					
						
							|  |  |  | 	TIMER_STATE_IDLE, | 
					
						
							|  |  |  | 	TIMER_STATE_TICKING, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct pthread_timer { | 
					
						
							|  |  |  | 	int pipe[2]; | 
					
						
							|  |  |  | 	enum pthread_timer_state state; | 
					
						
							|  |  |  | 	unsigned int rate; | 
					
						
							|  |  |  | 	/*! Interval in ms for current rate */ | 
					
						
							|  |  |  | 	unsigned int interval; | 
					
						
							|  |  |  | 	unsigned int tick_count; | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 	unsigned int pending_ticks; | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 	struct timeval start; | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 	unsigned int continuous:1; | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void pthread_timer_destructor(void *obj); | 
					
						
							| 
									
										
										
										
											2008-08-10 00:47:56 +00:00
										 |  |  | static struct pthread_timer *find_timer(int handle, int unlinkobj); | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | static void write_byte(struct pthread_timer *timer); | 
					
						
							|  |  |  | static void read_pipe(struct pthread_timer *timer, unsigned int num); | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Data for the timing thread | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static struct { | 
					
						
							|  |  |  | 	pthread_t thread; | 
					
						
							|  |  |  | 	ast_mutex_t lock; | 
					
						
							|  |  |  | 	ast_cond_t cond; | 
					
						
							|  |  |  | 	unsigned int stop:1; | 
					
						
							|  |  |  | } timing_thread; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int pthread_timer_open(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct pthread_timer *timer; | 
					
						
							| 
									
										
										
										
											2008-06-24 02:16:59 +00:00
										 |  |  | 	int fd; | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!(timer = ao2_alloc(sizeof(*timer), pthread_timer_destructor))) { | 
					
						
							|  |  |  | 		errno = ENOMEM; | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	timer->pipe[PIPE_READ] = timer->pipe[PIPE_WRITE] = -1; | 
					
						
							|  |  |  | 	timer->state = TIMER_STATE_IDLE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pipe(timer->pipe)) { | 
					
						
							|  |  |  | 		ao2_ref(timer, -1); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_lock(pthread_timers); | 
					
						
							|  |  |  | 	if (!ao2_container_count(pthread_timers)) { | 
					
						
							|  |  |  | 		ast_mutex_lock(&timing_thread.lock); | 
					
						
							|  |  |  | 		ast_cond_signal(&timing_thread.cond); | 
					
						
							|  |  |  | 		ast_mutex_unlock(&timing_thread.lock); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ao2_link(pthread_timers, timer); | 
					
						
							|  |  |  | 	ao2_unlock(pthread_timers); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-24 02:16:59 +00:00
										 |  |  | 	fd = timer->pipe[PIPE_READ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_ref(timer, -1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fd; | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void pthread_timer_close(int handle) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct pthread_timer *timer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(timer = find_timer(handle, 1))) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_ref(timer, -1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int pthread_timer_set_rate(int handle, unsigned int rate) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct pthread_timer *timer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(timer = find_timer(handle, 0))) { | 
					
						
							|  |  |  | 		errno = EINVAL; | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-17 19:00:14 +00:00
										 |  |  | 	if (rate > MAX_RATE) { | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 		ast_log(LOG_ERROR, "res_timing_pthread only supports timers at a " | 
					
						
							|  |  |  | 				"max rate of %d / sec\n", MAX_RATE); | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 		errno = EINVAL; | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_lock(timer); | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if ((timer->rate = rate)) { | 
					
						
							|  |  |  | 		timer->interval = roundf(1000.0 / ((float) rate)); | 
					
						
							|  |  |  | 		timer->start = ast_tvnow(); | 
					
						
							|  |  |  | 		timer->state = TIMER_STATE_TICKING; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		timer->interval = 0; | 
					
						
							|  |  |  | 		timer->start = ast_tv(0, 0); | 
					
						
							|  |  |  | 		timer->state = TIMER_STATE_IDLE; | 
					
						
							| 
									
										
										
										
											2008-12-11 21:21:31 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 	timer->tick_count = 0; | 
					
						
							| 
									
										
										
										
											2009-05-29 16:15:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 	ao2_unlock(timer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_ref(timer, -1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void pthread_timer_ack(int handle, unsigned int quantity) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct pthread_timer *timer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_assert(quantity > 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(timer = find_timer(handle, 0))) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 	ao2_lock(timer); | 
					
						
							|  |  |  | 	read_pipe(timer, quantity); | 
					
						
							|  |  |  | 	ao2_unlock(timer); | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ao2_ref(timer, -1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int pthread_timer_enable_continuous(int handle) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct pthread_timer *timer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(timer = find_timer(handle, 0))) { | 
					
						
							|  |  |  | 		errno = EINVAL; | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_lock(timer); | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 	if (!timer->continuous) { | 
					
						
							|  |  |  | 		timer->continuous = 1; | 
					
						
							|  |  |  | 		write_byte(timer); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 	ao2_unlock(timer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_ref(timer, -1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int pthread_timer_disable_continuous(int handle) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct pthread_timer *timer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(timer = find_timer(handle, 0))) { | 
					
						
							|  |  |  | 		errno = EINVAL; | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_lock(timer); | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 	if (timer->continuous) { | 
					
						
							|  |  |  | 		timer->continuous = 0; | 
					
						
							|  |  |  | 		read_pipe(timer, 1); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 	ao2_unlock(timer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_ref(timer, -1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-17 21:22:40 +00:00
										 |  |  | static enum ast_timer_event pthread_timer_get_event(int handle) | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct pthread_timer *timer; | 
					
						
							| 
									
										
										
										
											2009-02-17 21:22:40 +00:00
										 |  |  | 	enum ast_timer_event res = AST_TIMING_EVENT_EXPIRED; | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!(timer = find_timer(handle, 0))) { | 
					
						
							|  |  |  | 		return res; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 	ao2_lock(timer); | 
					
						
							|  |  |  | 	if (timer->continuous && timer->pending_ticks == 1) { | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 		res = AST_TIMING_EVENT_CONTINUOUS; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 	ao2_unlock(timer); | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ao2_ref(timer, -1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-26 15:37:01 +00:00
										 |  |  | static unsigned int pthread_timer_get_max_rate(int handle) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return MAX_RATE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-10 00:47:56 +00:00
										 |  |  | static struct pthread_timer *find_timer(int handle, int unlinkobj) | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct pthread_timer *timer; | 
					
						
							|  |  |  | 	struct pthread_timer tmp_timer; | 
					
						
							|  |  |  | 	int flags = OBJ_POINTER; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tmp_timer.pipe[PIPE_READ] = handle; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-10 00:47:56 +00:00
										 |  |  | 	if (unlinkobj) { | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 		flags |= OBJ_UNLINK; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-25 01:01:49 +00:00
										 |  |  | 	if (!(timer = ao2_find(pthread_timers, &tmp_timer, flags))) { | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 		ast_assert(timer != NULL); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return timer; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void pthread_timer_destructor(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct pthread_timer *timer = obj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (timer->pipe[PIPE_READ] > -1) { | 
					
						
							|  |  |  | 		close(timer->pipe[PIPE_READ]); | 
					
						
							|  |  |  | 		timer->pipe[PIPE_READ] = -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (timer->pipe[PIPE_WRITE] > -1) { | 
					
						
							|  |  |  | 		close(timer->pipe[PIPE_WRITE]); | 
					
						
							|  |  |  | 		timer->pipe[PIPE_WRITE] = -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							| 
									
										
										
										
											2009-05-29 16:15:30 +00:00
										 |  |  |  * \note only PIPE_READ is guaranteed valid | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | static int pthread_timer_hash(const void *obj, const int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const struct pthread_timer *timer = obj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return timer->pipe[PIPE_READ]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							| 
									
										
										
										
											2009-05-29 16:15:30 +00:00
										 |  |  |  * \note only PIPE_READ is guaranteed valid | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2008-11-25 01:01:49 +00:00
										 |  |  | static int pthread_timer_cmp(void *obj, void *arg, int flags) | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct pthread_timer *timer1 = obj, *timer2 = arg; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-29 17:47:17 +00:00
										 |  |  | 	return (timer1->pipe[PIPE_READ] == timer2->pipe[PIPE_READ]) ? CMP_MATCH | CMP_STOP : 0; | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \retval 0 no timer tick needed | 
					
						
							|  |  |  |  * \retval non-zero write to the timing pipe needed | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int check_timer(struct pthread_timer *timer) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct timeval now; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 	if (timer->state == TIMER_STATE_IDLE) { | 
					
						
							| 
									
										
										
										
											2009-05-29 16:15:30 +00:00
										 |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-05-29 16:15:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 	now = ast_tvnow(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (timer->tick_count < (ast_tvdiff_ms(now, timer->start) / timer->interval)) { | 
					
						
							|  |  |  | 		timer->tick_count++; | 
					
						
							|  |  |  | 		if (!timer->tick_count) { | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 			/* Handle overflow. */ | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 			timer->start = now; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \pre timer is locked | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void read_pipe(struct pthread_timer *timer, unsigned int quantity) | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 	int rd_fd = timer->pipe[PIPE_READ]; | 
					
						
							| 
									
										
										
										
											2009-05-29 22:33:31 +00:00
										 |  |  | 	int pending_ticks = timer->pending_ticks; | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 	ast_assert(quantity); | 
					
						
							| 
									
										
										
										
											2009-05-29 22:33:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (timer->continuous && pending_ticks) { | 
					
						
							|  |  |  | 		pending_ticks--; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (quantity > pending_ticks) { | 
					
						
							|  |  |  | 		quantity = pending_ticks; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!quantity) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	do { | 
					
						
							|  |  |  | 		unsigned char buf[1024]; | 
					
						
							|  |  |  | 		ssize_t res; | 
					
						
							| 
									
										
										
										
											2010-07-21 16:15:00 +00:00
										 |  |  | 		struct pollfd pfd = { | 
					
						
							|  |  |  | 			.fd = rd_fd, | 
					
						
							|  |  |  | 			.events = POLLIN, | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 		}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-21 16:15:00 +00:00
										 |  |  | 		if (ast_poll(&pfd, 1, 0) != 1) { | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 			ast_debug(1, "Reading not available on timing pipe, " | 
					
						
							|  |  |  | 					"quantity: %u\n", quantity); | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-29 16:15:30 +00:00
										 |  |  | 		res = read(rd_fd, buf, | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 			(quantity < sizeof(buf)) ? quantity : sizeof(buf)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (res == -1) { | 
					
						
							|  |  |  | 			if (errno == EAGAIN) { | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 			ast_log(LOG_ERROR, "read failed on timing pipe: %s\n", | 
					
						
							|  |  |  | 					strerror(errno)); | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		quantity -= res; | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 		timer->pending_ticks -= res; | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 	} while (quantity); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \pre timer is locked | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void write_byte(struct pthread_timer *timer) | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 	ssize_t res; | 
					
						
							|  |  |  | 	unsigned char x = 42; | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 	do { | 
					
						
							|  |  |  | 		res = write(timer->pipe[PIPE_WRITE], &x, 1); | 
					
						
							|  |  |  | 	} while (res == -1 && errno == EAGAIN); | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 	if (res == -1) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "Error writing to timing pipe: %s\n", | 
					
						
							|  |  |  | 				strerror(errno)); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		timer->pending_ticks++; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-25 01:01:49 +00:00
										 |  |  | static int run_timer(void *obj, void *arg, int flags) | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct pthread_timer *timer = obj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (timer->state == TIMER_STATE_IDLE) { | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_lock(timer); | 
					
						
							|  |  |  | 	if (check_timer(timer)) { | 
					
						
							| 
									
										
										
										
											2009-05-29 20:06:59 +00:00
										 |  |  | 		write_byte(timer); | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	ao2_unlock(timer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *do_timing(void *arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct timeval next_wakeup = ast_tvnow(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (!timing_thread.stop) { | 
					
						
							|  |  |  | 		struct timespec ts = { 0, }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-25 01:01:49 +00:00
										 |  |  | 		ao2_callback(pthread_timers, OBJ_NODATA, run_timer, NULL); | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-19 18:30:49 +00:00
										 |  |  | 		next_wakeup = ast_tvadd(next_wakeup, ast_tv(0, 5000)); | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		ts.tv_sec = next_wakeup.tv_sec; | 
					
						
							|  |  |  | 		ts.tv_nsec = next_wakeup.tv_usec * 1000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ast_mutex_lock(&timing_thread.lock); | 
					
						
							|  |  |  | 		if (!timing_thread.stop) { | 
					
						
							|  |  |  | 			if (ao2_container_count(pthread_timers)) { | 
					
						
							|  |  |  | 				ast_cond_timedwait(&timing_thread.cond, &timing_thread.lock, &ts); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				ast_cond_wait(&timing_thread.cond, &timing_thread.lock); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ast_mutex_unlock(&timing_thread.lock); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int init_timing_thread(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ast_mutex_init(&timing_thread.lock); | 
					
						
							|  |  |  | 	ast_cond_init(&timing_thread.cond, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_pthread_create_background(&timing_thread.thread, NULL, do_timing, NULL)) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "Unable to start timing thread.\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int load_module(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-29 16:15:30 +00:00
										 |  |  | 	if (!(pthread_timers = ao2_container_alloc(PTHREAD_TIMER_BUCKETS, | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 		pthread_timer_hash, pthread_timer_cmp))) { | 
					
						
							|  |  |  | 		return AST_MODULE_LOAD_DECLINE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (init_timing_thread()) { | 
					
						
							|  |  |  | 		ao2_ref(pthread_timers, -1); | 
					
						
							|  |  |  | 		pthread_timers = NULL; | 
					
						
							|  |  |  | 		return AST_MODULE_LOAD_DECLINE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-17 21:22:40 +00:00
										 |  |  | 	return (timing_funcs_handle = ast_register_timing_interface(&pthread_timing)) ? | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 		AST_MODULE_LOAD_SUCCESS : AST_MODULE_LOAD_DECLINE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int unload_module(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-02-17 21:22:40 +00:00
										 |  |  | 	int res; | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-17 21:22:40 +00:00
										 |  |  | 	ast_mutex_lock(&timing_thread.lock); | 
					
						
							|  |  |  | 	timing_thread.stop = 1; | 
					
						
							|  |  |  | 	ast_cond_signal(&timing_thread.cond); | 
					
						
							|  |  |  | 	ast_mutex_unlock(&timing_thread.lock); | 
					
						
							|  |  |  | 	pthread_join(timing_thread.thread, NULL); | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-17 21:22:40 +00:00
										 |  |  | 	if (!(res = ast_unregister_timing_interface(timing_funcs_handle))) { | 
					
						
							|  |  |  | 		ao2_ref(pthread_timers, -1); | 
					
						
							|  |  |  | 		pthread_timers = NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-17 21:22:40 +00:00
										 |  |  | 	return res; | 
					
						
							| 
									
										
										
										
											2008-06-16 13:08:13 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-06-09 16:22:04 +00:00
										 |  |  | AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "pthread Timing Interface", | 
					
						
							|  |  |  | 		.load = load_module, | 
					
						
							|  |  |  | 		.unload = unload_module, | 
					
						
							| 
									
										
										
										
											2010-07-20 19:35:02 +00:00
										 |  |  | 		.load_pri = AST_MODPRI_CHANNEL_DEPEND, | 
					
						
							| 
									
										
										
										
											2009-06-09 16:22:04 +00:00
										 |  |  | 		); |