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