unixfuncs.c

00001 /*
00002  * iaxclient: a cross-platform IAX softphone library
00003  *
00004  * Copyrights:
00005  * Copyright (C) 2003-2006, Horizon Wimba, Inc.
00006  * Copyright (C) 2007, Wimba, Inc.
00007  *
00008  * Contributors:
00009  * Steve Kann <stevek@stevek.com>
00010  *
00011  * This program is free software, distributed under the terms of
00012  * the GNU Lesser (Library) General Public License.
00013  */
00014 
00015 #define _BSD_SOURCE
00016 #include <unistd.h>
00017 #ifndef __USE_POSIX199309
00018 #define __USE_POSIX199309
00019 #endif
00020 #include <time.h>
00021 #include "iaxclient_lib.h"
00022 
00023 #if TIME_WITH_SYS_TIME
00024 # include <sys/time.h>
00025 # include <time.h>
00026 #else
00027 # if HAVE_SYS_TIME_H
00028 #  include <sys/time.h>
00029 # else
00030 #  include <time.h>
00031 # endif
00032 #endif
00033 
00034 #ifndef NULL
00035 #define NULL (0)
00036 #endif
00037 
00038 /* Unix-specific functions */
00039 
00040 void os_init(void)
00041 {
00042 }
00043 
00044 void iaxc_millisleep(long ms)
00045 {
00046         struct timespec req;
00047 
00048         req.tv_nsec = (ms%1000)*1000*1000;
00049         req.tv_sec = ms/1000;
00050 
00051         /* yes, it can return early.  We don't care */
00052         nanosleep(&req,NULL);
00053 }
00054 
00055 
00056 /* TODO: Implement for X/MacOSX? */
00057 int iaxci_post_event_callback(iaxc_event ev)
00058 {
00059 #if 0
00060         iaxc_event *e;
00061         e = malloc(sizeof(ev));
00062         *e = ev;
00063 
00064         /* XXX Test return value? */
00065         PostMessage(post_event_handle,post_event_id,(WPARAM) NULL, (LPARAM) e);
00066 #endif
00067         return 0;
00068 }
00069 
00070 #ifdef MACOSX
00071     /* Presently, OSX allows user-level processes to request RT
00072      * priority.  The API is nice, but the scheduler presently ignores
00073      * the parameters (but the API validates that you're not asking for
00074      * too much).  See
00075      * http://lists.apple.com/archives/darwin-development/2004/Feb/msg00079.html
00076      */
00077 /* include mach stuff for declaration of thread_policy stuff */
00078 #include <mach/mach.h>
00079 
00080 int iaxci_prioboostbegin()
00081 {
00082         struct thread_time_constraint_policy ttcpolicy;
00083         int params [2] = {CTL_HW,HW_BUS_FREQ};
00084         int hzms;
00085         size_t sz;
00086         int ret;
00087 
00088         /* get hz */
00089         sz = sizeof (hzms);
00090         sysctl (params, 2, &hzms, &sz, NULL, 0);
00091 
00092         /* make hzms actually hz per ms */
00093         hzms /= 1000;
00094 
00095         /* give us at least how much? 6-8ms every 10ms (we generally need less) */
00096         ttcpolicy.period = 10 * hzms; /* 10 ms */
00097         ttcpolicy.computation = 2 * hzms;
00098         ttcpolicy.constraint = 3 * hzms;
00099         ttcpolicy.preemptible = 1;
00100 
00101         if ( (ret = thread_policy_set(mach_thread_self(),
00102                         THREAD_TIME_CONSTRAINT_POLICY, (int *)&ttcpolicy,
00103                         THREAD_TIME_CONSTRAINT_POLICY_COUNT)) != KERN_SUCCESS )
00104         {
00105                 fprintf(stderr, "thread_policy_set failed: %d.\n", ret);
00106         }
00107         return 0;
00108 }
00109 
00110 int iaxci_prioboostend()
00111 {
00112     /* TODO */
00113     return 0;
00114 }
00115 
00116 #else
00117 
00118 
00119 /* Priority boosting/monitoring:  Adapted from portaudio/pa_unix.c ,
00120  * which carries the following copyright notice:
00121  * PortAudio Portable Real-Time Audio Library
00122  * Latest Version at: http://www.portaudio.com
00123  * Linux OSS Implementation by douglas repetto and Phil Burk
00124  *
00125  * Copyright (c) 1999-2000 Phil Burk
00126  *
00127  * Permission is hereby granted, free of charge, to any person obtaining
00128  * a copy of this software and associated documentation files
00129  * (the "Software"), to deal in the Software without restriction,
00130  * including without limitation the rights to use, copy, modify, merge,
00131  * publish, distribute, sublicense, and/or sell copies of the Software,
00132  * and to permit persons to whom the Software is furnished to do so,
00133  * subject to the following conditions:
00134  *
00135  * The above copyright notice and this permission notice shall be
00136  * included in all copies or substantial portions of the Software.
00137  *
00138  * Any person wishing to distribute modifications to the Software is
00139  * requested to send the modifications to the original developer so that
00140  * they can be incorporated into the canonical version.
00141  *
00142  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00143  */
00144 
00145 /* It has been clarified by the authors that the request to send modifications
00146    is a request, and not a condition */
00147 
00148 /* Theory:
00149  *  The main thread is boosted to a medium real-time priority.
00150  *  Two additional threads are created:
00151  *  Canary:  Runs as normal priority, updates a timevalue every second.
00152  *  WatchDog:  Runs as a higher real-time priority.  Checks to see that
00153  *            Canary is running.  If Canary isn't running, lowers
00154  *            priority of calling thread, which has presumably run away
00155  */
00156 
00157 #include <stdio.h>
00158 #include <sys/ioctl.h>
00159 #include <sys/time.h>
00160 #include <fcntl.h>
00161 #include <unistd.h>
00162 #include <signal.h>
00163 #include <sched.h>
00164 #include <pthread.h>
00165 #include <errno.h>
00166 
00167 //#define DBUG(...) fprintf(stderr, __VA_ARGS__)
00168 #define DBUG(...)
00169 #define ERR_RPT(...) fprintf(stderr, __VA_ARGS__)
00170 
00171 #define SCHEDULER_POLICY SCHED_RR
00172 #define WATCHDOG_INTERVAL_USEC 1000000
00173 #define WATCHDOG_MAX_SECONDS 3
00174 
00175 typedef void *(*pthread_function_t)(void *);
00176 
00177 typedef struct {
00178         int priority;
00179         pthread_t ThreadID;
00180 
00181         struct timeval CanaryTime;
00182         int CanaryRun;
00183         pthread_t CanaryThread;
00184         int IsCanaryThreadValid;
00185 
00186         int WatchDogRun;
00187         pthread_t WatchDogThread;
00188         int IsWatchDogThreadValid;
00189 
00190 } prioboost;
00191 
00192 static prioboost *pb;
00193 
00194 static int CanaryProc( prioboost *b)
00195 {
00196         int result = 0;
00197         struct sched_param schat = { 0 };
00198 
00199         /* set us up with normal priority, please */
00200         if( pthread_setschedparam(pthread_self(), SCHED_OTHER, &schat) != 0)
00201                 return 1;
00202 
00203         while( b->CanaryRun)
00204         {
00205                 usleep( WATCHDOG_INTERVAL_USEC );
00206                 gettimeofday( &b->CanaryTime, NULL );
00207         }
00208 
00209         return result;
00210 }
00211 
00212 static int WatchDogProc( prioboost *b )
00213 {
00214         struct sched_param    schp = { 0 };
00215         int                   maxPri;
00216 
00217         /* Run at a priority level above main thread so we can still run if it hangs. */
00218         /* Rise more than 1 because of rumored off-by-one scheduler bugs. */
00219         schp.sched_priority = b->priority + 4;
00220         maxPri = sched_get_priority_max(SCHEDULER_POLICY);
00221         if( schp.sched_priority > maxPri ) schp.sched_priority = maxPri;
00222 
00223         if (pthread_setschedparam(pthread_self(), SCHEDULER_POLICY, &schp) != 0)
00224         {
00225                 ERR_RPT("WatchDogProc: cannot set watch dog priority!\n");
00226                 goto killAudio;
00227         }
00228 
00229         DBUG("prioboost: WatchDog priority set to level %d!\n", schp.sched_priority);
00230 
00231         /* Compare watchdog time with audio and canary thread times. */
00232         /* Sleep for a while or until thread cancelled. */
00233         while( b->WatchDogRun )
00234         {
00235 
00236                 int              delta;
00237                 struct timeval   currentTime;
00238 
00239                 usleep( WATCHDOG_INTERVAL_USEC );
00240                 gettimeofday( &currentTime, NULL );
00241 
00242 #if 0
00243                 /* If audio thread is not advancing, then it must be hung so kill it. */
00244                 delta = currentTime.tv_sec - b->EntryTime.tv_sec;
00245                 DBUG(("WatchDogProc: audio delta = %d\n", delta ));
00246                 if( delta > WATCHDOG_MAX_SECONDS )
00247                 {
00248                         goto killAudio;
00249                 }
00250 #endif
00251 
00252                 /* If canary died, then lower audio priority and halt canary. */
00253                 delta = currentTime.tv_sec - b->CanaryTime.tv_sec;
00254                 DBUG("WatchDogProc: dogging, delta = %ld, mysec=%d\n", delta, currentTime.tv_sec);
00255                 if( delta > WATCHDOG_MAX_SECONDS )
00256                 {
00257                         ERR_RPT("WatchDogProc: canary died!\n");
00258                         goto lowerAudio;
00259                 }
00260         }
00261 
00262         DBUG("WatchDogProc: exiting.\n");
00263         return 0;
00264 
00265 lowerAudio:
00266         {
00267                 struct sched_param    schat = { 0 };
00268                 if( pthread_setschedparam(b->ThreadID, SCHED_OTHER, &schat) != 0)
00269                 {
00270                         ERR_RPT("WatchDogProc: failed to lower audio priority. errno = %d\n", errno );
00271                         /* Fall through into killing audio thread. */
00272                 }
00273                 else
00274                 {
00275                         ERR_RPT("WatchDogProc: lowered audio priority to prevent hogging of CPU.\n");
00276                         goto cleanup;
00277                 }
00278         }
00279 
00280 killAudio:
00281         ERR_RPT("WatchDogProc: killing hung audio thread!\n");
00282         //pthread_cancel( b->ThreadID);
00283         //pthread_join( b->ThreadID);
00284         exit(1);
00285 
00286 cleanup:
00287         b->CanaryRun = 0;
00288         DBUG("WatchDogProc: cancel Canary\n");
00289         pthread_cancel( b->CanaryThread );
00290         DBUG("WatchDogProc: join Canary\n");
00291         pthread_join( b->CanaryThread, NULL );
00292         DBUG("WatchDogProc: forget Canary\n");
00293         b->IsCanaryThreadValid = 0;
00294 
00295 #ifdef GNUSTEP
00296         GSUnregisterCurrentThread();  /* SB20010904 */
00297 #endif
00298         return 0;
00299 }
00300 
00301 static void StopWatchDog( prioboost *b )
00302 {
00303         /* Cancel WatchDog thread if there is one. */
00304         if( b->IsWatchDogThreadValid )
00305         {
00306                 b->WatchDogRun = 0;
00307                 DBUG("StopWatchDog: cancel WatchDog\n");
00308                 pthread_cancel( b->WatchDogThread );
00309                 pthread_join( b->WatchDogThread, NULL );
00310                 b->IsWatchDogThreadValid = 0;
00311         }
00312         /* Cancel Canary thread if there is one. */
00313         if( b->IsCanaryThreadValid )
00314         {
00315                 b->CanaryRun = 0;
00316                 DBUG("StopWatchDog: cancel Canary\n");
00317                 pthread_cancel( b->CanaryThread );
00318                 DBUG("StopWatchDog: join Canary\n");
00319                 pthread_join( b->CanaryThread, NULL );
00320                 b->IsCanaryThreadValid = 0;
00321         }
00322 }
00323 
00324 
00325 static int StartWatchDog( prioboost *b)
00326 {
00327         int  hres;
00328         int  result = 0;
00329 
00330         /* The watch dog watches for these timer updates */
00331         gettimeofday( &b->CanaryTime, NULL );
00332 
00333         /* Launch a canary thread to detect priority abuse. */
00334         b->CanaryRun = 1;
00335         hres = pthread_create(&(b->CanaryThread),
00336                         NULL /*pthread_attr_t * attr*/,
00337                         (pthread_function_t)CanaryProc, b);
00338         if( hres != 0 )
00339         {
00340                 b->IsCanaryThreadValid = 0;
00341                 result = 1;
00342                 goto error;
00343         }
00344         b->IsCanaryThreadValid = 1;
00345 
00346         /* Launch a watchdog thread to prevent runaway audio thread. */
00347         b->WatchDogRun = 1;
00348         hres = pthread_create(&(b->WatchDogThread),
00349                         NULL /*pthread_attr_t * attr*/,
00350                         (pthread_function_t)WatchDogProc, b);
00351         if( hres != 0 )     {
00352                 b->IsWatchDogThreadValid = 0;
00353                 result = 1;
00354                 goto error;
00355         }
00356         b->IsWatchDogThreadValid = 1;
00357         return result;
00358 
00359 error:
00360         StopWatchDog( b );
00361         return result;
00362 }
00363 
00364 int iaxci_prioboostbegin()
00365 {
00366         struct sched_param   schp = { 0 };
00367         prioboost *b = calloc(sizeof(*b),1);
00368 
00369         int result = 0;
00370 
00371         b->priority = (sched_get_priority_max(SCHEDULER_POLICY) -
00372                         sched_get_priority_min(SCHEDULER_POLICY)) / 2;
00373         schp.sched_priority = b->priority;
00374 
00375         b->ThreadID = pthread_self();
00376 
00377         if (pthread_setschedparam(b->ThreadID, SCHEDULER_POLICY, &schp) != 0)
00378         {
00379                 DBUG("prioboost: only superuser can use real-time priority.\n");
00380         }
00381         else
00382         {
00383                 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. */
00384                 result = StartWatchDog( b );
00385         }
00386 
00387         if(result == 0)  {
00388                 pb = b;
00389         } else {
00390                 pb = NULL;
00391                 schp.sched_priority = 0;
00392                 pthread_setschedparam(b->ThreadID, SCHED_OTHER, &schp);
00393         }
00394 
00395         return result;
00396 }
00397 
00398 int iaxci_prioboostend()
00399 {
00400         if(pb) StopWatchDog(pb);
00401         return 0;
00402 }
00403 
00404 #endif
00405 

Generated on Mon Sep 24 15:43:29 2007 for IAXClient by  doxygen 1.5.3