Skip to content

Commit 5b32ef2

Browse files
committed
Improve race exercise.
The race exercise races very infrequently on some platforms. Therefore: - Provide a variable to spawn more threads - Provide a run script that invokes the executable until a race occurs - Add thread sanitizer and valgrind as optional steps to the instructions Fix #353.
1 parent 94c9a7b commit 5b32ef2

File tree

5 files changed

+47
-28
lines changed

5 files changed

+47
-28
lines changed

code/race/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ clean:
88
rm -f *o $(PROGRAM_NAME) *~ core $(PROGRAM_NAME).sol
99

1010
$(PROGRAM_NAME) : $(PROGRAM_NAME).cpp
11-
${CXX} -g -std=c++17 -O2 -pthread -Wall -Wextra -L. -o $@ $<
11+
${CXX} ${CXXFLAGS} -g -std=c++17 -O2 -pthread -Wall -Wextra -L. -o $@ $<
1212

1313
$(PROGRAM_NAME).sol : solution/$(PROGRAM_NAME).sol.cpp
14-
${CXX} -g -std=c++17 -O2 -pthread -Wall -Wextra -L. -o $@ $<
14+
${CXX} ${CXXFLAGS} -g -std=c++17 -O2 -pthread -Wall -Wextra -L. -o $@ $<

code/race/README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11

22
## Instructions
33

4-
* Compile, run many times, see what happens
5-
* E.g. in bash, use: `while true; do ./racing; done`
6-
* (Optional) You can use `valgrind --tool=helgrind ./racing` to proof your assumption
4+
* Compile and run the executable, see if it races
5+
* If you have a bash shell, try `./run ./racing`, which keeps invoking the executable
6+
until a race condition is detected
7+
* (Optional) You can use `valgrind --tool=helgrind ./racing` to prove your assumption
8+
* (Optional) If your operating system supports it, recompile with thread sanitizer.
9+
With Makefile, use e.g. `make CXXFLAGS="-fsanitize=thread"`
710
* Use a mutex to fix the issue
811
* See the difference in execution time
9-
* (Optional) Check agan with `valgrind` if the problem is fixed
12+
* (Optional) Check again with `valgrind` or thread sanitizer if the problem is fixed

code/race/racing.cpp

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
#include <iostream>
22
#include <thread>
3+
#include <vector>
34

45
/*
5-
* This program tries to increment an integer 200 times in two threads.
6-
* Check whether the result is indeed always 200.
6+
* This program tries to increment an integer 100 times from multiple threads.
7+
* If the result comes out at 100*nThread, it stays silent, but it will print
8+
* an error if a race condition is detected.
9+
* If you don't see it racing, try ./run ./racing, which keeps invoking the
10+
* executable until a race condition is detected.
711
*/
812

13+
constexpr unsigned int nThread = 2;
14+
915
int main() {
1016
int nError = 0;
1117

@@ -19,20 +25,17 @@ int main() {
1925
}
2026
};
2127

22-
// Run with two threads
23-
std::thread t1(inc100);
24-
std::thread t2(inc100);
25-
for (auto t : {&t1,&t2}) t->join();
28+
// Start up all threads:
29+
std::vector<std::thread> threads;
30+
for (unsigned int i = 0; i < nThread; ++i) threads.emplace_back(inc100);
31+
for (auto & thread : threads) thread.join();
2632

2733
// Check
28-
if (a != 200) {
29-
std::cout << "Race: " << a << ' ';
34+
if (a != nThread * 100) {
35+
std::cerr << "Race detected! Result: " << a << '\n';
3036
nError++;
31-
} else {
32-
std::cout << '.';
3337
}
3438
}
35-
std::cout << '\n';
3639

3740
return nError;
3841
}

code/race/run

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
3+
PROGRAM="./racing"
4+
if [ $# -ge 1 ]; then
5+
PROGRAM=$1
6+
fi
7+
8+
while true; do
9+
$PROGRAM || break;
10+
done

code/race/solution/racing.sol.cpp

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
#include <iostream>
22
#include <thread>
3+
#include <vector>
34
#include <mutex>
45

56
/*
6-
* This program tries to increment an integer 200 times in two threads.
7-
* We fix the race condition by locking a mutex before each increment.
7+
* This program tries to increment an integer 100 times from multiple threads.
8+
* If the result comes out at 100*nThread, it stays silent, but it will print
9+
* an error if a race condition is detected.
10+
* To run it in a loop, use
11+
* ./run ./racing.sol
812
*/
913

14+
constexpr unsigned int nThread = 2;
15+
1016
int main() {
1117
int nError = 0;
1218

@@ -22,20 +28,17 @@ int main() {
2228
}
2329
};
2430

25-
// Run with two threads
26-
std::thread t1(inc100);
27-
std::thread t2(inc100);
28-
for (auto t : {&t1,&t2}) t->join();
31+
// Start up all threads:
32+
std::vector<std::thread> threads;
33+
for (unsigned int i = 0; i < nThread; ++i) threads.emplace_back(inc100);
34+
for (auto & thread : threads) thread.join();
2935

3036
// Check
31-
if (a != 200) {
32-
std::cout << "Race: " << a << ' ';
37+
if (a != nThread * 100) {
38+
std::cerr << "Race detected! Result: " << a << '\n';
3339
nError++;
34-
} else {
35-
std::cout << '.';
3640
}
3741
}
38-
std::cout << '\n';
3942

4043
return nError;
4144
}

0 commit comments

Comments
 (0)