Newer
Older
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright © 2020 Florian Fischer, 2021 Florian Schmaus
#include <atomic> // for atomic
#include <cstdlib> // for exit, EXIT_FAILURE, EXIT_SUC...
#include <ctime> // for nanosleep, timespec
#include <iostream> // for operator<<, basic_ostream
#include <thread> // for this_thread
#include "Actor.hpp" // for Actor
#include "BinaryPrivateSemaphore.hpp" // for BPS
#include "CountingPrivateSemaphore.hpp" // for CPS
#include "Debug.hpp"
#include "Fiber.hpp"
#include "Runtime.hpp"
#include "emper-config.h"
#include "emper.hpp"
#include "lib/adt/LockedUnboundedQueue.hpp"
/*
* This test triggered a spurious wakeup bug in the UnboundedBlockingMpscQueue
* of the AlarmActor which was not triggered by the AlarmActorTest.
*
* Although we are not completely sure why the signaling main thread is needed
* we assume that the shorter AlarmActor::receive function or different scheduling
* caused by the main thread trigger the spurious wakeup in UnboundedBlockingMpscQueue::get
*/
lib::adt::LockedUnboundedQueue<BPS> wakeupQueue;
std::atomic<bool> quit = false;
class AlarmActor : public Actor<BPS*> {
protected:
void receive(BPS* sem) override { wakeupQueue.enqueue(sem); }
public:
AlarmActor(Runtime& runtime) : Actor(runtime) {}
void stop() { Actor::stop(); }
};
auto main(int argc, char* argv[]) -> int {
unsigned int sleeper_count = std::thread::hardware_concurrency();
unsigned int sleeps = EMPER_LOG_LEVEL > Info ? 100 : 1000;
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
if (argc > 3) {
std::cerr << "Usage: " << argv[0] << " [fiber count] [block count]" << std::endl;
exit(EXIT_FAILURE);
}
const int DECIMAL = 10;
if (argc > 1) {
sleeper_count = strtol(argv[1], nullptr, DECIMAL);
}
if (argc > 2) {
sleeps = strtol(argv[2], nullptr, DECIMAL);
}
Runtime runtime;
Fiber* fiber = Fiber::from([&] {
AlarmActor alarmActor(runtime);
alarmActor.start();
CPS cps;
for (unsigned int i = 0; i < sleeper_count; ++i) {
spawn(
[&alarmActor, &sleeps] {
for (unsigned int i = 1; i <= sleeps; ++i) {
BPS sem;
alarmActor.tell(&sem);
sem.wait();
}
},
cps);
}
// Wait for the sleeping fibers to finish
cps.wait();
// Wait for the actor to become idle.
bool actorIdle = alarmActor.waitUntilIdle(60);
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
if (!actorIdle) {
std::cerr << "FAILURE: Actor did not went idle";
quit = true;
exit(EXIT_FAILURE);
}
alarmActor.stop();
quit = true;
exit(EXIT_SUCCESS);
});
runtime.scheduleFromAnywhere(*fiber);
while (!quit) {
BPS* sem = wakeupQueue.dequeue();
if (sem) {
sem->signalFromAnywhere();
} else {
// clang-tidy-11 probably crashes when parsing C++ comparison in chrono.h
// See: https://bugs.llvm.org/show_bug.cgi?id=47768.
// std::this_thread::sleep_for(std::chrono::nanoseconds(1000));
// -> use nanosleep directly
struct timespec duration;
duration.tv_sec = 0;
duration.tv_nsec = 100;
nanosleep(&duration, nullptr);
}
}
runtime.waitUntilFinished();
return EXIT_FAILURE;
}