Skip to content

Commit fbc8c47

Browse files
authored
v.0.0.7 (#11)
* when_all safer implementation (I suspect a bug in Clang codegen for coroutines) * result_core::publish_result doesn't move the consumer context * timer queue only accepts callables + args. bind returns the same callable if no arguments were given * timer uses std::chrono::milliseconds instead of size_t * error messages style unification
1 parent 0fad684 commit fbc8c47

File tree

15 files changed

+287
-332
lines changed

15 files changed

+287
-332
lines changed

README.md

Lines changed: 22 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
# concurrencpp, the C++ concurrency library
23

34
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -682,51 +683,27 @@ class timer_queue {
682683
*/
683684
bool shutdown_requested() const noexcept;
684685
685-
686-
/*
687-
Creates a new running timer where *this is the associated timer_queue.
688-
Throws std::invalid_argument if executor is null.
689-
Throws errors::timer_queue_shutdown if shutdown had been called before.
690-
*/
691-
template<class callable_type>
692-
timer make_timer(
693-
size_t due_time,
694-
size_t frequency,
695-
std::shared_ptr<concurrencpp::executor> executor,
696-
callable_type&& callable);
697-
698686
/*
699687
Creates a new running timer where *this is associated timer_queue.
700688
Throws std::invalid_argument if executor is null.
701689
Throws errors::timer_queue_shutdown if shutdown had been called before.
702690
*/
703691
template<class callable_type, class ... argumet_types>
704692
timer make_timer(
705-
size_t due_time,
706-
size_t frequency,
693+
std::chrono::milliseconds due_time,
694+
std::chrono::milliseconds frequency,
707695
std::shared_ptr<concurrencpp::executor> executor,
708696
callable_type&& callable,
709697
argumet_types&& ... arguments);
710698
711-
/*
712-
Creates a new one shot timer where *this is associated timer_queue.
713-
Throws std::invalid_argument if executor is null.
714-
Throws errors::timer_queue_shutdown if shutdown had been called before.
715-
*/
716-
template<class callable_type>
717-
timer make_one_shot_timer(
718-
size_t due_time,
719-
std::shared_ptr<concurrencpp::executor> executor,
720-
callable_type&& callable);
721-
722699
/*
723700
Creates a new one shot timer where *this is associated timer_queue.
724701
Throws std::invalid_argument if executor is null.
725702
Throws errors::timer_queue_shutdown if shutdown had been called before.
726703
*/
727704
template<class callable_type, class ... argumet_types>
728705
timer make_one_shot_timer(
729-
size_t due_time,
706+
std::chrono::milliseconds due_time,
730707
std::shared_ptr<concurrencpp::executor> executor,
731708
callable_type&& callable,
732709
argumet_types&& ... arguments);
@@ -736,7 +713,7 @@ class timer_queue {
736713
Throws std::invalid_argument if executor is null.
737714
Throws errors::timer_queue_shutdown if shutdown had been called before.
738715
*/
739-
result<void> make_delay_object(size_t due_time, std::shared_ptr<concurrencpp::executor> executor);
716+
result<void> make_delay_object(std::chrono::milliseconds due_time, std::shared_ptr<concurrencpp::executor> executor);
740717
};
741718
```
742719

@@ -778,7 +755,7 @@ class timer {
778755
Returns the due time in milliseconds the timer was defined with.
779756
Throws concurrencpp::errors::empty_timer is *this is empty.
780757
*/
781-
size_t get_due_time() const;
758+
std::chrono::milliseconds get_due_time() const;
782759

783760
/*
784761
Returns the executor the timer was defined with.
@@ -796,13 +773,13 @@ class timer {
796773
Returns the frequency in milliseconds the timer was defined with.
797774
Throws concurrencpp::errors::empty_timer is *this is empty.
798775
*/
799-
size_t get_frequency() const;
776+
std::chrono::milliseconds get_frequency() const;
800777

801778
/*
802779
Sets new frequency for this timer. Callables that have been scheduled to run at this point are not affected.
803780
Throws concurrencpp::errors::empty_timer is *this is empty.
804781
*/
805-
void set_frequency(size_t new_frequency);
782+
void set_frequency(std::chrono::milliseconds new_frequency);
806783

807784
/*
808785
Returns true is *this is not an empty timer, false otherwise.
@@ -817,19 +794,21 @@ class timer {
817794
818795
#include <iostream>
819796
797+
using namespace std::chrono_literals;
798+
820799
int main() {
821800
concurrencpp::runtime runtime;
822801
std::atomic_size_t counter = 1;
823802
concurrencpp::timer timer = runtime.timer_queue()->make_timer(
824-
1'500,
825-
2'000,
803+
1'500ms,
804+
2'000ms,
826805
runtime.thread_pool_executor(),
827806
[&] {
828807
const auto c = counter.fetch_add(1);
829808
std::cout << "timer was invoked for the " << c << "th time" << std::endl;
830809
});
831810
832-
std::this_thread::sleep_for(std::chrono::seconds(12));
811+
std::this_thread::sleep_for(12s);
833812
return 0;
834813
}
835814
```
@@ -845,16 +824,18 @@ A oneshot timer is a one-time timer with only a due time - after it schedules it
845824

846825
#include <iostream>
847826

827+
using namespace std::chrono_literals;
828+
848829
int main() {
849830
concurrencpp::runtime runtime;
850831
concurrencpp::timer timer = runtime.timer_queue()->make_one_shot_timer(
851-
3'000,
832+
3'000ms,
852833
runtime.thread_executor(),
853834
[&] {
854835
std::cout << "hello and goodbye" << std::endl;
855836
});
856837

857-
std::this_thread::sleep_for(std::chrono::seconds(4));
838+
std::this_thread::sleep_for(4s);
858839
return 0;
859840
}
860841
```
@@ -870,6 +851,8 @@ A delay object is a result object that becomes ready when its due time is reache
870851
871852
#include <iostream>
872853
854+
using namespace std::chrono_literals;
855+
873856
concurrencpp::null_result delayed_task(
874857
std::shared_ptr<concurrencpp::timer_queue> tq,
875858
std::shared_ptr<concurrencpp::thread_pool_executor> ex) {
@@ -879,15 +862,15 @@ concurrencpp::null_result delayed_task(
879862
std::cout << "task was invoked " << counter << " times." << std::endl;
880863
counter++;
881864
882-
co_await tq->make_delay_object(1'500, ex);
865+
co_await tq->make_delay_object(1'500ms, ex);
883866
}
884867
}
885868
886869
int main() {
887870
concurrencpp::runtime runtime;
888871
delayed_task(runtime.timer_queue(), runtime.thread_pool_executor());
889872
890-
std::this_thread::sleep_for(std::chrono::seconds(10));
873+
std::this_thread::sleep_for(10s);
891874
return 0;
892875
}
893876
```
@@ -1107,5 +1090,5 @@ In this example, we created an executor which logs actions like enqueuing a task
11071090

11081091
### Supported platforms
11091092
* Linux (requires clang-9 and above).
1110-
* machOS (requires clang-9 and above).
1093+
* macOS (requires clang-9 and above).
11111094
* Windows (requires Windows 10, visual studio 2019 and above).

concurrencpp/src/results/constants.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,27 +55,27 @@ namespace concurrencpp::details::consts {
5555
"result::resolve_via() - given executor is null.";
5656

5757
inline const char* k_result_awaitable_error_msg =
58-
"concurrencpp::awaitable_type<type>::await_suspend - awaitable is empty.";
58+
"concurrencpp::awaitable_type<type>::await_suspend() - awaitable is empty.";
5959

6060
inline const char* k_result_resolver_error_msg =
61-
"result_resolver<type>::await_suspend - awaitable is empty.";
61+
"result_resolver<type>::await_suspend() - awaitable is empty.";
6262

6363
inline const char* k_executor_exception_error_msg =
6464
"concurrencpp::result - an exception was thrown while trying to enqueue result continuation.";
6565

6666

6767
inline const char* k_make_exceptional_result_exception_null_error_msg =
68-
"make_exception_result - given exception_ptr is null.";
68+
"make_exception_result() - given exception_ptr is null.";
6969

7070

7171
inline const char* k_when_all_empty_result_error_msg =
72-
"concurrencpp::when_all - one of the result objects is empty.";
72+
"concurrencpp::when_all() - one of the result objects is empty.";
7373

7474
inline const char* k_when_any_empty_result_error_msg =
75-
"concurrencpp::when_any - one of the result objects is empty.";
75+
"concurrencpp::when_any() - one of the result objects is empty.";
7676

7777
inline const char* k_when_any_empty_range_error_msg =
78-
"concurrencpp::when_any - given range contains no elements.";
78+
"concurrencpp::when_any() - given range contains no elements.";
7979
}
8080

8181
#endif

concurrencpp/src/results/result_core.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,10 @@ bool result_core_base::await_via(
116116
return handle_done_state(await_ctx, force_rescheduling);
117117
}
118118

119-
void result_core_base::when_all(std::weak_ptr<when_all_state_base> when_all_state) noexcept {
119+
void result_core_base::when_all(std::shared_ptr<when_all_state_base> when_all_state) noexcept {
120120
const auto state = m_pc_state.load(std::memory_order_acquire);
121121
if (state == pc_state::producer) {
122-
assert(!when_all_state.expired());
123-
auto state = when_all_state.lock();
124-
return state->on_result_ready();
122+
return when_all_state->on_result_ready();
125123
}
126124

127125
assert_consumer_idle();
@@ -138,11 +136,8 @@ void result_core_base::when_all(std::weak_ptr<when_all_state_base> when_all_stat
138136
}
139137

140138
assert_done();
141-
auto state_ptr = std::get<4>(m_consumer).lock();
142-
143-
if (static_cast<bool>(state_ptr)) {
144-
state_ptr->on_result_ready();
145-
}
139+
auto& state_ptr = std::get<4>(m_consumer);
140+
state_ptr->on_result_ready();
146141
}
147142

148143
concurrencpp::details::when_any_status result_core_base::when_any(

concurrencpp/src/results/result_core.h

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ namespace concurrencpp::details {
154154
std::experimental::coroutine_handle<>,
155155
await_context,
156156
std::shared_ptr<wait_context>,
157-
std::weak_ptr<when_all_state_base>,
157+
std::shared_ptr<when_all_state_base>,
158158
when_any_ctx>;
159159

160160
enum class pc_state {
@@ -185,7 +185,7 @@ namespace concurrencpp::details {
185185
std::experimental::coroutine_handle<> caller_handle,
186186
bool force_rescheduling);
187187

188-
void when_all(std::weak_ptr<when_all_state_base> when_all_state) noexcept;
188+
void when_all(std::shared_ptr<when_all_state_base> when_all_state) noexcept;
189189

190190
when_any_status when_any(std::shared_ptr<when_any_state_base> when_any_state, size_t index) noexcept;
191191

@@ -369,27 +369,24 @@ namespace concurrencpp::details {
369369
}
370370

371371
case 2: {
372-
auto await_ctx = std::move(std::get<2>(this->m_consumer));
373-
return this->schedule_continuation(await_ctx);
372+
return this->schedule_continuation(std::get<2>(this->m_consumer));
374373
}
375374

376375
case 3: {
377-
const auto wait_ctx = std::move(std::get<3>(this->m_consumer));
376+
auto& wait_ctx = std::get<3>(this->m_consumer);
378377
wait_ctx->notify();
379378
return;
380379
}
381380

382381
case 4: {
383-
auto when_all_state_weak = std::move(std::get<4>(this->m_consumer));
384-
auto when_all_state = when_all_state_weak.lock();
385-
if (static_cast<bool>(when_all_state)) {
386-
when_all_state->on_result_ready();
387-
}
382+
auto& when_all_state = std::get<4>(this->m_consumer);
383+
assert(static_cast<bool>(when_all_state));
384+
when_all_state->on_result_ready();
388385
return;
389386
}
390387

391388
case 5: {
392-
auto when_any_ctx = std::move(std::get<5>(this->m_consumer));
389+
auto& when_any_ctx = std::get<5>(this->m_consumer);
393390
auto& when_any_state = when_any_ctx.first;
394391
assert(static_cast<bool>(when_any_state));
395392
when_any_state->on_result_ready(when_any_ctx.second);

concurrencpp/src/results/when_result.h

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,18 @@ namespace concurrencpp::details {
6565

6666
private:
6767
tuple_type m_tuple;
68-
result_core<tuple_type> m_core_ptr;
68+
std::shared_ptr<result_core<tuple_type>> m_core_ptr;
6969

7070
template<class type>
7171
void set_state(result<type>& result) noexcept {
7272
auto core_ptr = when_result_helper::get_core(result);
73-
core_ptr->when_all(this->weak_from_this());
73+
core_ptr->when_all(this->shared_from_this());
7474
}
7575

7676
public:
7777
when_all_tuple_state(result_types&& ... results) noexcept :
78-
m_tuple(std::forward<result_types>(results)...) {
78+
m_tuple(std::forward<result_types>(results)...),
79+
m_core_ptr(std::make_shared<result_core<tuple_type>>()) {
7980
m_counter = sizeof ... (result_types);
8081
}
8182

@@ -88,14 +89,12 @@ namespace concurrencpp::details {
8889
return;
8990
}
9091

91-
m_core_ptr.set_result(std::move(m_tuple));
92-
m_core_ptr.publish_result();
92+
m_core_ptr->set_result(std::move(m_tuple));
93+
m_core_ptr->publish_result();
9394
}
9495

9596
result<tuple_type> get_result() noexcept {
96-
return {
97-
std::shared_ptr<result_core<tuple_type>>(this->shared_from_this(), &m_core_ptr)
98-
};
97+
return { m_core_ptr };
9998
}
10099
};
101100

@@ -106,23 +105,24 @@ namespace concurrencpp::details {
106105

107106
private:
108107
std::vector<type> m_vector;
109-
result_core<std::vector<type>> m_core_ptr;
108+
std::shared_ptr<result_core<std::vector<type>>> m_core_ptr;
110109

111110
template<class given_type>
112111
void set_state(result<given_type>& result) noexcept {
113112
auto core_ptr = when_result_helper::get_core(result);;
114-
core_ptr->when_all(this->weak_from_this());
113+
core_ptr->when_all(this->shared_from_this());
115114
}
116115

117116
public:
118117
template<class iterator_type>
119118
when_all_vector_state(iterator_type begin, iterator_type end) :
120-
m_vector(std::make_move_iterator(begin), std::make_move_iterator(end)) {
119+
m_vector(std::make_move_iterator(begin), std::make_move_iterator(end)),
120+
m_core_ptr(std::make_shared<result_core<std::vector<type>>>()) {
121121
m_counter = m_vector.size();
122122
}
123123

124124
void set_state() noexcept {
125-
for(auto& result : m_vector) {
125+
for (auto& result : m_vector) {
126126
set_state(result);
127127
}
128128
}
@@ -132,14 +132,12 @@ namespace concurrencpp::details {
132132
return;
133133
}
134134

135-
m_core_ptr.set_result(std::move(m_vector));
136-
m_core_ptr.publish_result();
135+
m_core_ptr->set_result(std::move(m_vector));
136+
m_core_ptr->publish_result();
137137
}
138138

139139
result<std::vector<type>> get_result() noexcept {
140-
return {
141-
std::shared_ptr<result_core<std::vector<type>>>(this->shared_from_this(), &m_core_ptr)
142-
};
140+
return { m_core_ptr };
143141
}
144142
};
145143
}
@@ -381,7 +379,7 @@ namespace concurrencpp {
381379

382380
template<class iterator_type>
383381
result<when_any_result<std::vector<typename std::iterator_traits<iterator_type>::value_type>>>
384-
when_any(iterator_type begin, iterator_type end) {
382+
when_any(iterator_type begin, iterator_type end) {
385383
details::when_result_helper::throw_if_empty_range(
386384
details::consts::k_when_any_empty_result_error_msg,
387385
begin,

0 commit comments

Comments
 (0)