localtimeやstrtokは本当にスレッドセーフにできないのか (3): GCC __thread keyword

3.3以降のgccを使っているのであれば、pthread_getspecific関数を使うのと同様のことを、特殊なキーワード __thread を使って実現できます。上で示したlocaltime関数の、長ったらしいソースコードは、次のように簡潔に書き直せます。

#include <time.h>
#include <stdlib.h>

struct tm* localtime(const time_t* timep) {
    static __thread struct tm ret;
    return localtime_r(timep, &ret);
}

このコードを gcc -S -fverbose-asm してみると、pthread_key と pthread_once を使った上のソースコードより簡潔なコードが吐かれております。移植性を気にしないなら、可読性・速度ともにこちらの方法が勝っているとおもいます。


ちなみに、どれくらい簡潔かというと…次の通り。手元の環境では、素のlocaltimeに比べて、速度の低下は10%〜20%程度に抑えられました。これなら使えるかも。

localtime:
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %ebx
        call    .L2
.L2:
        popl    %ebx
        addl    $_GLOBAL_OFFSET_TABLE_+[.-.L2], %ebx
        subl    $12, %esp
        leal    ret.0@TLSGD(,%ebx,1), %eax
        call    ___tls_get_addr@PLT
        pushl   %eax
        pushl   8(%ebp)
        call    localtime_r@PLT
        movl    -4(%ebp), %ebx
        leave
        ret

実行結果も、次のように特に問題ありません。

$ gcc -D_REENTRANT -O2 -fPIC -Wall -W -c localtime2.c
$ gcc -shared -Wl,-soname,libtsf.so.1 -o libtsf.so.1.0.2 localtime2.o -lpthread
$ LD_PRELOAD=./libtsf.so.1.0.2 ./a.out
main thread ID = 4143980736
Thread ID 4143975344, return address is 0xf7000b70, result = Thu Sep  9 20:55:13 2004
Thread ID 4143975344, return address is 0xf7000b70, result = Thu Sep  9 20:55:13 2004
Thread ID 4133481392, return address is 0xf65feb70, result = Thu Sep  9 20:55:13 2004
Thread ID 4133481392, return address is 0xf65feb70, result = Thu Sep  9 20:55:13 2004
thread1 ID = 4143975344
thread2 ID = 4133481392
Thread ID 4143980736, return address is 0xf7002080, result = Thu Sep  9 20:55:13 2004
Thread ID 4143980736, return address is 0xf7002080, result = Thu Sep  9 20:55:13 2004

参考:
http://lucille.sourceforge.net/blog/archives/000308.html
http://docs.sun.com/db/doc/817-4912/6mkdg5432?l=ja&a=view