source: doc/papers/concurrency/examples/ProdCons.cpp @ 1f11818

Last change on this file since 1f11818 was ae6b6cf, checked in by tdelisle <tdelisle@…>, 6 years ago

Implemented ProdCons? using C++20 coroutines and a dispatcher pattern, it works but is also a lovecraftian horror

  • Property mode set to 100644
File size: 5.6 KB
RevLine 
[709e0e0]1#include <cassert>
2#include <cstdlib>
3#include <iostream>
4#include <experimental/coroutine>
5#include <unistd.h>
6
7int random(int max) {
8        return std::rand() % max;
9}
10
11struct Prod;
12struct Cons;
13
[ae6b6cf]14struct resumable {
15        virtual resumable * resume() = 0;
16};
17
18struct Prod : public resumable {
[709e0e0]19        struct local {
20                Cons * c;
21                int N, money, receipt;
22        };
23
24        struct promise_type {
25                local _l;
[ae6b6cf]26                resumable * next;
[709e0e0]27
28                Prod get_return_object() {
29                        return Prod(std::experimental::coroutine_handle<promise_type>::from_promise(*this));
30                }
31
32                auto initial_suspend() { return std::experimental::suspend_never(); }
33                auto final_suspend()   { return std::experimental::suspend_always(); }
34
35                void return_void() {}
36                void unhandled_exception() {}
37        };
38
39        struct data {
40                promise_type * _promise = nullptr;
41                bool await_ready() noexcept { return false; }
42                void await_suspend(std::experimental::coroutine_handle<promise_type> _coroutine) noexcept {
43                        _promise = &_coroutine.promise();
44                }
45                local & await_resume() noexcept { assert(_promise); return _promise->_l; }
46        };
47
48        std::experimental::coroutine_handle<promise_type> _coroutine = nullptr;
49
50        explicit Prod(std::experimental::coroutine_handle<promise_type> coroutine)
51                : _coroutine(coroutine)
52        {}
53
54        ~Prod() {
55                if(_coroutine) { _coroutine.destroy(); }
56        }
57
58        Prod() = default;
59        Prod(Prod const &) = delete;
60        Prod& operator=(Prod const &) = delete;
61
62        Prod(Prod&& other) {
63                std::swap(_coroutine, other._coroutine);
64        }
65
66        Prod& operator=(Prod&& other) {
67                if(&other != this) {
68                        _coroutine = other._coroutine;
69                        other._coroutine = nullptr;
70                }
71                return *this;
72        }
73
74        static Prod main();
75
[ae6b6cf]76        struct payment_return;
77
78        payment_return payment(int money);
[709e0e0]79
80        auto start(int N, Cons & c) {
81                _coroutine.promise()._l.c = &c;
82                _coroutine.promise()._l.N = N;
83                _coroutine.promise()._l.receipt = 0;
[ae6b6cf]84        }
85
86        virtual resumable * resume() override final {
[709e0e0]87                _coroutine.resume();
[ae6b6cf]88                return _coroutine.promise().next;
[709e0e0]89        }
90};
91
[ae6b6cf]92struct Cons : public resumable {
[709e0e0]93        struct local {
94                Prod * p;
95                int p1, p2, status;
96                bool done;
97        };
98
99        struct promise_type {
100                local _l;
[ae6b6cf]101                resumable * next;
[709e0e0]102
103                Cons get_return_object() {
104                        return Cons(std::experimental::coroutine_handle<promise_type>::from_promise(*this));
105                }
106
107                auto initial_suspend() { return std::experimental::suspend_never(); }
108                auto final_suspend()   { return std::experimental::suspend_always(); }
109
110                void return_void() {}
111                void unhandled_exception() {}
112        };
113
114        struct data {
115                Prod * _p;
116                data(Prod & prod) : _p(&prod) {}
117                promise_type * _promise = nullptr;
118                bool await_ready() noexcept { return false; }
119                void await_suspend(std::experimental::coroutine_handle<promise_type> _coroutine) noexcept {
120                        _promise = &_coroutine.promise();
121                }
122                local & await_resume() noexcept { assert(_promise); _promise->_l.p = _p; return _promise->_l; }
123        };
124
125        std::experimental::coroutine_handle<promise_type> _coroutine = nullptr;
126
127        explicit Cons(std::experimental::coroutine_handle<promise_type> coroutine)
128                : _coroutine(coroutine)
129        {}
130
131        ~Cons() {
132                if(_coroutine) { _coroutine.destroy(); }
133        }
134
135        Cons() = default;
136        Cons(Cons const &) = delete;
137        Cons& operator=(Cons const &) = delete;
138
139        Cons(Cons&& other) {
140                std::swap(_coroutine, other._coroutine);
141        }
142
143        Cons& operator=(Cons&& other) {
144                if(&other != this) {
145                        _coroutine = other._coroutine;
146                        other._coroutine = nullptr;
147                }
148                return *this;
149        }
150
151        static Cons main( Prod & prod );
152
153        auto deliver(int p1, int p2) {
154                _coroutine.promise()._l.p1 = p1;
155                _coroutine.promise()._l.p2 = p2;
156
157                struct ret {
158                        int _status;
[ae6b6cf]159                        Cons * c;
[709e0e0]160                        bool await_ready() { return false; }
[ae6b6cf]161                        void await_suspend(std::experimental::coroutine_handle<Prod::promise_type> _coroutine) {
162                                _coroutine.promise().next = c;
163                        }
[709e0e0]164                        int await_resume() { return _status; }
165                };
[ae6b6cf]166                return ret{ _coroutine.promise()._l.status, this };
[709e0e0]167        }
168
169        auto stop() {
170                _coroutine.promise()._l.done = true;
171                struct ret {
[ae6b6cf]172                        Cons * c;
173                        Prod::promise_type * _promise;
[709e0e0]174                        bool await_ready() { return false; }
[ae6b6cf]175                        void await_suspend(std::experimental::coroutine_handle<Prod::promise_type> _coroutine) {
176                                _promise = &_coroutine.promise();
177                                _promise->next = c;
178                        }
179                        void await_resume() {
180                                _promise->next = nullptr;
181                        }
[709e0e0]182                };
[ae6b6cf]183                return ret{this, nullptr};
[709e0e0]184        }
[ae6b6cf]185
186        virtual resumable * resume() override final {
187                _coroutine.resume();
188                return _coroutine.promise().next;
189        }
190};
191
192struct Prod::payment_return {
193        int _receipt;
194        Prod * p;
195        bool await_ready() { return false; }
196        void await_suspend(std::experimental::coroutine_handle<Cons::promise_type> _coroutine) {
197                _coroutine.promise().next = p;
198        }
199        int await_resume() { return _receipt; }
[709e0e0]200};
201
[ae6b6cf]202Prod::payment_return Prod::payment(int money)  {
203        _coroutine.promise()._l.money = money;
204        return payment_return{ _coroutine.promise()._l.receipt, this };
205}
206
[709e0e0]207Prod Prod::main() {
208        auto & p = co_await Prod::data();
209        for(int i = 0; i < p.N; i++) {
210                int p1 = random(100), p2 = random(100);
[ae6b6cf]211                std::cout << p1 << " " << p2 << std::endl;
[709e0e0]212                int status = co_await p.c->deliver(p1, p2);
[ae6b6cf]213                std::cout << " $" << p.money << std::endl << status << std::endl;
[709e0e0]214                p.receipt += 1;
215        }
216        co_await p.c->stop();
[ae6b6cf]217        std::cout << "prod stops" << std::endl;
[709e0e0]218}
219
220Cons Cons::main( Prod & prod ) {
221        auto & c = co_await Cons::data( prod );
222        int money = 1, receipt;
223        for(;!c.done ;) {
[ae6b6cf]224                std::cout << c.p1 << " " << c.p2 << std::endl;
225                std::cout << " $ " << money << std::endl;
[709e0e0]226                c.status += 1;
227                receipt = co_await c.p->payment( money );
[ae6b6cf]228                std::cout << " # " << receipt << std::endl;
[709e0e0]229                money += 1;
230        }
[ae6b6cf]231        std::cout << "cons stops" << std::endl;
232}
233
234void dispatch(resumable * r) {
235        while((r = r->resume()));
[709e0e0]236}
237
238int main() {
239        auto prod = Prod::main();
240        auto cons = Cons::main( prod );
241        srandom( getpid() );
242        prod.start(5, cons);
[ae6b6cf]243        dispatch(&prod);
[709e0e0]244}
Note: See TracBrowser for help on using the repository browser.