source: libcfa/src/concurrency/coroutine.hfa @ 58d64a4

ADTarm-ehast-experimentalenumforall-pointer-decayjacob/cs343-translationnew-ast-unique-exprpthread-emulationqualifiedEnum
Last change on this file since 58d64a4 was 1c01c58, checked in by Andrew Beach <ajbeach@…>, 4 years ago

Rather large commit to get coroutine cancellation working.

This includes what you would expect, like new code in exceptions and a new
test, but it also includes a bunch of other things.

New coroutine state, currently just marks that the stack was cancelled. New
helpers for checking code structure and generating vtables. Changes to the
coroutine interface so resume may throw exceptions on cancellation, plus the
exception type that is thrown. Changes to the coroutine keyword generation to
generate exception code for each type of coroutine.

  • Property mode set to 100644
File size: 7.1 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// coroutine --
8//
9// Author           : Thierry Delisle
10// Created On       : Mon Nov 28 12:27:26 2016
11// Last Modified By : Peter A. Buhr
12// Last Modified On : Tue Feb  4 12:29:26 2020
13// Update Count     : 11
14//
15
16#pragma once
17
18#include <assert.h>
19#include "invoke.h"
20#include "../exception.hfa"
21
22//-----------------------------------------------------------------------------
23// Exception thrown from resume when a coroutine stack is cancelled.
24// Should not have to be be sized (see trac #196).
25FORALL_DATA_EXCEPTION(CoroutineCancelled,
26                (dtype coroutine_t | sized(coroutine_t)), (coroutine_t)) (
27        coroutine_t * the_coroutine;
28        exception_t * the_exception;
29);
30
31forall(dtype T)
32void mark_exception(CoroutineCancelled(T) *);
33
34forall(dtype T | sized(T))
35void copy(CoroutineCancelled(T) * dst, CoroutineCancelled(T) * src);
36
37forall(dtype T)
38const char * msg(CoroutineCancelled(T) *);
39
40//-----------------------------------------------------------------------------
41// Coroutine trait
42// Anything that implements this trait can be resumed.
43// Anything that is resumed is a coroutine.
44trait is_coroutine(dtype T | sized(T)
45                | is_resumption_exception(CoroutineCancelled(T))
46                | VTABLE_ASSERTION(CoroutineCancelled, (T))) {
47        void main(T & this);
48        $coroutine * get_coroutine(T & this);
49};
50
51#define DECL_COROUTINE(X) static inline $coroutine* get_coroutine(X& this) { return &this.__cor; } void main(X& this)
52
53//-----------------------------------------------------------------------------
54// Ctors and dtors
55// void ?{}( coStack_t & this );
56// void ^?{}( coStack_t & this );
57
58void  ?{}( $coroutine & this, const char name[], void * storage, size_t storageSize );
59void ^?{}( $coroutine & this );
60
61static inline void ?{}( $coroutine & this)                                       { this{ "Anonymous Coroutine", 0p, 0 }; }
62static inline void ?{}( $coroutine & this, size_t stackSize)                     { this{ "Anonymous Coroutine", 0p, stackSize }; }
63static inline void ?{}( $coroutine & this, void * storage, size_t storageSize )  { this{ "Anonymous Coroutine", storage, storageSize }; }
64static inline void ?{}( $coroutine & this, const char name[])                    { this{ name, 0p, 0 }; }
65static inline void ?{}( $coroutine & this, const char name[], size_t stackSize ) { this{ name, 0p, stackSize }; }
66
67//-----------------------------------------------------------------------------
68// Public coroutine API
69forall(dtype T | is_coroutine(T))
70void prime(T & cor);
71
72static inline struct $coroutine * active_coroutine() { return TL_GET( this_thread )->curr_cor; }
73
74//-----------------------------------------------------------------------------
75// PRIVATE exposed because of inline
76
77// Start coroutine routines
78extern "C" {
79        void __cfactx_invoke_coroutine(void (*main)(void *), void * this);
80
81        forall(dtype T)
82        void __cfactx_start(void (*main)(T &), struct $coroutine * cor, T & this, void (*invoke)(void (*main)(void *), void *));
83
84        extern void __cfactx_coroutine_unwind(struct _Unwind_Exception * storage, struct $coroutine *) __attribute__ ((__noreturn__));
85
86        extern void __cfactx_switch( struct __stack_context_t * from, struct __stack_context_t * to ) asm ("__cfactx_switch");
87}
88
89// Private wrappers for context switch and stack creation
90// Wrapper for co
91static inline void $ctx_switch( $coroutine * src, $coroutine * dst ) __attribute__((nonnull (1, 2))) {
92        // set state of current coroutine to inactive
93        src->state = src->state == Halted ? Halted : Blocked;
94
95        // set new coroutine that task is executing
96        TL_GET( this_thread )->curr_cor = dst;
97
98        // context switch to specified coroutine
99        verify( dst->context.SP );
100        __cfactx_switch( &src->context, &dst->context );
101        // when __cfactx_switch returns we are back in the src coroutine
102
103        // set state of new coroutine to active
104        src->state = Active;
105
106        if( unlikely(src->cancellation != 0p) ) {
107                __cfactx_coroutine_unwind(src->cancellation, src);
108        }
109}
110
111extern void __stack_prepare   ( __stack_info_t * this, size_t size /* ignored if storage already allocated */);
112
113// Suspend implementation inlined for performance
114extern "C" {
115        static inline void __cfactx_suspend(void) {
116                // optimization : read TLS once and reuse it
117                // Safety note: this is preemption safe since if
118                // preemption occurs after this line, the pointer
119                // will also migrate which means this value will
120                // stay in syn with the TLS
121                $coroutine * src = TL_GET( this_thread )->curr_cor;
122
123                assertf( src->last != 0,
124                        "Attempt to suspend coroutine \"%.256s\" (%p) that has never been resumed.\n"
125                        "Possible cause is a suspend executed in a member called by a coroutine user rather than by the coroutine main.",
126                        src->name, src );
127                assertf( src->last->state != Halted,
128                        "Attempt by coroutine \"%.256s\" (%p) to suspend back to terminated coroutine \"%.256s\" (%p).\n"
129                        "Possible cause is terminated coroutine's main routine has already returned.",
130                        src->name, src, src->last->name, src->last );
131
132                $ctx_switch( src, src->last );
133        }
134}
135
136forall(dtype T | is_coroutine(T))
137void __cfaehm_cancelled_coroutine( T & cor, $coroutine * desc );
138
139// Resume implementation inlined for performance
140forall(dtype T | is_coroutine(T))
141static inline T & resume(T & cor) {
142        // optimization : read TLS once and reuse it
143        // Safety note: this is preemption safe since if
144        // preemption occurs after this line, the pointer
145        // will also migrate which means this value will
146        // stay in syn with the TLS
147        $coroutine * src = TL_GET( this_thread )->curr_cor;
148        $coroutine * dst = get_coroutine(cor);
149
150        if( unlikely(dst->context.SP == 0p) ) {
151                TL_GET( this_thread )->curr_cor = dst;
152                __stack_prepare(&dst->stack, 65000);
153                __cfactx_start(main, dst, cor, __cfactx_invoke_coroutine);
154                TL_GET( this_thread )->curr_cor = src;
155        }
156
157        // not resuming self ?
158        if ( src != dst ) {
159                assertf( dst->state != Halted ,
160                        "Attempt by coroutine %.256s (%p) to resume terminated coroutine %.256s (%p).\n"
161                        "Possible cause is terminated coroutine's main routine has already returned.",
162                        src->name, src, dst->name, dst );
163
164                // set last resumer
165                dst->last = src;
166                dst->starter = dst->starter ? dst->starter : src;
167        }
168
169        // always done for performance testing
170        $ctx_switch( src, dst );
171        if ( unlikely(dst->cancellation) ) {
172                __cfaehm_cancelled_coroutine( cor, dst );
173        }
174
175        return cor;
176}
177
178static inline void resume( $coroutine * dst ) __attribute__((nonnull (1))) {
179        // optimization : read TLS once and reuse it
180        // Safety note: this is preemption safe since if
181        // preemption occurs after this line, the pointer
182        // will also migrate which means this value will
183        // stay in syn with the TLS
184        $coroutine * src = TL_GET( this_thread )->curr_cor;
185
186        // not resuming self ?
187        if ( src != dst ) {
188                assertf( dst->state != Halted ,
189                        "Attempt by coroutine %.256s (%p) to resume terminated coroutine %.256s (%p).\n"
190                        "Possible cause is terminated coroutine's main routine has already returned.",
191                        src->name, src, dst->name, dst );
192
193                // set last resumer
194                dst->last = src;
195        }
196
197        // always done for performance testing
198        $ctx_switch( src, dst );
199}
200
201// Local Variables: //
202// mode: c //
203// tab-width: 4 //
204// End: //
Note: See TracBrowser for help on using the repository browser.