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,12 @@ _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY void release(ptrdiff_t __update = 1) { - if(0 < __a_.fetch_add(__update, memory_order_release)) + 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) ; - else if(__update > 1) + else if (__update > 1) __a_.notify_all(); else __a_.notify_one(); @@ -131,8 +136,6 @@ } }; -#define _LIBCPP_SEMAPHORE_MAX (numeric_limits::max()) - template class counting_semaphore { @@ -144,7 +147,17 @@ } _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 +166,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,35 @@ +//===----------------------------------------------------------------------===// +// +// 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: has-unix-headers +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_HARDENED_MODE=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,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: has-unix-headers +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_HARDENED_MODE=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(s.max()), "update is greater than the expected value"); + } + + return 0; +}