summaryrefslogtreecommitdiffstats
path: root/vespamalloc/src/tests/test1/testatomic.cpp
blob: 1222493446c7b448bff6cca13f3de3061ea95faa (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
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/fastos/fastos.h>
#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/vespalib/util/atomic.h>
#include <vector>

using vespalib::Atomic;

class Test : public vespalib::TestApp
{
public:
    int Main();
private:
    template<typename T>
    void testSwap(T initial);
    template<typename T>
    void testSwapStress(T v, int numThreads);
};

template <typename T>
class Stress : public FastOS_Runnable
{
private:
    void Run(FastOS_ThreadInterface * ti, void * arg);
    void stressSwap(T & value);
public:
    Stress(T * value) : _value(value), _successCount(0), _failedCount(0) { }
    void wait() { _wait.Lock(); _wait.Unlock(); }
    FastOS_Mutex _wait;
    T      * _value;
    size_t   _successCount;
    size_t   _failedCount;
};

TEST_APPHOOK(Test);

template<typename T>
void Test::testSwap(T initial)
{
    T value(initial);

    ASSERT_TRUE(Atomic::cmpSwap(&value, initial+1, initial));
    ASSERT_TRUE(value == initial+1);

    ASSERT_TRUE(!Atomic::cmpSwap(&value, initial+2, initial));
    ASSERT_TRUE(value == initial+1);
}

template<typename T>
void Test::testSwapStress(T v, int numThreads)
{
    T old(v);
    std::vector<Stress<T> *> contexts;
    std::vector<FastOS_ThreadInterface *> threads;
    FastOS_ThreadPool threadPool(512*1024);

    for(int i=0; i < numThreads; i++) {
        contexts.push_back(new Stress<T>(&v));
    }

    for(size_t i = 0; i < contexts.size(); i++) {
        threads.push_back(threadPool.NewThread(contexts[i]));
    }
    FastOS_Thread::Sleep(1000);
    size_t succesCount(0);
    size_t failedCount(0);
    for(size_t i = 0; i < contexts.size(); i++) {
        Stress<T> * s = contexts[i];
        s->wait();
        succesCount += s->_successCount;
        failedCount += s->_failedCount;
    }
    ASSERT_TRUE(v == 0);
    ASSERT_TRUE(old == succesCount);
    fprintf(stderr, "%ld threads counting down from %" PRIu64 " had %ld succesfull and %ld unsuccessful attempts\n",
            contexts.size(), uint64_t(old), succesCount, failedCount);
    for(size_t i = 0; i < contexts.size(); i++) {
        delete contexts[i];
    }
}

template <typename T>
void Stress<T>::Run(FastOS_ThreadInterface *, void *)
{
    _wait.Lock();
    stressSwap(*_value);
    _wait.Unlock();
}

template <typename T>
void Stress<T>::stressSwap(T & value)
{
    for (T old = value; old > 0; old = value) {
        if (Atomic::cmpSwap(&value, old-1, old)) {
            _successCount++;
        } else {
            _failedCount++;
        }
    }
}

int Test::Main()
{
    TEST_INIT("atomic");

    testSwap<uint32_t>(6);
    testSwap<uint32_t>(7);
    testSwap<uint32_t>(uint32_t(-6));
    testSwap<uint32_t>(uint32_t(-7));
    testSwap<uint64_t>(6);
    testSwap<uint64_t>(7);
    testSwap<uint64_t>(uint64_t(-6));
    testSwap<uint64_t>(uint64_t(-7));
    testSwapStress<uint64_t>(0x1000000, 4);
    testSwapStress<uint32_t>(0x1000000, 4);

    TEST_DONE();
}