mirror of
https://github.com/ETLCPP/etl.git
synced 2026-10-28 18:07:10 +08:00
Add future.h (#1618)
This commit is contained in:
parent
41bfe65dda
commit
d87cbd37f4
247
docs/multi-tasking/future.md
Normal file
247
docs/multi-tasking/future.md
Normal file
@ -0,0 +1,247 @@
|
||||
---
|
||||
title: "future / promise"
|
||||
---
|
||||
|
||||
{{< callout type="info">}}
|
||||
Header: `future.h`
|
||||
From: `20.50.0`
|
||||
{{< /callout >}}
|
||||
|
||||
A lightweight, heap-free `future` / `promise` pair for passing a single result from a producer (e.g. an ISR, a
|
||||
thread or an RPC response handler) to a consumer.
|
||||
|
||||
Unlike `std::future`/`std::promise`, the shared state is **not** allocated. It is an `etl::shared_state<T>` object
|
||||
whose storage and lifetime are managed by the user, for example as an element of a fixed-size pending-request table.
|
||||
`etl::future<T>` and `etl::promise<T>` are non-owning handles to it.
|
||||
|
||||
```cpp
|
||||
template <typename T>
|
||||
class shared_state;
|
||||
|
||||
template <typename T>
|
||||
class future;
|
||||
|
||||
template <typename T>
|
||||
class promise;
|
||||
```
|
||||
|
||||
All three are specialised for `void`, where there is no value, only a *ready* notification, and for references
|
||||
(`T&`), where a pointer to the referenced object is stored. `T` must not be an array type.
|
||||
|
||||
**Differences from the standard library**
|
||||
- `etl::future<T>` has the semantics of `std::shared_future<T>`: it is copyable, and `get()` does not consume the
|
||||
value, so it may be called more than once, by every copy. `get()` returns `const T&` (`R&` for `future<R&>`).
|
||||
- `etl::promise<T>` is, like `std::promise<T>`, move-only (C++11) or non-copyable (C++03).
|
||||
- `get()` does not block. Poll `is_ready()` or use an external notification.
|
||||
- There is no `set_exception()` and no *broken promise* notification. A consumer cannot distinguish a slow producer
|
||||
from one that has gone away.
|
||||
|
||||
**Availability**
|
||||
The header requires `etl::atomic_bool`. The classes are only defined when `ETL_HAS_ATOMIC` is `1`
|
||||
(see `etl::atomic`).
|
||||
|
||||
**Thread safety**
|
||||
`set_value()` and `is_ready()`/`get()` may be called from different threads or contexts, provided that at most
|
||||
**one** context calls `set_value()` and at most **one** context calls `get()`. The ready flag uses
|
||||
release/acquire ordering, so a value written by `set_value()` is visible after `is_ready()` returns `true`.
|
||||
`reset()` must not be called concurrently with any other member.
|
||||
|
||||
## shared_state
|
||||
|
||||
Holds the result and an atomic *ready* flag. Non-copyable.
|
||||
|
||||
```cpp
|
||||
shared_state()
|
||||
```
|
||||
**Description**
|
||||
Constructs a state that is not ready.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
~shared_state()
|
||||
```
|
||||
**Description**
|
||||
Destroys the stored value, if one has been set.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
void set_value(const T& value)
|
||||
void set_value(T&& value) // C++11
|
||||
void set_value(T& value) // shared_state<T&> only
|
||||
void set_value() // shared_state<void> only
|
||||
```
|
||||
**Description**
|
||||
Stores the value (or, for `shared_state<T&>`, the address of the referenced object) and marks the state as ready.
|
||||
Asserts `etl::promise_already_satisfied` if the state is already ready. Call `reset()` first to reuse the state.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
bool is_ready() const noexcept
|
||||
```
|
||||
**Description**
|
||||
Returns `true` if a value has been set.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
T& get()
|
||||
const T& get() const
|
||||
T& get() const // shared_state<T&> only
|
||||
```
|
||||
**Description**
|
||||
Returns a reference to the stored value.
|
||||
Not available for `shared_state<void>`.
|
||||
Asserts `etl::future_not_ready` if the state is not ready.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
void reset()
|
||||
```
|
||||
**Description**
|
||||
Destroys the stored value, if any, and marks the state as not ready, so that it can be reused.
|
||||
|
||||
## future
|
||||
|
||||
A non-owning handle to a `shared_state`. Copyable, with the semantics of `std::shared_future`.
|
||||
|
||||
```cpp
|
||||
future() noexcept
|
||||
```
|
||||
**Description**
|
||||
Constructs a future that is not associated with a shared state. `valid()` returns `false`.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
explicit future(etl::shared_state<T>& state) noexcept
|
||||
```
|
||||
**Description**
|
||||
Constructs a future associated with `state`.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
bool valid() const noexcept
|
||||
```
|
||||
**Description**
|
||||
Returns `true` if the future is associated with a shared state.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
bool is_ready() const noexcept
|
||||
```
|
||||
**Description**
|
||||
Returns `true` if the future is valid and the result is available.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
const T& get() const
|
||||
R& get() const // future<R&> only
|
||||
void get() const // future<void> only
|
||||
```
|
||||
**Description**
|
||||
Returns the result.
|
||||
Asserts `etl::future_no_state` if the future is not valid (default constructed, no shared state).
|
||||
Asserts `etl::future_not_ready` if the future is valid but the result is not yet available.
|
||||
Does not block. Poll `is_ready()` or use an external notification first.
|
||||
The value is not moved out and the future remains valid, so `get()` may be called more than once.
|
||||
|
||||
## promise
|
||||
|
||||
A non-owning handle to a `shared_state` that fulfils it. Move-only (C++11) or non-copyable (C++03), so that there is
|
||||
only one producer.
|
||||
|
||||
```cpp
|
||||
promise() noexcept
|
||||
```
|
||||
**Description**
|
||||
Constructs a promise that is not associated with a shared state. `valid()` returns `false`.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
explicit promise(etl::shared_state<T>& state) noexcept
|
||||
```
|
||||
**Description**
|
||||
Constructs a promise associated with `state`.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
promise(promise&& other) noexcept // C++11
|
||||
promise& operator=(promise&& other) noexcept // C++11
|
||||
```
|
||||
**Description**
|
||||
Transfers the association with the shared state. `other` is no longer valid.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
bool valid() const noexcept
|
||||
```
|
||||
**Description**
|
||||
Returns `true` if the promise is associated with a shared state.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
etl::future<T> get_future()
|
||||
```
|
||||
**Description**
|
||||
Returns a future associated with the same shared state.
|
||||
May be called more than once. All futures refer to the same state.
|
||||
Asserts `etl::future_no_state` if the promise is not valid.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
void set_value(const T& value)
|
||||
void set_value(T&& value) // C++11
|
||||
void set_value(R& value) // promise<R&> only
|
||||
void set_value() // promise<void> only
|
||||
```
|
||||
**Description**
|
||||
Fulfils the promise.
|
||||
Asserts `etl::future_no_state` if the promise is not valid.
|
||||
Asserts `etl::promise_already_satisfied` if the shared state is already ready.
|
||||
|
||||
## Errors
|
||||
|
||||
| Exception | Reason | Error text |
|
||||
| -------------------------------- | -------------------------------------------------------- | --------------------------- |
|
||||
| `etl::future_exception` | Base of all future/promise exceptions. | |
|
||||
| `etl::future_not_ready` | `get()` on a future or state that is not ready. | `future:not ready` |
|
||||
| `etl::promise_already_satisfied` | `set_value()` on a state that is already ready. | `promise:already satisfied` |
|
||||
| `etl::future_no_state` | Use of a `future` or `promise` that has no shared state. | `future:no state` |
|
||||
|
||||
`etl::future_exception` is derived from `etl::exception`.
|
||||
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
#include "etl/future.h"
|
||||
|
||||
etl::shared_state<int> state; // Owned by the application.
|
||||
|
||||
// Consumer
|
||||
etl::promise<int> promise(state);
|
||||
etl::future<int> future = promise.get_future();
|
||||
|
||||
// Producer (e.g. in an ISR or another thread)
|
||||
promise.set_value(42);
|
||||
|
||||
// Consumer
|
||||
if (future.is_ready())
|
||||
{
|
||||
int result = future.get(); // 42
|
||||
}
|
||||
|
||||
// Reuse the state for the next request.
|
||||
state.reset();
|
||||
```
|
||||
@ -112,5 +112,6 @@ SOFTWARE.
|
||||
#define ETL_FORMAT_FILE_ID "79"
|
||||
#define ETL_INPLACE_FUNCTION_FILE_ID "80"
|
||||
#define ETL_INTRUSIVE_AVL_TREE_FILE_ID "81"
|
||||
#define ETL_CYCLIC_VALUE_FILE_ID "82"
|
||||
#define ETL_FUTURE_FILE_ID "82"
|
||||
#define ETL_CYCLIC_VALUE_FILE_ID "83"
|
||||
#endif
|
||||
|
||||
603
include/etl/future.h
Normal file
603
include/etl/future.h
Normal file
@ -0,0 +1,603 @@
|
||||
///\file
|
||||
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
https://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2026 BMW AG
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions :
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef ETL_FUTURE_INCLUDED
|
||||
#define ETL_FUTURE_INCLUDED
|
||||
|
||||
#include "platform.h"
|
||||
#include "alignment.h"
|
||||
#include "atomic.h"
|
||||
#include "error_handler.h"
|
||||
#include "exception.h"
|
||||
#include "file_error_numbers.h"
|
||||
#include "memory.h"
|
||||
#include "nullptr.h"
|
||||
#include "placement_new.h"
|
||||
#include "static_assert.h"
|
||||
#include "type_traits.h"
|
||||
#include "utility.h"
|
||||
|
||||
#if ETL_HAS_ATOMIC
|
||||
|
||||
namespace etl
|
||||
{
|
||||
//***************************************************************************
|
||||
/// Exception types for future/promise.
|
||||
//***************************************************************************
|
||||
class future_exception : public etl::exception
|
||||
{
|
||||
public:
|
||||
|
||||
future_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
|
||||
: exception(reason_, file_name_, line_number_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class future_not_ready : public etl::future_exception
|
||||
{
|
||||
public:
|
||||
|
||||
future_not_ready(string_type file_name_, numeric_type line_number_)
|
||||
: future_exception(ETL_ERROR_TEXT("future:not ready", ETL_FUTURE_FILE_ID"A"), file_name_, line_number_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class promise_already_satisfied : public etl::future_exception
|
||||
{
|
||||
public:
|
||||
|
||||
promise_already_satisfied(string_type file_name_, numeric_type line_number_)
|
||||
: future_exception(ETL_ERROR_TEXT("promise:already satisfied", ETL_FUTURE_FILE_ID"B"), file_name_, line_number_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class future_no_state : public etl::future_exception
|
||||
{
|
||||
public:
|
||||
|
||||
future_no_state(string_type file_name_, numeric_type line_number_)
|
||||
: future_exception(ETL_ERROR_TEXT("future:no state", ETL_FUTURE_FILE_ID"C"), file_name_, line_number_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Shared state between a promise and a future.
|
||||
/// No heap allocation. The storage and lifetime of the shared_state are
|
||||
/// managed by the user, e.g. as an element of a fixed-size table.
|
||||
///
|
||||
/// Thread safety: set_value() and is_ready()/get() may be called from
|
||||
/// different threads provided that at most ONE thread calls set_value()
|
||||
/// and at most ONE thread calls get(). The atomic ready flag provides
|
||||
/// acquire/release ordering.
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
class shared_state
|
||||
{
|
||||
public:
|
||||
|
||||
ETL_STATIC_ASSERT(!etl::is_array<T>::value, "etl::shared_state: T must not be an array type");
|
||||
|
||||
shared_state()
|
||||
: ready_(false)
|
||||
{
|
||||
}
|
||||
|
||||
~shared_state()
|
||||
{
|
||||
if (is_ready())
|
||||
{
|
||||
ptr()->~T();
|
||||
}
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Store the result value and mark the state as ready.
|
||||
//*************************************************************************
|
||||
void set_value(const T& value)
|
||||
{
|
||||
ETL_ASSERT(!is_ready(), ETL_ERROR(promise_already_satisfied));
|
||||
::new (static_cast<void*>(&storage_)) T(value);
|
||||
ready_.store(true, etl::memory_order_release);
|
||||
}
|
||||
|
||||
#if ETL_USING_CPP11
|
||||
//*************************************************************************
|
||||
/// Store the result value (move) and mark the state as ready.
|
||||
//*************************************************************************
|
||||
void set_value(T&& value)
|
||||
{
|
||||
ETL_ASSERT(!is_ready(), ETL_ERROR(promise_already_satisfied));
|
||||
::new (static_cast<void*>(&storage_)) T(etl::move(value));
|
||||
ready_.store(true, etl::memory_order_release);
|
||||
}
|
||||
#endif
|
||||
|
||||
//*************************************************************************
|
||||
/// Check whether the result is available.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
bool is_ready() const ETL_NOEXCEPT
|
||||
{
|
||||
return ready_.load(etl::memory_order_acquire);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Get the stored value.
|
||||
/// Asserts etl::future_not_ready if the state is not ready.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
T& get()
|
||||
{
|
||||
ETL_ASSERT(is_ready(), ETL_ERROR(future_not_ready));
|
||||
return *ptr();
|
||||
}
|
||||
|
||||
ETL_NODISCARD
|
||||
const T& get() const
|
||||
{
|
||||
ETL_ASSERT(is_ready(), ETL_ERROR(future_not_ready));
|
||||
return *ptr();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Reset for reuse.
|
||||
//*************************************************************************
|
||||
void reset()
|
||||
{
|
||||
if (is_ready())
|
||||
{
|
||||
ptr()->~T();
|
||||
ready_.store(false, etl::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
//*************************************************************************
|
||||
/// Pointer to the object in the storage.
|
||||
/// Laundered, as the storage is reused after reset().
|
||||
//*************************************************************************
|
||||
T* ptr()
|
||||
{
|
||||
return etl::launder(reinterpret_cast<T*>(&storage_));
|
||||
}
|
||||
|
||||
const T* ptr() const
|
||||
{
|
||||
return etl::launder(reinterpret_cast<const T*>(&storage_));
|
||||
}
|
||||
|
||||
// Non-copyable.
|
||||
shared_state(const shared_state&) ETL_DELETE;
|
||||
shared_state& operator=(const shared_state&) ETL_DELETE;
|
||||
|
||||
typename etl::aligned_storage<sizeof(T), etl::alignment_of<T>::value>::type storage_;
|
||||
etl::atomic_bool ready_;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Specialisation for references — stores a pointer to the referenced object.
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
class shared_state<T&>
|
||||
{
|
||||
public:
|
||||
|
||||
shared_state()
|
||||
: p_value_(ETL_NULLPTR)
|
||||
, ready_(false)
|
||||
{
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Store the reference and mark the state as ready.
|
||||
//*************************************************************************
|
||||
void set_value(T& value)
|
||||
{
|
||||
ETL_ASSERT(!is_ready(), ETL_ERROR(promise_already_satisfied));
|
||||
p_value_ = etl::addressof(value);
|
||||
ready_.store(true, etl::memory_order_release);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Check whether the result is available.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
bool is_ready() const ETL_NOEXCEPT
|
||||
{
|
||||
return ready_.load(etl::memory_order_acquire);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Get the stored reference.
|
||||
/// Asserts etl::future_not_ready if the state is not ready.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
T& get() const
|
||||
{
|
||||
ETL_ASSERT(is_ready(), ETL_ERROR(future_not_ready));
|
||||
return *p_value_;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Reset for reuse.
|
||||
//*************************************************************************
|
||||
void reset()
|
||||
{
|
||||
ready_.store(false, etl::memory_order_release);
|
||||
p_value_ = ETL_NULLPTR;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// Non-copyable.
|
||||
shared_state(const shared_state&) ETL_DELETE;
|
||||
shared_state& operator=(const shared_state&) ETL_DELETE;
|
||||
|
||||
T* p_value_;
|
||||
etl::atomic_bool ready_;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Specialisation for void — no value storage, just a ready flag.
|
||||
//***************************************************************************
|
||||
template <>
|
||||
class shared_state<void>
|
||||
{
|
||||
public:
|
||||
|
||||
shared_state()
|
||||
: ready_(false)
|
||||
{
|
||||
}
|
||||
|
||||
void set_value()
|
||||
{
|
||||
ETL_ASSERT(!is_ready(), ETL_ERROR(promise_already_satisfied));
|
||||
ready_.store(true, etl::memory_order_release);
|
||||
}
|
||||
|
||||
ETL_NODISCARD
|
||||
bool is_ready() const ETL_NOEXCEPT
|
||||
{
|
||||
return ready_.load(etl::memory_order_acquire);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
ready_.store(false, etl::memory_order_release);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
shared_state(const shared_state&) ETL_DELETE;
|
||||
shared_state& operator=(const shared_state&) ETL_DELETE;
|
||||
|
||||
etl::atomic_bool ready_;
|
||||
};
|
||||
|
||||
namespace private_future
|
||||
{
|
||||
//*************************************************************************
|
||||
/// The result type of future<T>::get().
|
||||
/// const T& for values, R& for T = R&.
|
||||
/// (C++03 has no reference collapsing, so const T& cannot be used for both.)
|
||||
//*************************************************************************
|
||||
template <typename T>
|
||||
struct future_get_result
|
||||
{
|
||||
typedef const T& type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct future_get_result<T&>
|
||||
{
|
||||
typedef T& type;
|
||||
};
|
||||
} // namespace private_future
|
||||
|
||||
//***************************************************************************
|
||||
/// A lightweight future — a non-owning handle to a shared_state<T>.
|
||||
/// The shared_state lifetime must be managed externally.
|
||||
/// Has the semantics of std::shared_future: it is copyable, get() does not
|
||||
/// consume the value and may be called more than once, by every copy.
|
||||
/// get() returns const T&, or R& for T = R&.
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
class future
|
||||
{
|
||||
public:
|
||||
|
||||
future() ETL_NOEXCEPT
|
||||
: state_(ETL_NULLPTR)
|
||||
{
|
||||
}
|
||||
|
||||
explicit future(shared_state<T>& state) ETL_NOEXCEPT
|
||||
: state_(&state)
|
||||
{
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Returns true when the result is available.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
bool is_ready() const ETL_NOEXCEPT
|
||||
{
|
||||
return (state_ != ETL_NULLPTR) && state_->is_ready();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Returns true if this future is associated with a shared state.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
bool valid() const ETL_NOEXCEPT
|
||||
{
|
||||
return state_ != ETL_NULLPTR;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Get the result.
|
||||
/// Asserts etl::future_no_state if not valid (no shared state).
|
||||
/// Asserts etl::future_not_ready if valid but the result is not ready.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
typename private_future::future_get_result<T>::type get() const
|
||||
{
|
||||
ETL_ASSERT(valid(), ETL_ERROR(future_no_state));
|
||||
ETL_ASSERT(!valid() || state_->is_ready(), ETL_ERROR(future_not_ready));
|
||||
return state_->get();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
shared_state<T>* state_;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Specialisation for void.
|
||||
//***************************************************************************
|
||||
template <>
|
||||
class future<void>
|
||||
{
|
||||
public:
|
||||
|
||||
future() ETL_NOEXCEPT
|
||||
: state_(ETL_NULLPTR)
|
||||
{
|
||||
}
|
||||
|
||||
explicit future(shared_state<void>& state) ETL_NOEXCEPT
|
||||
: state_(&state)
|
||||
{
|
||||
}
|
||||
|
||||
ETL_NODISCARD
|
||||
bool is_ready() const ETL_NOEXCEPT
|
||||
{
|
||||
return (state_ != ETL_NULLPTR) && state_->is_ready();
|
||||
}
|
||||
|
||||
ETL_NODISCARD
|
||||
bool valid() const ETL_NOEXCEPT
|
||||
{
|
||||
return state_ != ETL_NULLPTR;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Asserts etl::future_no_state if not valid (no shared state).
|
||||
/// Asserts etl::future_not_ready if valid but the result is not ready.
|
||||
//*************************************************************************
|
||||
void get() const
|
||||
{
|
||||
ETL_ASSERT(valid(), ETL_ERROR(future_no_state));
|
||||
ETL_ASSERT(!valid() || state_->is_ready(), ETL_ERROR(future_not_ready));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
shared_state<void>* state_;
|
||||
};
|
||||
|
||||
namespace private_future
|
||||
{
|
||||
//*************************************************************************
|
||||
/// Common base of the promise specialisations.
|
||||
/// Like std::promise, a promise is move-only (C++11), so there is only
|
||||
/// ever one producer. Non-copyable in C++03.
|
||||
//*************************************************************************
|
||||
template <typename T>
|
||||
class promise_base
|
||||
{
|
||||
public:
|
||||
|
||||
//***********************************************************************
|
||||
/// Returns true if the promise is associated with a shared state.
|
||||
//***********************************************************************
|
||||
ETL_NODISCARD
|
||||
bool valid() const ETL_NOEXCEPT
|
||||
{
|
||||
return state_ != ETL_NULLPTR;
|
||||
}
|
||||
|
||||
//***********************************************************************
|
||||
/// Get the associated future.
|
||||
/// Asserts etl::future_no_state if the promise is not valid.
|
||||
//***********************************************************************
|
||||
ETL_NODISCARD
|
||||
etl::future<T> get_future()
|
||||
{
|
||||
ETL_ASSERT(valid(), ETL_ERROR(future_no_state));
|
||||
return etl::future<T>(*state_);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
promise_base() ETL_NOEXCEPT
|
||||
: state_(ETL_NULLPTR)
|
||||
{
|
||||
}
|
||||
|
||||
explicit promise_base(etl::shared_state<T>& state) ETL_NOEXCEPT
|
||||
: state_(&state)
|
||||
{
|
||||
}
|
||||
|
||||
#if ETL_USING_CPP11
|
||||
//***********************************************************************
|
||||
/// Move constructor. The moved from promise is no longer valid.
|
||||
//***********************************************************************
|
||||
promise_base(promise_base&& other) ETL_NOEXCEPT
|
||||
: state_(other.state_)
|
||||
{
|
||||
other.state_ = ETL_NULLPTR;
|
||||
}
|
||||
|
||||
//***********************************************************************
|
||||
/// Move assignment. The moved from promise is no longer valid.
|
||||
//***********************************************************************
|
||||
promise_base& operator=(promise_base&& other) ETL_NOEXCEPT
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
state_ = other.state_;
|
||||
other.state_ = ETL_NULLPTR;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
etl::shared_state<T>* state_;
|
||||
|
||||
private:
|
||||
|
||||
// Non-copyable.
|
||||
promise_base(const promise_base&) ETL_DELETE;
|
||||
promise_base& operator=(const promise_base&) ETL_DELETE;
|
||||
};
|
||||
} // namespace private_future
|
||||
|
||||
//***************************************************************************
|
||||
/// A lightweight promise — a non-owning handle that can set the value
|
||||
/// in a shared_state<T>.
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
class promise : public private_future::promise_base<T>
|
||||
{
|
||||
public:
|
||||
|
||||
promise() ETL_NOEXCEPT
|
||||
: private_future::promise_base<T>()
|
||||
{
|
||||
}
|
||||
|
||||
explicit promise(etl::shared_state<T>& state) ETL_NOEXCEPT
|
||||
: private_future::promise_base<T>(state)
|
||||
{
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Set the value (fulfil the promise).
|
||||
//*************************************************************************
|
||||
void set_value(const T& value)
|
||||
{
|
||||
ETL_ASSERT(this->valid(), ETL_ERROR(future_no_state));
|
||||
this->state_->set_value(value);
|
||||
}
|
||||
|
||||
#if ETL_USING_CPP11
|
||||
void set_value(T&& value)
|
||||
{
|
||||
ETL_ASSERT(this->valid(), ETL_ERROR(future_no_state));
|
||||
this->state_->set_value(etl::move(value));
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Specialisation for references.
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
class promise<T&> : public private_future::promise_base<T&>
|
||||
{
|
||||
public:
|
||||
|
||||
promise() ETL_NOEXCEPT
|
||||
: private_future::promise_base<T&>()
|
||||
{
|
||||
}
|
||||
|
||||
explicit promise(etl::shared_state<T&>& state) ETL_NOEXCEPT
|
||||
: private_future::promise_base<T&>(state)
|
||||
{
|
||||
}
|
||||
|
||||
void set_value(T& value)
|
||||
{
|
||||
ETL_ASSERT(this->valid(), ETL_ERROR(future_no_state));
|
||||
this->state_->set_value(value);
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Specialisation for void.
|
||||
//***************************************************************************
|
||||
template <>
|
||||
class promise<void> : public private_future::promise_base<void>
|
||||
{
|
||||
public:
|
||||
|
||||
promise() ETL_NOEXCEPT
|
||||
: private_future::promise_base<void>()
|
||||
{
|
||||
}
|
||||
|
||||
explicit promise(etl::shared_state<void>& state) ETL_NOEXCEPT
|
||||
: private_future::promise_base<void>(state)
|
||||
{
|
||||
}
|
||||
|
||||
void set_value()
|
||||
{
|
||||
ETL_ASSERT(this->valid(), ETL_ERROR(future_no_state));
|
||||
this->state_->set_value();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace etl
|
||||
|
||||
#endif // ETL_HAS_ATOMIC
|
||||
|
||||
#endif // ETL_FUTURE_INCLUDED
|
||||
@ -196,6 +196,7 @@ add_executable(etl_tests
|
||||
test_function.cpp
|
||||
test_functional.cpp
|
||||
test_function_traits.cpp
|
||||
test_future.cpp
|
||||
test_gamma.cpp
|
||||
test_hash.cpp
|
||||
test_hfsm.cpp
|
||||
|
||||
@ -184,6 +184,7 @@ etl_test_sources = files(
|
||||
'test_function.cpp',
|
||||
'test_function_traits.cpp',
|
||||
'test_functional.cpp',
|
||||
'test_future.cpp',
|
||||
'test_gamma.cpp',
|
||||
'test_hash.cpp',
|
||||
'test_hfsm.cpp',
|
||||
|
||||
@ -234,6 +234,7 @@ target_sources(tests PRIVATE
|
||||
function.h.t.cpp
|
||||
functional.h.t.cpp
|
||||
function_traits.h.t.cpp
|
||||
future.h.t.cpp
|
||||
gamma.h.t.cpp
|
||||
gcd.h.t.cpp
|
||||
generic_pool.h.t.cpp
|
||||
|
||||
29
test/syntax_check/future.h.t.cpp
Normal file
29
test/syntax_check/future.h.t.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
https://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2026 BMW AG
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions :
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#include <etl/future.h>
|
||||
628
test/test_future.cpp
Normal file
628
test/test_future.cpp
Normal file
@ -0,0 +1,628 @@
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
https://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2026 BMW AG
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions :
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#include "unit_test_framework.h"
|
||||
|
||||
#include "etl/future.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#if ETL_HAS_ATOMIC
|
||||
|
||||
namespace
|
||||
{
|
||||
//---------------------------------------------------------------------------
|
||||
// A simple struct to test non-primitive types in future/promise.
|
||||
//---------------------------------------------------------------------------
|
||||
struct Point
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
|
||||
bool operator==(const Point& other) const
|
||||
{
|
||||
return x == other.x && y == other.y;
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Counts the number of live objects, to check construction/destruction.
|
||||
//---------------------------------------------------------------------------
|
||||
struct Counted
|
||||
{
|
||||
static int live;
|
||||
|
||||
explicit Counted(int value_)
|
||||
: value(value_)
|
||||
{
|
||||
++live;
|
||||
}
|
||||
|
||||
Counted(const Counted& other)
|
||||
: value(other.value)
|
||||
{
|
||||
++live;
|
||||
}
|
||||
|
||||
~Counted()
|
||||
{
|
||||
--live;
|
||||
}
|
||||
|
||||
int value;
|
||||
};
|
||||
|
||||
int Counted::live = 0;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// A move-only type that records whether it has been moved from.
|
||||
//---------------------------------------------------------------------------
|
||||
struct MoveOnly
|
||||
{
|
||||
explicit MoveOnly(int value_)
|
||||
: value(value_)
|
||||
, moved_from(false)
|
||||
{
|
||||
}
|
||||
|
||||
MoveOnly(MoveOnly&& other)
|
||||
: value(other.value)
|
||||
, moved_from(false)
|
||||
{
|
||||
other.moved_from = true;
|
||||
}
|
||||
|
||||
MoveOnly(const MoveOnly&) = delete;
|
||||
MoveOnly& operator=(const MoveOnly&) = delete;
|
||||
|
||||
int value;
|
||||
bool moved_from;
|
||||
};
|
||||
|
||||
SUITE(test_future)
|
||||
{
|
||||
//*************************************************************************
|
||||
// shared_state<T> tests
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_shared_state_int_default_not_ready)
|
||||
{
|
||||
etl::shared_state<int> state;
|
||||
CHECK(!state.is_ready());
|
||||
}
|
||||
|
||||
TEST(test_shared_state_int_set_value_becomes_ready)
|
||||
{
|
||||
etl::shared_state<int> state;
|
||||
state.set_value(42);
|
||||
CHECK(state.is_ready());
|
||||
CHECK_EQUAL(42, state.get());
|
||||
}
|
||||
|
||||
TEST(test_shared_state_int_get_const)
|
||||
{
|
||||
etl::shared_state<int> state;
|
||||
state.set_value(77);
|
||||
const etl::shared_state<int>& cref = state;
|
||||
CHECK_EQUAL(77, cref.get());
|
||||
}
|
||||
|
||||
TEST(test_shared_state_int_reset)
|
||||
{
|
||||
etl::shared_state<int> state;
|
||||
state.set_value(10);
|
||||
CHECK(state.is_ready());
|
||||
state.reset();
|
||||
CHECK(!state.is_ready());
|
||||
}
|
||||
|
||||
TEST(test_shared_state_int_reset_when_not_ready)
|
||||
{
|
||||
etl::shared_state<int> state;
|
||||
// Should be safe to reset even when not ready.
|
||||
state.reset();
|
||||
CHECK(!state.is_ready());
|
||||
}
|
||||
|
||||
TEST(test_shared_state_double)
|
||||
{
|
||||
etl::shared_state<double> state;
|
||||
state.set_value(3.14);
|
||||
CHECK(state.is_ready());
|
||||
CHECK_CLOSE(3.14, state.get(), 0.001);
|
||||
}
|
||||
|
||||
TEST(test_shared_state_struct)
|
||||
{
|
||||
etl::shared_state<Point> state;
|
||||
Point p{10, 20};
|
||||
state.set_value(p);
|
||||
CHECK(state.is_ready());
|
||||
CHECK_EQUAL(10, state.get().x);
|
||||
CHECK_EQUAL(20, state.get().y);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// shared_state<void> tests
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_shared_state_void_default_not_ready)
|
||||
{
|
||||
etl::shared_state<void> state;
|
||||
CHECK(!state.is_ready());
|
||||
}
|
||||
|
||||
TEST(test_shared_state_void_set_value_becomes_ready)
|
||||
{
|
||||
etl::shared_state<void> state;
|
||||
state.set_value();
|
||||
CHECK(state.is_ready());
|
||||
}
|
||||
|
||||
TEST(test_shared_state_void_reset)
|
||||
{
|
||||
etl::shared_state<void> state;
|
||||
state.set_value();
|
||||
CHECK(state.is_ready());
|
||||
state.reset();
|
||||
CHECK(!state.is_ready());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// future<T> tests
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_future_int_default_invalid)
|
||||
{
|
||||
etl::future<int> f;
|
||||
CHECK(!f.valid());
|
||||
CHECK(!f.is_ready());
|
||||
}
|
||||
|
||||
TEST(test_future_int_from_state_valid_not_ready)
|
||||
{
|
||||
etl::shared_state<int> state;
|
||||
etl::future<int> f(state);
|
||||
CHECK(f.valid());
|
||||
CHECK(!f.is_ready());
|
||||
}
|
||||
|
||||
TEST(test_future_int_becomes_ready_after_set)
|
||||
{
|
||||
etl::shared_state<int> state;
|
||||
etl::future<int> f(state);
|
||||
state.set_value(55);
|
||||
CHECK(f.is_ready());
|
||||
CHECK_EQUAL(55, f.get());
|
||||
}
|
||||
|
||||
TEST(test_future_int_const_get)
|
||||
{
|
||||
etl::shared_state<int> state;
|
||||
state.set_value(88);
|
||||
const etl::future<int> f(state);
|
||||
CHECK(f.is_ready());
|
||||
CHECK_EQUAL(88, f.get());
|
||||
}
|
||||
|
||||
TEST(test_future_double)
|
||||
{
|
||||
etl::shared_state<double> state;
|
||||
etl::future<double> f(state);
|
||||
state.set_value(2.718);
|
||||
CHECK(f.is_ready());
|
||||
CHECK_CLOSE(2.718, f.get(), 0.001);
|
||||
}
|
||||
|
||||
TEST(test_future_struct)
|
||||
{
|
||||
etl::shared_state<Point> state;
|
||||
etl::future<Point> f(state);
|
||||
state.set_value(Point{3, 4});
|
||||
CHECK(f.is_ready());
|
||||
CHECK_EQUAL(3, f.get().x);
|
||||
CHECK_EQUAL(4, f.get().y);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// future<void> tests
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_future_void_default_invalid)
|
||||
{
|
||||
etl::future<void> f;
|
||||
CHECK(!f.valid());
|
||||
CHECK(!f.is_ready());
|
||||
}
|
||||
|
||||
TEST(test_future_void_from_state)
|
||||
{
|
||||
etl::shared_state<void> state;
|
||||
etl::future<void> f(state);
|
||||
CHECK(f.valid());
|
||||
CHECK(!f.is_ready());
|
||||
state.set_value();
|
||||
CHECK(f.is_ready());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// promise<T> tests
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_promise_int_default_invalid)
|
||||
{
|
||||
etl::promise<int> p;
|
||||
CHECK(!p.valid());
|
||||
}
|
||||
|
||||
TEST(test_promise_int_set_value_fulfils_future)
|
||||
{
|
||||
etl::shared_state<int> state;
|
||||
etl::promise<int> p(state);
|
||||
etl::future<int> f = p.get_future();
|
||||
CHECK(f.valid());
|
||||
CHECK(!f.is_ready());
|
||||
p.set_value(123);
|
||||
CHECK(f.is_ready());
|
||||
CHECK_EQUAL(123, f.get());
|
||||
}
|
||||
|
||||
TEST(test_promise_double)
|
||||
{
|
||||
etl::shared_state<double> state;
|
||||
etl::promise<double> p(state);
|
||||
etl::future<double> f = p.get_future();
|
||||
p.set_value(1.41421);
|
||||
CHECK(f.is_ready());
|
||||
CHECK_CLOSE(1.41421, f.get(), 0.00001);
|
||||
}
|
||||
|
||||
TEST(test_promise_struct)
|
||||
{
|
||||
etl::shared_state<Point> state;
|
||||
etl::promise<Point> p(state);
|
||||
etl::future<Point> f = p.get_future();
|
||||
p.set_value(Point{-5, 7});
|
||||
CHECK(f.is_ready());
|
||||
CHECK_EQUAL(-5, f.get().x);
|
||||
CHECK_EQUAL(7, f.get().y);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// promise<void> tests
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_promise_void_default_invalid)
|
||||
{
|
||||
etl::promise<void> p;
|
||||
CHECK(!p.valid());
|
||||
}
|
||||
|
||||
TEST(test_promise_void_set_value_fulfils_future)
|
||||
{
|
||||
etl::shared_state<void> state;
|
||||
etl::promise<void> p(state);
|
||||
etl::future<void> f = p.get_future();
|
||||
CHECK(f.valid());
|
||||
CHECK(!f.is_ready());
|
||||
p.set_value();
|
||||
CHECK(f.is_ready());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Integration / edge-case tests
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_multiple_futures_same_state)
|
||||
{
|
||||
// Two futures can observe the same shared_state.
|
||||
etl::shared_state<int> state;
|
||||
etl::future<int> f1(state);
|
||||
etl::future<int> f2(state);
|
||||
CHECK(!f1.is_ready());
|
||||
CHECK(!f2.is_ready());
|
||||
state.set_value(999);
|
||||
CHECK(f1.is_ready());
|
||||
CHECK(f2.is_ready());
|
||||
CHECK_EQUAL(999, f1.get());
|
||||
CHECK_EQUAL(999, f2.get());
|
||||
}
|
||||
|
||||
TEST(test_shared_state_reuse_after_reset)
|
||||
{
|
||||
etl::shared_state<int> state;
|
||||
etl::promise<int> p(state);
|
||||
etl::future<int> f = p.get_future();
|
||||
p.set_value(1);
|
||||
CHECK_EQUAL(1, f.get());
|
||||
|
||||
// Reset and reuse.
|
||||
state.reset();
|
||||
CHECK(!f.is_ready());
|
||||
|
||||
// Set a different value.
|
||||
state.set_value(2);
|
||||
CHECK(f.is_ready());
|
||||
CHECK_EQUAL(2, f.get());
|
||||
}
|
||||
|
||||
TEST(test_move_semantics_set_value)
|
||||
{
|
||||
etl::shared_state<MoveOnly> state;
|
||||
etl::promise<MoveOnly> p(state);
|
||||
etl::future<MoveOnly> f = p.get_future();
|
||||
MoveOnly value(50);
|
||||
p.set_value(etl::move(value));
|
||||
CHECK(value.moved_from);
|
||||
CHECK(f.is_ready());
|
||||
CHECK_EQUAL(50, f.get().value);
|
||||
CHECK(!f.get().moved_from);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Lifetime of the stored value
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_shared_state_destroys_value)
|
||||
{
|
||||
Counted::live = 0;
|
||||
{
|
||||
etl::shared_state<Counted> state;
|
||||
CHECK_EQUAL(0, Counted::live);
|
||||
state.set_value(Counted(1));
|
||||
CHECK_EQUAL(1, Counted::live);
|
||||
}
|
||||
CHECK_EQUAL(0, Counted::live);
|
||||
}
|
||||
|
||||
TEST(test_shared_state_not_ready_destroys_nothing)
|
||||
{
|
||||
Counted::live = 0;
|
||||
{
|
||||
etl::shared_state<Counted> state;
|
||||
}
|
||||
CHECK_EQUAL(0, Counted::live);
|
||||
}
|
||||
|
||||
TEST(test_shared_state_reset_destroys_value_and_reuse)
|
||||
{
|
||||
Counted::live = 0;
|
||||
{
|
||||
etl::shared_state<Counted> state;
|
||||
state.set_value(Counted(1));
|
||||
CHECK_EQUAL(1, Counted::live);
|
||||
state.reset();
|
||||
CHECK_EQUAL(0, Counted::live);
|
||||
CHECK(!state.is_ready());
|
||||
state.set_value(Counted(2));
|
||||
CHECK_EQUAL(1, Counted::live);
|
||||
CHECK_EQUAL(2, state.get().value);
|
||||
}
|
||||
CHECK_EQUAL(0, Counted::live);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Reference specialisations
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_reference)
|
||||
{
|
||||
int value = 1;
|
||||
etl::shared_state<int&> state;
|
||||
etl::promise<int&> p(state);
|
||||
etl::future<int&> f = p.get_future();
|
||||
CHECK(!f.is_ready());
|
||||
p.set_value(value);
|
||||
CHECK(f.is_ready());
|
||||
CHECK(&f.get() == &value);
|
||||
f.get() = 2;
|
||||
CHECK_EQUAL(2, value);
|
||||
|
||||
state.reset();
|
||||
CHECK(!f.is_ready());
|
||||
|
||||
int other = 3;
|
||||
p.set_value(other);
|
||||
CHECK(&f.get() == &other);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Types
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_future_get_return_types)
|
||||
{
|
||||
CHECK((std::is_same<const int&, decltype(etl::declval<etl::future<int>&>().get())>::value));
|
||||
CHECK((std::is_same<const int&, decltype(etl::declval<const etl::future<int>&>().get())>::value));
|
||||
CHECK((std::is_same<int&, decltype(etl::declval<etl::future<int&>&>().get())>::value));
|
||||
CHECK((std::is_same<void, decltype(etl::declval<etl::future<void>&>().get())>::value));
|
||||
}
|
||||
|
||||
TEST(test_future_is_copyable)
|
||||
{
|
||||
CHECK(std::is_copy_constructible<etl::future<int>>::value);
|
||||
CHECK(std::is_copy_assignable<etl::future<int>>::value);
|
||||
|
||||
etl::shared_state<int> state;
|
||||
etl::future<int> f1(state);
|
||||
etl::future<int> f2(f1);
|
||||
state.set_value(5);
|
||||
CHECK_EQUAL(5, f1.get());
|
||||
CHECK_EQUAL(5, f2.get());
|
||||
CHECK_EQUAL(5, f2.get()); // get() does not consume the value.
|
||||
}
|
||||
|
||||
TEST(test_promise_is_move_only)
|
||||
{
|
||||
CHECK(!std::is_copy_constructible<etl::promise<int>>::value);
|
||||
CHECK(!std::is_copy_assignable<etl::promise<int>>::value);
|
||||
CHECK(std::is_nothrow_move_constructible<etl::promise<int>>::value);
|
||||
CHECK(std::is_nothrow_move_assignable<etl::promise<int>>::value);
|
||||
|
||||
CHECK(!std::is_copy_constructible<etl::promise<int&>>::value);
|
||||
CHECK(std::is_nothrow_move_constructible<etl::promise<int&>>::value);
|
||||
|
||||
CHECK(!std::is_copy_constructible<etl::promise<void>>::value);
|
||||
CHECK(std::is_nothrow_move_constructible<etl::promise<void>>::value);
|
||||
}
|
||||
|
||||
TEST(test_promise_move_construct)
|
||||
{
|
||||
etl::shared_state<int> state;
|
||||
etl::promise<int> p1(state);
|
||||
etl::promise<int> p2(etl::move(p1));
|
||||
CHECK(!p1.valid());
|
||||
CHECK(p2.valid());
|
||||
|
||||
etl::future<int> f = p2.get_future();
|
||||
p2.set_value(7);
|
||||
CHECK_EQUAL(7, f.get());
|
||||
}
|
||||
|
||||
TEST(test_promise_move_assign)
|
||||
{
|
||||
etl::shared_state<void> state;
|
||||
etl::promise<void> p1(state);
|
||||
etl::promise<void> p2;
|
||||
CHECK(!p2.valid());
|
||||
p2 = etl::move(p1);
|
||||
CHECK(!p1.valid());
|
||||
CHECK(p2.valid());
|
||||
|
||||
etl::future<void> f = p2.get_future();
|
||||
p2.set_value();
|
||||
CHECK(f.is_ready());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Errors
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_shared_state_get_not_ready)
|
||||
{
|
||||
etl::shared_state<int> state;
|
||||
const etl::shared_state<int>& cstate = state;
|
||||
etl::shared_state<int&> rstate;
|
||||
CHECK_THROW((void)state.get(), etl::future_not_ready);
|
||||
CHECK_THROW((void)cstate.get(), etl::future_not_ready);
|
||||
CHECK_THROW((void)rstate.get(), etl::future_not_ready);
|
||||
}
|
||||
|
||||
TEST(test_shared_state_set_value_twice)
|
||||
{
|
||||
etl::shared_state<int> state;
|
||||
state.set_value(1);
|
||||
CHECK_THROW(state.set_value(2), etl::promise_already_satisfied);
|
||||
CHECK_EQUAL(1, state.get());
|
||||
|
||||
int a = 1;
|
||||
int b = 2;
|
||||
etl::shared_state<int&> rstate;
|
||||
rstate.set_value(a);
|
||||
CHECK_THROW(rstate.set_value(b), etl::promise_already_satisfied);
|
||||
CHECK(&rstate.get() == &a);
|
||||
|
||||
etl::shared_state<void> vstate;
|
||||
vstate.set_value();
|
||||
CHECK_THROW(vstate.set_value(), etl::promise_already_satisfied);
|
||||
}
|
||||
|
||||
TEST(test_promise_set_value_twice)
|
||||
{
|
||||
Counted::live = 0;
|
||||
{
|
||||
etl::shared_state<Counted> state;
|
||||
etl::promise<Counted> p(state);
|
||||
p.set_value(Counted(1));
|
||||
CHECK_THROW(p.set_value(Counted(2)), etl::promise_already_satisfied);
|
||||
CHECK_EQUAL(1, state.get().value);
|
||||
CHECK_EQUAL(1, Counted::live);
|
||||
}
|
||||
CHECK_EQUAL(0, Counted::live);
|
||||
}
|
||||
|
||||
TEST(test_future_get_not_ready)
|
||||
{
|
||||
etl::shared_state<int> state;
|
||||
etl::future<int> f(state);
|
||||
etl::shared_state<void> vstate;
|
||||
etl::future<void> vf(vstate);
|
||||
CHECK_THROW((void)f.get(), etl::future_not_ready);
|
||||
CHECK_THROW(vf.get(), etl::future_not_ready);
|
||||
}
|
||||
|
||||
TEST(test_future_get_no_state)
|
||||
{
|
||||
etl::future<int> f;
|
||||
etl::future<int&> rf;
|
||||
etl::future<void> vf;
|
||||
CHECK_THROW((void)f.get(), etl::future_no_state);
|
||||
CHECK_THROW((void)rf.get(), etl::future_no_state);
|
||||
CHECK_THROW(vf.get(), etl::future_no_state);
|
||||
}
|
||||
|
||||
TEST(test_promise_no_state)
|
||||
{
|
||||
int value = 1;
|
||||
etl::promise<int> p;
|
||||
etl::promise<int&> rp;
|
||||
etl::promise<void> vp;
|
||||
|
||||
CHECK_THROW((void)p.get_future(), etl::future_no_state);
|
||||
CHECK_THROW(p.set_value(1), etl::future_no_state);
|
||||
CHECK_THROW((void)rp.get_future(), etl::future_no_state);
|
||||
CHECK_THROW(rp.set_value(value), etl::future_no_state);
|
||||
CHECK_THROW((void)vp.get_future(), etl::future_no_state);
|
||||
CHECK_THROW(vp.set_value(), etl::future_no_state);
|
||||
}
|
||||
|
||||
TEST(test_promise_no_state_after_move)
|
||||
{
|
||||
etl::shared_state<int> state;
|
||||
etl::promise<int> p1(state);
|
||||
etl::promise<int> p2(etl::move(p1));
|
||||
CHECK_THROW(p1.set_value(1), etl::future_no_state);
|
||||
CHECK(!state.is_ready());
|
||||
}
|
||||
|
||||
TEST(test_exceptions_derive_from_future_exception)
|
||||
{
|
||||
etl::future<int> f;
|
||||
CHECK_THROW((void)f.get(), etl::future_exception);
|
||||
|
||||
etl::promise<int> p;
|
||||
CHECK_THROW(p.set_value(1), etl::future_exception);
|
||||
|
||||
etl::shared_state<int> state;
|
||||
state.set_value(1);
|
||||
CHECK_THROW(state.set_value(2), etl::future_exception);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#endif // ETL_HAS_ATOMIC
|
||||
@ -10395,6 +10395,7 @@
|
||||
<ClCompile Include="..\test_expected.cpp" />
|
||||
<ClCompile Include="..\test_format.cpp" />
|
||||
<ClCompile Include="..\test_function_traits.cpp" />
|
||||
<ClCompile Include="..\test_future.cpp" />
|
||||
<ClCompile Include="..\test_hfsm_recurse_to_inner_state_on_start.cpp" />
|
||||
<ClCompile Include="..\test_hfsm_transition_on_enter.cpp" />
|
||||
<ClCompile Include="..\test_index_of_type.cpp" />
|
||||
|
||||
@ -3587,6 +3587,9 @@
|
||||
<ClCompile Include="..\test_function_traits.cpp">
|
||||
<Filter>Tests\Types</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_future.cpp">
|
||||
<Filter>Tests\Misc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_singleton_base.cpp">
|
||||
<Filter>Tests\Patterns</Filter>
|
||||
</ClCompile>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user