1 /*
2  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  *
4  * Version: MPL 1.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
17  *
18  * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
19  *
20  * The Initial Developer of the Original Code is
21  * Anthony Minessale II <anthm@freeswitch.org>
22  * Portions created by the Initial Developer are Copyright (C)
23  * the Initial Developer. All Rights Reserved.
24  *
25  * Contributor(s):
26  * Timo Teräs <timo.teras@iki.fi>
27  *
28  *
29  * mod_timerfd.c -- Timer implementation using Linux timerfd
30  *
31  */
32 
33 #include <switch.h>
34 #include <sys/timerfd.h>
35 #include <sys/epoll.h>
36 
37 SWITCH_MODULE_LOAD_FUNCTION(mod_timerfd_load);
38 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_timerfd_shutdown);
39 SWITCH_MODULE_RUNTIME_FUNCTION(mod_timerfd_runtime);
40 SWITCH_MODULE_DEFINITION(mod_timerfd, mod_timerfd_load, mod_timerfd_shutdown, mod_timerfd_runtime);
41 
42 #define MAX_INTERVAL		2000 /* ms */
43 
44 struct interval_timer {
45 	int			fd;
46 	int			users;
47 	switch_size_t		tick;
48 	switch_mutex_t		*mutex;
49 	switch_thread_cond_t	*cond;
50 };
51 typedef struct interval_timer interval_timer_t;
52 
53 static switch_memory_pool_t *module_pool = NULL;
54 static switch_mutex_t *interval_timers_mutex;
55 static interval_timer_t interval_timers[MAX_INTERVAL + 1];
56 static int interval_poll_fd;
57 
timerfd_start_interval(interval_timer_t * it,int interval)58 static switch_status_t timerfd_start_interval(interval_timer_t *it, int interval)
59 {
60 	struct itimerspec val;
61 	struct epoll_event e;
62 	int fd;
63 
64 	it->users++;
65 	if (it->users > 1)
66 		return SWITCH_STATUS_SUCCESS;
67 
68 	it->tick = 0;
69 	switch_mutex_init(&it->mutex, SWITCH_MUTEX_NESTED, module_pool);
70 	switch_thread_cond_create(&it->cond, module_pool);
71 
72 	fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
73 	if (fd < 0)
74 		return SWITCH_STATUS_GENERR;
75 
76 	val.it_interval.tv_sec = interval / 1000;
77 	val.it_interval.tv_nsec = (interval % 1000) * 1000000;
78 	val.it_value.tv_sec = 0;
79 	val.it_value.tv_nsec = 100000;
80 
81 	if (timerfd_settime(fd, 0, &val, NULL) < 0) {
82 		close(fd);
83 		return SWITCH_STATUS_GENERR;
84 	}
85 
86 	e.events = EPOLLIN | EPOLLERR;
87 	e.data.ptr = it;
88 	if (epoll_ctl(interval_poll_fd, EPOLL_CTL_ADD, fd, &e) < 0) {
89 		close(fd);
90 		return SWITCH_STATUS_GENERR;
91 	}
92 
93 	it->fd = fd;
94 	return SWITCH_STATUS_SUCCESS;
95 }
96 
timerfd_stop_interval(interval_timer_t * it)97 static switch_status_t timerfd_stop_interval(interval_timer_t *it)
98 {
99 	it->users--;
100 	if (it->users > 0)
101 		return SWITCH_STATUS_SUCCESS;
102 
103 	close(it->fd);
104 	it->fd = -1;
105 	return SWITCH_STATUS_SUCCESS;
106 }
107 
timerfd_init(switch_timer_t * timer)108 static switch_status_t timerfd_init(switch_timer_t *timer)
109 {
110 	interval_timer_t *it;
111 	int rc;
112 
113 	if (timer->interval < 1 || timer->interval > MAX_INTERVAL)
114 		return SWITCH_STATUS_GENERR;
115 
116 	it = &interval_timers[timer->interval];
117 	switch_mutex_lock(interval_timers_mutex);
118 	rc = timerfd_start_interval(it, timer->interval);
119 	timer->private_info = it;
120 	switch_mutex_unlock(interval_timers_mutex);
121 
122 	return rc;
123 }
124 
timerfd_step(switch_timer_t * timer)125 static switch_status_t timerfd_step(switch_timer_t *timer)
126 {
127 	timer->tick++;
128 	timer->samplecount += timer->samples;
129 
130 	return SWITCH_STATUS_SUCCESS;
131 }
132 
timerfd_next(switch_timer_t * timer)133 static switch_status_t timerfd_next(switch_timer_t *timer)
134 {
135 	interval_timer_t *it = timer->private_info;
136 
137 	if ((int)(timer->tick - it->tick) < -1)
138 		timer->tick = it->tick;
139 	timerfd_step(timer);
140 
141 	switch_mutex_lock(it->mutex);
142 	while ((int)(timer->tick - it->tick) > 0)
143 		switch_thread_cond_wait(it->cond, it->mutex);
144 	switch_mutex_unlock(it->mutex);
145 
146 	return SWITCH_STATUS_SUCCESS;
147 }
148 
timerfd_sync(switch_timer_t * timer)149 static switch_status_t timerfd_sync(switch_timer_t *timer)
150 {
151 	interval_timer_t *it = timer->private_info;
152 
153 	timer->tick = it->tick;
154 
155 	return SWITCH_STATUS_SUCCESS;
156 }
157 
timerfd_check(switch_timer_t * timer,switch_bool_t step)158 static switch_status_t timerfd_check(switch_timer_t *timer, switch_bool_t step)
159 {
160 	interval_timer_t *it = timer->private_info;
161 	int diff = (int)(timer->tick - it->tick);
162 
163 	if (diff > 0) {
164 		/* still pending */
165 		timer->diff = diff;
166 		return SWITCH_STATUS_FALSE;
167 	} else {
168 		/* timer pending */
169 		timer->diff = 0;
170 		if (step)
171 			timerfd_step(timer);
172 		return SWITCH_STATUS_SUCCESS;
173 	}
174 }
175 
timerfd_destroy(switch_timer_t * timer)176 static switch_status_t timerfd_destroy(switch_timer_t *timer)
177 {
178 	interval_timer_t *it = timer->private_info;
179 	int rc;
180 
181 	switch_mutex_lock(interval_timers_mutex);
182 	rc = timerfd_stop_interval(it);
183 	switch_mutex_unlock(interval_timers_mutex);
184 
185 	return rc;
186 }
187 
SWITCH_MODULE_LOAD_FUNCTION(mod_timerfd_load)188 SWITCH_MODULE_LOAD_FUNCTION(mod_timerfd_load)
189 {
190 	switch_timer_interface_t *timer_interface;
191 
192 	module_pool = pool;
193 
194 	interval_poll_fd = epoll_create(16);
195 	if (interval_poll_fd < 0)
196 		return SWITCH_STATUS_GENERR;
197 
198 	switch_mutex_init(&interval_timers_mutex, SWITCH_MUTEX_NESTED, module_pool);
199 	memset(interval_timers, 0, sizeof(interval_timers));
200 
201 	/* connect my internal structure to the blank pointer passed to me */
202 	*module_interface = switch_loadable_module_create_module_interface(pool, modname);
203 	timer_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_TIMER_INTERFACE);
204 	timer_interface->interface_name = "timerfd";
205 	timer_interface->timer_init = timerfd_init;
206 	timer_interface->timer_next = timerfd_next;
207 	timer_interface->timer_step = timerfd_step;
208 	timer_interface->timer_sync = timerfd_sync;
209 	timer_interface->timer_check = timerfd_check;
210 	timer_interface->timer_destroy = timerfd_destroy;
211 
212 	return SWITCH_STATUS_SUCCESS;
213 }
214 
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_timerfd_shutdown)215 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_timerfd_shutdown)
216 {
217 	close(interval_poll_fd);
218 
219 	return SWITCH_STATUS_SUCCESS;
220 }
221 
SWITCH_MODULE_RUNTIME_FUNCTION(mod_timerfd_runtime)222 SWITCH_MODULE_RUNTIME_FUNCTION(mod_timerfd_runtime)
223 {
224 	struct epoll_event e[16];
225 	interval_timer_t *it;
226 	uint64_t u64;
227 	int i, r;
228 
229 	do {
230 		r = epoll_wait(interval_poll_fd, e, sizeof(e) / sizeof(e[0]), 1000);
231 		if (r < 0)
232 			break;
233 		for (i = 0; i < r; i++) {
234 			it = e[i].data.ptr;
235 			if ((e[i].events & EPOLLIN) &&
236 			    read(it->fd, &u64, sizeof(u64)) == sizeof(u64)) {
237 				switch_mutex_lock(it->mutex);
238 				it->tick += u64;
239 				switch_thread_cond_broadcast(it->cond);
240 				switch_mutex_unlock(it->mutex);
241 			}
242 		}
243 	} while (1);
244 
245 	return SWITCH_STATUS_TERM;
246 }
247 
248 /* For Emacs:
249  * Local Variables:
250  * mode:c
251  * indent-tabs-mode:t
252  * tab-width:4
253  * c-basic-offset:4
254  * End:
255  * For VIM:
256  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
257  */
258