2014-01-15 25 views
1

$ man errno所述,「errno由ISO C標準定義爲int類型的可修改左值,且不能被明確聲明; errno可能是宏,errno是線程本地;設置它在一個線程中不影響其在任何其他線程中的值「。使用pthread實現errno模擬

我正在開發一個可在POSIX和Windows中工作的C庫,所以不是使用errnoGetLastError/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 
+1

它不會出現你曾經初始化一次變量。另外,假設你只想要一次執行一次,你需要在你的pthread_key_create()調用後設置一次爲0(這意味着你想要初始化爲1)。因爲它代表你的一次封鎖可能不會被調用,所以你的pthread_setspecific()會出錯(你可以通過檢查pthread_setspecific()的返回值來檢查它,在成功時返回0) – KorreyD

+1

'lock'應該有一個初始化器' PTHREAD_MUTEX_INITIALIZER'。 –

回答

3

您對once條件是錯誤的。它應該是!once

但是使用int因爲這不能保證工作。有一個特殊的類型pthread_once_t與功能pthread_once爲您正在嘗試實現的任務。

此外,lock應該有一個初始化程序,PTHREAD_MUTEX_INITIALIZER

一種利用pthread_once_t應該是這樣的:

static pthread_key_t key; 
static pthread_once_t once = PTHREAD_ONCE_INIT; 

static 
void init_error_key_once(void) { 
    pthread_key_create(&key, cg_free); 
} 

cg_error * CG_ERRNO_TLS_POSIX(void) 
{ 
    cg_error * error = NULL; 
    pthread_once(&once, init_error_key_once); 
    ... 

} 
+0

謝謝,現在它工作。我已經添加了一個工作版本;你還看到任何錯誤?沒有太多關於TLS的文章,所以有人可能會覺得它很有用。 – ghostmansd

+0

@ghostmansd,'pthread_once_t'不應該像這樣使用。把所有初始化的東西放在一個函數中,並使用POSIX函數'pthread_once'。 –

+0

我需要'CG_ERRNO_TLS'裏面還是'init_error_key_once'裏面的'malloc'?我再次更新函數;希望我明白你的正確。 – ghostmansd