strerror: Don't directly depend on pthread

This commit is contained in:
Martin Willi
2014-06-04 15:53:00 +02:00
parent 4189cd2f03
commit eb94f58595
3 changed files with 100 additions and 38 deletions
+2
View File
@@ -55,6 +55,7 @@ void utils_init()
#ifdef WIN32 #ifdef WIN32
windows_init(); windows_init();
#endif /* WIN32 */ #endif /* WIN32 */
strerror_init();
} }
/** /**
@@ -65,6 +66,7 @@ void utils_deinit()
#ifdef WIN32 #ifdef WIN32
windows_deinit(); windows_deinit();
#endif /* WIN32 */ #endif /* WIN32 */
strerror_deinit();
} }
/** /**
+88 -38
View File
@@ -15,7 +15,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <pthread.h>
#include <library.h>
#include <threading/thread_value.h>
#include <threading/spinlock.h>
#include "strerror.h" #include "strerror.h"
@@ -25,22 +28,16 @@
#define STRERROR_BUF_LEN 256 #define STRERROR_BUF_LEN 256
/** /**
* Key to store thread-specific error buffer * Thread specific strerror buffer, as char*
*/ */
static pthread_key_t strerror_buf_key; static thread_value_t *strerror_buf;
#ifndef HAVE_STRERROR_R
/** /**
* Only initialize the key above once * Lock to access strerror() safely
*/ */
static pthread_once_t strerror_buf_key_once = PTHREAD_ONCE_INIT; static spinlock_t *strerror_lock;
#endif /* HAVE_STRERROR_R */
/**
* Create the key used for the thread-specific error buffer
*/
static void create_strerror_buf_key()
{
pthread_key_create(&strerror_buf_key, free);
}
/** /**
* Retrieve the error buffer assigned to the current thread (or create it) * Retrieve the error buffer assigned to the current thread (or create it)
@@ -48,50 +45,103 @@ static void create_strerror_buf_key()
static inline char *get_strerror_buf() static inline char *get_strerror_buf()
{ {
char *buf; char *buf;
bool old = FALSE;
pthread_once(&strerror_buf_key_once, create_strerror_buf_key); if (!strerror_buf)
buf = pthread_getspecific(strerror_buf_key); {
return NULL;
}
buf = strerror_buf->get(strerror_buf);
if (!buf) if (!buf)
{ {
if (lib->leak_detective)
{
old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
}
buf = malloc(STRERROR_BUF_LEN); buf = malloc(STRERROR_BUF_LEN);
pthread_setspecific(strerror_buf_key, buf); strerror_buf->set(strerror_buf, buf);
if (lib->leak_detective)
{
lib->leak_detective->set_state(lib->leak_detective, old);
}
} }
return buf; return buf;
} }
#ifdef HAVE_STRERROR_R /**
* Use real strerror() below
*/
#undef strerror
/* /*
* Described in header. * Described in header.
*/ */
const char *strerror_safe(int errnum) const char *strerror_safe(int errnum)
{ {
char *buf = get_strerror_buf(), *msg; char *buf, *msg;
#ifdef STRERROR_R_CHAR_P buf = get_strerror_buf();
if (!buf)
{
/* library not initialized? fallback */
return strerror(errnum);
}
#ifdef HAVE_STRERROR_R
# ifdef STRERROR_R_CHAR_P
/* char* version which may or may not return the original buffer */ /* char* version which may or may not return the original buffer */
msg = strerror_r(errnum, buf, STRERROR_BUF_LEN); msg = strerror_r(errnum, buf, STRERROR_BUF_LEN);
#else # else
/* int version returns 0 on success */ /* int version returns 0 on success */
msg = strerror_r(errnum, buf, STRERROR_BUF_LEN) ? "Unknown error" : buf; msg = strerror_r(errnum, buf, STRERROR_BUF_LEN) ? "Unknown error" : buf;
#endif # endif
#else /* HAVE_STRERROR_R */
/* use a lock to ensure calling strerror(3) is thread-safe */
strerror_lock->lock(strerror_lock);
msg = strncpy(buf, strerror(errnum), STRERROR_BUF_LEN);
strerror_lock->unlock(strerror_lock);
buf[STRERROR_BUF_LEN - 1] = '\0';
#endif /* HAVE_STRERROR_R */
return msg; return msg;
} }
#else /* HAVE_STRERROR_R */
/* we actually wan't to call strerror(3) below */
#undef strerror
/*
* Described in header.
*/
const char *strerror_safe(int errnum)
{
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
char *buf = get_strerror_buf();
/* use a mutex to ensure calling strerror(3) is thread-safe */ /**
pthread_mutex_lock(&mutex); * free() with disabled leak detective
strncpy(buf, strerror(errnum), STRERROR_BUF_LEN); */
pthread_mutex_unlock(&mutex); static void free_no_ld(void *buf)
buf[STRERROR_BUF_LEN - 1] = '\0'; {
return buf; bool old = FALSE;
if (lib->leak_detective)
{
old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
}
free(buf);
if (lib->leak_detective)
{
lib->leak_detective->set_state(lib->leak_detective, old);
}
}
/**
* See header
*/
void strerror_init()
{
strerror_buf = thread_value_create(free_no_ld);
#ifndef HAVE_STRERROR_R
strerror_lock = spinlock_create();
#endif
}
/**
* See header
*/
void strerror_deinit()
{
strerror_buf->destroy(strerror_buf);
strerror_buf = NULL;
#ifndef HAVE_STRERROR_R
strerror_lock->destroy(strerror_lock);
#endif
} }
#endif /* HAVE_STRERROR_R */
+10
View File
@@ -32,6 +32,16 @@
*/ */
const char *strerror_safe(int errnum); const char *strerror_safe(int errnum);
/**
* Initialize strerror_safe()
*/
void strerror_init();
/**
* Deinitialize strerror_safe()
*/
void strerror_deinit();
/** /**
* Replace usages of strerror(3) with thread-safe variant. * Replace usages of strerror(3) with thread-safe variant.
*/ */