Skip to content

Commit

Permalink
Minor fixes to await and await docs. (#555)
Browse files Browse the repository at this point in the history
* Improved docs for await and related operations
* Improved implementation of await
* Fixed several test cases for invoke_waiting.
  • Loading branch information
sean-parent authored Jan 25, 2025
1 parent d80dacd commit d1016a8
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 135 deletions.
28 changes: 26 additions & 2 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"
}
},
{
Expand All @@ -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",
Expand Down
50 changes: 6 additions & 44 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -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:

```
Expand Down Expand Up @@ -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.
\]
> \]
2 changes: 1 addition & 1 deletion docs/include/stlab/algorithm/reverse.hpp/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/include/stlab/concurrency/await.hpp/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
49 changes: 17 additions & 32 deletions docs/tools/docker-tools/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions docs/tools/docker-tools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
55 changes: 20 additions & 35 deletions include/stlab/concurrency/await.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,25 @@ 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 <class F>
auto invoke_waiting(F&& f) {
#if STLAB_TASK_SYSTEM(PORTABLE)
if (!detail::pts().wake()) detail::pts().add_thread();
#endif

std::forward<F>(f)();
return std::forward<F>(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 <class T>
auto await(future<T>&& x) -> T {
if (x.is_ready()) return std::move(x).get_ready(); // if ready, done
Expand All @@ -65,35 +70,15 @@ auto await(future<T>&& 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<std::mutex> 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<std::mutex> lock{m};
condition.wait(lock, [&] { return result.is_ready(); });
invoke_waiting([&] {
std::unique_lock<std::mutex> lock{m};
condition.wait(lock, [&] { return result.is_ready(); });
});
return std::move(result).get_ready();

#endif
}

/// Equivalent to `await(copy(x))`.

template <class T>
[[deprecated("implicit copy deprecated, use `await(std::move(f))` or `await(stlab::copy(f))`"
" instead.")]]
Expand Down Expand Up @@ -128,8 +113,10 @@ struct blocking_get_guarded {
}

auto wait_for(const std::chrono::nanoseconds& timeout) -> future<T> {
std::unique_lock<std::mutex> lock{_mutex};
_timed_out = !_condition.wait_for(lock, timeout, [&] { return _result.valid(); });
_timed_out = !invoke_waiting([&] {
std::unique_lock<std::mutex> lock{_mutex};
return _condition.wait_for(lock, timeout, [&] { return _result.valid(); });
});
return _timed_out ? future<T>{} : std::move(_result);
}
};
Expand All @@ -140,10 +127,6 @@ template <class T>
auto await_for(future<T>&& x, const std::chrono::nanoseconds& timeout) -> future<T> {
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<detail::blocking_get_guarded<T>>();

auto hold = std::move(x).recover(immediate_executor, [_p = stlab::make_weak_ptr(p)](auto&& r) {
Expand All @@ -155,6 +138,8 @@ auto await_for(future<T>&& x, const std::chrono::nanoseconds& timeout) -> future
return result.valid() ? std::move(result) : std::move(hold);
}

/// Equivalent to `await_for(copy(x), timeout)`.

template <class T>
[[deprecated("implicit copy deprecated, use `await_for(std::move(f), t)` or"
" `await_for(stlab::copy(f), t)` instead.")]]
Expand Down
9 changes: 6 additions & 3 deletions include/stlab/test/model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include <iostream>
#include <mutex>

#include <stlab/concurrency/await.hpp>

/**************************************************************************************************/

namespace stlab {
Expand All @@ -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<std::mutex> lock(_mutex);
while (count != remaining())
_condition.wait(lock);
stlab::invoke_waiting([&] {
std::unique_lock<std::mutex> lock(_mutex);
_condition.wait(lock, [&] { return count == remaining(); });
});
}

friend inline auto operator<<(std::ostream& out, const annotate_counters& x) -> std::ostream& {
Expand Down
Loading

0 comments on commit d1016a8

Please sign in to comment.