Skip to content

Commit c9ae7d0

Browse files
David-HaimfriendlyanonNN---
authored
Version 0.1.1 (#40)
* full move to ctest * timer queue idling mechanism * unified shutdown exception * test folder cleaning up * clang format changes * optimizations Co-authored-by: friendlyanon <1736896+friendlyanon@users.noreply.github.com> Co-authored-by: friendlyanon <friendlyanon@users.noreply.github.com> Co-authored-by: NN <580536+NN---@users.noreply.github.com> Co-authored-by: NN <NN---@users.noreply.github.com>
1 parent f037e8c commit c9ae7d0

File tree

87 files changed

+1828
-1648
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+1828
-1648
lines changed

.clang-format

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ SpacesInContainerLiterals: 'true'
5151
SpacesInParentheses: 'false'
5252
SpacesInSquareBrackets: 'false'
5353
Standard: Cpp11
54-
ColumnLimit: 165
54+
ColumnLimit: 135
5555
IndentWidth: 4
5656
BinPackArguments: 'false'
5757
BinPackParameters: 'false'

.github/workflows/ci.yml

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ on:
44
push:
55
branches:
66
- master
7+
- develop
78

89
pull_request:
910
branches:
1011
- master
12+
- develop
1113

1214
workflow_dispatch: ~
1315

@@ -22,19 +24,19 @@ jobs:
2224
strategy:
2325
matrix:
2426
conf:
25-
- name: Ubuntu (Clang 10 - TSAN)
27+
- name: Ubuntu (Clang 11 - TSAN)
2628
os: ubuntu-20.04
27-
cc: clang-10
28-
cxx: clang++-10
29+
cc: clang-11
30+
cxx: clang++-11
2931
tsan: YES
3032

31-
- name: Ubuntu (Clang 10 - no TSAN)
33+
- name: Ubuntu (Clang 11 - no TSAN)
3234
os: ubuntu-20.04
33-
cc: clang-10
34-
cxx: clang++-10
35+
cc: clang-11
36+
cxx: clang++-11
3537
tsan: NO
3638

37-
- name: macOS (Clang 10 - no TSAN)
39+
- name: macOS (Clang 11 - no TSAN)
3840
os: macos-latest
3941
cc: clang
4042
cxx: clang++
@@ -77,9 +79,16 @@ jobs:
7779
"${{ steps.tools.outputs.ninja }}"
7880
${{ steps.cores.outputs.plus_one }}]==])
7981
82+
- name: Install clang 11
83+
run: |
84+
sudo apt-get update
85+
sudo apt-get install clang-11 libc++-11-dev libc++abi-11-dev
86+
if: ${{ startsWith(matrix.conf.os, 'ubuntu') }}
87+
8088
- name: Build examples
8189
run: cmake -P cmake/ciBuild.cmake -- example build/example
8290
${{ steps.args.outputs.args }}
91+
continue-on-error: ${{ startsWith(matrix.conf.os, 'macos') }}
8392

8493
- name: Build tests
8594
id: build_tests

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.16)
22

33
project(concurrencpp
4-
VERSION 0.1.0
4+
VERSION 0.1.1
55
LANGUAGES CXX)
66

77
include(cmake/coroutineOptions.cmake)

README.md

Lines changed: 67 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
2-
31
# concurrencpp, the C++ concurrency library
42

53
![Latest Release](https://img.shields.io/github/v/release/David-Haim/concurrencpp.svg) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -25,7 +23,7 @@ concurrencpp main advantages are:
2523
* [Executor types](#executor-types)
2624
* [Using executors](#using-executors)
2725
* [`thread_pool_executor` API](#thread_pool_executor-api)
28-
* [`manual_executor` API](#thread_pool_executor-api)
26+
* [`manual_executor` API](#manual_executor-api)
2927
* [Result objects](#result-objects)
3028
* [`result` API](#result-api)
3129
* [Parallel coroutines](#parallel-coroutines)
@@ -52,6 +50,7 @@ concurrencpp main advantages are:
5250
* [`task` API](#task-api)
5351
* [Using a user-defined executor example](#example-using-a-user-defined-executor)
5452
* [Supported platforms and tools](#supported-platforms-and-tools)
53+
* [Building, installing and testing](#building-installing-and-testing)
5554

5655
----
5756

@@ -200,13 +199,13 @@ class executor {
200199
201200
/*
202201
Schedules a task to run in this executor.
203-
Throws concurrencpp::errors::executor_shutdown exception if shutdown was called before.
202+
Throws concurrencpp::errors::runtime_shutdown exception if shutdown was called before.
204203
*/
205204
virtual void enqueue(concurrencpp::task task) = 0;
206205
207206
/*
208207
Schedules a range of tasks to run in this executor.
209-
Throws concurrencpp::errors::executor_shutdown exception if shutdown was called before.
208+
Throws concurrencpp::errors::runtime_shutdown exception if shutdown was called before.
210209
*/
211210
virtual void enqueue(std::span<concurrencpp::task> tasks) = 0;
212211
@@ -227,36 +226,36 @@ class executor {
227226
- Tells underlying threads to exit their work loop and joins them.
228227
- Destroys unexecuted coroutines.
229228
- Makes subsequent calls to enqueue, post, submit, bulk_post and
230-
bulk_submit to throw concurrencpp::errors::executor_shutdown exception.
229+
bulk_submit to throw concurrencpp::errors::runtime_shutdown exception.
231230
- Makes shutdown_requested return true.
232231
*/
233232
virtual void shutdown() noexcept = 0;
234233
235234
/*
236235
Turns a callable and its arguments into a task object and schedules it to run in this executor using enqueue.
237236
Arguments are passed to the task by decaying them first.
238-
Throws errors::executor_shutdown exception if shutdown has been called before.
237+
Throws errors::runtime_shutdown exception if shutdown has been called before.
239238
*/
240239
template<class callable_type, class ... argument_types>
241240
void post(callable_type&& callable, argument_types&& ... arguments);
242241
243242
/*
244243
Like post, but returns a result object that marshals the asynchronous result.
245-
Throws errors::executor_shutdown exception if shutdown has been called before.
244+
Throws errors::runtime_shutdown exception if shutdown has been called before.
246245
*/
247246
template<class callable_type, class ... argument_types>
248247
result<type> submit(callable_type&& callable, argument_types&& ... arguments);
249248
250249
/*
251250
Turns an array of callables into an array of tasks and schedules them to run in this executor using enqueue.
252-
Throws errors::executor_shutdown exception if shutdown has been called before.
251+
Throws errors::runtime_shutdown exception if shutdown has been called before.
253252
*/
254253
template<class callable_type>
255254
void bulk_post(std::span<callable_type> callable_list);
256255
257256
/*
258257
Like bulk_post, but returns an array of result objects that marshal the asynchronous results.
259-
Throws errors::executor_shutdown exception if shutdown has been called before.
258+
Throws errors::runtime_shutdown exception if shutdown has been called before.
260259
*/
261260
template<class callable_type>
262261
std::vector<concurrencpp::result<type>> bulk_submit(std::span<callable_type> callable_list);
@@ -528,7 +527,7 @@ class result{
528527
Returns true if this is a non-empty result.
529528
Applications must not use this object if this->operator bool() is false.
530529
*/
531-
operator bool() const noexcept;
530+
explicit operator bool() const noexcept;
532531

533532
/*
534533
Queries the status of *this.
@@ -877,7 +876,7 @@ class share_result {
877876
Returns true if this is a non-empty shared-result.
878877
Applications must not use this object if this->operator bool() is false.
879878
*/
880-
operator bool() const noexcept;
879+
explicit operator bool() const noexcept;
881880

882881
/*
883882
Queries the status of *this.
@@ -1112,12 +1111,12 @@ Regular timers have four properties that define them:
11121111
4. Frequency - from the time the timer was scheduled to run for the first time, the interval in milliseconds the callable will be scheduled to run periodically, until the timer is destructed or cancelled.
11131112
11141113
Like other objects in concurrencpp, timers are a move only type that can be empty.
1115-
When a timer is destructed or `timer::cancel` is called, the timer cancels its scheduled but not yet executed tasks. Ongoing tasks are uneffected. The timer callable must be thread safe. It is recommended to set the due time and the frequency of a timer to a granularity of 50 milliseconds.
1114+
When a timer is destructed or `timer::cancel` is called, the timer cancels its scheduled but not yet executed tasks. Ongoing tasks are uneffected. The timer callable must be thread safe. It is recommended to set the due time and the frequency of timers to a granularity of 50 milliseconds.
11161115
11171116
A timer queue is a concurrencpp worker that manages a collection of timers and processes them in just one thread of execution. It is also the agent used to create new timers.
11181117
When a timer deadline (whether it is the timer's due-time or frequency) has reached, the timer queue "fires" the timer by scheduling its callable to run on the associated executor as a task.
11191118
1120-
Just like executors, timer queues also adhere to the RAII concept. When the runtime object gets out of scope, It shuts down the timer queue, cancelling all pending timers. After a timer queue has been shut down, any subsequent call to `make_timer`, `make_onshot_timer` and `make_delay_object` will throw an `errors::timer_queue_shutdown` exception.
1119+
Just like executors, timer queues also adhere to the RAII concept. When the runtime object gets out of scope, It shuts down the timer queue, cancelling all pending timers. After a timer queue has been shut down, any subsequent call to `make_timer`, `make_onshot_timer` and `make_delay_object` will throw an `errors::runtime_shutdown` exception.
11211120
Applications must not try to shut down timer queues by themselves.
11221121
11231122
#### `timer_queue` API:
@@ -1132,7 +1131,7 @@ class timer_queue {
11321131
Shuts down this timer_queue:
11331132
Tells the underlying thread of execution to quit and joins it.
11341133
Cancels all pending timers.
1135-
After this call, invocation of any method besides shutdown and shutdown_requested will throw an errors::timer_queue_shutdown.
1134+
After this call, invocation of any method besides shutdown and shutdown_requested will throw an errors::runtime_shutdown.
11361135
If shutdown had been called before, this method has no effect.
11371136
*/
11381137
void shutdown() noexcept;
@@ -1145,7 +1144,7 @@ class timer_queue {
11451144
/*
11461145
Creates a new running timer where *this is the associated timer_queue.
11471146
Throws std::invalid_argument if executor is null.
1148-
Throws errors::timer_queue_shutdown if shutdown had been called before.
1147+
Throws errors::runtime_shutdown if shutdown had been called before.
11491148
*/
11501149
template<class callable_type, class ... argumet_types>
11511150
timer make_timer(
@@ -1158,7 +1157,7 @@ class timer_queue {
11581157
/*
11591158
Creates a new one-shot timer where *this is the associated timer_queue.
11601159
Throws std::invalid_argument if executor is null.
1161-
Throws errors::timer_queue_shutdown if shutdown had been called before.
1160+
Throws errors::runtime_shutdown if shutdown had been called before.
11621161
*/
11631162
template<class callable_type, class ... argumet_types>
11641163
timer make_one_shot_timer(
@@ -1170,7 +1169,7 @@ class timer_queue {
11701169
/*
11711170
Creates a new delay object where *this is the associated timer_queue.
11721171
Throws std::invalid_argument if executor is null.
1173-
Throws errors::timer_queue_shutdown if shutdown had been called before.
1172+
Throws errors::runtime_shutdown if shutdown had been called before.
11741173
*/
11751174
result<void> make_delay_object(
11761175
std::chrono::milliseconds due_time,
@@ -1249,7 +1248,7 @@ class timer {
12491248
Returns true is *this is not an empty timer, false otherwise.
12501249
The timer should not be used if this->operator bool() is false.
12511250
*/
1252-
operator bool() const noexcept;
1251+
explicit operator bool() const noexcept;
12531252
};
12541253
```
12551254
@@ -1346,7 +1345,7 @@ In this example, we created a coroutine (that does not marshal any result or thr
13461345

13471346
The concurrencpp runtime object is the agent used to acquire, store and create new executors.
13481347
The runtime must be created as a value type as soon as the main function starts to run.
1349-
When the concurrencpp runtime gets out of scope, it iterates over its stored executors and shuts them down one by one by calling `executor::shutdown`. Executors then exit their inner work loop and any subsequent attempt to schedule a new task will throw a `concurrencpp::executor_shutdown` exception. The runtime also contains the global timer queue used to create timers and delay objects.
1348+
When the concurrencpp runtime gets out of scope, it iterates over its stored executors and shuts them down one by one by calling `executor::shutdown`. Executors then exit their inner work loop and any subsequent attempt to schedule a new task will throw a `concurrencpp::runtime_shutdown` exception. The runtime also contains the global timer queue used to create timers and delay objects.
13501349
Upon destruction, stored executors will destroy unexecuted tasks, and wait for ongoing tasks to finish. If an ongoing task tries to use an executor to spawn new tasks or schedule its own task continuation - an exception will be thrown. In this case, ongoing tasks need to quit as soon as possible, allowing their underlying executors to quit. The timer queue will also be shut down, cancelling all running timers. With this RAII style of code, no tasks can be processed before the creation of the runtime object, and while/after the runtime gets out of scope.
13511350
This frees concurrent applications from needing to communicate termination messages explicitly. Tasks are free use executors as long as the runtime object is alive.
13521351

@@ -1435,7 +1434,7 @@ New executors can be created using `runtime::make_executor`. Applications must n
14351434
Another important point is to handle shutdown correctly: `shutdown`, `shutdown_requested` and `enqueue` should all monitor the executor state and behave accordingly when invoked:
14361435
* `shutdown` should tell underlying threads to quit and then join them.
14371436
* `shutdown` might be called multiple times, and the method must handle this scenario by ignoring any subsequent call to `shutdown` after the first invocation.
1438-
* `enqueue` must throw a `concurrencpp::errors::executor_shutdown` exception if `shutdown` had been called before.
1437+
* `enqueue` must throw a `concurrencpp::errors::runtime_shutdown` exception if `shutdown` had been called before.
14391438
14401439
Implementing an executor is one of the rare cases applications need to work with `concurrencpp::task` class directly. `concurrencpp::task` is a `std::function` like object, but with a few differences.
14411440
Like `std::function`, the task object stores a callable that acts as the asynchronous operation.
@@ -1501,7 +1500,7 @@ Task objects apply the short-buffer-optimization (sbo) for regular, small callab
15011500
/*
15021501
Returns true if *this stores a callable. false otherwise.
15031502
*/
1504-
operator bool() const noexcept;
1503+
expliit operator bool() const noexcept;
15051504
15061505
/*
15071506
Returns true if *this stores a callable,
@@ -1571,7 +1570,7 @@ public:
15711570

15721571
std::unique_lock<std::mutex> lock(_lock);
15731572
if (_shutdown_requested) {
1574-
throw concurrencpp::errors::executor_shutdown("logging executor - executor was shutdown.");
1573+
throw concurrencpp::errors::runtime_shutdown("logging executor - executor was shutdown.");
15751574
}
15761575

15771576
_queue.emplace(std::move(task));
@@ -1583,7 +1582,7 @@ public:
15831582

15841583
std::unique_lock<std::mutex> lock(_lock);
15851584
if (_shutdown_requested) {
1586-
throw concurrencpp::errors::executor_shutdown("logging executor - executor was shutdown.");
1585+
throw concurrencpp::errors::runtime_shutdown("logging executor - executor was shutdown.");
15871586
}
15881587

15891588
for (auto& task : tasks) {
@@ -1636,3 +1635,47 @@ In this example, we created an executor which logs actions like enqueuing a task
16361635

16371636
* **Operating systems:** Linux, macOS, Windows (Windows 10 and above)
16381637
* **Compilers:** MSVC (Visual Studio 2019 version 16.8.2 and above), Clang (Clang-11 and above)
1638+
* **Tools:** CMake (3.16 and above)
1639+
1640+
### Building, installing and testing
1641+
1642+
##### Building the library on Windows (release mode)
1643+
```cmake
1644+
$ git clone https://github.com/David-Haim/concurrencpp.git
1645+
$ cd concurrencpp
1646+
$ cmake -S . -B build/lib
1647+
$ cmake --build build/lib --config Release
1648+
```
1649+
##### Running the tests on Windows (debug + release mode)
1650+
```cmake
1651+
$ git clone https://github.com/David-Haim/concurrencpp.git
1652+
$ cd concurrencpp
1653+
$ cmake -S test -B build/test
1654+
$ cmake --build build/test
1655+
<# for release mode: cmake --build build/test --config Release #>
1656+
$ cd build/test
1657+
$ ctest . -V -C Debug
1658+
<# for release mode: ctest . -V -C Release #>
1659+
```
1660+
##### Building the library on *nix platforms (release mode)
1661+
```cmake
1662+
$ git clone https://github.com/David-Haim/concurrencpp.git
1663+
$ cd concurrencpp
1664+
$ cmake -DCMAKE_BUILD_TYPE=Release -S . -B build/lib
1665+
$ cmake --build build/lib
1666+
#optional, install the library: sudo cmake --install build/lib
1667+
```
1668+
##### Running the tests on *nix platforms
1669+
1670+
With clang, it is also possible to run the tests with TSAN (thread sanitizer) support.
1671+
1672+
```cmake
1673+
$ git clone https://github.com/David-Haim/concurrencpp.git
1674+
$ cd concurrencpp
1675+
$ cmake -S test -B build/test
1676+
#for release mode: cmake -DCMAKE_BUILD_TYPE=Release -S test -B build/test
1677+
#for TSAN mode: cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_THREAD_SANITIZER=Yes -S test -B build/test
1678+
$ cmake --build build/test
1679+
$ cd build/test
1680+
$ ctest . -V
1681+
```

example/async_sql/source/main.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ result<std::shared_ptr<db_connection>> connect_async() {
5454
result_promise<std::shared_ptr<db_connection>> m_result_promise;
5555

5656
public:
57-
connection_callback(result_promise<std::shared_ptr<db_connection>> result_promise) noexcept : m_result_promise(std::move(result_promise)) {}
57+
connection_callback(result_promise<std::shared_ptr<db_connection>> result_promise) noexcept :
58+
m_result_promise(std::move(result_promise)) {}
5859

5960
void on_connection(std::exception_ptr error, std::shared_ptr<db_connection> connection) override {
6061
if (error) {

example/synchronous_web_socket/source/mock_web_socket.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ namespace mock_web_socket {
2424
return std::rand() % range + min;
2525
}
2626

27-
const std::string cities[] = {"London", "New York City", "Tokyo", "Paris", "Singapore", "Amsterdam", "Seoul", "Berlin", "Hong Kong", "Sydney"};
27+
const std::string cities[] =
28+
{"London", "New York City", "Tokyo", "Paris", "Singapore", "Amsterdam", "Seoul", "Berlin", "Hong Kong", "Sydney"};
2829
} // namespace mock_web_socket
2930

3031
void mock_web_socket::web_socket::open(std::string_view) {

include/concurrencpp/errors.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,8 @@ namespace concurrencpp::errors {
3333
result_already_retrieved(const std::string& message) : runtime_error(message) {}
3434
};
3535

36-
struct executor_shutdown : public std::runtime_error {
37-
executor_shutdown(const std::string& message) : runtime_error(message) {}
38-
};
39-
40-
struct timer_queue_shutdown : public std::runtime_error {
41-
timer_queue_shutdown(const std::string& message) : runtime_error(message) {}
36+
struct runtime_shutdown : public std::runtime_error {
37+
runtime_shutdown(const std::string& message) : runtime_error(message) {}
4238
};
4339
} // namespace concurrencpp::errors
4440

include/concurrencpp/executors/derivable_executor.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ namespace concurrencpp {
99
class derivable_executor : public executor {
1010

1111
private:
12-
concrete_executor_type* self() noexcept {
13-
return static_cast<concrete_executor_type*>(this);
12+
concrete_executor_type& self() noexcept {
13+
return *static_cast<concrete_executor_type*>(this);
1414
}
1515

1616
public:

0 commit comments

Comments
 (0)