aboutsummaryrefslogtreecommitdiffstats
path: root/util/system/mutex_ut.cpp
blob: 3327408600878bb80717cf648ea162346af486da (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include "mutex.h" 
#include "atomic.h" 
 
#include <library/cpp/testing/unittest/registar.h>

#include <util/thread/pool.h>
#include <util/random/random.h>

class TMutexTest: public TTestBase { 
    UNIT_TEST_SUITE(TMutexTest);
    UNIT_TEST(TestBasics) 
    UNIT_TEST(TestFake) 
    UNIT_TEST(TestRecursive) 
    UNIT_TEST_SUITE_END();

    struct TSharedData {
        TSharedData()
            : sharedCounter(0)
            , failed(false)
        {
        }

        volatile ui32 sharedCounter;
        TMutex mutex;
        TFakeMutex fakeMutex;

        bool failed;
    };

    class TThreadTask: public IObjectInQueue { 
    public: 
        using PFunc = void (TThreadTask::*)(void);

        TThreadTask(PFunc func, TSharedData& data, size_t id) 
            : Func_(func) 
            , Data_(data) 
            , Id_(id) 
        { 
        } 

        void Process(void*) override {
            THolder<TThreadTask> This(this); 
 
            (this->*Func_)(); 
        } 

#define FAIL_ASSERT(cond)    \ 
    if (!(cond)) {           \ 
        Data_.failed = true; \ 
    }
        void RunBasics() { 
            Data_.mutex.Acquire(); 
 
            ui32 oldCounter = ui32(Data_.sharedCounter + Id_); 
            Data_.sharedCounter = oldCounter; 
            usleep(10 + RandomNumber<ui32>() % 10); 
            FAIL_ASSERT(Data_.sharedCounter == oldCounter); 

            Data_.mutex.Release(); 
        } 

        void RunFakeMutex() { 
            bool res = Data_.fakeMutex.TryAcquire(); 
            FAIL_ASSERT(res); 
        } 
 
        void RunRecursiveMutex() { 
            for (size_t i = 0; i < Id_ + 1; ++i) { 
                Data_.mutex.Acquire(); 
                ++Data_.sharedCounter; 
                usleep(1); 
            }
            FAIL_ASSERT(Data_.sharedCounter == Id_ + 1); 

            bool res = Data_.mutex.TryAcquire(); 
            FAIL_ASSERT(res); 
            Data_.mutex.Release(); 
 
            for (size_t i = 0; i < Id_; ++i) { 
                --Data_.sharedCounter; 
                Data_.mutex.Release(); 
            }
            FAIL_ASSERT(Data_.sharedCounter == 1); 
            --Data_.sharedCounter; 
            Data_.mutex.Release(); 
        } 

#undef FAIL_ASSERT

    private: 
        PFunc Func_; 
        TSharedData& Data_; 
        size_t Id_; 
    };

private: 
#define RUN_CYCLE(what, count)                                              \ 
    Q_.Start(count);                                                        \ 
    for (size_t i = 0; i < count; ++i) {                                    \ 
        UNIT_ASSERT(Q_.Add(new TThreadTask(&TThreadTask::what, Data_, i))); \ 
    }                                                                       \ 
    Q_.Stop();                                                              \ 
    bool b = Data_.failed;                                                  \ 
    Data_.failed = false;                                                   \ 
    UNIT_ASSERT(!b); 

    void TestBasics() { 
        RUN_CYCLE(RunBasics, 5); 

        UNIT_ASSERT(Data_.sharedCounter == 10); 
        Data_.sharedCounter = 0; 
    } 

    void TestFake() { 
        RUN_CYCLE(RunFakeMutex, 3); 
    } 

    void TestRecursive() { 
        RUN_CYCLE(RunRecursiveMutex, 4); 
    } 

#undef RUN_CYCLE

private: 
    TSharedData Data_; 
    TThreadPool Q_;
};

UNIT_TEST_SUITE_REGISTRATION(TMutexTest)