From aba6a6833192a37777f7c75e06bca7145fe6cef3 Mon Sep 17 00:00:00 2001 From: lixianjing Date: Sun, 17 Nov 2024 17:30:19 +0800 Subject: [PATCH] add atomic.h --- docs/changes.md | 1 + src/tkc/atomic.h | 870 ++++++++++++++++++++++++++++++++++++++++++ tests/SConscript | 8 +- tests/atomic_test.cpp | 109 ++++++ 4 files changed, 987 insertions(+), 1 deletion(-) create mode 100644 src/tkc/atomic.h create mode 100644 tests/atomic_test.cpp diff --git a/docs/changes.md b/docs/changes.md index 48cb7a7f6..3b87ae88c 100644 --- a/docs/changes.md +++ b/docs/changes.md @@ -4,6 +4,7 @@ * 完善 TK_STRINGIZE(感谢兆坤提供补丁) * value完善调试log(感谢兆坤提供补丁) * 修复拼写错误(感谢兆坤提供补丁) + * 增加原子操作(感谢兆坤提供补丁) 2024/11/15 * 修复拼写错误(感谢兆坤提供补丁) diff --git a/src/tkc/atomic.h b/src/tkc/atomic.h new file mode 100644 index 000000000..0f3f03cb9 --- /dev/null +++ b/src/tkc/atomic.h @@ -0,0 +1,870 @@ +/** + * File: atomic.h + * Author: AWTK Develop Team + * Brief: 原子操作 + * + * Copyright (c) 2024 - 2024 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * License file for more details. + * + */ + +/** + * History: + * ================================================================ + * 2024-11-17 Shen ZhaoKun created + * + */ + +#ifndef TK_ATOMIC_H +#define TK_ATOMIC_H + +#include "tkc/types_def.h" +#include "tkc/value.h" +#include "tkc/mem.h" + +/** + * @class tk_atomic_t + * @export none + * 原子操作类。 + */ +typedef struct _tk_atomic_t tk_atomic_t; + +/** + * @method tk_atomic_support_value_type + * @annotation ["static"] + * @export none + * 判断原子操作是否支持该类型。 + * + * @param {value_type_t} type 类型。 + * + * @return {bool_t} 返回 TRUE 表示支持,FALSE 表示不支持。 + */ +static inline bool_t tk_atomic_support_value_type(value_type_t type) { + switch (type) { + case VALUE_TYPE_BOOL: + case VALUE_TYPE_INT8: + case VALUE_TYPE_UINT8: + case VALUE_TYPE_INT16: + case VALUE_TYPE_UINT16: + case VALUE_TYPE_INT32: + case VALUE_TYPE_UINT32: + case VALUE_TYPE_INT64: + case VALUE_TYPE_UINT64: + return TRUE; + default: + return FALSE; + } +} + +#if defined(WIN32) && !defined(MINGW) +#include + +struct _tk_atomic_t { + volatile value_t value; +}; + +static inline ret_t tk_atomic_deinit(tk_atomic_t* atomic) { + return_value_if_fail(atomic != NULL, RET_BAD_PARAMS); + memset(atomic, 0, sizeof(tk_atomic_t)); + return RET_OK; +} + +static inline ret_t tk_atomic_init(tk_atomic_t* atomic, const value_t* v) { + ret_t ret = RET_OK; + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + return_value_if_fail(tk_atomic_support_value_type(v->type), RET_BAD_PARAMS); + + ret = tk_atomic_deinit(atomic); + return_value_if_fail(RET_OK == ret, ret); + + ret = value_copy((value_t*)(&atomic->value), v); + return_value_if_fail(RET_OK == ret, ret); + + return ret; +} + +static ret_t tk_atomic_exchange(tk_atomic_t* atomic, value_t* v) { + value_t tmp; + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + + switch (atomic->value.type) { + case VALUE_TYPE_BOOL: { + value_set_bool(&tmp, + InterlockedExchange8((CHAR*)(&atomic->value.value.b), (CHAR)value_bool(v))); + } break; + case VALUE_TYPE_INT8: { + value_set_int8(&tmp, + InterlockedExchange8((CHAR*)(&atomic->value.value.i8), (CHAR)value_int8(v))); + } break; + case VALUE_TYPE_UINT8: { + value_set_uint8(&tmp, + InterlockedExchange8((CHAR*)(&atomic->value.value.u8), (CHAR)value_uint8(v))); + } break; + case VALUE_TYPE_INT16: { + value_set_int16( + &tmp, InterlockedExchange16((SHORT*)(&atomic->value.value.i16), (SHORT)value_int16(v))); + } break; + case VALUE_TYPE_UINT16: { + value_set_uint16( + &tmp, InterlockedExchange16((SHORT*)(&atomic->value.value.u16), (SHORT)value_uint16(v))); + } break; + case VALUE_TYPE_INT32: { + value_set_int32(&tmp, + InterlockedExchange((LONG*)(&atomic->value.value.i32), (LONG)value_int32(v))); + } break; + case VALUE_TYPE_UINT32: { + value_set_uint32( + &tmp, InterlockedExchange((LONG*)(&atomic->value.value.u32), (LONG)value_uint32(v))); + } break; + case VALUE_TYPE_INT64: { + value_set_int64( + &tmp, InterlockedExchange64((LONG64*)(&atomic->value.value.i64), (LONG64)value_int64(v))); + } break; + case VALUE_TYPE_UINT64: { + value_set_uint64(&tmp, InterlockedExchange64((LONG64*)(&atomic->value.value.u64), + (LONG64)value_uint64(v))); + } break; + default: { + /* tk_atomic_support_value_type() 已经判断过了,正常不可能会跑到这里 */ + assert(!"Not support type!"); + return RET_BAD_PARAMS; + } break; + } + + return value_copy(v, &tmp); +} + +static inline ret_t tk_atomic_store(tk_atomic_t* atomic, const value_t* v) { + value_t tmp; + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + + value_copy(&tmp, v); + return tk_atomic_exchange(atomic, &tmp); +} + +static ret_t tk_atomic_load(const tk_atomic_t* atomic, value_t* v) { + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + + value_set_int(v, 0); + + switch (atomic->value.type) { + case VALUE_TYPE_BOOL: { + v->value.b = atomic->value.value.b; + } break; + case VALUE_TYPE_INT8: { + v->value.i8 = atomic->value.value.i8; + } break; + case VALUE_TYPE_UINT8: { + v->value.u8 = atomic->value.value.u8; + } break; + case VALUE_TYPE_INT16: { + v->value.i16 = atomic->value.value.i16; + } break; + case VALUE_TYPE_UINT16: { + v->value.u16 = atomic->value.value.u16; + } break; + case VALUE_TYPE_INT32: { + v->value.i32 = atomic->value.value.i32; + } break; + case VALUE_TYPE_UINT32: { + v->value.u32 = atomic->value.value.u32; + } break; + case VALUE_TYPE_INT64: { + v->value.i64 = atomic->value.value.i64; + } break; + case VALUE_TYPE_UINT64: { + v->value.u64 = atomic->value.value.u64; + } break; + default: { + /* tk_atomic_support_value_type() 已经判断过了,正常不可能会跑到这里 */ + assert(!"Not support type!"); + return RET_BAD_PARAMS; + } break; + } + + v->type = atomic->value.type; + + return RET_OK; +} + +static ret_t tk_atomic_fetch_add(tk_atomic_t* atomic, value_t* v) { + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + + switch (atomic->value.type) { + case VALUE_TYPE_BOOL: { + if (value_bool(v)) { + tk_atomic_store(atomic, v); + } + } break; + case VALUE_TYPE_INT8: { + if (1 == value_int(v)) { + InterlockedIncrement16((SHORT*)(&atomic->value.value.i8)); + } else { + InterlockedAdd((LONG*)(&atomic->value.value.i8), (LONG)value_int8(v)); + } + } break; + case VALUE_TYPE_UINT8: { + if (1 == value_int(v)) { + InterlockedIncrement16((SHORT*)(&atomic->value.value.u8)); + } else { + InterlockedAdd((LONG*)(&atomic->value.value.u8), (LONG)value_uint8(v)); + } + } break; + case VALUE_TYPE_INT16: { + if (1 == value_int(v)) { + InterlockedIncrement16((SHORT*)(&atomic->value.value.i16)); + } else { + InterlockedAdd((LONG*)(&atomic->value.value.i16), (LONG)value_int16(v)); + } + } break; + case VALUE_TYPE_UINT16: { + if (1 == value_int(v)) { + InterlockedIncrement16((SHORT*)(&atomic->value.value.u16)); + } else { + InterlockedAdd((LONG*)(&atomic->value.value.u16), (LONG)value_uint16(v)); + } + } break; + case VALUE_TYPE_INT32: { + if (1 == value_int(v)) { + InterlockedIncrement((LONG*)(&atomic->value.value.i32)); + } else { + InterlockedAdd((LONG*)(&atomic->value.value.i32), (LONG)value_int32(v)); + } + } break; + case VALUE_TYPE_UINT32: { + if (1 == value_int(v)) { + InterlockedIncrement((LONG*)(&atomic->value.value.u32)); + } else { + InterlockedAdd((LONG*)(&atomic->value.value.u32), (LONG)value_uint32(v)); + } + } break; + case VALUE_TYPE_INT64: { + if (1 == value_int(v)) { + InterlockedIncrement64((LONG64*)(&atomic->value.value.i64)); + } else { + InterlockedAdd64((LONG64*)(&atomic->value.value.i64), (LONG64)value_int64(v)); + } + } break; + case VALUE_TYPE_UINT64: { + if (1 == value_int(v)) { + InterlockedIncrement64((LONG64*)(&atomic->value.value.u64)); + } else { + InterlockedAdd64((LONG64*)(&atomic->value.value.u64), (LONG64)value_uint64(v)); + } + } break; + default: { + /* tk_atomic_support_value_type() 已经判断过了,正常不可能会跑到这里 */ + assert(!"Not support type!"); + return RET_BAD_PARAMS; + } break; + } + + return RET_OK; +} + +static ret_t tk_atomic_fetch_sub(tk_atomic_t* atomic, value_t* v) { + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + + switch (atomic->value.type) { + case VALUE_TYPE_BOOL: { + if (value_bool(v)) { + value_t tmp; + tk_atomic_store(atomic, value_set_bool(&tmp, FALSE)); + } + } break; + case VALUE_TYPE_INT8: { + if (1 == value_int(v)) { + InterlockedDecrement16((SHORT*)(&atomic->value.value.i8)); + } else { + InterlockedAdd((LONG*)(&atomic->value.value.i8), -(LONG)value_int8(v)); + } + } break; + case VALUE_TYPE_UINT8: { + if (1 == value_int(v)) { + InterlockedDecrement16((SHORT*)(&atomic->value.value.u8)); + } else { + InterlockedAdd((LONG*)(&atomic->value.value.u8), -(LONG)value_uint8(v)); + } + } break; + case VALUE_TYPE_INT16: { + if (1 == value_int(v)) { + InterlockedDecrement16((SHORT*)(&atomic->value.value.i16)); + } else { + InterlockedAdd((LONG*)(&atomic->value.value.i16), -(LONG)value_int16(v)); + } + } break; + case VALUE_TYPE_UINT16: { + if (1 == value_int(v)) { + InterlockedDecrement16((SHORT*)(&atomic->value.value.u16)); + } else { + InterlockedAdd((LONG*)(&atomic->value.value.u16), -(LONG)value_uint16(v)); + } + } break; + case VALUE_TYPE_INT32: { + if (1 == value_int(v)) { + InterlockedDecrement((LONG*)(&atomic->value.value.i32)); + } else { + InterlockedAdd((LONG*)(&atomic->value.value.i32), -(LONG)value_int32(v)); + } + } break; + case VALUE_TYPE_UINT32: { + if (1 == value_int(v)) { + InterlockedDecrement((LONG*)(&atomic->value.value.u32)); + } else { + InterlockedAdd((LONG*)(&atomic->value.value.u32), -(LONG)value_uint32(v)); + } + } break; + case VALUE_TYPE_INT64: { + if (1 == value_int(v)) { + InterlockedDecrement64((LONG64*)(&atomic->value.value.i64)); + } else { + InterlockedAdd64((LONG64*)(&atomic->value.value.i64), -(LONG64)value_int64(v)); + } + } break; + case VALUE_TYPE_UINT64: { + if (1 == value_int(v)) { + InterlockedDecrement64((LONG64*)(&atomic->value.value.u64)); + } else { + InterlockedAdd64((LONG64*)(&atomic->value.value.u64), -(LONG64)value_uint64(v)); + } + } break; + default: { + /* tk_atomic_support_value_type() 已经判断过了,正常不可能会跑到这里 */ + assert(!"Not support type!"); + return RET_BAD_PARAMS; + } break; + } + + return RET_OK; +} +#elif ((__cplusplus >= 201103L) || (__STDC_VERSION__ >= 201112L)) && !defined(__STDC_NO_ATOMICS__) +#ifdef __cplusplus +#include +#define _Atomic(X) std::atomic +#else +#include +#endif /* __cplusplus */ + +struct _tk_atomic_t { + value_type_t type : 8; + union _value { + _Atomic(bool_t) b; + _Atomic(int8_t) i8; + _Atomic(uint8_t) u8; + _Atomic(int16_t) i16; + _Atomic(uint16_t) u16; + _Atomic(int32_t) i32; + _Atomic(uint32_t) u32; + _Atomic(int64_t) i64; + _Atomic(uint64_t) u64; + } value; +}; + +static inline ret_t tk_atomic_deinit(tk_atomic_t* atomic) { + return_value_if_fail(atomic != NULL, RET_BAD_PARAMS); + memset(atomic, 0, sizeof(tk_atomic_t)); + return RET_OK; +} + +static ret_t tk_atomic_init(tk_atomic_t* atomic, const value_t* v) { + ret_t ret = RET_OK; + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + return_value_if_fail(tk_atomic_support_value_type(v->type), RET_BAD_PARAMS); + + ret = tk_atomic_deinit(atomic); + return_value_if_fail(RET_OK == ret, ret); + + atomic->type = v->type; + + switch (atomic->type) { + case VALUE_TYPE_BOOL: { + atomic_init(&atomic->value.b, value_bool(v)); + } break; + case VALUE_TYPE_INT8: { + atomic_init(&atomic->value.i8, value_int8(v)); + } break; + case VALUE_TYPE_UINT8: { + atomic_init(&atomic->value.u8, value_uint8(v)); + } break; + case VALUE_TYPE_INT16: { + atomic_init(&atomic->value.i16, value_int16(v)); + } break; + case VALUE_TYPE_UINT16: { + atomic_init(&atomic->value.u16, value_uint16(v)); + } break; + case VALUE_TYPE_INT32: { + atomic_init(&atomic->value.i32, value_int32(v)); + } break; + case VALUE_TYPE_UINT32: { + atomic_init(&atomic->value.u32, value_uint32(v)); + } break; + case VALUE_TYPE_INT64: { + atomic_init(&atomic->value.i64, value_int64(v)); + } break; + case VALUE_TYPE_UINT64: { + atomic_init(&atomic->value.u64, value_uint64(v)); + } break; + default: { + /* tk_atomic_support_value_type() 已经判断过了,正常不可能会跑到这里 */ + assert(!"Not support type!"); + return RET_BAD_PARAMS; + } break; + } + + return ret; +} + +static ret_t tk_atomic_exchange(tk_atomic_t* atomic, value_t* v) { + value_t tmp; + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + + switch (atomic->type) { + case VALUE_TYPE_BOOL: { + value_set_bool(&tmp, atomic_exchange(&atomic->value.b, value_bool(v))); + } break; + case VALUE_TYPE_INT8: { + value_set_int8(&tmp, atomic_exchange(&atomic->value.i8, value_int8(v))); + } break; + case VALUE_TYPE_UINT8: { + value_set_uint8(&tmp, atomic_exchange(&atomic->value.u8, value_uint8(v))); + } break; + case VALUE_TYPE_INT16: { + value_set_int16(&tmp, atomic_exchange(&atomic->value.i16, value_int16(v))); + } break; + case VALUE_TYPE_UINT16: { + value_set_uint16(&tmp, atomic_exchange(&atomic->value.u16, value_uint16(v))); + } break; + case VALUE_TYPE_INT32: { + value_set_int32(&tmp, atomic_exchange(&atomic->value.i32, value_int32(v))); + } break; + case VALUE_TYPE_UINT32: { + value_set_uint32(&tmp, atomic_exchange(&atomic->value.u32, value_uint32(v))); + } break; + case VALUE_TYPE_INT64: { + value_set_int64(&tmp, atomic_exchange(&atomic->value.i64, value_int64(v))); + } break; + case VALUE_TYPE_UINT64: { + value_set_uint64(&tmp, atomic_exchange(&atomic->value.u64, value_uint64(v))); + } break; + default: { + /* tk_atomic_support_value_type() 已经判断过了,正常不可能会跑到这里 */ + assert(!"Not support type!"); + return RET_BAD_PARAMS; + } break; + } + + return value_copy(v, &tmp); +} + +static ret_t tk_atomic_store(tk_atomic_t* atomic, const value_t* v) { + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + + switch (atomic->type) { + case VALUE_TYPE_BOOL: { + atomic_store(&atomic->value.b, value_bool(v)); + } break; + case VALUE_TYPE_INT8: { + atomic_store(&atomic->value.i8, value_int8(v)); + } break; + case VALUE_TYPE_UINT8: { + atomic_store(&atomic->value.u8, value_uint8(v)); + } break; + case VALUE_TYPE_INT16: { + atomic_store(&atomic->value.i16, value_int16(v)); + } break; + case VALUE_TYPE_UINT16: { + atomic_store(&atomic->value.u16, value_uint16(v)); + } break; + case VALUE_TYPE_INT32: { + atomic_store(&atomic->value.i32, value_int32(v)); + } break; + case VALUE_TYPE_UINT32: { + atomic_store(&atomic->value.u32, value_uint32(v)); + } break; + case VALUE_TYPE_INT64: { + atomic_store(&atomic->value.i64, value_int64(v)); + } break; + case VALUE_TYPE_UINT64: { + atomic_store(&atomic->value.u64, value_uint64(v)); + } break; + default: { + /* tk_atomic_support_value_type() 已经判断过了,正常不可能会跑到这里 */ + assert(!"Not support type!"); + return RET_BAD_PARAMS; + } break; + } + + return RET_OK; +} + +static ret_t tk_atomic_load(const tk_atomic_t* atomic, value_t* v) { + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + + switch (atomic->type) { + case VALUE_TYPE_BOOL: { + value_set_bool(v, atomic_load(&atomic->value.b)); + } break; + case VALUE_TYPE_INT8: { + value_set_int8(v, atomic_load(&atomic->value.i8)); + } break; + case VALUE_TYPE_UINT8: { + value_set_uint8(v, atomic_load(&atomic->value.u8)); + } break; + case VALUE_TYPE_INT16: { + value_set_int16(v, atomic_load(&atomic->value.i16)); + } break; + case VALUE_TYPE_UINT16: { + value_set_uint16(v, atomic_load(&atomic->value.u16)); + } break; + case VALUE_TYPE_INT32: { + value_set_int32(v, atomic_load(&atomic->value.i32)); + } break; + case VALUE_TYPE_UINT32: { + value_set_uint32(v, atomic_load(&atomic->value.u32)); + } break; + case VALUE_TYPE_INT64: { + value_set_int64(v, atomic_load(&atomic->value.i64)); + } break; + case VALUE_TYPE_UINT64: { + value_set_uint64(v, atomic_load(&atomic->value.u64)); + } break; + default: { + /* tk_atomic_support_value_type() 已经判断过了,正常不可能会跑到这里 */ + assert(!"Not support type!"); + return RET_BAD_PARAMS; + } break; + } + + return RET_OK; +} + +static ret_t tk_atomic_fetch_add(tk_atomic_t* atomic, value_t* v) { + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + + switch (atomic->type) { + case VALUE_TYPE_BOOL: { + if (value_bool(v)) { + tk_atomic_store(atomic, v); + } + } break; + case VALUE_TYPE_INT8: { + atomic_fetch_add(&atomic->value.i8, value_int8(v)); + } break; + case VALUE_TYPE_UINT8: { + atomic_fetch_add(&atomic->value.u8, value_uint8(v)); + } break; + case VALUE_TYPE_INT16: { + atomic_fetch_add(&atomic->value.i16, value_int16(v)); + } break; + case VALUE_TYPE_UINT16: { + atomic_fetch_add(&atomic->value.u16, value_uint16(v)); + } break; + case VALUE_TYPE_INT32: { + atomic_fetch_add(&atomic->value.i32, value_int32(v)); + } break; + case VALUE_TYPE_UINT32: { + atomic_fetch_add(&atomic->value.u32, value_uint32(v)); + } break; + case VALUE_TYPE_INT64: { + atomic_fetch_add(&atomic->value.i64, value_int64(v)); + } break; + case VALUE_TYPE_UINT64: { + atomic_fetch_add(&atomic->value.u64, value_uint64(v)); + } break; + default: { + /* tk_atomic_support_value_type() 已经判断过了,正常不可能会跑到这里 */ + assert(!"Not support type!"); + return RET_BAD_PARAMS; + } break; + } + + return RET_OK; +} + +static ret_t tk_atomic_fetch_sub(tk_atomic_t* atomic, value_t* v) { + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + + switch (atomic->type) { + case VALUE_TYPE_BOOL: { + if (value_bool(v)) { + value_t tmp; + tk_atomic_store(atomic, value_set_bool(&tmp, FALSE)); + } + } break; + case VALUE_TYPE_INT8: { + atomic_fetch_sub(&atomic->value.i8, value_int8(v)); + } break; + case VALUE_TYPE_UINT8: { + atomic_fetch_sub(&atomic->value.u8, value_uint8(v)); + } break; + case VALUE_TYPE_INT16: { + atomic_fetch_sub(&atomic->value.i16, value_int16(v)); + } break; + case VALUE_TYPE_UINT16: { + atomic_fetch_sub(&atomic->value.u16, value_uint16(v)); + } break; + case VALUE_TYPE_INT32: { + atomic_fetch_sub(&atomic->value.i32, value_int32(v)); + } break; + case VALUE_TYPE_UINT32: { + atomic_fetch_sub(&atomic->value.u32, value_uint32(v)); + } break; + case VALUE_TYPE_INT64: { + atomic_fetch_sub(&atomic->value.i64, value_int64(v)); + } break; + case VALUE_TYPE_UINT64: { + atomic_fetch_sub(&atomic->value.u64, value_uint64(v)); + } break; + default: { + /* tk_atomic_support_value_type() 已经判断过了,正常不可能会跑到这里 */ + assert(!"Not support type!"); + return RET_BAD_PARAMS; + } break; + } + + return RET_OK; +} +#else +/* 不支持原子操作,已退化为互斥锁,编译时请指定C11以上的C语言标准,以确保支持原子操作! */ +#pragma message(__FILE__ "(" TK_SRTINGIZE( \ + __LINE__) "): " \ + "Warning : Atomic operation is not supported and have degenerated into mutex, please specify the C language standard above C11 when compiling to ensure that atomic operation is supported!") +#ifdef __cplusplus +#pragma message("__cplusplus = " TK_SRTINGIZE(__cplusplus)) +#else +#pragma message("__STDC_VERSION__ = " TK_SRTINGIZE(__STDC_VERSION__)) +#endif /* __cplusplus */ +#ifdef __STDC_NO_ATOMICS__ +#pragma message("__STDC_NO_ATOMICS__ = TRUE") +#else +#pragma message("__STDC_NO_ATOMICS__ = FALSE") +#endif /* __STDC_NO_ATOMICS__ */ + +#include "tkc/mutex.h" + +struct _tk_atomic_t { + value_t value; + tk_mutex_t* lock; +}; + +/** + * @method tk_atomic_deinit + * @export none + * 释放原子操作类对象。 + * + * @param {tk_atomic_t*} atomic 原子操作类对象。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +static inline ret_t tk_atomic_deinit(tk_atomic_t* atomic) { + ret_t ret = RET_OK; + return_value_if_fail(atomic != NULL, RET_BAD_PARAMS); + if (atomic->lock != NULL) { + ret = tk_mutex_destroy(atomic->lock); + } + if (RET_OK == ret) { + memset(atomic, 0, sizeof(tk_atomic_t)); + } + return ret; +} + +/** + * @method tk_atomic_init + * @export none + * 初始化原子操作类对象。 + * + * @param {tk_atomic_t*} atomic 原子操作类对象。 + * @param {const value_t*} v 值。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +static inline ret_t tk_atomic_init(tk_atomic_t* atomic, const value_t* v) { + ret_t ret = RET_OK; + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + return_value_if_fail(tk_atomic_support_value_type(v->type), RET_BAD_PARAMS); + + ret = tk_atomic_deinit(atomic); + return_value_if_fail(RET_OK == ret, ret); + + ret = value_copy(&atomic->value, v); + return_value_if_fail(RET_OK == ret, ret); + + atomic->lock = tk_mutex_create(); + return_value_if_fail(atomic->lock != NULL, RET_OOM); + + return ret; +} + +/** + * @method tk_atomic_exchange + * @export none + * 原子交换操作。 + * + * @param {const tk_atomic_t*} atomic 原子操作类对象。 + * @param {value_t*} v 交换值。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +static inline ret_t tk_atomic_exchange(tk_atomic_t* atomic, value_t* v) { + ret_t ret = RET_OK; + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + + ret = tk_mutex_lock(atomic->lock); + return_value_if_fail(RET_OK == ret, ret); + + /* 读-修改-写 */ + value_t tmp; + value_copy(&tmp, &atomic->value); + ret = value_copy(&atomic->value, v); + value_copy(v, &tmp); +error: + tk_mutex_unlock(atomic->lock); + return ret; +} + +/** + * @method tk_atomic_store + * @export none + * 原子写操作。 + * + * @param {tk_atomic_t*} atomic 原子操作类对象。 + * @param {const value_t*} v 写入值。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +static inline ret_t tk_atomic_store(tk_atomic_t* atomic, const value_t* v) { + ret_t ret = RET_OK; + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + + ret = tk_mutex_lock(atomic->lock); + return_value_if_fail(RET_OK == ret, ret); + + ret = value_copy(&atomic->value, v); +error: + tk_mutex_unlock(atomic->lock); + return ret; +} + +/** + * @method tk_atomic_load + * @export none + * 原子读操作。 + * + * @param {const tk_atomic_t*} atomic 原子操作类对象。 + * @param {value_t*} v 用于返回读取值。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +static inline ret_t tk_atomic_load(const tk_atomic_t* atomic, value_t* v) { + ret_t ret = RET_OK; + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + + ret = tk_mutex_lock(atomic->lock); + return_value_if_fail(RET_OK == ret, ret); + + ret = value_copy(v, &atomic->value); +error: + tk_mutex_unlock(atomic->lock); + return ret; +} + +/** + * @method tk_atomic_fetch_add + * @export none + * 原子加操作。 + * + * @param {const tk_atomic_t*} atomic 原子操作类对象。 + * @param {value_t*} v 值。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +static inline ret_t tk_atomic_fetch_add(tk_atomic_t* atomic, value_t* v) { + ret_t ret = RET_OK; + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + + ret = tk_mutex_lock(atomic->lock); + return_value_if_fail(RET_OK == ret, ret); + + ret = value_add(&atomic->value, v, &atomic->value); +error: + tk_mutex_unlock(atomic->lock); + return ret; +} + +/** + * @method tk_atomic_fetch_sub + * @export none + * 原子减操作。 + * + * @param {const tk_atomic_t*} atomic 原子操作类对象。 + * @param {value_t*} v 值。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +static inline ret_t tk_atomic_fetch_sub(tk_atomic_t* atomic, value_t* v) { + ret_t ret = RET_OK; + return_value_if_fail(atomic != NULL && v != NULL, RET_BAD_PARAMS); + + ret = tk_mutex_lock(atomic->lock); + return_value_if_fail(RET_OK == ret, ret); + + ret = value_sub(&atomic->value, v, &atomic->value); +error: + tk_mutex_unlock(atomic->lock); + return ret; +} +#endif + +/** + * @method tk_atomic_destroy + * @annotation ["deconstructor"] + * @export none + * 销毁原子操作类对象。 + * + * @param {tk_atomic_t*} atomic 原子操作类对象。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +static inline ret_t tk_atomic_destroy(tk_atomic_t* atomic) { + ret_t ret = RET_OK; + return_value_if_fail(atomic != NULL, RET_BAD_PARAMS); + + ret = tk_atomic_deinit(atomic); + if (RET_OK == ret) { + TKMEM_FREE(atomic); + } + + return ret; +} + +/** + * @method tk_atomic_create + * @annotation ["constructor"] + * @export none + * 创建原子操作类对象。 + * + * @param {const value_t*} v 值。 + * + * @return {tk_atomic_t*} 返回原子操作类对象。 + */ +static inline tk_atomic_t* tk_atomic_create(const value_t* v) { + tk_atomic_t* ret = NULL; + return_value_if_fail(v != NULL, NULL); + return_value_if_fail(tk_atomic_support_value_type(v->type), NULL); + + ret = TKMEM_ZALLOC(tk_atomic_t); + return_value_if_fail(ret != NULL, NULL); + + goto_error_if_fail(RET_OK == tk_atomic_init(ret, v)); + + return ret; +error: + tk_atomic_destroy(ret); + return NULL; +} + +#endif /*TK_ATOMIC_H*/ diff --git a/tests/SConscript b/tests/SConscript index e44c39c07..007329289 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -3,6 +3,8 @@ import sys import platform import awtk_config as awtk +OS_NAME = platform.system(); + BIN_DIR=os.environ['BIN_DIR']; LIB_DIR=os.environ['LIB_DIR']; TK_ROOT=os.environ['TK_ROOT']; @@ -51,10 +53,14 @@ env.Program(os.path.join(BIN_DIR, 'ubjson_to_json'), ["ubjson_to_json.cpp"]) env.Program(os.path.join(BIN_DIR, 'format_xml'), ["format_xml.cpp"]) env.Program(os.path.join(BIN_DIR, 'async_call_test'), ["async_call_test.cpp"]) +if OS_NAME == 'Windows' : + env.Program(os.path.join(BIN_DIR, 'atomic_test'), ["atomic_test.cpp"]) +else : + env.Program(os.path.join(BIN_DIR, 'atomic_test'), ["atomic_test.cpp"], CCFLAGS = env["CCFLAGS"] + ' -std=c++11 ') + env.SharedLibrary(os.path.join(BIN_DIR+"/plugins_for_test", 'a'), ["a.c"]) env.SharedLibrary(os.path.join(BIN_DIR+"/plugins_for_test", 'b'), ["b.c"]) -OS_NAME = platform.system(); CROSS_COMPILE='CROSS_COMPILE' in os.environ and os.environ['CROSS_COMPILE'] == 'True' if not CROSS_COMPILE: LIBS=env['LIBS'] diff --git a/tests/atomic_test.cpp b/tests/atomic_test.cpp new file mode 100644 index 000000000..148f59320 --- /dev/null +++ b/tests/atomic_test.cpp @@ -0,0 +1,109 @@ +#include "tkc/utils.h" +#include "tkc/thread.h" +#include "tkc/atomic.h" + +#include "tkc/time_now.h" +#include "base/timer.h" + +#define THREAD_NUM 10 +#define NR 20000 +#define STEP 1 + +static tk_atomic_t s_atomic; + +static void* inc_thread(void* args) { + uint32_t id = (uint32_t)tk_pointer_to_int(args); + uint64_t i = 0; + value_t v; + + value_set_int(&v, STEP); + + for (i = 0; i < NR; i++) { + tk_atomic_fetch_add(&s_atomic, &v); + } + + return NULL; +} + +static void* dec_thread(void* args) { + uint32_t id = (uint32_t)tk_pointer_to_int(args); + uint64_t i = 0; + value_t v; + + value_set_int(&v, STEP); + + for (i = 0; i < NR; i++) { + tk_atomic_fetch_sub(&s_atomic, &v); + } + + return NULL; +} + +int main(int argc, char* argv[]) { + uint32_t i = 0; + value_t v; + tk_thread_t* threads[THREAD_NUM]; + tk_thread_entry_t entrys[] = { + inc_thread, + dec_thread, + }; + uint64_t entrys_init_value[ARRAY_SIZE(entrys)] = { + 0, + THREAD_NUM * NR * STEP, + }; + uint64_t entrys_correct[ARRAY_SIZE(entrys)] = { + THREAD_NUM * NR * STEP, + 0, + }; + uint64_t entrys_result[ARRAY_SIZE(entrys)] = {0}; + uint64_t entrys_time[ARRAY_SIZE(entrys)] = {0}; + uint64_t total_time = 0; + + platform_prepare(); + timer_prepare(time_now_ms); + + tk_atomic_init(&s_atomic, value_set_int(&v, 0)); + + for (i = 0; i < ARRAY_SIZE(entrys); i++) { + uint32_t j = 0; + value_t tmp; + + log_debug("entrys[%u] start\n", i); + + tk_atomic_store(&s_atomic, value_set_uint64(&v, entrys_init_value[i])); + + entrys_time[i] = timer_manager()->get_time(); + + for (j = 0; j < ARRAY_SIZE(threads); j++) { + threads[j] = tk_thread_create(entrys[i], tk_pointer_from_int(j)); + tk_thread_start(threads[j]); + } + + for (j = 0; j < ARRAY_SIZE(threads); j++) { + tk_thread_join(threads[j]); + tk_thread_destroy(threads[j]); + log_debug("%u stop\n", j); + } + + entrys_time[i] = timer_manager()->get_time() - entrys_time[i]; + + tk_atomic_load(&s_atomic, &tmp); + + entrys_result[i] = value_uint64(&tmp); + log_debug("entrys[%u] result %llu\n", i, entrys_result[i]); + } + + tk_atomic_deinit(&s_atomic); + + log_debug("\n"); + + for (i = 0; i < ARRAY_SIZE(entrys); i++) { + log_debug("entrys[%u] : time = %llu ms, result = %llu, correct = %s\n", i, entrys_time[i], + entrys_result[i], entrys_result[i] == entrys_correct[i] ? "True" : "False"); + total_time += entrys_time[i]; + } + + log_debug("\ntotal time = %llu ms\n", total_time); + + return 0; +}