baidu_thread_local_unittest.cpp 4.44 KB
Newer Older
gejun's avatar
gejun committed
1
// Copyright (c) 2014 Baidu, Inc.
gejun's avatar
gejun committed
2 3 4 5 6
// Author: Ge,Jun (gejun@baidu.com)
// Date: 2010-12-04 11:59

#include <gtest/gtest.h>
#include <errno.h>
7
#include "butil/thread_local.h"
gejun's avatar
gejun committed
8 9 10 11 12

namespace {

BAIDU_THREAD_LOCAL int * dummy = NULL;
const size_t NTHREAD = 8;
13 14 15
static bool processed[NTHREAD+1];
static bool deleted[NTHREAD+1];
static bool register_check = false;
gejun's avatar
gejun committed
16 17 18 19 20 21 22 23 24 25 26 27 28 29

struct YellObj {
    static int nc;
    static int nd;
    YellObj() {
        ++nc;
    }
    ~YellObj() {
        ++nd;
    }
};
int YellObj::nc = 0;
int YellObj::nd = 0;

30 31 32 33 34
static void check_global_variable() {
    EXPECT_TRUE(processed[NTHREAD]);
    EXPECT_TRUE(deleted[NTHREAD]);
    EXPECT_EQ(2, YellObj::nc);
    EXPECT_EQ(2, YellObj::nd);
gejun's avatar
gejun committed
35 36 37 38 39 40 41
}

class BaiduThreadLocalTest : public ::testing::Test{
protected:
    BaiduThreadLocalTest(){
        if (!register_check) {
            register_check = true;
42
            butil::thread_atexit(check_global_variable);
gejun's avatar
gejun committed
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
        }
    };
    virtual ~BaiduThreadLocalTest(){};
    virtual void SetUp() {
    };
    virtual void TearDown() {
    };
};


BAIDU_THREAD_LOCAL void* x;

void* foo(void* arg) {
    x = arg;
    usleep(10000);
    printf("x=%p\n", x);
    return NULL;
}

TEST_F(BaiduThreadLocalTest, thread_local_keyword) {
    pthread_t th[2];
    pthread_create(&th[0], NULL, foo, (void*)1);
    pthread_create(&th[1], NULL, foo, (void*)2);
    pthread_join(th[0], NULL);
    pthread_join(th[1], NULL);
}

void* yell(void*) {
71
    YellObj* p = butil::get_thread_local<YellObj>();
gejun's avatar
gejun committed
72 73 74
    EXPECT_TRUE(p);
    EXPECT_EQ(2, YellObj::nc);
    EXPECT_EQ(0, YellObj::nd);
75
    EXPECT_EQ(p, butil::get_thread_local<YellObj>());
gejun's avatar
gejun committed
76 77 78 79 80 81 82 83
    EXPECT_EQ(2, YellObj::nc);
    EXPECT_EQ(0, YellObj::nd);
    return NULL;
}

TEST_F(BaiduThreadLocalTest, get_thread_local) {
    YellObj::nc = 0;
    YellObj::nd = 0;
84
    YellObj* p = butil::get_thread_local<YellObj>();
gejun's avatar
gejun committed
85 86 87
    ASSERT_TRUE(p);
    ASSERT_EQ(1, YellObj::nc);
    ASSERT_EQ(0, YellObj::nd);
88
    ASSERT_EQ(p, butil::get_thread_local<YellObj>());
gejun's avatar
gejun committed
89 90 91 92 93 94 95 96 97 98
    ASSERT_EQ(1, YellObj::nc);
    ASSERT_EQ(0, YellObj::nd);
    pthread_t th;
    ASSERT_EQ(0, pthread_create(&th, NULL, yell, NULL));
    pthread_join(th, NULL);
    EXPECT_EQ(2, YellObj::nc);
    EXPECT_EQ(1, YellObj::nd);
}

void delete_dummy(void* arg) {
99
    *(bool*)arg = true;
gejun's avatar
gejun committed
100 101 102 103 104 105 106 107 108 109 110
    if (dummy) {
        delete dummy;
        dummy = NULL;
    } else {
        printf("dummy is NULL\n");
    }
}

void* proc_dummy(void* arg) {
    bool *p = (bool*)arg;
    *p = true;
111
    EXPECT_TRUE(dummy == NULL);
gejun's avatar
gejun committed
112
    dummy = new int(p - processed);
113
    butil::thread_atexit(delete_dummy, deleted + (p - processed));
gejun's avatar
gejun committed
114 115 116 117 118
    return NULL;
}

TEST_F(BaiduThreadLocalTest, sanity) {
    errno = 0;
119
    ASSERT_EQ(-1, butil::thread_atexit(NULL));
gejun's avatar
gejun committed
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
    ASSERT_EQ(EINVAL, errno);

    processed[NTHREAD] = false;
    deleted[NTHREAD] = false;
    proc_dummy(&processed[NTHREAD]);
    
    pthread_t th[NTHREAD];
    for (size_t i = 0; i < NTHREAD; ++i) {
        processed[i] = false;
        deleted[i] = false;
        ASSERT_EQ(0, pthread_create(&th[i], NULL, proc_dummy, processed + i));
    }
    for (size_t i = 0; i < NTHREAD; ++i) {
        ASSERT_EQ(0, pthread_join(th[i], NULL));
        ASSERT_TRUE(processed[i]);
        ASSERT_TRUE(deleted[i]);
    }
}

139 140 141 142 143 144 145
static std::ostringstream* oss = NULL;
inline std::ostringstream& get_oss() {
    if (oss == NULL) {
        oss = new std::ostringstream;
    }
    return *oss;
}
gejun's avatar
gejun committed
146 147

void fun1() {
148
    get_oss() << "fun1" << std::endl;
gejun's avatar
gejun committed
149 150 151
}

void fun2() {
152
    get_oss() << "fun2" << std::endl;
gejun's avatar
gejun committed
153 154 155
}

void fun3(void* arg) {
156
    get_oss() << "fun3(" << arg << ")" << std::endl;
gejun's avatar
gejun committed
157 158 159
}

void fun4(void* arg) {
160
    get_oss() << "fun4(" << arg << ")" << std::endl;
gejun's avatar
gejun committed
161 162
}

163 164
static void check_result() {
    ASSERT_EQ("fun4(0)\nfun3(0x2)\nfun2\n", get_oss().str());
gejun's avatar
gejun committed
165 166 167
}

TEST_F(BaiduThreadLocalTest, call_order_and_cancel) {
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
    butil::thread_atexit_cancel(NULL);
    butil::thread_atexit_cancel(NULL, NULL);

    ASSERT_EQ(0, butil::thread_atexit(check_result));

    ASSERT_EQ(0, butil::thread_atexit(fun1));
    ASSERT_EQ(0, butil::thread_atexit(fun1));
    ASSERT_EQ(0, butil::thread_atexit(fun2));
    ASSERT_EQ(0, butil::thread_atexit(fun3, (void*)1));
    ASSERT_EQ(0, butil::thread_atexit(fun3, (void*)1));
    ASSERT_EQ(0, butil::thread_atexit(fun3, (void*)2));
    ASSERT_EQ(0, butil::thread_atexit(fun4, NULL));

    butil::thread_atexit_cancel(NULL);
    butil::thread_atexit_cancel(NULL, NULL);
    butil::thread_atexit_cancel(fun1);
    butil::thread_atexit_cancel(fun3, NULL);
    butil::thread_atexit_cancel(fun3, (void*)1);
gejun's avatar
gejun committed
186 187
}

188
} // namespace