1 /*
2  * $Id$
3  * Portable Audio I/O Library
4  * UNIX platform-specific support functions
5  *
6  * Based on the Open Source API proposed by Ross Bencina
7  * Copyright (c) 1999-2000 Ross Bencina
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining
10  * a copy of this software and associated documentation files
11  * (the "Software"), to deal in the Software without restriction,
12  * including without limitation the rights to use, copy, modify, merge,
13  * publish, distribute, sublicense, and/or sell copies of the Software,
14  * and to permit persons to whom the Software is furnished to do so,
15  * subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
24  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  */
28 
29 /*
30  * The text above constitutes the entire PortAudio license; however,
31  * the PortAudio community also makes the following non-binding requests:
32  *
33  * Any person wishing to distribute modifications to the Software is
34  * requested to send the modifications to the original developer so that
35  * they can be incorporated into the canonical version. It is also
36  * requested that these non-binding requests be included along with the
37  * license above.
38  */
39 
40 /** @file
41  @ingroup unix_src
42 */
43 
44 #ifndef PA_UNIX_UTIL_H
45 #define PA_UNIX_UTIL_H
46 
47 #include "pa_cpuload.h"
48 #include <assert.h>
49 #include <pthread.h>
50 #include <signal.h>
51 
52 #ifdef __cplusplus
53 extern "C"
54 {
55 #endif /* __cplusplus */
56 
57 #define PA_MIN(x,y) ( (x) < (y) ? (x) : (y) )
58 #define PA_MAX(x,y) ( (x) > (y) ? (x) : (y) )
59 
60 /* Utilize GCC branch prediction for error tests */
61 #if defined __GNUC__ && __GNUC__ >= 3
62 #define UNLIKELY(expr) __builtin_expect( (expr), 0 )
63 #else
64 #define UNLIKELY(expr) (expr)
65 #endif
66 
67 #define STRINGIZE_HELPER(expr) #expr
68 #define STRINGIZE(expr) STRINGIZE_HELPER(expr)
69 
70 #define PA_UNLESS(expr, code) \
71     do { \
72         if( UNLIKELY( (expr) == 0 ) ) \
73         { \
74             PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
75             result = (code); \
76             goto error; \
77         } \
78     } while (0);
79 
80 static PaError paUtilErr_;          /* Used with PA_ENSURE */
81 
82 /* Check PaError */
83 #define PA_ENSURE(expr) \
84     do { \
85         if( UNLIKELY( (paUtilErr_ = (expr)) < paNoError ) ) \
86         { \
87             PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
88             result = paUtilErr_; \
89             goto error; \
90         } \
91     } while (0);
92 
93 #define PA_ASSERT_CALL(expr, success) \
94     paUtilErr_ = (expr); \
95     assert( success == paUtilErr_ );
96 
97 #define PA_ENSURE_SYSTEM(expr, success) \
98     do { \
99         if( UNLIKELY( (paUtilErr_ = (expr)) != success ) ) \
100         { \
101             /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
102             if( pthread_equal(pthread_self(), paUnixMainThread) ) \
103             { \
104                 PaUtil_SetLastHostErrorInfo( paALSA, paUtilErr_, strerror( paUtilErr_ ) ); \
105             } \
106             PaUtil_DebugPrint( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" ); \
107             result = paUnanticipatedHostError; \
108             goto error; \
109         } \
110     } while( 0 );
111 
112 typedef struct {
113     pthread_t callbackThread;
114 } PaUtilThreading;
115 
116 PaError PaUtil_InitializeThreading( PaUtilThreading *threading );
117 void PaUtil_TerminateThreading( PaUtilThreading *threading );
118 PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data );
119 PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult );
120 
121 /* State accessed by utility functions */
122 
123 /*
124 void PaUnix_SetRealtimeScheduling( int rt );
125 
126 void PaUtil_InitializeThreading( PaUtilThreading *th, PaUtilCpuLoadMeasurer *clm );
127 
128 PaError PaUtil_CreateCallbackThread( PaUtilThreading *th, void *(*CallbackThreadFunc)( void * ), PaStream *s );
129 
130 PaError PaUtil_KillCallbackThread( PaUtilThreading *th, PaError *exitResult );
131 
132 void PaUtil_CallbackUpdate( PaUtilThreading *th );
133 */
134 
135 extern pthread_t paUnixMainThread;
136 
137 typedef struct
138 {
139     pthread_mutex_t mtx;
140 } PaUnixMutex;
141 
142 PaError PaUnixMutex_Initialize( PaUnixMutex* self );
143 PaError PaUnixMutex_Terminate( PaUnixMutex* self );
144 PaError PaUnixMutex_Lock( PaUnixMutex* self );
145 PaError PaUnixMutex_Unlock( PaUnixMutex* self );
146 
147 typedef struct
148 {
149     pthread_t thread;
150     int parentWaiting;
151     int stopRequested;
152     int locked;
153     PaUnixMutex mtx;
154     pthread_cond_t cond;
155     volatile sig_atomic_t stopRequest;
156 } PaUnixThread;
157 
158 /** Initialize global threading state.
159  */
160 PaError PaUnixThreading_Initialize();
161 
162 /** Perish, passing on eventual error code.
163  *
164  * A thin wrapper around pthread_exit, will automatically pass on any error code to the joining thread.
165  * If the result indicates an error, i.e. it is not equal to paNoError, this function will automatically
166  * allocate a pointer so the error is passed on with pthread_exit. If the result indicates that all is
167  * well however, only a NULL pointer will be handed to pthread_exit. Thus, the joining thread should
168  * check whether a non-NULL result pointer is obtained from pthread_join and make sure to free it.
169  * @param result: The error code to pass on to the joining thread.
170  */
171 #define PaUnixThreading_EXIT(result) \
172     do { \
173         PaError* pres = NULL; \
174         if( paNoError != (result) ) \
175         { \
176             pres = malloc( sizeof (PaError) ); \
177             *pres = (result); \
178         } \
179         pthread_exit( pres ); \
180     } while (0);
181 
182 /** Spawn a thread.
183  *
184  * Intended for spawning the callback thread from the main thread. This function can even block (for a certain
185  * time or indefinitely) untill notified by the callback thread (using PaUnixThread_NotifyParent), which can be
186  * useful in order to make sure that callback has commenced before returning from Pa_StartStream.
187  * @param threadFunc: The function to be executed in the child thread.
188  * @param waitForChild: If not 0, wait for child thread to call PaUnixThread_NotifyParent. Less than 0 means
189  * wait for ever, greater than 0 wait for the specified time.
190  * @param rtSched: Enable realtime scheduling?
191  * @return: If timed out waiting on child, paTimedOut.
192  */
193 PaError PaUnixThread_New( PaUnixThread* self, void* (*threadFunc)( void* ), void* threadArg, PaTime waitForChild,
194         int rtSched );
195 
196 /** Terminate thread.
197  *
198  * @param wait: If true, request that background thread stop and wait untill it does, else cancel it.
199  * @param exitResult: If non-null this will upon return contain the exit status of the thread.
200  */
201 PaError PaUnixThread_Terminate( PaUnixThread* self, int wait, PaError* exitResult );
202 
203 /** Prepare to notify waiting parent thread.
204  *
205  * An internal lock must be held before the parent is notified in PaUnixThread_NotifyParent, call this to
206  * acquire it beforehand.
207  * @return: If parent is not waiting, paInternalError.
208  */
209 PaError PaUnixThread_PrepareNotify( PaUnixThread* self );
210 
211 /** Notify waiting parent thread.
212  *
213  * @return: If parent timed out waiting, paTimedOut. If parent was never waiting, paInternalError.
214  */
215 PaError PaUnixThread_NotifyParent( PaUnixThread* self );
216 
217 /** Has the parent thread requested this thread to stop?
218  */
219 int PaUnixThread_StopRequested( PaUnixThread* self );
220 
221 #ifdef __cplusplus
222 }
223 #endif /* __cplusplus */
224 #endif
225