]> git.mxchange.org Git - flightgear.git/blob - 3rdparty/iaxclient/lib/unixfuncs.c
VS2015 compatability fixes.
[flightgear.git] / 3rdparty / iaxclient / lib / unixfuncs.c
1 /*
2  * iaxclient: a cross-platform IAX softphone library
3  *
4  * Copyrights:
5  * Copyright (C) 2003-2006, Horizon Wimba, Inc.
6  * Copyright (C) 2007, Wimba, Inc.
7  *
8  * Contributors:
9  * Steve Kann <stevek@stevek.com>
10  *
11  * This program is free software, distributed under the terms of
12  * the GNU Lesser (Library) General Public License.
13  */
14
15 #define _BSD_SOURCE
16 #include <unistd.h>
17 #ifndef __USE_POSIX199309
18 #define __USE_POSIX199309
19 #endif
20 #include <time.h>
21 #include "iaxclient_lib.h"
22
23 #ifdef HAVE_CONFIG_H
24 #  include <config.h>
25 #endif
26
27 #if TIME_WITH_SYS_TIME
28 # include <sys/time.h>
29 # include <time.h>
30 #else
31 # ifdef HAVE_SYS_TIME_H
32 #  include <sys/time.h>
33 # else
34 #  include <time.h>
35 # endif
36 #endif
37
38 #ifndef NULL
39 #define NULL (0)
40 #endif
41
42 /* Unix-specific functions */
43
44 void os_init(void)
45 {
46 }
47
48 void iaxc_millisleep(long ms)
49 {
50         struct timespec req;
51
52         req.tv_nsec = (ms%1000)*1000*1000;
53         req.tv_sec = ms/1000;
54
55         /* yes, it can return early.  We don't care */
56         nanosleep(&req,NULL);
57 }
58
59
60 /* TODO: Implement for X/MacOSX? */
61 int iaxci_post_event_callback(iaxc_event ev)
62 {
63 #if 0
64         iaxc_event *e;
65         e = malloc(sizeof(ev));
66         *e = ev;
67
68         /* XXX Test return value? */
69         PostMessage(post_event_handle,post_event_id,(WPARAM) NULL, (LPARAM) e);
70 #endif
71         return 0;
72 }
73
74 #ifdef MACOSX
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
78      * too much).  See
79      * http://lists.apple.com/archives/darwin-development/2004/Feb/msg00079.html
80      */
81 /* include mach stuff for declaration of thread_policy stuff */
82 #include <mach/mach.h>
83
84 int iaxci_prioboostbegin()
85 {
86         struct thread_time_constraint_policy ttcpolicy;
87         int params [2] = {CTL_HW,HW_BUS_FREQ};
88         int hzms;
89         size_t sz;
90         int ret;
91
92         /* get hz */
93         sz = sizeof (hzms);
94         sysctl (params, 2, &hzms, &sz, NULL, 0);
95
96         /* make hzms actually hz per ms */
97         hzms /= 1000;
98
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;
104
105         if ( (ret = thread_policy_set(mach_thread_self(),
106                         THREAD_TIME_CONSTRAINT_POLICY, (int *)&ttcpolicy,
107                         THREAD_TIME_CONSTRAINT_POLICY_COUNT)) != KERN_SUCCESS )
108         {
109                 fprintf(stderr, "thread_policy_set failed: %d.\n", ret);
110         }
111         return 0;
112 }
113
114 int iaxci_prioboostend()
115 {
116     /* TODO */
117     return 0;
118 }
119
120 #else
121
122
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
128  *
129  * Copyright (c) 1999-2000 Phil Burk
130  *
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:
138  *
139  * The above copyright notice and this permission notice shall be
140  * included in all copies or substantial portions of the Software.
141  *
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.
145  *
146  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
147  */
148
149 /* It has been clarified by the authors that the request to send modifications
150    is a request, and not a condition */
151
152 /* Theory:
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
159  */
160
161 #include <stdio.h>
162 #include <sys/ioctl.h>
163 #include <sys/time.h>
164 #include <fcntl.h>
165 #include <unistd.h>
166 #include <signal.h>
167 #include <sched.h>
168 #include <pthread.h>
169 #include <errno.h>
170
171 //#define DBUG(...) fprintf(stderr, __VA_ARGS__)
172 #define DBUG(...)
173 #define ERR_RPT(...) fprintf(stderr, __VA_ARGS__)
174
175 #define SCHEDULER_POLICY SCHED_RR
176 #define WATCHDOG_INTERVAL_USEC 1000000
177 #define WATCHDOG_MAX_SECONDS 3
178
179 typedef void *(*pthread_function_t)(void *);
180
181 typedef struct {
182         int priority;
183         pthread_t ThreadID;
184
185         struct timeval CanaryTime;
186         int CanaryRun;
187         pthread_t CanaryThread;
188         int IsCanaryThreadValid;
189
190         int WatchDogRun;
191         pthread_t WatchDogThread;
192         int IsWatchDogThreadValid;
193
194 } prioboost;
195
196 static prioboost *pb;
197
198 static int CanaryProc( prioboost *b)
199 {
200         int result = 0;
201         struct sched_param schat = { 0 };
202
203         /* set us up with normal priority, please */
204         if( pthread_setschedparam(pthread_self(), SCHED_OTHER, &schat) != 0)
205                 return 1;
206
207         while( b->CanaryRun)
208         {
209                 usleep( WATCHDOG_INTERVAL_USEC );
210                 gettimeofday( &b->CanaryTime, NULL );
211         }
212
213         return result;
214 }
215
216 static int WatchDogProc( prioboost *b )
217 {
218         struct sched_param    schp = { 0 };
219         int                   maxPri;
220
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;
226
227         if (pthread_setschedparam(pthread_self(), SCHEDULER_POLICY, &schp) != 0)
228         {
229                 ERR_RPT("WatchDogProc: cannot set watch dog priority!\n");
230                 goto killAudio;
231         }
232
233         DBUG("prioboost: WatchDog priority set to level %d!\n", schp.sched_priority);
234
235         /* Compare watchdog time with audio and canary thread times. */
236         /* Sleep for a while or until thread cancelled. */
237         while( b->WatchDogRun )
238         {
239
240                 int              delta;
241                 struct timeval   currentTime;
242
243                 usleep( WATCHDOG_INTERVAL_USEC );
244                 gettimeofday( &currentTime, NULL );
245
246 #if 0
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 )
251                 {
252                         goto killAudio;
253                 }
254 #endif
255
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 )
260                 {
261                         ERR_RPT("WatchDogProc: canary died!\n");
262                         goto lowerAudio;
263                 }
264         }
265
266         DBUG("WatchDogProc: exiting.\n");
267         return 0;
268
269 lowerAudio:
270         {
271                 struct sched_param    schat = { 0 };
272                 if( pthread_setschedparam(b->ThreadID, SCHED_OTHER, &schat) != 0)
273                 {
274                         ERR_RPT("WatchDogProc: failed to lower audio priority. errno = %d\n", errno );
275                         /* Fall through into killing audio thread. */
276                 }
277                 else
278                 {
279                         ERR_RPT("WatchDogProc: lowered audio priority to prevent hogging of CPU.\n");
280                         goto cleanup;
281                 }
282         }
283
284 killAudio:
285         ERR_RPT("WatchDogProc: killing hung audio thread!\n");
286         //pthread_cancel( b->ThreadID);
287         //pthread_join( b->ThreadID);
288         exit(1);
289
290 cleanup:
291         b->CanaryRun = 0;
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;
298
299 #ifdef GNUSTEP
300         GSUnregisterCurrentThread();  /* SB20010904 */
301 #endif
302         return 0;
303 }
304
305 static void StopWatchDog( prioboost *b )
306 {
307         /* Cancel WatchDog thread if there is one. */
308         if( b->IsWatchDogThreadValid )
309         {
310                 b->WatchDogRun = 0;
311                 DBUG("StopWatchDog: cancel WatchDog\n");
312                 pthread_cancel( b->WatchDogThread );
313                 pthread_join( b->WatchDogThread, NULL );
314                 b->IsWatchDogThreadValid = 0;
315         }
316         /* Cancel Canary thread if there is one. */
317         if( b->IsCanaryThreadValid )
318         {
319                 b->CanaryRun = 0;
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;
325         }
326 }
327
328
329 static int StartWatchDog( prioboost *b)
330 {
331         int  hres;
332         int  result = 0;
333
334         /* The watch dog watches for these timer updates */
335         gettimeofday( &b->CanaryTime, NULL );
336
337         /* Launch a canary thread to detect priority abuse. */
338         b->CanaryRun = 1;
339         hres = pthread_create(&(b->CanaryThread),
340                         NULL /*pthread_attr_t * attr*/,
341                         (pthread_function_t)CanaryProc, b);
342         if( hres != 0 )
343         {
344                 b->IsCanaryThreadValid = 0;
345                 result = 1;
346                 goto error;
347         }
348         b->IsCanaryThreadValid = 1;
349
350         /* Launch a watchdog thread to prevent runaway audio thread. */
351         b->WatchDogRun = 1;
352         hres = pthread_create(&(b->WatchDogThread),
353                         NULL /*pthread_attr_t * attr*/,
354                         (pthread_function_t)WatchDogProc, b);
355         if( hres != 0 )     {
356                 b->IsWatchDogThreadValid = 0;
357                 result = 1;
358                 goto error;
359         }
360         b->IsWatchDogThreadValid = 1;
361         return result;
362
363 error:
364         StopWatchDog( b );
365         return result;
366 }
367
368 int iaxci_prioboostbegin()
369 {
370         struct sched_param   schp = { 0 };
371         prioboost *b = calloc(sizeof(*b),1);
372
373         int result = 0;
374
375         b->priority = (sched_get_priority_max(SCHEDULER_POLICY) -
376                         sched_get_priority_min(SCHEDULER_POLICY)) / 2;
377         schp.sched_priority = b->priority;
378
379         b->ThreadID = pthread_self();
380
381         if (pthread_setschedparam(b->ThreadID, SCHEDULER_POLICY, &schp) != 0)
382         {
383                 DBUG("prioboost: only superuser can use real-time priority.\n");
384         }
385         else
386         {
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 );
389         }
390
391         if(result == 0)  {
392                 pb = b;
393         } else {
394                 pb = NULL;
395                 schp.sched_priority = 0;
396                 pthread_setschedparam(b->ThreadID, SCHED_OTHER, &schp);
397         }
398
399         return result;
400 }
401
402 int iaxci_prioboostend()
403 {
404         if(pb) StopWatchDog(pb);
405         return 0;
406 }
407
408 #endif
409