如$ man errno
所述,「errno由ISO C標準定義爲int類型的可修改左值,且不能被明確聲明; errno可能是宏,errno是線程本地;設置它在一個線程中不影響其在任何其他線程中的值「。使用pthread實現errno模擬
我正在開發一個可在POSIX和Windows中工作的C庫,所以不是使用errno
和GetLastError/SetLastError
我決定堅持自己的錯誤類型。每個函數都返回錯誤代碼爲cg_error
對象,其中cg_error
只是一個typedef。但是,對於像自定義分配器這樣的函數,使用類似errno
的東西更好,但是使用我自己的cg_error
類型。在glibc的
AFAIK errno將被以這種方式實現:
#define errno (*__errno_location())
我試圖實現使用pthreads
在Linux和TlsAlloc
和朋友有類似的功能視窗。以下是我現在有(但只有POSIX,似乎是從文章一個Solaris實現在網絡上「線程專有存儲模式」找到):
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())
然而,當我嘗試設置或獲取cg_errno
,它的int值是6344768
,這不是我想要的。我究竟做錯了什麼?什麼是正確的方式來定義像errno
?提前致謝!
附:我知道我可以使用__thread
和__declspec(thread)
,但是這些東西是編譯器特定的(可能系統特定;我聽說__thread
不適用於,例如對於使用gcc的MacOSX)。
P.P.S.的cg_error
基值爲CG_ERROR_NONE
,它始終爲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
它不會出現你曾經初始化一次變量。另外,假設你只想要一次執行一次,你需要在你的pthread_key_create()調用後設置一次爲0(這意味着你想要初始化爲1)。因爲它代表你的一次封鎖可能不會被調用,所以你的pthread_setspecific()會出錯(你可以通過檢查pthread_setspecific()的返回值來檢查它,在成功時返回0) – KorreyD
'lock'應該有一個初始化器' PTHREAD_MUTEX_INITIALIZER'。 –