Last active
May 9, 2024 12:58
-
-
Save rioki/e225f21e39879fec37f32e9c2cd1365b to your computer and use it in GitHub Desktop.
sthread, like jthread, just simpler.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// latch | |
// Copyright 2017-2024 Sean Farrell <[email protected]> | |
// | |
// This program is free software. It comes without any warranty, to | |
// the extent permitted by applicable law. You can redistribute it | |
// and/or modify it under the terms of the Do What The Fuck You Want | |
// To Public License, Version 2, as published by Sam Hocevar. See | |
// http://www.wtfpl.net/ for more details. | |
#pragma once | |
#include <cstddef> | |
#include <mutex> | |
#include <condition_variable> | |
namespace stdex | |
{ | |
class latch | |
{ | |
public: | |
[[nodiscard]] static std::ptrdiff_t max() noexcept | |
{ | |
return std::numeric_limits<std::ptrdiff_t>::max(); | |
} | |
explicit latch(std::ptrdiff_t expected) noexcept | |
: count(expected) {} | |
~latch() = default; | |
void count_down(std::ptrdiff_t n = 1) noexcept | |
{ | |
auto lock = std::unique_lock<std::mutex>{mutex}; | |
count -= n; | |
if (count <= 0) | |
{ | |
cond.notify_all(); | |
} | |
} | |
void wait() const | |
{ | |
auto lock = std::unique_lock<std::mutex>{mutex}; | |
cond.wait(lock, [&]{return count <= 0;}); | |
} | |
private: | |
std::ptrdiff_t count; | |
mutable std::mutex mutex; | |
mutable std::condition_variable cond; | |
latch(const latch&) = delete; | |
latch& operator = (const latch&) = delete; | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// latch | |
// Copyright 2017-2024 Sean Farrell <[email protected]> | |
// | |
// This program is free software. It comes without any warranty, to | |
// the extent permitted by applicable law. You can redistribute it | |
// and/or modify it under the terms of the Do What The Fuck You Want | |
// To Public License, Version 2, as published by Sam Hocevar. See | |
// http://www.wtfpl.net/ for more details. | |
#include "latch.h" | |
#include <gtest/gtest.h> | |
#include <atomic> | |
#include <future> | |
using namespace std::chrono_literals; | |
TEST(latch, wait) | |
{ | |
auto my_latch = stdex::latch{12u}; | |
auto count = std::atomic<unsigned int>{0u}; | |
std::vector<std::future<void>> futures; | |
for (auto i = 0u; i < 12; i++) | |
{ | |
auto f = std::async(std::launch::async, [&] () { | |
count++; | |
my_latch.count_down(); | |
}); | |
futures.push_back(std::move(f)); | |
} | |
my_latch.wait(); | |
EXPECT_EQ(12u, count); | |
for (auto& f : futures) | |
{ | |
f.get(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// sthread | |
// Copyright 2024 Sean Farrell <[email protected]> | |
// | |
// This program is free software. It comes without any warranty, to | |
// the extent permitted by applicable law. You can redistribute it | |
// and/or modify it under the terms of the Do What The Fuck You Want | |
// To Public License, Version 2, as published by Sam Hocevar. See | |
// http://www.wtfpl.net/ for more details. | |
#pragma once | |
#include <thread> | |
#include <atomic> | |
#include <memory> | |
namespace stdex | |
{ | |
class stop_token | |
{ | |
public: | |
stop_token() noexcept | |
: state(std::make_shared<std::atomic<bool>>(false)) {} | |
stop_token(const stop_token&) noexcept = default; | |
stop_token(stop_token&&) noexcept = default; | |
~stop_token() = default; | |
stop_token& operator = (const stop_token&) noexcept = default; | |
stop_token& operator = (stop_token&&) noexcept = default; | |
[[nodiscard]] bool stop_requested() const noexcept | |
{ | |
return state->load(); | |
} | |
[[nodiscard]] bool stop_possible() const noexcept | |
{ | |
return !stop_requested(); | |
} | |
bool request_stop() noexcept | |
{ | |
auto prev_state = state->exchange(true); | |
return prev_state == false; | |
} | |
private: | |
std::shared_ptr<std::atomic<bool>> state; | |
}; | |
class sthread | |
{ | |
public: | |
using id = std::thread::id; | |
sthread() noexcept = default; | |
sthread(sthread&& other) noexcept = default; | |
template<class Function, class... Args> | |
explicit sthread(Function&& f, Args&&... args) | |
: impl(std::thread(std::forward<Function>(f), token, std::forward<Args>(args)...)) {} | |
~sthread() | |
{ | |
if (joinable()) | |
{ | |
request_stop(); | |
join(); | |
} | |
} | |
sthread& operator = (sthread&& other) noexcept = default; | |
[[nodiscard]] id get_id() const noexcept | |
{ | |
return impl.get_id(); | |
} | |
[[nodiscard]] bool joinable() const noexcept | |
{ | |
return impl.joinable(); | |
} | |
void join() | |
{ | |
impl.join(); | |
} | |
[[nodiscard]] bool stop_possible() const noexcept | |
{ | |
return joinable() && token.stop_possible(); | |
} | |
bool request_stop() noexcept | |
{ | |
return token.request_stop(); | |
} | |
private: | |
stop_token token; | |
std::thread impl; | |
sthread(const sthread&) noexcept = delete; | |
sthread& operator = (const sthread& other) noexcept = delete; | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// sthread | |
// Copyright 2024 Sean Farrell <[email protected]> | |
// | |
// This program is free software. It comes without any warranty, to | |
// the extent permitted by applicable law. You can redistribute it | |
// and/or modify it under the terms of the Do What The Fuck You Want | |
// To Public License, Version 2, as published by Sam Hocevar. See | |
// http://www.wtfpl.net/ for more details. | |
#include "sthread.h" | |
#include "latch.h" | |
#include <gtest/gtest.h> | |
TEST(sthread, create_and_destroy) | |
{ | |
auto thread = stdex::sthread{}; | |
EXPECT_FALSE(thread.joinable()); | |
EXPECT_FALSE(thread.stop_possible()); | |
} | |
TEST(sthread, launch_thread) | |
{ | |
auto tid = stdex::sthread::id{}; | |
auto thread = stdex::sthread{[&] (stdex::stop_token stoken) { | |
tid = std::this_thread::get_id(); | |
}}; | |
thread.join(); | |
EXPECT_NE(tid, std::this_thread::get_id()); | |
} | |
TEST(sthread, launch_thread_with_args) | |
{ | |
auto a = 0u; | |
auto thread = stdex::sthread{[&] (stdex::stop_token stoken, unsigned int awnser) { | |
a = awnser; | |
}, 42u}; | |
thread.join(); | |
EXPECT_EQ(a, 42u); | |
} | |
TEST(sthread, id) | |
{ | |
auto latch = stdex::latch{1}; | |
auto tid = stdex::sthread::id{}; | |
auto thread = stdex::sthread{[&] (stdex::stop_token stoken) { | |
tid = std::this_thread::get_id(); | |
latch.count_down(); | |
}}; | |
latch.wait(); | |
EXPECT_EQ(thread.get_id(), tid); | |
thread.join(); | |
} | |
TEST(sthread, empty_id) | |
{ | |
auto thread = stdex::sthread{}; | |
EXPECT_EQ(thread.get_id(), stdex::sthread::id{}); | |
} | |
TEST(sthread, move_assignment) | |
{ | |
auto latch = stdex::latch{1}; | |
auto outside = stdex::sthread{}; | |
{ | |
auto inside = stdex::sthread{[&] (stdex::stop_token token) { | |
latch.wait(); | |
}}; | |
outside = std::move(inside); | |
EXPECT_FALSE(inside.joinable()); | |
} | |
EXPECT_TRUE(outside.joinable()); | |
latch.count_down(); | |
outside.join(); | |
} | |
TEST(sthread, move_constructor) | |
{ | |
auto latch = stdex::latch{1}; | |
auto construct = [&] () { | |
auto inside = stdex::sthread{[&] (stdex::stop_token token) { | |
latch.wait(); | |
}}; | |
return inside; | |
}; | |
auto outside = construct(); | |
EXPECT_TRUE(outside.joinable()); | |
latch.count_down(); | |
outside.join(); | |
} | |
TEST(sthread, request_stop) | |
{ | |
auto thread = stdex::sthread{[] (stdex::stop_token token) { | |
while (!token.stop_requested()) | |
{ | |
std::this_thread::yield(); | |
} | |
}}; | |
thread.request_stop(); | |
thread.join(); | |
} | |
TEST(sthread, automatic_stop_request) | |
{ | |
auto thread = stdex::sthread{[] (stdex::stop_token token) { | |
while (!token.stop_requested()) | |
{ | |
std::this_thread::yield(); | |
} | |
}}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment