• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1[/
2          Copyright Oliver Kowalke 2014.
3 Distributed under the Boost Software License, Version 1.0.
4    (See accompanying file LICENSE_1_0.txt or copy at
5          http://www.boost.org/LICENSE_1_0.txt
6]
7
8[section:asymmetric Asymmetric coroutine]
9
10Two asymmetric coroutine types - __push_coro__ and __pull_coro__ - provide a
11unidirectional transfer of data.
12[note ['asymmetric_coroutine<>] is a typedef of __coro__.]
13
14
15[heading __pull_coro__]
16__pull_coro__ transfers data from another execution context (== pulled-from).
17The template parameter defines the transferred parameter type.
18The constructor of __pull_coro__ takes a function (__coro_fn__) accepting a
19reference to an __push_coro__ as argument. Instantiating an __pull_coro__ passes
20the control of execution to __coro_fn__ and a complementary __push_coro__ is
21synthesized by the library and passed as reference to __coro_fn__.
22
23This kind of coroutine provides __pull_coro_op__. This method only switches
24context; it transfers no data.
25
26__pull_coro__ provides input iterators (__pull_coro_it__) and __begin__/__end__
27are overloaded. The increment-operation switches the context and transfers data.
28
29        typedef boost::coroutines2::coroutine<int>   coro_t;
30
31        coro_t::pull_type source(
32            [&](coro_t::push_type& sink){
33                int first=1,second=1;
34                sink(first);
35                sink(second);
36                for(int i=0;i<8;++i){
37                    int third=first+second;
38                    first=second;
39                    second=third;
40                    sink(third);
41                }
42            });
43
44        for(auto i:source)
45            std::cout << i <<  " ";
46
47        output:
48        1 1 2 3 5 8 13 21 34 55
49
50In this example an __pull_coro__ is created in the main execution context taking
51a lambda function (== __coro_fn__) which calculates Fibonacci numbers in a
52simple ['for]-loop.
53The __coro_fn__ is executed in a newly created execution context which is
54managed by the instance of __pull_coro__.
55An __push_coro__ is automatically generated by the library and passed as
56reference to the lambda function. Each time the lambda function calls
57__push_coro_op__ with another Fibonacci number, __push_coro__ transfers it back
58to the main execution context. The local state of __coro_fn__ is preserved and
59will be restored upon transferring execution control back to __coro_fn__
60to calculate the next Fibonacci number.
61Because __pull_coro__ provides input iterators and __begin__/__end__ are
62overloaded, a ['range-based for]-loop can be used to iterate over the generated
63Fibonacci numbers.
64
65
66[heading __push_coro__]
67__push_coro__ transfers data to the other execution context (== pushed-to).
68The template parameter defines the transferred parameter type.
69The constructor of __push_coro__ takes a function (__coro_fn__) accepting a
70reference to an __pull_coro__ as argument. In contrast to __pull_coro__,
71instantiating an __push_coro__ does not pass the control of execution to
72__coro_fn__ - instead the first call of __push_coro_op__ synthesizes a
73complementary __pull_coro__ and passes it as reference to __coro_fn__.
74
75The __push_coro__ interface does not contain a ['get()]-function: you can not retrieve
76values from another execution context with this kind of coroutine.
77
78__push_coro__ provides output iterators (__push_coro_it__) and
79__begin__/__end__ are overloaded. The increment-operation switches the context
80and transfers data.
81
82        typedef boost::coroutines2::coroutine<std::string>   coro_t;
83
84        struct FinalEOL{
85            ~FinalEOL(){
86                std::cout << std::endl;
87            }
88        };
89
90        const int num=5, width=15;
91        coro_t::push_type writer(
92            [&](coro_t::pull_type& in){
93                // finish the last line when we leave by whatever means
94                FinalEOL eol;
95                // pull values from upstream, lay them out 'num' to a line
96                for (;;){
97                    for(int i=0;i<num;++i){
98                        // when we exhaust the input, stop
99                        if(!in) return;
100                        std::cout << std::setw(width) << in.get();
101                        // now that we've handled this item, advance to next
102                        in();
103                    }
104                    // after 'num' items, line break
105                    std::cout << std::endl;
106                }
107            });
108
109        std::vector<std::string> words{
110            "peas", "porridge", "hot", "peas",
111            "porridge", "cold", "peas", "porridge",
112            "in", "the", "pot", "nine",
113            "days", "old" };
114
115        std::copy(begin(words),end(words),begin(writer));
116
117        output:
118                   peas       porridge            hot           peas       porridge
119                   cold           peas       porridge             in            the
120                    pot           nine           days            old
121
122In this example an __push_coro__ is created in the main execution context
123accepting a lambda function (== __coro_fn__) which requests strings and lays out
124'num' of them on each line.
125This demonstrates the inversion of control permitted by coroutines. Without
126coroutines, a utility function to perform the same job would necessarily
127accept each new value as a function parameter, returning after processing that
128single value. That function would depend on a static state variable. A
129__coro_fn__, however, can request each new value as if by calling a function
130-- even though its caller also passes values as if by calling a function.
131The __coro_fn__ is executed in a newly created execution context which is
132managed by the instance of __push_coro__.
133The main execution context passes the strings to the __coro_fn__ by calling
134__push_coro_op__.
135An __pull_coro__ instance is automatically generated by the library and passed as
136reference to the lambda function. The __coro_fn__ accesses the strings passed
137from the main execution context by calling __pull_coro_get__ and lays those
138strings out on ['std::cout] according the parameters 'num' and 'width'.
139The local state of __coro_fn__ is preserved and will be restored after
140transferring execution control back to __coro_fn__.
141Because __push_coro__ provides output iterators and __begin__/__end__ are
142overloaded, the ['std::copy] algorithm can be used to iterate over the vector
143containing the strings and pass them one by one to the coroutine.
144
145
146[heading coroutine-function]
147The __coro_fn__ returns ['void] and takes its counterpart-coroutine as
148argument, so that using the coroutine passed as argument to __coro_fn__ is the
149only way to transfer data and execution control back to the caller.
150Both coroutine types take the same template argument.
151For __pull_coro__ the __coro_fn__ is entered at __pull_coro__ construction.
152For __push_coro__ the __coro_fn__ is not entered at __push_coro__ construction
153but entered by the first invocation of __push_coro_op__.
154After execution control is returned from __coro_fn__ the state of the
155coroutine can be checked via __pull_coro_bool__ returning `true` if the
156coroutine is still valid (__coro_fn__ has not terminated). Unless the first
157template parameter is `void`, `true` also implies that a data value is
158available.
159
160
161[heading passing data from a pull-coroutine to main-context]
162In order to transfer data from an __pull_coro__ to the main-context the framework
163synthesizes an __push_coro__ associated with the __pull_coro__ instance in the
164main-context. The synthesized __push_coro__ is passed as argument to __coro_fn__.
165The __coro_fn__ must call this __push_coro_op__ in order to transfer each
166data value back to the main-context.
167In the main-context, the __pull_coro_bool__ determines whether the coroutine is
168still valid and a data value is available or __coro_fn__ has terminated
169(__pull_coro__ is invalid; no data value available). Access to the transferred
170data value is given by __pull_coro_get__.
171
172        typedef boost::coroutines2::coroutine<int>   coro_t;
173
174        coro_t::pull_type source( // constructor enters coroutine-function
175            [&](coro_t::push_type& sink){
176                sink(1); // push {1} back to main-context
177                sink(1); // push {1} back to main-context
178                sink(2); // push {2} back to main-context
179                sink(3); // push {3} back to main-context
180                sink(5); // push {5} back to main-context
181                sink(8); // push {8} back to main-context
182            });
183
184        while(source){            // test if pull-coroutine is valid
185            int ret=source.get(); // access data value
186            source();             // context-switch to coroutine-function
187        }
188
189
190[heading passing data from main-context to a push-coroutine]
191In order to transfer data to an __push_coro__ from the main-context the framework
192synthesizes an __pull_coro__ associated with the __push_coro__ instance in the
193main-context. The synthesized __pull_coro__ is passed as argument to __coro_fn__.
194The main-context must call this __push_coro_op__ in order to transfer each data
195value into the __coro_fn__.
196Access to the transferred data value is given by __pull_coro_get__.
197
198        typedef boost::coroutines2::coroutine<int>   coro_t;
199
200        coro_t::push_type sink( // constructor does NOT enter coroutine-function
201            [&](coro_t::pull_type& source){
202                for (int i:source) {
203                    std::cout << i <<  " ";
204                }
205            });
206
207        std::vector<int> v{1,1,2,3,5,8,13,21,34,55};
208        for( int i:v){
209            sink(i); // push {i} to coroutine-function
210        }
211
212
213[heading accessing parameters]
214Parameters returned from or transferred to the __coro_fn__ can be accessed with
215__pull_coro_get__.
216
217Splitting-up the access of parameters from context switch function enables to
218check if __pull_coro__ is valid after return from __pull_coro_op__, e.g.
219__pull_coro__ has values and __coro_fn__ has not terminated.
220
221        typedef boost::coroutines2::coroutine<boost::tuple<int,int>> coro_t;
222
223        coro_t::push_type sink(
224            [&](coro_t::pull_type& source){
225                // access tuple {7,11}; x==7 y==1
226                int x,y;
227                boost::tie(x,y)=source.get();
228            });
229
230        sink(boost::make_tuple(7,11));
231
232
233[heading exceptions]
234An exception thrown inside an __pull_coro__'s __coro_fn__ before its first call
235to __push_coro_op__ will be re-thrown by the __pull_coro__ constructor. After an
236__pull_coro__'s __coro_fn__'s first call to __push_coro_op__, any subsequent
237exception inside that __coro_fn__ will be re-thrown by __pull_coro_op__.
238__pull_coro_get__ does not throw.
239
240An exception thrown inside an __push_coro__'s __coro_fn__ will be re-thrown by
241__push_coro_op__.
242
243[important Code executed by __coro_fn__ must not prevent the propagation of the
244__forced_unwind__ exception.  Absorbing that exception will cause stack
245unwinding to fail.  Thus, any code that catches all exceptions must re-throw any
246pending __forced_unwind__ exception.]
247
248        try {
249            // code that might throw
250        } catch(const boost::coroutines2::detail::forced_unwind&) {
251            throw;
252        } catch(...) {
253            // possibly not re-throw pending exception
254        }
255
256[important Do not jump from inside a catch block and than re-throw the
257exception in another execution context.]
258
259
260[heading Stack unwinding]
261Sometimes it is necessary to unwind the stack of an unfinished coroutine to
262destroy local stack variables so they can release allocated resources (RAII
263pattern). The `attributes` argument of the coroutine constructor
264indicates whether the destructor should unwind the stack (stack is unwound by
265default).
266
267Stack unwinding assumes the following preconditions:
268
269* The coroutine is not __not_a_coro__
270* The coroutine is not complete
271* The coroutine is not running
272* The coroutine owns a stack
273
274After unwinding, a __coro__ is complete.
275
276        struct X {
277            X(){
278                std::cout<<"X()"<<std::endl;
279            }
280
281            ~X(){
282                std::cout<<"~X()"<<std::endl;
283            }
284        };
285
286        {
287            typedef boost::coroutines2::coroutine<void>::push_type   coro_t;
288
289            coro_t::push_type sink(
290                [&](coro_t::pull_type& source){
291                    X x;
292                    for(int=0;;++i){
293                        std::cout<<"fn(): "<<i<<std::endl;
294                        // transfer execution control back to main()
295                        source();
296                    }
297                });
298
299            sink();
300            sink();
301            sink();
302            sink();
303            sink();
304
305            std::cout<<"sink is complete: "<<std::boolalpha<<!sink<<"\n";
306        }
307
308        output:
309            X()
310            fn(): 0
311            fn(): 1
312            fn(): 2
313            fn(): 3
314            fn(): 4
315            fn(): 5
316            sink is complete: false
317            ~X()
318
319
320[heading Range iterators]
321__boost_coroutine__ provides output- and input-iterators using __boost_range__.
322__pull_coro__ can be used via input-iterators using __begin__ and __end__.
323
324        typedef boost::coroutines2::coroutine< int > coro_t;
325
326        int number=2,exponent=8;
327        coro_t::pull_type source(
328            [&](coro_t::push_type & sink){
329                int counter=0,result=1;
330                while(counter++<exponent){
331                    result=result*number;
332                    sink(result);
333                }
334            });
335
336        for (auto i:source)
337            std::cout << i <<  " ";
338
339        output:
340            2 4 8 16 32 64 128 256
341
342['coroutine<>::pull_type::iterator::operator++()] corresponds to
343__pull_coro_op__; ['coroutine<>::pull_type::iterator::operator*()]
344roughly corresponds to __pull_coro_get__. An iterator originally obtained from
345__begin__ of an __pull_coro__ compares equal to an iterator obtained from
346__end__ of that same __pull_coro__ instance when its __pull_coro_bool__ would
347return `false`].
348
349[note If `T` is a move-only type, then
350['coroutine<T>::pull_type::iterator] may only be dereferenced once
351before it is incremented again.]
352
353Output-iterators can be created from __push_coro__.
354
355        typedef boost::coroutines2::coroutine<int>   coro_t;
356
357        coro_t::push_type sink(
358            [&](coro_t::pull_type& source){
359                while(source){
360                    std::cout << source.get() <<  " ";
361                    source();
362                }
363            });
364
365        std::vector<int> v{1,1,2,3,5,8,13,21,34,55};
366        std::copy(begin(v),end(v),begin(sink));
367
368['coroutine<>::push_type::iterator::operator*()] roughly
369corresponds to __push_coro_op__. An iterator originally obtained from
370__begin__ of an __push_coro__ compares equal to an iterator obtained from
371__end__ of that same __push_coro__ instance when its __push_coro_bool__ would
372return `false`.
373
374
375[heading Exit a __coro_fn__]
376__coro_fn__ is exited with a simple return statement jumping back to the calling
377routine. The __pull_coro__, __push_coro__ becomes complete, e.g. __pull_coro_bool__,
378__push_coro_bool__ will return `false`.
379
380[important After returning from __coro_fn__ the __coro__ is complete (can not
381resumed with __push_coro_op__, __pull_coro_op__).]
382
383
384
385[section:pull_coro Class `coroutine<>::pull_type`]
386
387    #include <boost/coroutine2/coroutine.hpp>
388
389    template< typename R >
390    class coroutine<>::pull_type
391    {
392    public:
393        template< typename Fn >
394        pull_type( Fn && fn);
395
396        template< typename StackAllocator, typename Fn >
397        pull_type( StackAllocator stack_alloc, Fn && fn);
398
399        pull_type( pull_type const& other)=delete;
400
401        pull_type & operator=( pull_type const& other)=delete;
402
403        ~pull_type();
404
405        pull_type( pull_type && other) noexcept;
406
407        pull_type & operator=( pull_type && other) noexcept;
408
409        pull_coroutine & operator()();
410
411        explicit operator bool() const noexcept;
412
413        bool operator!() const noexcept;
414
415        R get() noexcept;
416    };
417
418    template< typename R >
419    range_iterator< pull_type< R > >::type begin( pull_type< R > &);
420
421    template< typename R >
422    range_iterator< pull_type< R > >::type end( pull_type< R > &);
423
424[heading `template< typename Fn >
425          pull_type( Fn && fn)`]
426[variablelist
427[[Effects:] [Creates a coroutine which will execute `fn`, and enters it.]]
428[[Throws:] [Exceptions thrown inside __coro_fn__.]]
429]
430
431[heading `template< typename StackAllocator, typename Fn >
432          pull_type( StackAllocator const& stack_alloc, Fn && fn)`]
433[variablelist
434[[Effects:] [Creates a coroutine which will execute `fn`.
435For allocating/deallocating the stack `stack_alloc` is used.]]
436[[Throws:] [Exceptions thrown inside __coro_fn__.]]
437]
438
439[heading `~pull_type()`]
440[variablelist
441[[Effects:] [Destroys the context and deallocates the stack.]]
442]
443
444[heading `pull_type( pull_type && other)`]
445[variablelist
446[[Effects:] [Moves the internal data of `other` to `*this`.
447`other` becomes __not_a_coro__.]]
448[[Throws:] [Nothing.]]
449]
450
451[heading `pull_type & operator=( pull_type && other)`]
452[variablelist
453[[Effects:] [Destroys the internal data of `*this` and moves the
454internal data of `other` to `*this`. `other` becomes __not_a_coro__.]]
455[[Throws:] [Nothing.]]
456]
457
458[heading `explicit operator bool() const noexcept`]
459[variablelist
460[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
461has returned (completed), the function returns `false`. Otherwise `true`.]]
462[[Throws:] [Nothing.]]
463]
464
465[heading `bool operator!() const noexcept`]
466[variablelist
467[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
468has returned (completed), the function returns `true`. Otherwise `false`.]]
469[[Throws:] [Nothing.]]
470]
471
472[heading `pull_type<> & operator()()`]
473[variablelist
474[[Preconditions:] [`*this` is not a __not_a_coro__.]]
475[[Effects:] [Execution control is transferred to __coro_fn__ (no parameter is
476passed to the coroutine-function).]]
477[[Throws:] [Exceptions thrown inside __coro_fn__.]]
478]
479
480[heading `R get() noexcept`]
481
482    R    coroutine<R,StackAllocator>::pull_type::get();
483    R&   coroutine<R&,StackAllocator>::pull_type::get();
484    void coroutine<void,StackAllocator>::pull_type::get()=delete;
485
486[variablelist
487[[Preconditions:] [`*this` is not a __not_a_coro__.]]
488[[Returns:] [Returns data transferred from coroutine-function via
489__push_coro_op__.]]
490[[Throws:] [`invalid_result`]]
491[[Note:] [If `R` is a move-only type, you may only call `get()` once before
492the next __pull_coro_op__ call.]]
493]
494
495[heading Non-member function `begin( pull_type< R > &)`]
496    template< typename R >
497    range_iterator< pull_type< R > >::type begin( pull_type< R > &);
498
499[variablelist
500[[Returns:] [Returns a range-iterator (input-iterator).]]
501]
502
503[heading Non-member function `end( pull_type< R > &)`]
504    template< typename R >
505    range_iterator< pull_type< R > >::type end( pull_type< R > &);
506
507[variablelist
508[[Returns:] [Returns an end range-iterator (input-iterator).]]
509[[Note:] [When first obtained from `begin( pull_type< R > &)`, or after some
510number of increment operations, an iterator will compare equal to the iterator
511returned by `end( pull_type< R > &)` when the corresponding __pull_coro_bool__
512would return `false`.]]
513]
514
515[endsect]
516
517
518[section:push_coro Class `coroutine<>::push_type`]
519
520    #include <boost/coroutine2/coroutine.hpp>
521
522    template< typename Arg >
523    class coroutine<>::push_type
524    {
525    public:
526        template< typename Fn >
527        push_type( Fn && fn);
528
529        template< typename StackAllocator, typename Fn >
530        push_type( StackAllocator stack_alloc, Fn && fn);
531
532        push_type( push_type const& other)=delete;
533
534        push_type & operator=( push_type const& other)=delete;
535
536        ~push_type();
537
538        push_type( push_type && other) noexcept;
539
540        push_type & operator=( push_type && other) noexcept;
541
542        explicit operator bool() const noexcept;
543
544        bool operator!() const noexcept;
545
546        push_type & operator()( Arg arg);
547    };
548
549    template< typename Arg >
550    range_iterator< push_type< Arg > >::type begin( push_type< Arg > &);
551
552    template< typename Arg >
553    range_iterator< push_type< Arg > >::type end( push_type< Arg > &);
554
555[heading `template< typename Fn >
556          push_type( Fn && fn)`]
557[variablelist
558[[Effects:] [Creates a coroutine which will execute `fn`.]]
559]
560
561[heading `template< typename StackAllocator, typename Fn >
562          push_type( StackAllocator const& stack_alloc, Fn && fn)`]
563[variablelist
564[[Effects:] [Creates a coroutine which will execute `fn`.
565For allocating/deallocating the stack `stack_alloc` is used.]]
566]
567
568[heading `~push_type()`]
569[variablelist
570[[Effects:] [Destroys the context and deallocates the stack.]]
571]
572
573[heading `push_type( push_type && other) noexcept`]
574[variablelist
575[[Effects:] [Moves the internal data of `other` to `*this`.
576`other` becomes __not_a_coro__.]]
577[[Throws:] [Nothing.]]
578]
579
580[heading `push_type & operator=( push_type && other) noexcept`]
581[variablelist
582[[Effects:] [Destroys the internal data of `*this` and moves the
583internal data of `other` to `*this`. `other` becomes __not_a_coro__.]]
584[[Throws:] [Nothing.]]
585]
586
587[heading `explicit operator bool() const noexcept`]
588[variablelist
589[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
590has returned (completed), the function returns `false`. Otherwise `true`.]]
591[[Throws:] [Nothing.]]
592]
593
594[heading `bool operator!() const noexcept`]
595[variablelist
596[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
597has returned (completed), the function returns `true`. Otherwise `false`.]]
598[[Throws:] [Nothing.]]
599]
600
601[heading `push_type & operator()(Arg arg)`]
602
603        push_type& coroutine<Arg>::push_type::operator()(Arg);
604        push_type& coroutine<Arg&>::push_type::operator()(Arg&);
605        push_type& coroutine<void>::push_type::operator()();
606
607[variablelist
608[[Preconditions:] [operator unspecified-bool-type() returns `true` for `*this`.]]
609[[Effects:] [Execution control is transferred to __coro_fn__ and the argument
610`arg` is passed to the coroutine-function.]]
611[[Throws:] [Exceptions thrown inside __coro_fn__.]]
612]
613
614[heading Non-member function `begin( push_type< Arg > &)`]
615    template< typename Arg >
616    range_iterator< push_type< Arg > >::type begin( push_type< Arg > &);
617
618[variablelist
619[[Returns:] [Returns a range-iterator (output-iterator).]]
620]
621
622[heading Non-member function `end( push_type< Arg > &)`]
623    template< typename Arg >
624    range_iterator< push_type< Arg > >::type end( push_type< Arg > &);
625
626[variablelist
627[[Returns:] [Returns a end range-iterator (output-iterator).]]
628[[Note:] [When first obtained from `begin( push_type< R > &)`, or after some
629number of increment operations, an iterator will compare equal to the iterator
630returned by `end( push_type< R > &)` when the corresponding __push_coro_bool__
631would return `false`.]]
632]
633
634[endsect]
635
636
637
638[endsect]
639