As $ man errno
says, "errno is defined by the ISO C standard to be a modifiable lvalue of type int, and must not be explicitly declared; errno may be a macro. errno is thread-local; setting it in one thread does not affect its value in any other thread".
I'm developing a C library which will work both in POSIX and Windows, so instead of using errno
and GetLastError/SetLastError
I've decided to stick to my own error type. Every my function returns error code as cg_error
object, where cg_error
is just a typedef. However, for some functions like custom allocators it is still better to use something like errno
, but with my own cg_error
type.
AFAIK errno in glibc is implemented this way:
#define errno (*__errno_location ())
I'm trying to implement a similar function using pthreads
on Linux and TlsAlloc
and friends for Windows. Here is what I have now (yet only POSIX, seems to be a Solaris implementation from article "Thread-Specific Storage pattern" found on the Web):
cg_error * CG_ERRNO_TLS(void)
{
#if CG_FEATURE_POSIX
static int once;
static pthread_key_t key;
static pthread_mutex_t lock;
cg_error * error = NULL;
if (once)
{
pthread_mutex_lock(&lock);
if (once)
{
(void) pthread_key_create(&key, cg_free);
once = 1;
}
pthread_mutex_unlock(&lock);
}
error = pthread_getspecific(key);
if (!error)
{
error = cg_malloc(sizeof(*error));
(void) pthread_setspecific(key, error);
}
return error;
#endif
}
#define cg_errno (*CG_ERRNO_TLS())
However, when I try to set or get cg_errno
, its int value is 6344768
, which is not what I want. What am I doing wrong? What's the right way to define something like errno
? Thanks in advance!
P.S. I know that I could use __thread
and __declspec(thread)
, but such things are compiler-specific (and probably system specific; I've heard that __thread
doesn't work e.g. for MacOSX with gcc).
P.P.S. Base value of cg_error
is CG_ERROR_NONE
, which is always 0.
UPDATE:
#if CG_FEATURE_POSIX
static pthread_key_t cg_errno_key;
static pthread_once_t cg_errno_once = PTHREAD_ONCE_INIT;
static void cg_errno_init(void)
{ (void) pthread_key_create(&cg_errno_key, cg_free); }
cg_error * cg_errno_storage(void)
{
cg_error * error = NULL;
(void) pthread_once(&cg_errno_once, cg_errno_init);
error = pthread_getspecific(cg_errno_key);
if (!error)
{
error = cg_malloc(sizeof(*error));
(void) pthread_setspecific(cg_errno_key, error);
}
return error;
}
#define cg_errno (*cg_errno_storage())
#endif