1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15#include <climits>
16#include <thread>
17#include <cstdint>
18#include <gtest/gtest.h>
19
20#include "param_atomic.h"
21#include "param_common.h"
22#include "param_utils.h"
23
24using namespace testing::ext;
25const int THREAD_NUM = 5;
26const int MAX_NUM = 10;
27
28namespace init_ut {
29class AtomicUnitTest : public testing::Test {
30public:
31    static void SetUpTestCase(void) {};
32    static void TearDownTestCase(void) {};
33    void SetUp(void) {};
34    void TearDown(void) {};
35};
36
37/**
38 * 测试方法:
39 *  1,create 线程,执行全局变量写操作
40 *      store with dirty
41 *      sleep
42 *      store clear dirty
43 *      store commit ++
44 *  2,read 现成,执行全局变量读参数
45 *
46 */
47using AtomicTestData = struct {
48    ATOMIC_UINT32 commitId;
49    uint32_t data;
50};
51
52static AtomicTestData g_testData = { 0, 0 };
53
54static void *TestSetData(void *args)
55{
56    while (g_testData.data < MAX_NUM) {
57        uint32_t commitId = ATOMIC_LOAD_EXPLICIT(&g_testData.commitId, MEMORY_ORDER_RELAXED);
58        ATOMIC_STORE_EXPLICIT(&g_testData.commitId, commitId | PARAM_FLAGS_MODIFY, MEMORY_ORDER_RELAXED);
59        g_testData.data++;
60        usleep(200 * 1000); // 200 * 1000 wait
61        printf("TestSetData data: %d commit: %d \n", g_testData.data, g_testData.commitId & PARAM_FLAGS_COMMITID);
62        uint32_t flags = commitId & ~PARAM_FLAGS_COMMITID;
63        ATOMIC_STORE_EXPLICIT(&g_testData.commitId, (++commitId) | flags, MEMORY_ORDER_RELEASE);
64        futex_wake(&g_testData.commitId, INT_MAX);
65        usleep(100); // 100 wait
66    }
67    return nullptr;
68}
69
70static inline uint32_t TestReadCommitId(AtomicTestData *entry)
71{
72    uint32_t commitId = ATOMIC_LOAD_EXPLICIT(&entry->commitId, MEMORY_ORDER_ACQUIRE);
73    while (commitId & PARAM_FLAGS_MODIFY) {
74        futex_wait(&entry->commitId, commitId);
75        commitId = ATOMIC_LOAD_EXPLICIT(&entry->commitId, MEMORY_ORDER_ACQUIRE);
76    }
77    return commitId & PARAM_FLAGS_COMMITID;
78}
79
80static inline int TestReadParamValue(AtomicTestData *entry, uint32_t *commitId)
81{
82    uint32_t data;
83    uint32_t id = *commitId;
84    do {
85        *commitId = id;
86        data = entry->data;
87        id = TestReadCommitId(entry);
88    } while (*commitId != id); // if change, must read
89    return data;
90}
91
92static void *TestReadData(void *args)
93{
94    uint32_t data = 0;
95    while (data < MAX_NUM) {
96        uint32_t commitId = TestReadCommitId(&g_testData);
97        data = TestReadParamValue(&g_testData, &commitId);
98        printf("[ %d] TestReadData data: %d commit: %d \n", gettid(), data, commitId);
99        usleep(10); // 10 wait
100    }
101    return nullptr;
102}
103
104HWTEST_F(AtomicUnitTest, Init_AtomicUnitTest_001, TestSize.Level0)
105{
106    printf("AtomicUnitTest_001 \n");
107    pthread_t writeThread = 0;
108    pthread_t readThread[THREAD_NUM] = { 0 };
109    pthread_create(&writeThread, nullptr, TestSetData, nullptr);
110    for (size_t i = 0; i < THREAD_NUM; i++) {
111        pthread_create(&readThread[i], nullptr, TestReadData, nullptr);
112    }
113    pthread_join(writeThread, nullptr);
114    printf("AtomicUnitTest_001 end \n");
115}
116}