Skip to content

Commit

Permalink
centralize clock handling for pthread_cond_timedwait. Resolves #795, #…
Browse files Browse the repository at this point in the history
  • Loading branch information
RossBencina committed Jan 20, 2024
1 parent daaf637 commit 8cc171e
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 12 deletions.
14 changes: 10 additions & 4 deletions src/hostapi/jack/pa_jack.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#include <jack/jack.h>

#include "pa_util.h"
#include "pa_pthread_util.h"
#include "pa_hostapi.h"
#include "pa_stream.h"
#include "pa_process.h"
Expand Down Expand Up @@ -168,6 +169,7 @@ typedef struct

pthread_mutex_t mtx;
pthread_cond_t cond;
PaUtilClockId condClockId;
unsigned long inputBase, outputBase;

/* For dealing with the process thread */
Expand Down Expand Up @@ -758,14 +760,18 @@ PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi,
int activated = 0;
jack_status_t jackStatus = 0;
*hostApi = NULL; /* Initialize to NULL */
pthread_condattr_t cattr;

UNLESS( jackHostApi = (PaJackHostApiRepresentation*)
PaUtil_AllocateZeroInitializedMemory( sizeof(PaJackHostApiRepresentation) ), paInsufficientMemory );
UNLESS( jackHostApi->deviceInfoMemory = PaUtil_CreateAllocationGroup(), paInsufficientMemory );

mainThread_ = pthread_self();
ASSERT_CALL( pthread_mutex_init( &jackHostApi->mtx, NULL ), 0 );
ASSERT_CALL( pthread_cond_init( &jackHostApi->cond, NULL ), 0 );

ASSERT_CALL( pthread_condattr_init( &cattr ), 0 );
jackHostApi->condClockId = PaPthreadUtil_NegotiateCondAttrClock( &cattr );
ASSERT_CALL( pthread_cond_init( &jackHostApi->cond, &cattr), 0 );

/* Try to become a client of the JACK server. If we cannot do
* this, then this API cannot be used.
Expand Down Expand Up @@ -1049,11 +1055,11 @@ static PaError WaitCondition( PaJackHostApiRepresentation *hostApi )
{
PaError result = paNoError;
int err = 0;
PaTime pt = PaUtil_GetTime();
struct timespec ts;

ts.tv_sec = (time_t) floor( pt + 10 * 60 /* 10 minutes */ );
ts.tv_nsec = (long) ((pt - floor( pt )) * 1000000000);
PaPthreadUtil_GetTime( hostApi->condClockId, &ts );
ts.tv_sec += 10 * 60; /* 10 minutes */

/* XXX: Best enclose in loop, in case of spurious wakeups? */
err = pthread_cond_timedwait( &hostApi->cond, &hostApi->mtx, &ts );

Expand Down
88 changes: 88 additions & 0 deletions src/os/unix/pa_pthread_util.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* $Id$
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2024 Ross Bencina, 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.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/*
* The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* 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. It is also
* requested that these non-binding requests be included along with the
* license above.
*/

#include "pa_pthread_util.h"

PaUtilClockId PaPthreadUtil_NegotiateCondAttrClock( pthread_condattr_t *cattr )
{
#if PAUTIL_USE_POSIX_ADVANCED_REALTIME
/* Set most suitable timeout clock and return its id.
If a clock can't be set, return the default clock.
*/
clockid_t clockId;

/* try each potential clockid in order of preferences until one succeeds:*/
#if defined(CLOCK_BOOTTIME )
if( pthread_condattr_setclock( &cattr, CLOCK_BOOTTIME ) == 0 )
return CLOCK_BOOTTIME;
#endif

#if defined(CLOCK_MONOTONIC)
if( pthread_condattr_setclock( &cattr, CLOCK_MONOTONIC ) == 0 )
return CLOCK_MONOTONIC;
#endif

#if defined(CLOCK_REALTIME)
if( pthread_condattr_setclock( &cattr, CLOCK_REALTIME ) == 0 )
return CLOCK_REALTIME;
#endif

/* fallback to returning the current clock id*/
if ( pthread_condattr_getclock( &cattr, &clockId) == 0 )
return clockId;

/* fallback to returning the default expected clock id*/
PA_DEBUG(( "%s: could not configure condattr clock\n", __FUNCTION__));
return CLOCK_REALTIME;
#else /* not PAUTIL_USE_POSIX_ADVANCED_REALTIME */
return 0; /* dummy value */
#endif
}

void PaPthreadUtil_GetTime( PaUtilClockId clockId, struct timespec *ts )
{
#if PAUTIL_USE_POSIX_ADVANCED_REALTIME
clock_gettime(clockId, ts);
#else /* not PAUTIL_USE_POSIX_ADVANCED_REALTIME */
struct timeval tv;
gettimeofday(&tv, NULL);
ts->tv_sec = tv.tv_sec;
ts->tv_nsec = tv.tv_usec * 1000;
#endif
}
99 changes: 99 additions & 0 deletions src/os/unix/pa_pthread_util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* $Id$
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2024 Ross Bencina, 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.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/*
* The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* 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. It is also
* requested that these non-binding requests be included along with the
* license above.
*/

/** @file
@ingroup unix_src
*/

#ifndef PA_PTHREAD_UTIL_H
#define PA_PTHREAD_UTIL_H

#include <sys/types.h> /* clockid_t */
#include <time.h> /* timespec */
#include <pthread.h>

#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */

/*
Use the presence of CLOCK_REALTIME as a proxy for the availability of
pthread_condattr_setclock, pthread_condattr_getclock and clock_gettime.
Otherewise use a fallback path.
On Apple, stick with default unix time using gettimeofday,
since CLOCK_MONOTONIC is known to be buggy:
https://discussions.apple.com/thread/253778121?sortBy=best
And clock_gettime is not available pre-Sierra:
https://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x
https://stackoverflow.com/a/21352348
*/
#ifdef CLOCK_REALTIME && !defined(__APPLE__)
#define PAUTIL_USE_POSIX_ADVANCED_REALTIME 1
#else
#define PAUTIL_USE_POSIX_ADVANCED_REALTIME 0
#endif

#if PAUTIL_USE_POSIX_ADVANCED_REALTIME

#define PaClockId clockid_t

#else

#define PaUtilClockId int /* dummy type */

#endif

/** Negotiate the most suitable clock for condvar timeouts, set the clock
* on cattr and return the clock's id.
*/
PaUtilClockId PaPthreadUtil_NegotiateCondAttrClock( pthread_condattr_t *cattr );

/** Get the current time according to the clock referred to by clockId, as
* previously returned by PaPthreadUtil_NegotiateCondAttrTimeoutClock().
*/
void PaPthreadUtil_GetTime( PaUtilClockId clockId, struct timespec *ts );

#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
15 changes: 7 additions & 8 deletions src/os/unix/pa_unix_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -284,9 +284,7 @@ PaError PaUnixThread_New( PaUnixThread* self, void* (*threadFunc)( void* ), void
memset( self, 0, sizeof (PaUnixThread) );
PaUnixMutex_Initialize( &self->mtx );
PA_ASSERT_CALL( pthread_condattr_init( &cattr ), 0 );
#if defined(CLOCK_MONOTONIC) && !defined(__APPLE__)
PA_ASSERT_CALL( pthread_condattr_setclock( &cattr, CLOCK_MONOTONIC ), 0 );
#endif
self->condClockId = PaPthreadUtil_NegotiateCondAttrClock( &cattr );
PA_ASSERT_CALL( pthread_cond_init( &self->cond, &cattr), 0 );

self->parentWaiting = 0 != waitForChild;
Expand Down Expand Up @@ -366,23 +364,24 @@ PaError PaUnixThread_New( PaUnixThread* self, void* (*threadFunc)( void* ), void

if( self->parentWaiting )
{
PaTime till;
PaTime now, deadline;
struct timespec ts;
int res = 0;
PaTime now;

PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );

/* Wait for stream to be started */
now = PaUtil_GetTime();
till = now + waitForChild;
PaPthreadUtil_GetTime( self->condClockId, &ts );
now = ts.tv_sec + ts.tv_nsec * 1e9;
deadline = now + waitForChild;

while( self->parentWaiting && !res )
{
if( waitForChild > 0 )
{
ts.tv_sec = (time_t) floor( till );
ts.tv_nsec = (long) ((till - floor( till )) * 1e9);
ts.tv_sec = (time_t) floor( deadline );
ts.tv_nsec = (long) ((deadline - floor( deadline )) * 1e9);
res = pthread_cond_timedwait( &self->cond, &self->mtx.mtx, &ts );
}
else
Expand Down
2 changes: 2 additions & 0 deletions src/os/unix/pa_unix_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#define PA_UNIX_UTIL_H

#include "pa_util.h"
#include "pa_pthread_util.h"
#include "pa_cpuload.h"
#include <assert.h>
#include <pthread.h>
Expand Down Expand Up @@ -150,6 +151,7 @@ typedef struct
int locked;
PaUnixMutex mtx;
pthread_cond_t cond;
PaUtilClockId condClockId;
volatile sig_atomic_t stopRequest;
} PaUnixThread;

Expand Down

0 comments on commit 8cc171e

Please sign in to comment.