458 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			458 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /**
 | |
|  * Copyright (c) 2021 OceanBase
 | |
|  * OceanBase CE is licensed under Mulan PubL v2.
 | |
|  * You can use this software according to the terms and conditions of the Mulan PubL v2.
 | |
|  * You may obtain a copy of Mulan PubL v2 at:
 | |
|  *          http://license.coscl.org.cn/MulanPubL-2.0
 | |
|  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 | |
|  * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 | |
|  * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 | |
|  * See the Mulan PubL v2 for more details.
 | |
|  */
 | |
| 
 | |
| #include "easy_atomic.h"
 | |
| #include <pthread.h>
 | |
| #include <easy_test.h>
 | |
| 
 | |
| /**
 | |
|  * 测试 easy_atomic.h
 | |
|  */
 | |
| #define TEST_ATOMIC_ADD32(v1, init_value, add_value) \
 | |
|     {v1 = (init_value); \
 | |
|         easy_atomic32_add(&v1, (add_value)); \
 | |
|         EXPECT_EQ(v1, (int32_t)((int64_t)(init_value) + (add_value))); \
 | |
|         v1 = (init_value); \
 | |
|         typeof(v1) vv = easy_atomic32_add_return(&v1, add_value);\
 | |
|         EXPECT_EQ(vv, (int32_t)((int64_t)(init_value) + (add_value))); \
 | |
|         EXPECT_EQ(v1, (int32_t)((int64_t)(init_value) + (add_value)));}
 | |
| 
 | |
| #define TEST_ATOMIC_ADD(v2, init_value, add_value) \
 | |
|     {v2 = (easy_atomic_t)(init_value); \
 | |
|         int64_t                 cv = v2 + (easy_atomic_t)add_value; \
 | |
|         easy_atomic_add(&v2, (easy_atomic_t)(add_value)); \
 | |
|         EXPECT_EQ(v2, cv); \
 | |
|         v2 = (easy_atomic_t)(init_value); \
 | |
|         EXPECT_EQ(easy_atomic_add_return(&v2, (easy_atomic_t)add_value), cv); \
 | |
|         EXPECT_EQ(v2, cv);}
 | |
| 
 | |
| #define TEST_ATOMIC_CMP_SET(v3, init_value, set_value) \
 | |
|     v3 = (easy_atomic_t)(init_value); \
 | |
|     if (init_value != set_value) EXPECT_EQ(easy_atomic_cmp_set(&v3, set_value, init_value), 0); \
 | |
|     EXPECT_EQ(easy_atomic_cmp_set(&v3, init_value, set_value), 1); \
 | |
|     EXPECT_EQ(v3, (easy_atomic_t)(set_value));
 | |
| 
 | |
| #define TEST_ATOMIC_INC(i, v3, start, end) \
 | |
|     for (v3 = i = (start); i < (end);) { \
 | |
|         easy_atomic_inc(&v3); \
 | |
|         i ++; \
 | |
|         EXPECT_EQ(v3, i); }
 | |
| 
 | |
| #define TEST_ATOMIC_DEC(i, v3, start, end) \
 | |
|     for (v3 = i = (start); i > (end);) { \
 | |
|         easy_atomic_dec(&v3); \
 | |
|         i --; \
 | |
|         EXPECT_EQ(v3, i); }
 | |
| 
 | |
| #define TEST_ATOMIC_INC32(i, v4, start, end) \
 | |
|     for (v4 = i = (start); i < (end);) { \
 | |
|         easy_atomic32_inc(&v4); \
 | |
|         i ++; \
 | |
|         EXPECT_EQ(v4, i); }
 | |
| 
 | |
| #define TEST_ATOMIC_DEC32(i, v3, start, end) \
 | |
|     for (v3 = i = (start); i > (end);) { \
 | |
|         easy_atomic32_dec(&v3); \
 | |
|         i --; \
 | |
|         EXPECT_EQ(v3, i); }
 | |
| 
 | |
| #define INT32_MAX_VALUE 0x7fffffff
 | |
| #define INT32_MIN_VALUE 0x80000000
 | |
| #if __WORDSIZE == 64
 | |
| #define INT64_MAX_VALUE 0x7fffffffffffffff
 | |
| #define INT64_MIN_VALUE 0x8000000000000000
 | |
| #else
 | |
| #define INT64_MAX_VALUE INT32_MAX_VALUE
 | |
| #define INT64_MIN_VALUE INT32_MIN_VALUE
 | |
| #endif
 | |
| 
 | |
| TEST(easy_atomic, func)
 | |
| {
 | |
|     easy_atomic32_t         v1 = 0;
 | |
|     easy_atomic_t           v2 = 0;
 | |
| 
 | |
|     easy_atomic32_add(&v1, 1);
 | |
|     EXPECT_EQ(v1, 1);
 | |
|     easy_atomic32_add(&v1, 10);
 | |
|     EXPECT_EQ(v1, 11);
 | |
|     EXPECT_EQ(easy_atomic32_add_return(&v1, 100), 111);
 | |
|     EXPECT_EQ(v1, 111);
 | |
| 
 | |
|     TEST_ATOMIC_ADD32(v1, INT32_MAX_VALUE, INT32_MIN_VALUE); // 2147483647 + -2147483648 = -1
 | |
|     TEST_ATOMIC_ADD32(v1, INT32_MAX_VALUE, INT32_MAX_VALUE); // 2147483647 + 2147483647 = -2
 | |
|     TEST_ATOMIC_ADD32(v1, 0x0, INT32_MIN_VALUE);  // 0 + -2147483648 = -2147483648
 | |
|     TEST_ATOMIC_ADD32(v1, 0x0, INT32_MAX_VALUE);  // 0 + 2147483647 = 2147483647
 | |
|     TEST_ATOMIC_ADD32(v1, INT32_MIN_VALUE, INT32_MIN_VALUE);  // -2147483648 + -2147483648 = 0
 | |
|     TEST_ATOMIC_ADD32(v1, INT32_MIN_VALUE, INT32_MAX_VALUE);  // -2147483648 + 2147483647 = -1
 | |
| 
 | |
|     easy_atomic_add(&v2, 1);
 | |
|     EXPECT_EQ(v2, 1);
 | |
|     easy_atomic_add(&v2, 10);
 | |
|     EXPECT_EQ(v2, 11);
 | |
|     EXPECT_EQ(easy_atomic_add_return(&v2, 100), 111);
 | |
|     EXPECT_EQ(v2, 111);
 | |
| 
 | |
|     EXPECT_EQ(easy_atomic_cmp_set(&v2, 110, 1), 0);
 | |
|     EXPECT_EQ(v2, 111);
 | |
|     EXPECT_EQ(easy_atomic_cmp_set(&v2, 111, 1), 1);
 | |
|     EXPECT_EQ(v2, 1);
 | |
| 
 | |
|     TEST_ATOMIC_ADD(v2, INT64_MAX_VALUE, INT64_MIN_VALUE);
 | |
|     //TEST_ATOMIC_ADD(v2, INT64_MAX_VALUE, INT64_MAX_VALUE);
 | |
|     TEST_ATOMIC_ADD(v2, 0x0, INT64_MIN_VALUE);
 | |
|     TEST_ATOMIC_ADD(v2, 0x0, INT64_MAX_VALUE);
 | |
|     TEST_ATOMIC_ADD(v2, INT64_MIN_VALUE, INT64_MIN_VALUE);
 | |
|     TEST_ATOMIC_ADD(v2, INT64_MIN_VALUE, INT64_MAX_VALUE);
 | |
| 
 | |
|     TEST_ATOMIC_CMP_SET(v2, INT64_MAX_VALUE, INT64_MIN_VALUE);
 | |
|     TEST_ATOMIC_CMP_SET(v2, INT64_MAX_VALUE, INT64_MAX_VALUE);
 | |
|     TEST_ATOMIC_CMP_SET(v2, 0x0, INT64_MIN_VALUE);
 | |
|     TEST_ATOMIC_CMP_SET(v2, 0x0, INT64_MAX_VALUE);
 | |
|     TEST_ATOMIC_CMP_SET(v2, INT64_MIN_VALUE, INT64_MIN_VALUE);
 | |
|     TEST_ATOMIC_CMP_SET(v2, INT64_MIN_VALUE, INT64_MAX_VALUE);
 | |
| 
 | |
|     EXPECT_EQ(easy_trylock(&v2), 0);
 | |
|     easy_unlock(&v2);
 | |
|     EXPECT_EQ(v2, 0);
 | |
| 
 | |
|     EXPECT_EQ(easy_trylock(&v2), 1);
 | |
|     EXPECT_EQ(v2, 1);
 | |
|     easy_unlock(&v2);
 | |
|     EXPECT_EQ(v2, 0);
 | |
| 
 | |
|     easy_spin_lock(&v2);
 | |
|     EXPECT_EQ(v2, 1);
 | |
|     EXPECT_EQ(easy_trylock(&v2), 0);
 | |
|     easy_unlock(&v2);
 | |
|     EXPECT_EQ(v2, 0);
 | |
| 
 | |
|     // 64 bit, atomic inc and dec
 | |
|     int64_t                 i = 0;
 | |
|     easy_atomic_t           v3 = 0;
 | |
|     TEST_ATOMIC_INC(i, v3, -1000, 1000);
 | |
|     TEST_ATOMIC_DEC(i, v3, 1000, -1000);
 | |
|     easy_atomic_t           v3_t = INT64_MAX_VALUE;
 | |
|     easy_atomic_add_return(&v3_t, 1000);
 | |
|     TEST_ATOMIC_INC(i, v3, INT64_MAX_VALUE - 1000, v3_t);
 | |
|     TEST_ATOMIC_DEC(i, v3, v3_t, INT64_MAX_VALUE - 1000);
 | |
| 
 | |
|     // 32 bit, atomic inc and dec
 | |
|     easy_atomic32_t         v4 = 0;
 | |
|     TEST_ATOMIC_INC32(i, v4, -1000, 1000);
 | |
|     TEST_ATOMIC_DEC32(i, v4, 1000, -1000);
 | |
|     easy_atomic32_t         v4_t = INT32_MAX_VALUE;
 | |
|     easy_atomic32_add_return(&v4_t, 1000);
 | |
|     TEST_ATOMIC_INC32(i, v4, INT32_MAX_VALUE - 1000, v4_t);
 | |
|     TEST_ATOMIC_DEC32(i, v4, v4_t, INT32_MAX_VALUE - 1000);
 | |
| }
 | |
| 
 | |
| typedef struct easy_atomic_mt_args_t {
 | |
|     easy_atomic_t           lock;
 | |
|     int64_t                 loop_count;
 | |
|     easy_atomic_t           v1;
 | |
|     int64_t                 v2;
 | |
|     int64_t                 sleep_value;
 | |
| } easy_atomic_mt_args_t;
 | |
| 
 | |
| void *easy_atomic_mthread_start(void *args)
 | |
| {
 | |
|     int                     i;
 | |
|     easy_atomic_mt_args_t   *p = (easy_atomic_mt_args_t *)args;
 | |
| 
 | |
|     for(i = 0; i < p->loop_count; i++) {
 | |
|         easy_atomic_add(&p->v1, 1);
 | |
|     }
 | |
| 
 | |
|     volatile int            t;
 | |
| 
 | |
|     for(i = 0; i < p->loop_count; i++) {
 | |
|         easy_spin_lock(&p->lock);
 | |
|         t = p->v2;
 | |
|         p->v2 = t + 1;
 | |
|         easy_spin_unlock(&p->lock);
 | |
|     }
 | |
| 
 | |
|     struct timespec         req;
 | |
| 
 | |
|     struct timespec         rem;
 | |
| 
 | |
|     req.tv_sec = 0;
 | |
| 
 | |
|     req.tv_nsec = 1000000;
 | |
| 
 | |
|     {
 | |
|         easy_spin_lock(&p->lock);
 | |
|         t = p->sleep_value;
 | |
|         EXPECT_EQ(nanosleep(&req, &rem), 0);
 | |
|         p->sleep_value = t + 1;
 | |
|         easy_spin_unlock(&p->lock);
 | |
|     }
 | |
| 
 | |
|     return (void *)NULL;
 | |
| }
 | |
| 
 | |
| TEST(easy_atomic, mthread)
 | |
| {
 | |
|     const int               thread_count = 10;
 | |
|     pthread_t               tids[thread_count];
 | |
|     int                     i;
 | |
|     easy_atomic_mt_args_t   param;
 | |
|     param.lock = 0;
 | |
|     param.loop_count = 50000;
 | |
|     param.v1 = 0;
 | |
|     param.v2 = 0;
 | |
|     param.sleep_value = 0;
 | |
| 
 | |
|     for(i = 0; i < thread_count; i++) {
 | |
|         pthread_create(&tids[i], NULL, easy_atomic_mthread_start, ¶m);
 | |
|     }
 | |
| 
 | |
|     for(i = 0; i < thread_count; i++) {
 | |
|         pthread_join(tids[i], NULL);
 | |
|     }
 | |
| 
 | |
|     EXPECT_EQ(param.v1, param.loop_count * thread_count);
 | |
|     EXPECT_EQ(param.v2, param.loop_count * thread_count);
 | |
|     EXPECT_EQ(param.sleep_value, thread_count);
 | |
| }
 | |
| 
 | |
| void *easy_atomic_mthread_trylock_start(void *args)
 | |
| {
 | |
|     int                     i;
 | |
|     easy_atomic_mt_args_t   *p = (easy_atomic_mt_args_t *)args;
 | |
| 
 | |
|     volatile int            t;
 | |
| 
 | |
|     for(i = 0; i < p->loop_count;) {
 | |
|         if (easy_trylock(&p->lock)) {
 | |
|             t = p->v2;
 | |
|             p->v2 = t + 1;
 | |
|             easy_unlock(&p->lock);
 | |
|             i ++;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|     struct timespec         req;
 | |
| 
 | |
|     struct timespec         rem;
 | |
| 
 | |
|     req.tv_sec = 0;
 | |
| 
 | |
|     req.tv_nsec = 1000000;
 | |
|     */
 | |
| 
 | |
|     while(1) {
 | |
|         if (easy_trylock(&p->lock) == 0)
 | |
|             continue;
 | |
| 
 | |
|         int                     t = p->v1;
 | |
|         //EXPECT_EQ(nanosleep(&req, &rem), 0);
 | |
|         p->v1 = t + 1;
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     easy_unlock(&p->lock);
 | |
| 
 | |
|     return (void *)NULL;
 | |
| }
 | |
| 
 | |
| TEST(easy_atomic, mthread_trylock)
 | |
| {
 | |
|     const int               thread_count = 10;
 | |
|     pthread_t               tids[thread_count];
 | |
|     int                     i;
 | |
|     easy_atomic_mt_args_t   param;
 | |
|     param.lock = 0;
 | |
|     param.loop_count = 10000;
 | |
|     param.v1 = 0;
 | |
|     param.v2 = 0;
 | |
| 
 | |
|     for(i = 0; i < thread_count; i++) {
 | |
|         pthread_create(&tids[i], NULL, easy_atomic_mthread_trylock_start, ¶m);
 | |
|     }
 | |
| 
 | |
|     for(i = 0; i < thread_count; i++) {
 | |
|         pthread_join(tids[i], NULL);
 | |
|     }
 | |
| 
 | |
|     EXPECT_EQ(param.v1, thread_count);
 | |
|     EXPECT_EQ(param.v2, param.loop_count * thread_count);
 | |
| }
 | |
| 
 | |
| typedef struct easy_atomic_mt_op_args_t {
 | |
|     easy_atomic_t           int64_add;
 | |
|     easy_atomic_t           int64_add_return;
 | |
|     easy_atomic_t           int64_inc;
 | |
|     easy_atomic_t           int64_dec;
 | |
|     easy_atomic_t           int64_set;
 | |
|     easy_atomic_t           int64_set_sum;
 | |
|     easy_atomic_t           int64_sum;
 | |
|     easy_atomic32_t         int32_add;
 | |
|     easy_atomic32_t         int32_add_return;
 | |
|     easy_atomic32_t         int32_inc;
 | |
|     easy_atomic32_t         int32_dec;
 | |
|     easy_atomic32_t         int32_sum;
 | |
|     int64_t                 loop_count;
 | |
| } easy_atomic_mt_op_args_t;
 | |
| 
 | |
| void *easy_atomic_mthread_op_start(void *args)
 | |
| {
 | |
|     easy_atomic_mt_op_args_t *p = (easy_atomic_mt_op_args_t *)args;
 | |
|     int                     i;
 | |
| 
 | |
|     for(i = 0; i < p->loop_count; i++) {
 | |
|         easy_atomic_add(&p->int64_add, 1);
 | |
|         easy_atomic_add(&p->int64_sum, 1);
 | |
|     }
 | |
| 
 | |
|     for(i = 0; i < p->loop_count; i++) {
 | |
|         easy_atomic_add_return(&p->int64_add_return, 1);
 | |
|         easy_atomic_add_return(&p->int64_sum, 1);
 | |
|     }
 | |
| 
 | |
|     for(i = 0; i < p->loop_count; i++) {
 | |
|         easy_atomic_inc(&p->int64_inc);
 | |
|         easy_atomic_inc(&p->int64_sum);
 | |
|     }
 | |
| 
 | |
|     for(i = 0; i < p->loop_count; i++) {
 | |
|         easy_atomic_dec(&p->int64_dec);
 | |
|         easy_atomic_dec(&p->int64_sum);
 | |
|     }
 | |
| 
 | |
|     for(i = 0; i < p->loop_count; i++) {
 | |
|         easy_atomic32_add(&p->int32_add, 1);
 | |
|         easy_atomic32_add(&p->int32_sum, 1);
 | |
|     }
 | |
| 
 | |
|     for(i = 0; i < p->loop_count; i++) {
 | |
|         easy_atomic32_add_return(&p->int32_add_return, 1);
 | |
|         easy_atomic32_add_return(&p->int32_sum, 1);
 | |
|     }
 | |
| 
 | |
|     for(i = 0; i < p->loop_count; i++) {
 | |
|         easy_atomic32_inc(&p->int32_inc);
 | |
|         easy_atomic32_inc(&p->int32_sum);
 | |
|     }
 | |
| 
 | |
|     for(i = 0; i < p->loop_count; i++) {
 | |
|         easy_atomic32_dec(&p->int32_dec);
 | |
|         easy_atomic32_dec(&p->int32_sum);
 | |
|     }
 | |
| 
 | |
|     int                     j = 0;
 | |
|     int                     t = 0;
 | |
|     easy_atomic_t           v;
 | |
| 
 | |
|     for(j = 0; j < p->loop_count; j++) {
 | |
|         do {
 | |
|             v = p->int64_set;
 | |
|             t = easy_atomic_cmp_set(&p->int64_set, v, v + 1);
 | |
|         } while(t == 0);
 | |
| 
 | |
|         easy_atomic_add(&p->int64_set_sum, t);
 | |
|     }
 | |
| 
 | |
|     return (void *)NULL;
 | |
| }
 | |
| 
 | |
| TEST(easy_atomic, mthread_atomic_op)
 | |
| {
 | |
|     const int               thread_count = 10;
 | |
|     pthread_t               tids[thread_count];
 | |
|     int                     i;
 | |
|     easy_atomic_mt_op_args_t param;
 | |
|     memset(¶m, 0, sizeof(param));
 | |
|     param.loop_count = 10000;
 | |
| 
 | |
|     for(i = 0; i < thread_count; i++) {
 | |
|         pthread_create(&tids[i], NULL, easy_atomic_mthread_op_start, ¶m);
 | |
|     }
 | |
| 
 | |
|     for(i = 0; i < thread_count; i++) {
 | |
|         pthread_join(tids[i], NULL);
 | |
|     }
 | |
| 
 | |
|     EXPECT_EQ(param.int64_add, param.loop_count * thread_count);
 | |
|     EXPECT_EQ(param.int64_add_return, param.loop_count * thread_count);
 | |
|     EXPECT_EQ(param.int64_inc, param.loop_count * thread_count);
 | |
|     EXPECT_EQ(param.int64_dec,  - param.loop_count * thread_count);
 | |
|     EXPECT_EQ(param.int64_sum, param.loop_count * 2 * thread_count);
 | |
| 
 | |
|     EXPECT_EQ(param.int32_add, param.loop_count * thread_count);
 | |
|     EXPECT_EQ(param.int32_add_return, param.loop_count * thread_count);
 | |
|     EXPECT_EQ(param.int32_inc, param.loop_count * thread_count);
 | |
|     EXPECT_EQ(param.int32_dec, - param.loop_count * thread_count);
 | |
|     EXPECT_EQ(param.int32_sum, param.loop_count * 2 * thread_count);
 | |
| 
 | |
|     EXPECT_EQ(param.int64_set_sum, param.loop_count * thread_count);
 | |
| }
 | |
| 
 | |
| TEST(easy_atomic, easy_bit)
 | |
| {
 | |
|     uint64_t                x = 0;
 | |
|     int                     i;
 | |
| 
 | |
|     for(i = 0; i < 8; i++) easy_set_bit(i * 8, &x);
 | |
| 
 | |
|     EXPECT_EQ(x, __INT64_C(0x0101010101010101));
 | |
| 
 | |
|     for(i = 0; i < 8; i++) easy_set_bit(i * 8 + 7, &x);
 | |
| 
 | |
|     EXPECT_EQ(x, __INT64_C(0x8181818181818181));
 | |
| 
 | |
|     for(i = 0; i < 8; i++) easy_clear_bit(i * 8 + 7, &x);
 | |
| 
 | |
|     EXPECT_EQ(x, __INT64_C(0x0101010101010101));
 | |
| 
 | |
|     for(i = 0; i < 8; i++) easy_clear_bit(i * 8, &x);
 | |
| 
 | |
|     EXPECT_EQ(x, 0);
 | |
| }
 | |
| 
 | |
| TEST(easy_atomic, easy_spinrwlock)
 | |
| {
 | |
|     easy_spinrwlock_t       lock = EASY_SPINRWLOCK_INITIALIZER;
 | |
| 
 | |
|     int                     ret = easy_spinrwlock_rdlock(&lock);
 | |
|     EXPECT_EQ(EASY_OK, ret);
 | |
|     ret = easy_spinrwlock_unlock(&lock);
 | |
|     EXPECT_EQ(EASY_OK, ret);
 | |
| 
 | |
|     ret = easy_spinrwlock_wrlock(&lock);
 | |
|     EXPECT_EQ(EASY_OK, ret);
 | |
|     ret = easy_spinrwlock_unlock(&lock);
 | |
|     EXPECT_EQ(EASY_OK, ret);
 | |
| 
 | |
|     ret = easy_spinrwlock_rdlock(&lock);
 | |
|     EXPECT_EQ(EASY_OK, ret);
 | |
|     ret = easy_spinrwlock_try_rdlock(&lock);
 | |
|     EXPECT_EQ(EASY_OK, ret);
 | |
|     ret = easy_spinrwlock_unlock(&lock);
 | |
|     EXPECT_EQ(EASY_OK, ret);
 | |
|     ret = easy_spinrwlock_try_wrlock(&lock);
 | |
|     EXPECT_EQ(EASY_AGAIN, ret);
 | |
|     ret = easy_spinrwlock_unlock(&lock);
 | |
|     EXPECT_EQ(EASY_OK, ret);
 | |
| 
 | |
|     ret = easy_spinrwlock_wrlock(&lock);
 | |
|     EXPECT_EQ(EASY_OK, ret);
 | |
|     ret = easy_spinrwlock_try_rdlock(&lock);
 | |
|     EXPECT_EQ(EASY_AGAIN, ret);
 | |
|     ret = easy_spinrwlock_try_wrlock(&lock);
 | |
|     EXPECT_EQ(EASY_AGAIN, ret);
 | |
|     ret = easy_spinrwlock_unlock(&lock);
 | |
|     EXPECT_EQ(EASY_OK, ret);
 | |
| }
 | |
| 
 | 
