2 * iaxclient: a cross-platform IAX softphone library
5 * Copyright (C) 2003-2006, Horizon Wimba, Inc.
6 * Copyright (C) 2007, Wimba, Inc.
9 * Steve Kann <stevek@stevek.com>
11 * This program is free software, distributed under the terms of
12 * the GNU Lesser (Library) General Public License.
17 #ifndef __USE_POSIX199309
18 #define __USE_POSIX199309
21 #include "iaxclient_lib.h"
27 #if TIME_WITH_SYS_TIME
28 # include <sys/time.h>
31 # ifdef HAVE_SYS_TIME_H
32 # include <sys/time.h>
42 /* Unix-specific functions */
48 void iaxc_millisleep(long ms)
52 req.tv_nsec = (ms%1000)*1000*1000;
55 /* yes, it can return early. We don't care */
60 /* TODO: Implement for X/MacOSX? */
61 int iaxci_post_event_callback(iaxc_event ev)
65 e = malloc(sizeof(ev));
68 /* XXX Test return value? */
69 PostMessage(post_event_handle,post_event_id,(WPARAM) NULL, (LPARAM) e);
75 /* Presently, OSX allows user-level processes to request RT
76 * priority. The API is nice, but the scheduler presently ignores
77 * the parameters (but the API validates that you're not asking for
79 * http://lists.apple.com/archives/darwin-development/2004/Feb/msg00079.html
81 /* include mach stuff for declaration of thread_policy stuff */
82 #include <mach/mach.h>
84 int iaxci_prioboostbegin()
86 struct thread_time_constraint_policy ttcpolicy;
87 int params [2] = {CTL_HW,HW_BUS_FREQ};
94 sysctl (params, 2, &hzms, &sz, NULL, 0);
96 /* make hzms actually hz per ms */
99 /* give us at least how much? 6-8ms every 10ms (we generally need less) */
100 ttcpolicy.period = 10 * hzms; /* 10 ms */
101 ttcpolicy.computation = 2 * hzms;
102 ttcpolicy.constraint = 3 * hzms;
103 ttcpolicy.preemptible = 1;
105 if ( (ret = thread_policy_set(mach_thread_self(),
106 THREAD_TIME_CONSTRAINT_POLICY, (int *)&ttcpolicy,
107 THREAD_TIME_CONSTRAINT_POLICY_COUNT)) != KERN_SUCCESS )
109 fprintf(stderr, "thread_policy_set failed: %d.\n", ret);
114 int iaxci_prioboostend()
123 /* Priority boosting/monitoring: Adapted from portaudio/pa_unix.c ,
124 * which carries the following copyright notice:
125 * PortAudio Portable Real-Time Audio Library
126 * Latest Version at: http://www.portaudio.com
127 * Linux OSS Implementation by douglas repetto and Phil Burk
129 * Copyright (c) 1999-2000 Phil Burk
131 * Permission is hereby granted, free of charge, to any person obtaining
132 * a copy of this software and associated documentation files
133 * (the "Software"), to deal in the Software without restriction,
134 * including without limitation the rights to use, copy, modify, merge,
135 * publish, distribute, sublicense, and/or sell copies of the Software,
136 * and to permit persons to whom the Software is furnished to do so,
137 * subject to the following conditions:
139 * The above copyright notice and this permission notice shall be
140 * included in all copies or substantial portions of the Software.
142 * Any person wishing to distribute modifications to the Software is
143 * requested to send the modifications to the original developer so that
144 * they can be incorporated into the canonical version.
146 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
149 /* It has been clarified by the authors that the request to send modifications
150 is a request, and not a condition */
153 * The main thread is boosted to a medium real-time priority.
154 * Two additional threads are created:
155 * Canary: Runs as normal priority, updates a timevalue every second.
156 * WatchDog: Runs as a higher real-time priority. Checks to see that
157 * Canary is running. If Canary isn't running, lowers
158 * priority of calling thread, which has presumably run away
162 #include <sys/ioctl.h>
163 #include <sys/time.h>
171 //#define DBUG(...) fprintf(stderr, __VA_ARGS__)
173 #define ERR_RPT(...) fprintf(stderr, __VA_ARGS__)
175 #define SCHEDULER_POLICY SCHED_RR
176 #define WATCHDOG_INTERVAL_USEC 1000000
177 #define WATCHDOG_MAX_SECONDS 3
179 typedef void *(*pthread_function_t)(void *);
185 struct timeval CanaryTime;
187 pthread_t CanaryThread;
188 int IsCanaryThreadValid;
191 pthread_t WatchDogThread;
192 int IsWatchDogThreadValid;
196 static prioboost *pb;
198 static int CanaryProc( prioboost *b)
201 struct sched_param schat = { 0 };
203 /* set us up with normal priority, please */
204 if( pthread_setschedparam(pthread_self(), SCHED_OTHER, &schat) != 0)
209 usleep( WATCHDOG_INTERVAL_USEC );
210 gettimeofday( &b->CanaryTime, NULL );
216 static int WatchDogProc( prioboost *b )
218 struct sched_param schp = { 0 };
221 /* Run at a priority level above main thread so we can still run if it hangs. */
222 /* Rise more than 1 because of rumored off-by-one scheduler bugs. */
223 schp.sched_priority = b->priority + 4;
224 maxPri = sched_get_priority_max(SCHEDULER_POLICY);
225 if( schp.sched_priority > maxPri ) schp.sched_priority = maxPri;
227 if (pthread_setschedparam(pthread_self(), SCHEDULER_POLICY, &schp) != 0)
229 ERR_RPT("WatchDogProc: cannot set watch dog priority!\n");
233 DBUG("prioboost: WatchDog priority set to level %d!\n", schp.sched_priority);
235 /* Compare watchdog time with audio and canary thread times. */
236 /* Sleep for a while or until thread cancelled. */
237 while( b->WatchDogRun )
241 struct timeval currentTime;
243 usleep( WATCHDOG_INTERVAL_USEC );
244 gettimeofday( ¤tTime, NULL );
247 /* If audio thread is not advancing, then it must be hung so kill it. */
248 delta = currentTime.tv_sec - b->EntryTime.tv_sec;
249 DBUG(("WatchDogProc: audio delta = %d\n", delta ));
250 if( delta > WATCHDOG_MAX_SECONDS )
256 /* If canary died, then lower audio priority and halt canary. */
257 delta = currentTime.tv_sec - b->CanaryTime.tv_sec;
258 DBUG("WatchDogProc: dogging, delta = %ld, mysec=%d\n", delta, currentTime.tv_sec);
259 if( delta > WATCHDOG_MAX_SECONDS )
261 ERR_RPT("WatchDogProc: canary died!\n");
266 DBUG("WatchDogProc: exiting.\n");
271 struct sched_param schat = { 0 };
272 if( pthread_setschedparam(b->ThreadID, SCHED_OTHER, &schat) != 0)
274 ERR_RPT("WatchDogProc: failed to lower audio priority. errno = %d\n", errno );
275 /* Fall through into killing audio thread. */
279 ERR_RPT("WatchDogProc: lowered audio priority to prevent hogging of CPU.\n");
285 ERR_RPT("WatchDogProc: killing hung audio thread!\n");
286 //pthread_cancel( b->ThreadID);
287 //pthread_join( b->ThreadID);
292 DBUG("WatchDogProc: cancel Canary\n");
293 pthread_cancel( b->CanaryThread );
294 DBUG("WatchDogProc: join Canary\n");
295 pthread_join( b->CanaryThread, NULL );
296 DBUG("WatchDogProc: forget Canary\n");
297 b->IsCanaryThreadValid = 0;
300 GSUnregisterCurrentThread(); /* SB20010904 */
305 static void StopWatchDog( prioboost *b )
307 /* Cancel WatchDog thread if there is one. */
308 if( b->IsWatchDogThreadValid )
311 DBUG("StopWatchDog: cancel WatchDog\n");
312 pthread_cancel( b->WatchDogThread );
313 pthread_join( b->WatchDogThread, NULL );
314 b->IsWatchDogThreadValid = 0;
316 /* Cancel Canary thread if there is one. */
317 if( b->IsCanaryThreadValid )
320 DBUG("StopWatchDog: cancel Canary\n");
321 pthread_cancel( b->CanaryThread );
322 DBUG("StopWatchDog: join Canary\n");
323 pthread_join( b->CanaryThread, NULL );
324 b->IsCanaryThreadValid = 0;
329 static int StartWatchDog( prioboost *b)
334 /* The watch dog watches for these timer updates */
335 gettimeofday( &b->CanaryTime, NULL );
337 /* Launch a canary thread to detect priority abuse. */
339 hres = pthread_create(&(b->CanaryThread),
340 NULL /*pthread_attr_t * attr*/,
341 (pthread_function_t)CanaryProc, b);
344 b->IsCanaryThreadValid = 0;
348 b->IsCanaryThreadValid = 1;
350 /* Launch a watchdog thread to prevent runaway audio thread. */
352 hres = pthread_create(&(b->WatchDogThread),
353 NULL /*pthread_attr_t * attr*/,
354 (pthread_function_t)WatchDogProc, b);
356 b->IsWatchDogThreadValid = 0;
360 b->IsWatchDogThreadValid = 1;
368 int iaxci_prioboostbegin()
370 struct sched_param schp = { 0 };
371 prioboost *b = calloc(sizeof(*b),1);
375 b->priority = (sched_get_priority_max(SCHEDULER_POLICY) -
376 sched_get_priority_min(SCHEDULER_POLICY)) / 2;
377 schp.sched_priority = b->priority;
379 b->ThreadID = pthread_self();
381 if (pthread_setschedparam(b->ThreadID, SCHEDULER_POLICY, &schp) != 0)
383 DBUG("prioboost: only superuser can use real-time priority.\n");
387 DBUG("prioboost: priority set to level %d!\n", schp.sched_priority); /* We are running at high priority so we should have a watchdog in case audio goes wild. */
388 result = StartWatchDog( b );
395 schp.sched_priority = 0;
396 pthread_setschedparam(b->ThreadID, SCHED_OTHER, &schp);
402 int iaxci_prioboostend()
404 if(pb) StopWatchDog(pb);