Index: libcxx/include/semaphore =================================================================== --- libcxx/include/semaphore +++ libcxx/include/semaphore @@ -81,6 +81,8 @@ */ +#define _LIBCPP_SEMAPHORE_MAX (numeric_limits::max()) + class __atomic_semaphore_base { __atomic_base __a_; @@ -93,9 +95,14 @@ _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY void release(ptrdiff_t __update = 1) { - if(0 < __a_.fetch_add(__update, memory_order_release)) - ; - else if(__update > 1) + auto __old = __a_.fetch_add(__update, memory_order_release); + _LIBCPP_ASSERT_UNCATEGORIZED(__update <= _LIBCPP_SEMAPHORE_MAX - __old, "update is greater than the expected value"); + + if (0 < __old) + { + // Nothing to do + } + else if (__update > 1) __a_.notify_all(); else __a_.notify_one(); @@ -131,20 +138,30 @@ } }; -#define _LIBCPP_SEMAPHORE_MAX (numeric_limits::max()) - template class counting_semaphore { __atomic_semaphore_base __semaphore_; public: + static_assert(__least_max_value >= 0, "The least maximum value must be a positive number"); + static constexpr ptrdiff_t max() noexcept { return __least_max_value; } _LIBCPP_INLINE_VISIBILITY - constexpr explicit counting_semaphore(ptrdiff_t __count) : __semaphore_(__count) { } + constexpr explicit counting_semaphore(ptrdiff_t __count) : __semaphore_(__count) + { + _LIBCPP_ASSERT_UNCATEGORIZED( + __count >= 0, + "counting_semaphore::counting_semaphore(ptrdiff_t): counting_semaphore cannot be " + "initialized with a negative value"); + _LIBCPP_ASSERT_UNCATEGORIZED( + __count <= max(), + "counting_semaphore::counting_semaphore(ptrdiff_t): counting_semaphore cannot be " + "initialized with a value greater than max()"); + } ~counting_semaphore() = default; counting_semaphore(const counting_semaphore&) = delete; @@ -153,6 +170,7 @@ _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY void release(ptrdiff_t __update = 1) { + _LIBCPP_ASSERT_UNCATEGORIZED(__update >= 0, "counting_semaphore:release called with a negative value"); __semaphore_.release(__update); } _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY Index: libcxx/test/libcxx/thread/thread.semaphore/assert.ctor.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/thread/thread.semaphore/assert.ctor.pass.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// REQUIRES: availability-synchronization_library-missing +// XFAIL: target={{.+}}-apple-macosx10.{{13|15}} + +// REQUIRES: has-unix-headers +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 + +// + +// constexpr explicit counting_semaphore(ptrdiff_t __count); + +// Make sure that constructing counting_semaphore with a negative value triggers an assertion + +#include + +#include "check_assertion.h" + +int main(int, char**) { + { + TEST_LIBCPP_ASSERT_FAILURE( + [] { std::counting_semaphore<> s(-1); }(), + "counting_semaphore::counting_semaphore(ptrdiff_t): counting_semaphore cannot be " + "initialized with a negative value"); + } + // We can't check the precondition for max() because there's no value + // that would violate the precondition (in our implementation) + + return 0; +} Index: libcxx/test/libcxx/thread/thread.semaphore/assert.release.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/thread/thread.semaphore/assert.release.pass.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// REQUIRES: availability-synchronization_library-missing +// XFAIL: target={{.+}}-apple-macosx10.{{13|15}} + +// REQUIRES: has-unix-headers +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 + +// + +// void release(ptrdiff_t __update = 1); + +// Make sure that calling release with a negative value triggers or with a value +// greater than expected triggers an assertion + +#include + +#include "check_assertion.h" + +int main(int, char**) { + { + std::counting_semaphore<> s(2); + TEST_LIBCPP_ASSERT_FAILURE(s.release(-1), "counting_semaphore:release called with a negative value"); + } + + { + // Call release with an arbitrary larger than expected value + std::counting_semaphore<> s(2); + TEST_LIBCPP_ASSERT_FAILURE( + s.release(std::counting_semaphore<>::max()), "update is greater than the expected value"); + } + + return 0; +} Index: libcxx/test/std/thread/thread.semaphore/ctor.compile.pass.cpp =================================================================== --- libcxx/test/std/thread/thread.semaphore/ctor.compile.pass.cpp +++ libcxx/test/std/thread/thread.semaphore/ctor.compile.pass.cpp @@ -29,3 +29,9 @@ constinit std::binary_semaphore bs(1); constinit std::counting_semaphore<> cs(1); #endif + +void not_positive() { + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The least maximum value must be a positive number}} + std::counting_semaphore<-1> s(2); + (void)s; +}