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
|
// tut
#include <tut/tut.hpp>
// geos
#include <geos/util/Interrupt.h>
// std
#include <chrono>
#include <functional>
#include <thread>
using geos::util::Interrupt;
using geos::util::CurrentThreadInterrupt;
namespace tut {
//
// Test Group
//
// Common data used in test cases.
struct test_interrupt_data {
static void workForever() {
try {
//std::cerr << "Started " << std::this_thread::get_id() << "." << std::endl;
while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
GEOS_CHECK_FOR_INTERRUPTS();
}
} catch (const std::exception&) {
//std::cerr << "Interrupted " << std::this_thread::get_id() << "." << std::endl;
}
}
static void interruptNow() {
Interrupt::request();
}
static std::map<std::thread::id, bool>* toInterrupt;
static void interruptIfRequested() {
if (toInterrupt == nullptr) {
return;
}
auto it = toInterrupt->find(std::this_thread::get_id());
if (it != toInterrupt->end() && it->second) {
it->second = false;
CurrentThreadInterrupt::interrupt();
}
}
static bool interruptCurrentThreadIfRequested() {
if (toInterrupt == nullptr) {
return false;
}
auto it = toInterrupt->find(std::this_thread::get_id());
if (it != toInterrupt->end() && it->second) {
it->second = false;
return true;
}
return false;
}
};
std::map<std::thread::id, bool>* test_interrupt_data::toInterrupt = nullptr;
typedef test_group<test_interrupt_data> group;
typedef group::object object;
group test_interrupt_group("geos::util::Interrupt");
//
// Test Cases
//
// Interrupt worker thread via global request from main thead
template<>
template<>
void object::test<1>
()
{
std::thread t(workForever);
Interrupt::request();
t.join();
}
// Interrupt worker thread via thread-specific request from worker thread using a callback
template<>
template<>
void object::test<2>
()
{
Interrupt::registerCallback(interruptIfRequested);
std::thread t1(workForever);
std::thread t2(workForever);
// Create map and add entries before exposing it to the interrupt
// callback that will be accessed from multiple threads. It's OK
// for multiple threads to modify entries in the map but not for
// multiple threads to create entries.
std::map<std::thread::id, bool> shouldInterrupt;
shouldInterrupt[t1.get_id()] = false;
shouldInterrupt[t2.get_id()] = false;
toInterrupt = &shouldInterrupt;
shouldInterrupt[t2.get_id()] = true;
t2.join();
shouldInterrupt[t1.get_id()] = true;
t1.join();
}
// Register separate callbacks for each thread. Each callback will
// request interruption of itself only.
template<>
template<>
void object::test<3>
()
{
bool interrupt1 = false;
int numCalls2 = 0;
auto cb1 = ([](void* data) -> int {
return *static_cast<bool*>(data);
});
auto cb2 = ([](void* data) -> int {
return ++*static_cast<int*>(data) > 5;
});
std::thread t1([&cb1, &interrupt1]() {
CurrentThreadInterrupt::registerCallback(cb1, &interrupt1);
});
std::thread t2([&cb2, &numCalls2]() {
CurrentThreadInterrupt::registerCallback(cb2, &numCalls2);
});
t2.join();
interrupt1 = true;
t1.join();
}
} // namespace tut
|