Friday, December 30, 2011

Missing pthread_mutex_timedlock on Android

I stumbled upon a problem today that the pthread_mutex_timedlock function definition is missing on Android, though it's declared in the pthread.h.
The first thought was to implement it as a loop with a call to pthread_mutex_trylock and some sleep until we are able to get the lock or until the timeout happens, felt a bit awkward.

I took a look a the Mono for Android source code and that's exactly what they do there (./mono/io-layer/mono-mutex.c), so go figure, looks like this solution might not be that bad after all.


Here is how it's implemented in Mono for example:
int
pthread_mutex_timedlock (pthread_mutex_t *mutex, CONST_NEEDED struct timespec *timeout)
{
 struct timeval timenow;
 struct timespec sleepytime;
 int retcode;
 
 /* This is just to avoid a completely busy wait */
 sleepytime.tv_sec = 0;
 sleepytime.tv_nsec = 10000000; /* 10ms */
 
 while ((retcode = pthread_mutex_trylock (mutex)) == EBUSY) {
  gettimeofday (&timenow, NULL);
  
  if (timenow.tv_sec >= timeout->tv_sec &&
      (timenow.tv_usec * 1000) >= timeout->tv_nsec) {
   return ETIMEDOUT;
  }
  
  nanosleep (&sleepytime, NULL);
 }
 
 return retcode;
}

5 comments:

Anonymous said...

Thanks. I will borrow it. Answer to your thought depends on 1) how frequently you need to call pthread_mutex_timedlock() and 2) how it is likely pthread_mutex_trylock() returns EBUSY.

Anonymous said...

Thank you! I will borrow it as well :)

Anonymous said...

FYI ... what happens if you enter the routine at a high number of nanoseconds?

if (timenow.tv_sec >= timeout->tv_sec &&
(timenow.tv_usec * 1000) >= timeout->tv_nsec)

entry: tv_sec = 10, tv_nsec = 999,999

Anonymous said...

If you did borrow it ... Consider that if tv_nsec is 999,999 in timeout, the conditional requires that *both* values at wakeup be >= the timeout values...

It should probably be an "if tv_sec > tv_sec || (tv_sec == tv_sec && tv_usec * 1000 >= tv_nsec)"

Anonymous said...

That should probably be

if (timenow.tv_sec > timeout->tv_sec ||
(timenow.tv_sec == timeout->tv_sec && (timenow.tv_usec * 1000) >= timeout->tv_nsec)) {

=)