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();
}
|