1 /*
2  * Copyright 2008-2014 Arsen Chaloyan
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * $Id: mpf_scheduler.c 2136 2014-07-04 06:33:36Z achaloyan@gmail.com $
17  */
18 
19 #include "mpf_scheduler.h"
20 
21 #ifdef WIN32
22 #define ENABLE_MULTIMEDIA_TIMERS
23 #endif
24 
25 #ifdef ENABLE_MULTIMEDIA_TIMERS
26 
27 #pragma warning(disable:4201)
28 #include <mmsystem.h>
29 #include <windows.h>
30 
31 #ifndef TIME_KILL_SYNCHRONOUS
32 #define TIME_KILL_SYNCHRONOUS   0x0100
33 #endif
34 
35 #else
36 #include <apr_thread_proc.h>
37 #endif
38 
39 
40 struct mpf_scheduler_t {
41 	apr_pool_t          *pool;
42 	unsigned long        resolution; /* scheduler resolution */
43 
44 	unsigned long        media_resolution;
45 	mpf_scheduler_proc_f media_proc;
46 	void                *media_obj;
47 
48 	unsigned long        timer_resolution;
49 	unsigned long        timer_elapsed_time;
50 	mpf_scheduler_proc_f timer_proc;
51 	void                *timer_obj;
52 
53 #ifdef ENABLE_MULTIMEDIA_TIMERS
54 	unsigned int         timer_id;
55 #else
56 	apr_thread_t        *thread;
57 	apt_bool_t           running;
58 #endif
59 };
60 
61 static APR_INLINE void mpf_scheduler_init(mpf_scheduler_t *scheduler);
62 
63 /** Create scheduler */
mpf_scheduler_create(apr_pool_t * pool)64 MPF_DECLARE(mpf_scheduler_t*) mpf_scheduler_create(apr_pool_t *pool)
65 {
66 	mpf_scheduler_t *scheduler = apr_palloc(pool,sizeof(mpf_scheduler_t));
67 	mpf_scheduler_init(scheduler);
68 	scheduler->pool = pool;
69 	scheduler->resolution = 0;
70 
71 	scheduler->media_resolution = 0;
72 	scheduler->media_obj = NULL;
73 	scheduler->media_proc = NULL;
74 
75 	scheduler->timer_resolution = 0;
76 	scheduler->timer_elapsed_time = 0;
77 	scheduler->timer_obj = NULL;
78 	scheduler->timer_proc = NULL;
79 	return scheduler;
80 }
81 
82 /** Destroy scheduler */
mpf_scheduler_destroy(mpf_scheduler_t * scheduler)83 MPF_DECLARE(void) mpf_scheduler_destroy(mpf_scheduler_t *scheduler)
84 {
85 	/* nothing to destroy */
86 }
87 
88 /** Set media processing clock */
mpf_scheduler_media_clock_set(mpf_scheduler_t * scheduler,unsigned long resolution,mpf_scheduler_proc_f proc,void * obj)89 MPF_DECLARE(apt_bool_t) mpf_scheduler_media_clock_set(
90 								mpf_scheduler_t *scheduler,
91 								unsigned long resolution,
92 								mpf_scheduler_proc_f proc,
93 								void *obj)
94 {
95 	scheduler->media_resolution = resolution;
96 	scheduler->media_proc = proc;
97 	scheduler->media_obj = obj;
98 	return TRUE;
99 }
100 
101 /** Set timer clock */
mpf_scheduler_timer_clock_set(mpf_scheduler_t * scheduler,unsigned long resolution,mpf_scheduler_proc_f proc,void * obj)102 MPF_DECLARE(apt_bool_t) mpf_scheduler_timer_clock_set(
103 								mpf_scheduler_t *scheduler,
104 								unsigned long resolution,
105 								mpf_scheduler_proc_f proc,
106 								void *obj)
107 {
108 	scheduler->timer_resolution = resolution;
109 	scheduler->timer_elapsed_time = 0;
110 	scheduler->timer_proc = proc;
111 	scheduler->timer_obj = obj;
112 	return TRUE;
113 }
114 
115 /** Set scheduler rate (n times faster than real-time) */
mpf_scheduler_rate_set(mpf_scheduler_t * scheduler,unsigned long rate)116 MPF_DECLARE(apt_bool_t) mpf_scheduler_rate_set(
117 								mpf_scheduler_t *scheduler,
118 								unsigned long rate)
119 {
120 	if(rate == 0 || rate > 10) {
121 		/* rate shows how many times scheduler should be faster than real-time,
122 		1 is the defualt and probably the only reasonable value,
123 		however, the rates up to 10 times faster should be acceptable */
124 		rate = 1;
125 	}
126 
127 	scheduler->media_resolution /= rate;
128 	scheduler->timer_resolution /= rate;
129 	return TRUE;
130 }
131 
mpf_scheduler_resolution_set(mpf_scheduler_t * scheduler)132 static APR_INLINE void mpf_scheduler_resolution_set(mpf_scheduler_t *scheduler)
133 {
134 	if(scheduler->media_resolution) {
135 		scheduler->resolution = scheduler->media_resolution;
136 	}
137 	else if(scheduler->timer_resolution) {
138 		scheduler->resolution = scheduler->timer_resolution;
139 	}
140 }
141 
142 
143 
144 #ifdef ENABLE_MULTIMEDIA_TIMERS
145 
mpf_scheduler_init(mpf_scheduler_t * scheduler)146 static APR_INLINE void mpf_scheduler_init(mpf_scheduler_t *scheduler)
147 {
148 	scheduler->timer_id = 0;
149 }
150 
mm_timer_proc(UINT uID,UINT uMsg,DWORD_PTR dwUser,DWORD_PTR dw1,DWORD_PTR dw2)151 static void CALLBACK mm_timer_proc(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
152 {
153 	mpf_scheduler_t *scheduler = (mpf_scheduler_t*) dwUser;
154 	if(scheduler->media_proc) {
155 		scheduler->media_proc(scheduler,scheduler->media_obj);
156 	}
157 
158 	if(scheduler->timer_proc) {
159 		scheduler->timer_elapsed_time += scheduler->resolution;
160 		if(scheduler->timer_elapsed_time >= scheduler->timer_resolution) {
161 			scheduler->timer_elapsed_time = 0;
162 			scheduler->timer_proc(scheduler,scheduler->timer_obj);
163 		}
164 	}
165 }
166 
167 /** Start scheduler */
mpf_scheduler_start(mpf_scheduler_t * scheduler)168 MPF_DECLARE(apt_bool_t) mpf_scheduler_start(mpf_scheduler_t *scheduler)
169 {
170 	mpf_scheduler_resolution_set(scheduler);
171 	scheduler->timer_id = timeSetEvent(
172 					scheduler->resolution, 0, mm_timer_proc, (DWORD_PTR) scheduler,
173 					TIME_PERIODIC | TIME_CALLBACK_FUNCTION | TIME_KILL_SYNCHRONOUS);
174 	return scheduler->timer_id ? TRUE : FALSE;
175 }
176 
177 /** Stop scheduler */
mpf_scheduler_stop(mpf_scheduler_t * scheduler)178 MPF_DECLARE(apt_bool_t) mpf_scheduler_stop(mpf_scheduler_t *scheduler)
179 {
180 	if(!scheduler) {
181 		return FALSE;
182 	}
183 
184 	timeKillEvent(scheduler->timer_id);
185 	scheduler->timer_id = 0;
186 	return TRUE;
187 }
188 
189 #else
190 
191 #include "apt_task.h"
192 
mpf_scheduler_init(mpf_scheduler_t * scheduler)193 static APR_INLINE void mpf_scheduler_init(mpf_scheduler_t *scheduler)
194 {
195 	scheduler->thread = NULL;
196 	scheduler->running = FALSE;
197 }
198 
timer_thread_proc(apr_thread_t * thread,void * data)199 static void* APR_THREAD_FUNC timer_thread_proc(apr_thread_t *thread, void *data)
200 {
201 	mpf_scheduler_t *scheduler = data;
202 	apr_interval_time_t timeout = scheduler->resolution * 1000;
203 	apr_interval_time_t time_drift = 0;
204 	apr_time_t time_now, time_last;
205 
206 #if APR_HAS_SETTHREADNAME
207 	apr_thread_name_set("MPF Scheduler");
208 #endif
209 	time_now = apr_time_now();
210 	while(scheduler->running == TRUE) {
211 		time_last = time_now;
212 
213 		if(scheduler->media_proc) {
214 			scheduler->media_proc(scheduler,scheduler->media_obj);
215 		}
216 
217 		if(scheduler->timer_proc) {
218 			scheduler->timer_elapsed_time += scheduler->resolution;
219 			if(scheduler->timer_elapsed_time >= scheduler->timer_resolution) {
220 				scheduler->timer_elapsed_time = 0;
221 				scheduler->timer_proc(scheduler,scheduler->timer_obj);
222 			}
223 		}
224 
225 		if(timeout > time_drift) {
226 			apr_sleep(timeout - time_drift);
227 		}
228 
229 		time_now = apr_time_now();
230 		time_drift += time_now - time_last - timeout;
231 #if 0
232 		printf("time_drift=%d\n",time_drift);
233 #endif
234 	}
235 
236 	apr_thread_exit(thread,APR_SUCCESS);
237 	return NULL;
238 }
239 
mpf_scheduler_start(mpf_scheduler_t * scheduler)240 MPF_DECLARE(apt_bool_t) mpf_scheduler_start(mpf_scheduler_t *scheduler)
241 {
242 	mpf_scheduler_resolution_set(scheduler);
243 
244 	scheduler->running = TRUE;
245 	if(apr_thread_create(&scheduler->thread,NULL,timer_thread_proc,scheduler,scheduler->pool) != APR_SUCCESS) {
246 		scheduler->running = FALSE;
247 		return FALSE;
248 	}
249 	return TRUE;
250 }
251 
mpf_scheduler_stop(mpf_scheduler_t * scheduler)252 MPF_DECLARE(apt_bool_t) mpf_scheduler_stop(mpf_scheduler_t *scheduler)
253 {
254 	if(!scheduler) {
255 		return FALSE;
256 	}
257 
258 	scheduler->running = FALSE;
259 	if(scheduler->thread) {
260 		apr_status_t s;
261 		apr_thread_join(&s,scheduler->thread);
262 		scheduler->thread = NULL;
263 	}
264 	return TRUE;
265 }
266 
267 #endif
268