[libcxx-commits] [PATCH] D145183: [libc++] Implement `stop_token`
Louis Dionne via Phabricator via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Mar 27 08:32:40 PDT 2023
ldionne requested changes to this revision.
ldionne added inline comments.
This revision now requires changes to proceed.
================
Comment at: libcxx/docs/Status/Cxx20.rst:52
.. [#note-P2231] P2231: Optional is complete. The changes to variant haven't been implemented yet.
+ .. [#note-P0660] P0660: Section 32.3 Stop Tokens is complete. ``jthread`` haven't been implemented yet.
----------------
================
Comment at: libcxx/include/__stop_token/stop_callback.h:32-37
+ static_assert(invocable<_Callback>,
+ "Mandates: stop_callback is instantiated with an argument for the template parameter Callback that "
+ "satisfies both invocable and destructible.");
+ static_assert(destructible<_Callback>,
+ "Mandates: stop_callback is instantiated with an argument for the template parameter Callback that "
+ "satisfies both invocable and destructible.");
----------------
That will make it easier for a user to find out what the problem is.
================
Comment at: libcxx/include/__stop_token/stop_callback.h:67
+ // st.stop_requested() was false and this is successfully added to the linked list
+ __st.__ref_counted_state_.__swap(__ref_counted_state_);
+ }
----------------
I think this is equivalent but clearer:
```
__ref_counted_state_ = std::move(__st.__ref_counted_state_);
```
So then I think we can probably add the following private constructor and then delegate to it from both these public constructors:
```
private:
struct __private_tag { };
template <class _RefCountedState, class _Cb>
_LIBCPP_HIDE_FROM_ABI explicit stop_callback(__private_tag, _RefCountedState&& __state, _Cb&& __cb) noexcept(is_nothrow_constructible_v<_Callback, _Cb>)
: __stop_callback_base(&stop_callback::__callback_fn_impl),
__callback_(std::forward<_Cb>(__cb)),
__ref_counted_state_() {
if (...) if (...) __ref_counted_state_ = std::forward<...>(...);
}
public:
template <class _Cb>
requires constructible_from<_Callback, _Cb>
_LIBCPP_HIDE_FROM_ABI explicit stop_callback(stop_token&& __st,
_Cb&& __cb) noexcept(is_nothrow_constructible_v<_Callback, _Cb>)
: stop_callback(__private_tag{}, std::move(__st.__state_), std::forward(__cb)) { }
// same for const ref ctor
```
And at that point, you have only one reference to `__callback_fn_impl`, so you could also just initialize `__stop_callback_base` inline with a stateless lambda if that makes the code clearer because more localized.
================
Comment at: libcxx/include/__stop_token/stop_callback.h:90
+ _LIBCPP_HIDE_FROM_ABI static void __callback_fn_impl(__stop_callback_base* __cb_base) noexcept {
+ std::forward<_Callback>(static_cast<stop_callback*>(__cb_base)->__callback_)();
+ }
----------------
I would add a comment here explaining that the stop callback is supposed to only be called once because we can't *guarantee* that this isn't a rvalue ref.
================
Comment at: libcxx/include/__stop_token/stop_source.h:37
+
+ _LIBCPP_HIDE_FROM_ABI explicit stop_source(nostopstate_t) noexcept {}
+
----------------
================
Comment at: libcxx/include/__stop_token/stop_source.h:49-59
+ if (__ref_counted_state_ != __other.__ref_counted_state_) {
+ auto __tmp = std::move(__ref_counted_state_);
+ __ref_counted_state_ = __other.__ref_counted_state_;
+ if (__ref_counted_state_.__has_state()) {
+ __ref_counted_state_.__get()->__increment_stop_source_counter();
+ }
+ if (__tmp.__has_state()) {
----------------
Is this equivalent?
```
if (__ref_counted_state_ != __other.__ref_counted_state_) {
if (__ref_counted_state_)
__ref_counted_state_->__decrement_stop_source_counter();
__ref_counted_state_ = __other.__ref_counted_state_;
if (__ref_counted_state_)
__ref_counted_state_->__increment_stop_source_counter();
}
return *this;
```
Or even this?
```
// NOTE: We increment before we decrement to make sure that we don't hit 0 by mistake in case of self-assignment.
if (__other.__ref_counted_state_)
__other.__ref_counted_state_->__increment_stop_source_counter();
if (__ref_counted_state_)
__ref_counted_state_->__decrement_stop_source_counter();
__ref_counted_state_ = __other.__ref_counted_state_;
return *this;
```
================
Comment at: libcxx/include/__stop_token/stop_source.h:71
+ _LIBCPP_HIDE_FROM_ABI void swap(stop_source& __other) noexcept {
+ __ref_counted_state_.__swap(__other.__ref_counted_state_);
+ }
----------------
Should the `__intrusive_shared_ptr` be `std::` swappable? Probably?
================
Comment at: libcxx/include/__stop_token/stop_source.h:83-86
+ if (!__ref_counted_state_.__has_state()) {
+ return false;
+ }
+ return __ref_counted_state_.__get()->__request_stop();
----------------
================
Comment at: libcxx/include/__stop_token/stop_source.h:94
+private:
+ __ref_counted_stop_state __ref_counted_state_;
+};
----------------
With the shared-ptr proposed change, this would become something like `__intrusive_shared_ptr<__stop_state> __state_`, which IMO is easier to understand since we immediately understand "okay this thing is basically a shared ptr".
================
Comment at: libcxx/include/__stop_token/stop_state.h:42
+
+ // "stop_source counter" is not used for lifetime reference counter.
+ // When number of stop_source reaches 0, the remaining stop_tokens's
----------------
================
Comment at: libcxx/include/__stop_token/stop_state.h:46
+ //
+ // "callback list locked" bit is for implementing the pesudo-mutex to guard the operations
+ // on the callback list
----------------
================
Comment at: libcxx/include/__stop_token/stop_state.h:115
+
+ _LIBCPP_HIDE_FROM_ABI __callback_list_lock __try_lock_for_request_stop() noexcept {
+ // If it is already stop_requested, do not try to request stop or lock the list again.
----------------
Can this be a `private` member?
================
Comment at: libcxx/include/__stop_token/stop_state.h:226-228
+ _LIBCPP_HIDE_FROM_ABI __stop_state* __get() const noexcept { return __stop_state_; }
+
+ _LIBCPP_HIDE_FROM_ABI bool __has_state() const noexcept { return __stop_state_ != nullptr; }
----------------
Since this is really close to a shared pointer, I think it would make sense to define `operator*`, `operator->` and `explicit operator bool` instead of these two. And also probably `operator<=>(nullptr_t)` so you can compare explicitly against `nullptr` and have that be equivalent to `operator bool`.
================
Comment at: libcxx/include/__stop_token/stop_state.h:240-250
+ // the memory order for increment/decrement the counter is the same for shared_ptr
+ // increment is relaxed and decrement is acq_rel
+ _LIBCPP_HIDE_FROM_ABI void __increment_ref_count(__stop_state* __state) {
+ __state->__ref_count_.fetch_add(1, std::memory_order_relaxed);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void __decrement_ref_count(__stop_state* __state) {
----------------
I think it would be possible to extract that logic out. Then, `__ref_counted_stop_state` would become something like `__intrusive_shared_ptr` or `__basic_shared_ptr`. The idea would be to assume that the refcount is in the value you point to and to parameterize on the operation used to manipulate (increment/decrement) it.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D145183/new/
https://reviews.llvm.org/D145183
More information about the libcxx-commits
mailing list