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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "mutex.h"
#include "debug.h"
#include "thread.h"
#include <kj/compat/gtest.h>
#include <stdlib.h>
#if _WIN32
#define NOGDI // NOGDI is needed to make EXPECT_EQ(123u, *lock) compile for some reason
#include <windows.h>
#undef NOGDI
#else
#include <pthread.h>
#include <unistd.h>
#endif
namespace kj {
namespace {
#if _WIN32
inline void delay() { Sleep(10); }
#else
inline void delay() { usleep(10000); }
#endif
TEST(Mutex, MutexGuarded) {
MutexGuarded<uint> value(123);
{
Locked<uint> lock = value.lockExclusive();
EXPECT_EQ(123u, *lock);
EXPECT_EQ(123u, value.getAlreadyLockedExclusive());
Thread thread([&]() {
Locked<uint> threadLock = value.lockExclusive();
EXPECT_EQ(456u, *threadLock);
*threadLock = 789;
});
delay();
EXPECT_EQ(123u, *lock);
*lock = 456;
auto earlyRelease = kj::mv(lock);
}
EXPECT_EQ(789u, *value.lockExclusive());
{
auto rlock1 = value.lockShared();
EXPECT_EQ(789u, *rlock1);
EXPECT_EQ(789u, value.getAlreadyLockedShared());
{
auto rlock2 = value.lockShared();
EXPECT_EQ(789u, *rlock2);
auto rlock3 = value.lockShared();
EXPECT_EQ(789u, *rlock3);
auto rlock4 = value.lockShared();
EXPECT_EQ(789u, *rlock4);
}
Thread thread2([&]() {
Locked<uint> threadLock = value.lockExclusive();
*threadLock = 321;
});
#if KJ_USE_FUTEX
// So, it turns out that pthread_rwlock on BSD "prioritizes" readers over writers. The result
// is that if one thread tries to take multiple read locks, but another thread happens to
// request a write lock it between, you get a deadlock. This seems to contradict the man pages
// and common sense, but this is how it is. The futex-based implementation doesn't currently
// have this problem because it does not prioritize writers. Perhaps it will in the future,
// but we'll leave this test here until then to make sure we notice the change.
delay();
EXPECT_EQ(789u, *rlock1);
{
auto rlock2 = value.lockShared();
EXPECT_EQ(789u, *rlock2);
auto rlock3 = value.lockShared();
EXPECT_EQ(789u, *rlock3);
auto rlock4 = value.lockShared();
EXPECT_EQ(789u, *rlock4);
}
#endif
delay();
EXPECT_EQ(789u, *rlock1);
auto earlyRelease = kj::mv(rlock1);
}
EXPECT_EQ(321u, *value.lockExclusive());
#if !_WIN32 // Not checked on win32.
EXPECT_DEBUG_ANY_THROW(value.getAlreadyLockedExclusive());
EXPECT_DEBUG_ANY_THROW(value.getAlreadyLockedShared());
#endif
EXPECT_EQ(321u, value.getWithoutLock());
}
#if KJ_USE_FUTEX // TODO(soon): Implement on pthread & win32
TEST(Mutex, When) {
MutexGuarded<uint> value(123);
{
uint m = value.when([](uint n) { return n < 200; }, [](uint& n) {
++n;
return n + 2;
});
KJ_EXPECT(m == 126);
KJ_EXPECT(*value.lockShared() == 124);
}
{
kj::Thread thread([&]() {
delay();
*value.lockExclusive() = 321;
});
uint m = value.when([](uint n) { return n > 200; }, [](uint& n) {
++n;
return n + 2;
});
KJ_EXPECT(m == 324);
KJ_EXPECT(*value.lockShared() == 322);
}
{
// Stress test. 100 threads each wait for a value and then set the next value.
*value.lockExclusive() = 0;
auto threads = kj::heapArrayBuilder<kj::Own<kj::Thread>>(100);
for (auto i: kj::zeroTo(100)) {
threads.add(kj::heap<kj::Thread>([i,&value]() {
if (i % 2 == 0) delay();
uint m = value.when([i](const uint& n) { return n == i; },
[](uint& n) { return n++; });
KJ_ASSERT(m == i);
}));
}
uint m = value.when([](uint n) { return n == 100; }, [](uint& n) {
return n++;
});
KJ_EXPECT(m == 100);
KJ_EXPECT(*value.lockShared() == 101);
}
}
#endif
TEST(Mutex, Lazy) {
Lazy<uint> lazy;
volatile bool initStarted = false;
Thread thread([&]() {
EXPECT_EQ(123u, lazy.get([&](SpaceFor<uint>& space) -> Own<uint> {
initStarted = true;
delay();
return space.construct(123);
}));
});
// Spin until the initializer has been entered in the thread.
while (!initStarted) {
#if _WIN32
Sleep(0);
#else
sched_yield();
#endif
}
EXPECT_EQ(123u, lazy.get([](SpaceFor<uint>& space) { return space.construct(456); }));
EXPECT_EQ(123u, lazy.get([](SpaceFor<uint>& space) { return space.construct(789); }));
}
TEST(Mutex, LazyException) {
Lazy<uint> lazy;
auto exception = kj::runCatchingExceptions([&]() {
lazy.get([&](SpaceFor<uint>& space) -> Own<uint> {
KJ_FAIL_ASSERT("foo") { break; }
return space.construct(123);
});
});
EXPECT_TRUE(exception != nullptr);
uint i = lazy.get([&](SpaceFor<uint>& space) -> Own<uint> {
return space.construct(456);
});
// Unfortunately, the results differ depending on whether exceptions are enabled.
// TODO(someday): Fix this? Does it matter?
#if KJ_NO_EXCEPTIONS
EXPECT_EQ(123, i);
#else
EXPECT_EQ(456, i);
#endif
}
} // namespace
} // namespace kj