413 lines
9.8 KiB
C
413 lines
9.8 KiB
C
/*
|
|
* iaxclient: a cross-platform IAX softphone library
|
|
*
|
|
* Copyrights:
|
|
* Copyright (C) 2003-2006, Horizon Wimba, Inc.
|
|
* Copyright (C) 2007, Wimba, Inc.
|
|
*
|
|
* Contributors:
|
|
* Steve Kann <stevek@stevek.com>
|
|
*
|
|
* This program is free software, distributed under the terms of
|
|
* the GNU Lesser (Library) General Public License.
|
|
*/
|
|
|
|
#define _BSD_SOURCE
|
|
#define _DEFAULT_SOURCE
|
|
#include <unistd.h>
|
|
#ifndef __USE_POSIX199309
|
|
#define __USE_POSIX199309
|
|
#endif
|
|
#include <time.h>
|
|
#include "iaxclient_lib.h"
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#if TIME_WITH_SYS_TIME
|
|
# include <sys/time.h>
|
|
# include <time.h>
|
|
#else
|
|
# ifdef HAVE_SYS_TIME_H
|
|
# include <sys/time.h>
|
|
# else
|
|
# include <time.h>
|
|
# endif
|
|
#endif
|
|
|
|
#ifndef NULL
|
|
#define NULL (0)
|
|
#endif
|
|
|
|
/* Unix-specific functions */
|
|
|
|
void os_init(void)
|
|
{
|
|
}
|
|
|
|
void iaxc_millisleep(long ms)
|
|
{
|
|
struct timespec req;
|
|
|
|
req.tv_nsec = (ms%1000)*1000*1000;
|
|
req.tv_sec = ms/1000;
|
|
|
|
/* yes, it can return early. We don't care */
|
|
nanosleep(&req,NULL);
|
|
}
|
|
|
|
|
|
/* TODO: Implement for X/MacOSX? */
|
|
int iaxci_post_event_callback(iaxc_event ev)
|
|
{
|
|
#if 0
|
|
iaxc_event *e;
|
|
e = malloc(sizeof(ev));
|
|
*e = ev;
|
|
|
|
/* XXX Test return value? */
|
|
PostMessage(post_event_handle,post_event_id,(WPARAM) NULL, (LPARAM) e);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#ifdef MACOSX
|
|
/* Presently, OSX allows user-level processes to request RT
|
|
* priority. The API is nice, but the scheduler presently ignores
|
|
* the parameters (but the API validates that you're not asking for
|
|
* too much). See
|
|
* http://lists.apple.com/archives/darwin-development/2004/Feb/msg00079.html
|
|
*/
|
|
/* include mach stuff for declaration of thread_policy stuff */
|
|
#include <mach/mach.h>
|
|
|
|
int iaxci_prioboostbegin()
|
|
{
|
|
struct thread_time_constraint_policy ttcpolicy;
|
|
int params [2] = {CTL_HW,HW_BUS_FREQ};
|
|
int hzms;
|
|
size_t sz;
|
|
int ret;
|
|
|
|
/* get hz */
|
|
sz = sizeof (hzms);
|
|
sysctl (params, 2, &hzms, &sz, NULL, 0);
|
|
|
|
/* make hzms actually hz per ms */
|
|
hzms /= 1000;
|
|
|
|
/* give us at least how much? 6-8ms every 10ms (we generally need less) */
|
|
ttcpolicy.period = 10 * hzms; /* 10 ms */
|
|
ttcpolicy.computation = 2 * hzms;
|
|
ttcpolicy.constraint = 3 * hzms;
|
|
ttcpolicy.preemptible = 1;
|
|
|
|
if ( (ret = thread_policy_set(mach_thread_self(),
|
|
THREAD_TIME_CONSTRAINT_POLICY, (int *)&ttcpolicy,
|
|
THREAD_TIME_CONSTRAINT_POLICY_COUNT)) != KERN_SUCCESS )
|
|
{
|
|
fprintf(stderr, "thread_policy_set failed: %d.\n", ret);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int iaxci_prioboostend()
|
|
{
|
|
/* TODO */
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
|
|
/* Priority boosting/monitoring: Adapted from portaudio/pa_unix.c ,
|
|
* which carries the following copyright notice:
|
|
* PortAudio Portable Real-Time Audio Library
|
|
* Latest Version at: http://www.portaudio.com
|
|
* Linux OSS Implementation by douglas repetto and Phil Burk
|
|
*
|
|
* Copyright (c) 1999-2000 Phil Burk
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files
|
|
* (the "Software"), to deal in the Software without restriction,
|
|
* including without limitation the rights to use, copy, modify, merge,
|
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
* and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* Any person wishing to distribute modifications to the Software is
|
|
* requested to send the modifications to the original developer so that
|
|
* they can be incorporated into the canonical version.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
*/
|
|
|
|
/* It has been clarified by the authors that the request to send modifications
|
|
is a request, and not a condition */
|
|
|
|
/* Theory:
|
|
* The main thread is boosted to a medium real-time priority.
|
|
* Two additional threads are created:
|
|
* Canary: Runs as normal priority, updates a timevalue every second.
|
|
* WatchDog: Runs as a higher real-time priority. Checks to see that
|
|
* Canary is running. If Canary isn't running, lowers
|
|
* priority of calling thread, which has presumably run away
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/time.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <sched.h>
|
|
#include <pthread.h>
|
|
#include <errno.h>
|
|
|
|
//#define DBUG(...) fprintf(stderr, __VA_ARGS__)
|
|
#define DBUG(...)
|
|
#define ERR_RPT(...) fprintf(stderr, __VA_ARGS__)
|
|
|
|
#define SCHEDULER_POLICY SCHED_RR
|
|
#define WATCHDOG_INTERVAL_USEC 1000000
|
|
#define WATCHDOG_MAX_SECONDS 3
|
|
|
|
typedef void *(*pthread_function_t)(void *);
|
|
|
|
typedef struct {
|
|
int priority;
|
|
pthread_t ThreadID;
|
|
|
|
struct timeval CanaryTime;
|
|
int CanaryRun;
|
|
pthread_t CanaryThread;
|
|
int IsCanaryThreadValid;
|
|
|
|
int WatchDogRun;
|
|
pthread_t WatchDogThread;
|
|
int IsWatchDogThreadValid;
|
|
|
|
} prioboost;
|
|
|
|
static prioboost *pb;
|
|
|
|
static int CanaryProc( prioboost *b)
|
|
{
|
|
int result = 0;
|
|
struct sched_param schat = { 0 };
|
|
|
|
/* set us up with normal priority, please */
|
|
if( pthread_setschedparam(pthread_self(), SCHED_OTHER, &schat) != 0)
|
|
return 1;
|
|
|
|
while( b->CanaryRun)
|
|
{
|
|
usleep( WATCHDOG_INTERVAL_USEC );
|
|
gettimeofday( &b->CanaryTime, NULL );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int WatchDogProc( prioboost *b )
|
|
{
|
|
struct sched_param schp = { 0 };
|
|
int maxPri;
|
|
|
|
/* Run at a priority level above main thread so we can still run if it hangs. */
|
|
/* Rise more than 1 because of rumored off-by-one scheduler bugs. */
|
|
schp.sched_priority = b->priority + 4;
|
|
maxPri = sched_get_priority_max(SCHEDULER_POLICY);
|
|
if( schp.sched_priority > maxPri ) schp.sched_priority = maxPri;
|
|
|
|
if (pthread_setschedparam(pthread_self(), SCHEDULER_POLICY, &schp) != 0)
|
|
{
|
|
ERR_RPT("WatchDogProc: cannot set watch dog priority!\n");
|
|
goto killAudio;
|
|
}
|
|
|
|
DBUG("prioboost: WatchDog priority set to level %d!\n", schp.sched_priority);
|
|
|
|
/* Compare watchdog time with audio and canary thread times. */
|
|
/* Sleep for a while or until thread cancelled. */
|
|
while( b->WatchDogRun )
|
|
{
|
|
|
|
int delta;
|
|
struct timeval currentTime;
|
|
|
|
usleep( WATCHDOG_INTERVAL_USEC );
|
|
gettimeofday( ¤tTime, NULL );
|
|
|
|
#if 0
|
|
/* If audio thread is not advancing, then it must be hung so kill it. */
|
|
delta = currentTime.tv_sec - b->EntryTime.tv_sec;
|
|
DBUG(("WatchDogProc: audio delta = %d\n", delta ));
|
|
if( delta > WATCHDOG_MAX_SECONDS )
|
|
{
|
|
goto killAudio;
|
|
}
|
|
#endif
|
|
|
|
/* If canary died, then lower audio priority and halt canary. */
|
|
delta = currentTime.tv_sec - b->CanaryTime.tv_sec;
|
|
DBUG("WatchDogProc: dogging, delta = %ld, mysec=%d\n", delta, currentTime.tv_sec);
|
|
if( delta > WATCHDOG_MAX_SECONDS )
|
|
{
|
|
ERR_RPT("WatchDogProc: canary died!\n");
|
|
goto lowerAudio;
|
|
}
|
|
}
|
|
|
|
DBUG("WatchDogProc: exiting.\n");
|
|
return 0;
|
|
|
|
lowerAudio:
|
|
{
|
|
struct sched_param schat = { 0 };
|
|
if( pthread_setschedparam(b->ThreadID, SCHED_OTHER, &schat) != 0)
|
|
{
|
|
ERR_RPT("WatchDogProc: failed to lower audio priority. errno = %d\n", errno );
|
|
/* Fall through into killing audio thread. */
|
|
}
|
|
else
|
|
{
|
|
ERR_RPT("WatchDogProc: lowered audio priority to prevent hogging of CPU.\n");
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
killAudio:
|
|
ERR_RPT("WatchDogProc: killing hung audio thread!\n");
|
|
//pthread_cancel( b->ThreadID);
|
|
//pthread_join( b->ThreadID);
|
|
exit(1);
|
|
|
|
cleanup:
|
|
b->CanaryRun = 0;
|
|
DBUG("WatchDogProc: cancel Canary\n");
|
|
pthread_cancel( b->CanaryThread );
|
|
DBUG("WatchDogProc: join Canary\n");
|
|
pthread_join( b->CanaryThread, NULL );
|
|
DBUG("WatchDogProc: forget Canary\n");
|
|
b->IsCanaryThreadValid = 0;
|
|
|
|
#ifdef GNUSTEP
|
|
GSUnregisterCurrentThread(); /* SB20010904 */
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void StopWatchDog( prioboost *b )
|
|
{
|
|
/* Cancel WatchDog thread if there is one. */
|
|
if( b->IsWatchDogThreadValid )
|
|
{
|
|
b->WatchDogRun = 0;
|
|
DBUG("StopWatchDog: cancel WatchDog\n");
|
|
pthread_cancel( b->WatchDogThread );
|
|
pthread_join( b->WatchDogThread, NULL );
|
|
b->IsWatchDogThreadValid = 0;
|
|
}
|
|
/* Cancel Canary thread if there is one. */
|
|
if( b->IsCanaryThreadValid )
|
|
{
|
|
b->CanaryRun = 0;
|
|
DBUG("StopWatchDog: cancel Canary\n");
|
|
pthread_cancel( b->CanaryThread );
|
|
DBUG("StopWatchDog: join Canary\n");
|
|
pthread_join( b->CanaryThread, NULL );
|
|
b->IsCanaryThreadValid = 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int StartWatchDog( prioboost *b)
|
|
{
|
|
int hres;
|
|
int result = 0;
|
|
|
|
/* The watch dog watches for these timer updates */
|
|
gettimeofday( &b->CanaryTime, NULL );
|
|
|
|
/* Launch a canary thread to detect priority abuse. */
|
|
b->CanaryRun = 1;
|
|
hres = pthread_create(&(b->CanaryThread),
|
|
NULL /*pthread_attr_t * attr*/,
|
|
(pthread_function_t)CanaryProc, b);
|
|
if( hres != 0 )
|
|
{
|
|
b->IsCanaryThreadValid = 0;
|
|
result = 1;
|
|
goto error;
|
|
}
|
|
b->IsCanaryThreadValid = 1;
|
|
|
|
/* Launch a watchdog thread to prevent runaway audio thread. */
|
|
b->WatchDogRun = 1;
|
|
hres = pthread_create(&(b->WatchDogThread),
|
|
NULL /*pthread_attr_t * attr*/,
|
|
(pthread_function_t)WatchDogProc, b);
|
|
if( hres != 0 ) {
|
|
b->IsWatchDogThreadValid = 0;
|
|
result = 1;
|
|
goto error;
|
|
}
|
|
b->IsWatchDogThreadValid = 1;
|
|
return result;
|
|
|
|
error:
|
|
StopWatchDog( b );
|
|
return result;
|
|
}
|
|
|
|
int iaxci_prioboostbegin()
|
|
{
|
|
struct sched_param schp = { 0 };
|
|
prioboost *b = calloc(sizeof(*b),1);
|
|
if (b == NULL)
|
|
return 1;
|
|
|
|
int result = 0;
|
|
|
|
b->priority = (sched_get_priority_max(SCHEDULER_POLICY) -
|
|
sched_get_priority_min(SCHEDULER_POLICY)) / 2;
|
|
schp.sched_priority = b->priority;
|
|
|
|
b->ThreadID = pthread_self();
|
|
|
|
if (pthread_setschedparam(b->ThreadID, SCHEDULER_POLICY, &schp) != 0)
|
|
{
|
|
DBUG("prioboost: only superuser can use real-time priority.\n");
|
|
}
|
|
else
|
|
{
|
|
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. */
|
|
result = StartWatchDog( b );
|
|
}
|
|
|
|
if(result == 0) {
|
|
pb = b;
|
|
} else {
|
|
pb = NULL;
|
|
schp.sched_priority = 0;
|
|
pthread_setschedparam(b->ThreadID, SCHED_OTHER, &schp);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int iaxci_prioboostend()
|
|
{
|
|
if(pb) StopWatchDog(pb);
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|