diff --git a/CMakePresets.json b/CMakePresets.json index 3e525db1..0ff60be0 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -21,6 +21,19 @@ "stlab.coverage": "OFF" } }, + { + "name": "debug-cpp20-tsan-ubsan-portable", + "description": "Ninja Debug Build", + "hidden": false, + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/ninja-cpp20-debug-thread-undefined", + "cacheVariables": { + "CMAKE_CXX_STANDARD": "20", + "CMAKE_BUILD_TYPE": "DEBUG", + "STLAB_TASK_SYSTEM": "portable", + "CMAKE_CXX_FLAGS": "-fsanitize=thread -fsanitize=undefined" + } + }, { "name": "ninja-cpp20-debug-thread-undefined", "description": "Ninja Debug Build", @@ -30,8 +43,7 @@ "cacheVariables": { "CMAKE_CXX_STANDARD": "20", "CMAKE_BUILD_TYPE": "DEBUG", - "CMAKE_CXX_FLAGS": "-fsanitize=thread -fsanitize=undefined", - "CMAKE_LINKER_FLAGS": "-fsanitize=thread -fsanitize=undefined" + "CMAKE_CXX_FLAGS": "-fsanitize=thread -fsanitize=undefined" } }, { @@ -45,6 +57,18 @@ "CMAKE_BUILD_TYPE": "DEBUG" } }, + { + "name": "xcode-cpp20-debug-portable", + "description": "", + "hidden": false, + "generator": "Xcode", + "binaryDir": "${sourceDir}/build/xcode-cpp20-debug-portable", + "cacheVariables": { + "CMAKE_CXX_STANDARD": "20", + "CMAKE_BUILD_TYPE": "DEBUG", + "STLAB_TASK_SYSTEM": "portable" + } + }, { "name": "ninja-cpp17-debug", "description": "Ninja Debug Build", diff --git a/docs/README.md b/docs/README.md index a8b31356..8ea4db3c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,54 +1,15 @@ # `stlab` Documentation -Pull requests for typos, examples, and other improvements are welcome. To file an issue, please use the [libraries repository](https://github.com/stlab/libraries). - -# Branch states - -\[ These are from the old site - FIX ME \] - -- **`master`:** [![master build](https://travis-ci.org/stlab/stlab.github.io.svg?branch=master)](https://travis-ci.org/stlab/stlab.github.io) [![master coverage](https://codecov.io/github/stlab/stlab.github.io/coverage.svg?branch=master)](https://codecov.io/gh/stlab/stlab.github.io/branch/master) - -- **`develop`:** [![develop build](https://travis-ci.org/stlab/stlab.github.io.svg?branch=develop)](https://travis-ci.org/stlab/stlab.github.io) - [![develop coverage](https://codecov.io/github/stlab/stlab.github.io/coverage.svg?branch=develop)](https://codecov.io/gh/stlab/stlab.github.io/branch/develop) - -# Building (Mac) - -You'll need Homebrew to make sure you have the most recent version of Ruby. - -## Setup - -1. Clone the repo -2. `brew install ruby` -3. `sudo gem install bundle jekyll github-pages liquid` - -Periodically run `gem update` and `bundle update` to make sure you have the latest jekyll tooling. - -## Building Docs +This site is available at [stlab.cc](https://stlab.cc). -``` -cd ./docs -bundle exec jekyll serve --watch -``` - -Documentation viewable at `localhost:4000` -Modifying sources should auto-regenerate the documentation +Pull requests for typos, examples, and other improvements are welcome. To file an issue, please use the [libraries repository](https://github.com/stlab/libraries). -## Building Examples +## Building the Documentation -1. `./build.sh` will download dependencies, build, and run all the `*.cpp` files in the `libraries` directory. +To run a local Jekyll server, see the instructions in the docker-tools [README](../tools/docker-tools/README.md). ## Running Hyde in Docker -The following directory structure is assumed. - -[ The longer term plan is to migrate the docs for the libraries into the library repo. We also need a plan for the structure of the build directory, a] - -``` -. # This directory stlab.github.io -../libraries # The stlab/libraries repo -../builds/hyde # The cmake build directory configures for building docs -``` - Configure the build as follows: ``` @@ -81,5 +42,6 @@ cd /mnt/host/libraries/docs ``` \[ this is from the old docs - need to update the docs and script. + > (or, simply `-u`) to generate the boilerplate for it. Then, fill in any fields marked as `__MISSING__`. Fields marked as `__OPTIONAL__` may be omitted. -\] +> \] diff --git a/docs/include/stlab/algorithm/reverse.hpp/index.md b/docs/include/stlab/algorithm/reverse.hpp/index.md index 3958978e..ee363bc1 100644 --- a/docs/include/stlab/algorithm/reverse.hpp/index.md +++ b/docs/include/stlab/algorithm/reverse.hpp/index.md @@ -3,7 +3,7 @@ layout: library title: stlab/algorithm/reverse.hpp hyde: owner: sean-parent - brief: __MISSING__ + brief: A collection of algorithms for reversing sequences. tags: - sourcefile library-type: sourcefile diff --git a/docs/include/stlab/concurrency/await.hpp/index.md b/docs/include/stlab/concurrency/await.hpp/index.md index 145b8455..7e79ba82 100644 --- a/docs/include/stlab/concurrency/await.hpp/index.md +++ b/docs/include/stlab/concurrency/await.hpp/index.md @@ -3,7 +3,7 @@ layout: library title: stlab/concurrency/await.hpp hyde: owner: sean-parent - brief: "Await provides utilities to synchronously await the value from a `future` and to notify the default executor that a task is waiting. Blocking calls are discouraged because they may lead to deadlocks or thread explosions.\n\nThere is a good presentation of the issues [here](https://youtu.be/Z86b3Rd09sE).\n" + brief: "Await provides utilities to await the value from a `future<>` synchronously and to notify the default executor that a task is waiting. Blocking calls are discouraged because they may lead to deadlocks or thread explosions.\n\nThere is a good presentation of the issues [here](https://youtu.be/Z86b3Rd09sE).\n" tags: - sourcefile library-type: sourcefile diff --git a/docs/tools/docker-tools/Dockerfile b/docs/tools/docker-tools/Dockerfile index f70bb975..f3d52add 100644 --- a/docs/tools/docker-tools/Dockerfile +++ b/docs/tools/docker-tools/Dockerfile @@ -5,17 +5,17 @@ FROM ubuntu:latest ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y \ - apt-utils \ - fswatch \ - g++ \ - git \ - libreadline-dev \ - make \ - nodejs \ - npm \ - rbenv \ - wget \ - zlib1g-dev + apt-utils \ + fswatch \ + g++ \ + git \ + libreadline-dev \ + make \ + nodejs \ + npm \ + rbenv \ + wget \ + zlib1g-dev RUN apt-get install -y libyaml-dev @@ -26,9 +26,6 @@ RUN export N_USE_XZ=0; n latest RUN npm install -g npm@latest RUN npm install -g browser-sync -###### Temporary ##### -RUN npm install -g sass-migrator - # Create a user "app" so everything is not running at root RUN useradd -ms /bin/bash app USER app @@ -47,12 +44,12 @@ RUN git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ru # Install ruby ARG RUBY_VERSION RUN if [ -z ${RUBY_VERSION+x} ]; then \ - rbenv install $(rbenv install -l | grep -v - | tail -1); \ - rbenv global $(rbenv install -l | grep -v - | tail -1); \ - else \ - rbenv install $RUBY_VERSION; \ - rbenv global $RUBY_VERSION; \ - fi + rbenv install $(rbenv install -l | grep -v - | tail -1); \ + rbenv global $(rbenv install -l | grep -v - | tail -1); \ + else \ + rbenv install $RUBY_VERSION; \ + rbenv global $RUBY_VERSION; \ + fi # Install bundler in global ruby RUN (eval "$(rbenv init -)"; gem install bundler) @@ -62,18 +59,6 @@ RUN (eval "$(rbenv init -)"; gem install bundler) USER app WORKDIR /home/app -# RUN mkdir ./install -# WORKDIR ./install -# COPY ./docs/Gemfile . -# COPY ./docs/Gemfile.lock . -# COPY ./docs/.ruby-version . - -# RUN (eval "$(rbenv init -)"; \ -# bundle config set frozen true; \ -# bundle install) - -# WORKDIR /home/app - EXPOSE 3000 3001 # Add version file last to avoid cache invalidation for minor releases diff --git a/docs/tools/docker-tools/README.md b/docs/tools/docker-tools/README.md index f2f32b78..ddccbd70 100644 --- a/docs/tools/docker-tools/README.md +++ b/docs/tools/docker-tools/README.md @@ -3,6 +3,7 @@ ## Setup ### Install Docker + If you don't already have Docker installed, [install Docker](https://docs.docker.com/get-docker/). ### Building the docker image diff --git a/include/stlab/concurrency/await.hpp b/include/stlab/concurrency/await.hpp index 643dc64c..16e0b078 100644 --- a/include/stlab/concurrency/await.hpp +++ b/include/stlab/concurrency/await.hpp @@ -36,8 +36,9 @@ inline namespace STLAB_VERSION_NAMESPACE() { /**************************************************************************************************/ /** - Assumes f _will wait_ and wakes or adds a thread to the thread pool (to the limit) before - invoking f. + Assumes `f` _will wait_ and wakes or adds a thread to the thread pool (to the limit) before + invoking `f`. If using a condition variable, wrap the duration of the mutex lock in `f` to avoid + deadlocks. */ template auto invoke_waiting(F&& f) { @@ -45,11 +46,15 @@ auto invoke_waiting(F&& f) { if (!detail::pts().wake()) detail::pts().add_thread(); #endif - std::forward(f)(); + return std::forward(f)(); } /**************************************************************************************************/ +/// Synchronously wait for the result `x`. If `x` resolves as an exception, the exception is +/// rethrown. When using the portable task system, an additional thread is added to the pool if no +/// threads are available and the maximum number of threads has not been reached. + template auto await(future&& x) -> T { if (x.is_ready()) return std::move(x).get_ready(); // if ready, done @@ -65,35 +70,15 @@ auto await(future&& x) -> T { condition.notify_one(); // must notify under lock } }); - -#if STLAB_TASK_SYSTEM(PORTABLE) - if (!detail::pts().wake()) detail::pts().add_thread(); - - /* - If no tasks are available we wait for one tick of the system clock and exponentially - back off on the wait as long as no tasks are available. - */ - - for (auto backoff{std::chrono::steady_clock::duration{std::chrono::milliseconds{1}}}; true; - backoff *= 2) { - { - std::unique_lock lock{m}; - if (condition.wait_for(lock, backoff, [&] { return result.is_ready(); })) { - return std::move(result).get_ready(); - } - } - detail::pts().wake(); // try to wake something to unstick. - } - -#else - - std::unique_lock lock{m}; - condition.wait(lock, [&] { return result.is_ready(); }); + invoke_waiting([&] { + std::unique_lock lock{m}; + condition.wait(lock, [&] { return result.is_ready(); }); + }); return std::move(result).get_ready(); - -#endif } +/// Equivalent to `await(copy(x))`. + template [[deprecated("implicit copy deprecated, use `await(std::move(f))` or `await(stlab::copy(f))`" " instead.")]] @@ -128,8 +113,10 @@ struct blocking_get_guarded { } auto wait_for(const std::chrono::nanoseconds& timeout) -> future { - std::unique_lock lock{_mutex}; - _timed_out = !_condition.wait_for(lock, timeout, [&] { return _result.valid(); }); + _timed_out = !invoke_waiting([&] { + std::unique_lock lock{_mutex}; + return _condition.wait_for(lock, timeout, [&] { return _result.valid(); }); + }); return _timed_out ? future{} : std::move(_result); } }; @@ -140,10 +127,6 @@ template auto await_for(future&& x, const std::chrono::nanoseconds& timeout) -> future { if (x.is_ready()) return std::move(x); -#if STLAB_TASK_SYSTEM(PORTABLE) - if (!detail::pts().wake()) detail::pts().add_thread(); -#endif - auto p = std::make_shared>(); auto hold = std::move(x).recover(immediate_executor, [_p = stlab::make_weak_ptr(p)](auto&& r) { @@ -155,6 +138,8 @@ auto await_for(future&& x, const std::chrono::nanoseconds& timeout) -> future return result.valid() ? std::move(result) : std::move(hold); } +/// Equivalent to `await_for(copy(x), timeout)`. + template [[deprecated("implicit copy deprecated, use `await_for(std::move(f), t)` or" " `await_for(stlab::copy(f), t)` instead.")]] diff --git a/include/stlab/test/model.hpp b/include/stlab/test/model.hpp index 38960ac0..5f5872bf 100644 --- a/include/stlab/test/model.hpp +++ b/include/stlab/test/model.hpp @@ -18,6 +18,8 @@ #include #include +#include + /**************************************************************************************************/ namespace stlab { @@ -41,9 +43,10 @@ struct annotate_counters { auto remaining() const -> std::size_t { return _copy_ctor + _move_ctor - _dtor + 1; } void wait(std::size_t count) { - std::unique_lock lock(_mutex); - while (count != remaining()) - _condition.wait(lock); + stlab::invoke_waiting([&] { + std::unique_lock lock(_mutex); + _condition.wait(lock, [&] { return count == remaining(); }); + }); } friend inline auto operator<<(std::ostream& out, const annotate_counters& x) -> std::ostream& { diff --git a/test/executor_test.cpp b/test/executor_test.cpp index 90030666..e3ae2dad 100644 --- a/test/executor_test.cpp +++ b/test/executor_test.cpp @@ -5,6 +5,7 @@ */ /**************************************************************************************************/ +#include #include #include @@ -106,7 +107,7 @@ BOOST_AUTO_TEST_CASE(all_high_prio_tasks_get_executed) { BOOST_AUTO_TEST_CASE(task_system_restarts_after_it_went_pending) { BOOST_TEST_MESSAGE("The task system restarts after it went to pending"); - atomic_bool done{false}; + bool done{false}; mutex m; condition_variable cv; @@ -120,10 +121,10 @@ BOOST_AUTO_TEST_CASE(task_system_restarts_after_it_went_pending) { }); { - unique_lock block{m}; - while (!done) { - cv.wait(block); - } + invoke_waiting([&] { + unique_lock block{m}; + cv.wait(block, [&] { return done; }); + }); } default_executor([&]() noexcept { @@ -136,10 +137,10 @@ BOOST_AUTO_TEST_CASE(task_system_restarts_after_it_went_pending) { }); { - unique_lock block{m}; - while (done) { - cv.wait(block); - } + invoke_waiting([&] { + unique_lock block{m}; + cv.wait(block, [&] { return !done; }); + }); } BOOST_REQUIRE(!done); @@ -286,9 +287,8 @@ BOOST_AUTO_TEST_CASE(MeasureTiming) { } }); - unique_lock lock{block}; - while (!done) - ready.wait(lock); + invoke_waiting( + unique_lock lock{block}; [&]{ ready.wait(lock, [&]{ return done; }); }); while (counter < 3 * iterations) { rest(); diff --git a/test/future_test_helper.hpp b/test/future_test_helper.hpp index a8733d46..4550d772 100644 --- a/test/future_test_helper.hpp +++ b/test/future_test_helper.hpp @@ -199,11 +199,11 @@ class blocking_policy { void set_context(thread_block_context* context) { _context = context; } void action() const { - lock_t lock(*_context->_mutex); - - while (!_context->_go || !_context->_may_proceed) { - _context->_thread_block.wait(lock); - } + stlab::invoke_waiting([&] { + lock_t lock(*_context->_mutex); + _context->_thread_block.wait(lock, + [&] { return _context->_go && _context->_may_proceed; }); + }); } };