Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: a3ff357e2ec7
Choose a base ref
...
head repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 5b7920cb4a4f
Choose a head ref
  • 12 commits
  • 5 files changed
  • 1 contributor

Commits on Nov 22, 2020

  1. Copy the full SHA
    288131a View commit details
  2. Copy the full SHA
    42a2dfd View commit details
  3. Copy the full SHA
    c432ecb View commit details
  4. Merge.

    pleroy committed Nov 22, 2020
    Copy the full SHA
    8565b44 View commit details
  5. Merge.

    pleroy committed Nov 22, 2020
    Copy the full SHA
    41891d0 View commit details
  6. Merge.

    pleroy committed Nov 22, 2020
    Copy the full SHA
    72fc599 View commit details
  7. A test.

    pleroy committed Nov 22, 2020
    Copy the full SHA
    363a5a4 View commit details
  8. A test.

    pleroy committed Nov 22, 2020
    Copy the full SHA
    09f7d53 View commit details
  9. Cleanup.

    pleroy committed Nov 22, 2020
    Copy the full SHA
    eab2131 View commit details
  10. Just say no to flakiness.

    pleroy committed Nov 22, 2020
    Copy the full SHA
    6b7c44b View commit details
  11. Lint.

    pleroy committed Nov 22, 2020
    Copy the full SHA
    04e8080 View commit details
  12. Merge pull request #2791 from pleroy/JThread

    A minimal implementation of the jthread library
    pleroy authored Nov 22, 2020
    Copy the full SHA
    5b7920c View commit details
Showing with 391 additions and 0 deletions.
  1. +3 −0 base/base.vcxproj
  2. +9 −0 base/base.vcxproj.filters
  3. +114 −0 base/jthread.hpp
  4. +145 −0 base/jthread_body.hpp
  5. +120 −0 base/jthread_test.cpp
3 changes: 3 additions & 0 deletions base/base.vcxproj
Original file line number Diff line number Diff line change
@@ -40,6 +40,8 @@
<ClInclude Include="graveyard_body.hpp" />
<ClInclude Include="hexadecimal.hpp" />
<ClInclude Include="hexadecimal_body.hpp" />
<ClInclude Include="jthread.hpp" />
<ClInclude Include="jthread_body.hpp" />
<ClInclude Include="macros.hpp" />
<ClInclude Include="mappable.hpp" />
<ClInclude Include="map_util.hpp" />
@@ -88,6 +90,7 @@
<ClCompile Include="flags_test.cpp" />
<ClCompile Include="function_test.cpp" />
<ClCompile Include="hexadecimal_test.cpp" />
<ClCompile Include="jthread_test.cpp" />
<ClCompile Include="not_null_test.cpp" />
<ClCompile Include="pull_serializer_test.cpp" />
<ClCompile Include="push_deserializer_test.cpp" />
9 changes: 9 additions & 0 deletions base/base.vcxproj.filters
Original file line number Diff line number Diff line change
@@ -194,6 +194,12 @@
<ClInclude Include="bits_body.hpp">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="jthread.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="jthread_body.hpp">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="not_null_test.cpp">
@@ -256,5 +262,8 @@
<ClCompile Include="bits_test.cpp">
<Filter>Test Files</Filter>
</ClCompile>
<ClCompile Include="jthread_test.cpp">
<Filter>Test Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
114 changes: 114 additions & 0 deletions base/jthread.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#pragma once

#include <functional>
#include <memory>
#include <set>
#include <thread>

#include "absl/synchronization/mutex.h"
#include "base/not_null.hpp"

namespace principia {
namespace base {
namespace internal_jthread {

// A minimal implementation of the C++20 jthread library, intended to be
// compatible.

class StopState;

// Provides the means to check if a stop request has been made or can be made,
// for its associated stop_source object.
// https://en.cppreference.com/w/cpp/thread/stop_token
class stop_token {
public:
bool stop_requested() const;

private:
explicit stop_token(not_null<StopState*> stop_state);

StopState& get_stop_state() const;

not_null<StopState*> const stop_state_;

friend class jthread;
friend class stop_callback;
friend class stop_source;
};

// Provides the means to issue a stop request. A stop request made for one
// stop_source object is visible to all stop_sources and stop_tokens of the same
// associated stop-state.
// https://en.cppreference.com/w/cpp/thread/stop_source
class stop_source {
public:
bool request_stop();

bool stop_requested() const;

stop_token get_token() const;

private:
explicit stop_source(not_null<StopState*> stop_state);

not_null<StopState*> const stop_state_;

friend class jthread;
};

// An RAII object type that registers a callback function for an associated
// stop_token object, such that the callback function will be invoked when the
// stop_token's associated stop_source is requested to stop.
// https://en.cppreference.com/w/cpp/thread/stop_callback
class stop_callback {
public:
explicit stop_callback(stop_token const& st, std::function<void()> callback);
~stop_callback();

private:
void Run() const;

std::function<void()> const callback_;
stop_token const stop_token_;

friend class StopState;
};

// A single thread of execution which and can be cancelled/stopped in certain
// situations.
// https://en.cppreference.com/w/cpp/thread/jthread
class jthread {
public:
jthread() = default;

template<typename Function, typename... Args>
jthread(Function&& f, Args&&... args);

~jthread();

void join();

void detach();

bool request_stop();

stop_source get_stop_source() const;

stop_token get_stop_token() const;

private:
std::unique_ptr<StopState> stop_state_;
std::thread thread_;
};

} // namespace internal_jthread

using internal_jthread::jthread;
using internal_jthread::stop_callback;
using internal_jthread::stop_source;
using internal_jthread::stop_token;

} // namespace base
} // namespace principia

#include "base/jthread_body.hpp"
145 changes: 145 additions & 0 deletions base/jthread_body.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#pragma once

#include <set>

#include "base/jthread.hpp"
#include "base/macros.hpp"

namespace principia {
namespace base {
namespace internal_jthread {

class StopState {
public:
StopState() = default;

bool request_stop();

bool stop_requested() const;

void Register(not_null<stop_callback*> callback);
void Unregister(not_null<stop_callback*> callback);

private:
mutable absl::Mutex lock_;
bool stop_requested_ GUARDED_BY(lock_) = false;
std::set<not_null<stop_callback*>> callbacks_ GUARDED_BY(lock_);
};

inline bool StopState::request_stop() {
// NOTE(phl): If performance matters here we could do double-locking.
std::set<not_null<stop_callback*>> callbacks;
{
absl::MutexLock l(&lock_);
if (stop_requested_) {
return false;
} else {
stop_requested_ = true;
callbacks.swap(callbacks_);
}
}
for (auto const callback : callbacks) {
callback->Run();
}
return true;
}

inline bool StopState::stop_requested() const {
absl::ReaderMutexLock l(&lock_);
return stop_requested_;
}

inline void StopState::Register(not_null<stop_callback*> const callback) {
{
absl::MutexLock l(&lock_);
if (!stop_requested_) {
callbacks_.insert(callback);
return;
}
}
callback->Run();
}

inline void StopState::Unregister(not_null<stop_callback*> const callback) {
absl::MutexLock l(&lock_);
callbacks_.erase(callback);
}

inline bool stop_token::stop_requested() const {
return stop_state_->stop_requested();
}

inline stop_token::stop_token(not_null<StopState*> const stop_state)
: stop_state_(stop_state) {}

inline StopState& stop_token::get_stop_state() const {
return *stop_state_;
}

inline bool stop_source::request_stop() {
return stop_state_->request_stop();
}

inline bool stop_source::stop_requested() const {
return stop_state_->stop_requested();
}

inline stop_token stop_source::get_token() const {
return stop_token(stop_state_);
}

inline stop_source::stop_source(not_null<StopState*> const stop_state)
: stop_state_(stop_state) {}

inline stop_callback::stop_callback(stop_token const& st,
std::function<void()> callback)
: callback_(std::move(callback)),
stop_token_(st) {
stop_token_.get_stop_state().Register(this);
}

inline stop_callback::~stop_callback() {
stop_token_.get_stop_state().Unregister(this);
}

inline void stop_callback::Run() const {
callback_();
}

template<typename Function, typename... Args>
jthread::jthread(Function&& f, Args&&... args)
: stop_state_(std::make_unique<StopState>()),
thread_(std::move(f),
stop_token(stop_state_.get()),
std::forward<Args>(args)...) {}

inline jthread::~jthread() {
stop_state_->request_stop();
if (thread_.joinable()) {
thread_.join();
}
}

inline void jthread::join() {
thread_.join();
}

inline void jthread::detach() {
thread_.detach();
}

inline bool jthread::request_stop() {
return stop_state_->request_stop();
}

inline stop_source jthread::get_stop_source() const {
return stop_source(stop_state_.get());
}

inline stop_token jthread::get_stop_token() const {
return stop_token(stop_state_.get());
}

} // namespace internal_jthread
} // namespace base
} // namespace principia
Loading