-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathcoroutine.hpp
More file actions
154 lines (117 loc) · 4.53 KB
/
coroutine.hpp
File metadata and controls
154 lines (117 loc) · 4.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/// @file
#ifndef coroutine_H
#define coroutine_H
#include <cstdint>
#include "hwlib.hpp"
#include "switch_to.hpp"
template< int32_t N = 0 > class coroutine;
class coroutine_context {
friend class coroutine< 0 >;
// a place to store the SP of the main 'couroutine'
static int32_t main_sp;
// pointer to the SP of the currently running coroutine
// Initially it points to a the main_sp
static int32_t * current_coroutine_sp_ptr;
};
/// coroutine class
//
/// This class implements a coroutine: an independent thread of execution
/// that can pass execution to another coroutine by calling resume() on
/// that coroutine.
/// This can be used as the basis for a co-operative multithreading system.
template<> class coroutine< 0 > {
private:
// place to store the SP of this coroutine when it is suspended
int32_t sp;
// pointer to the stack area used by this coroutine
int32_t * stack;
// size of *stack in integers
const size_t int_stack_size;
// marker for unused stack locations
static const int32_t marker = 0xDEADBEEF;
public:
/// the size (in bytes) of the stack allocated for this coroutine
const size_t stack_size;
protected:
coroutine( void body( void ), int32_t * stack, size_t int_stack_size ):
stack( stack ),
int_stack_size( int_stack_size ),
stack_size( 4 * int_stack_size )
{
// fill all stack locations with the marker
for( size_t i = 0; i < int_stack_size; ++i ){
stack[ i ] = marker;
}
// create a 'fake' initial Cortex stack frame,
// compatible with the switch_to.asm code
stack[ int_stack_size - 1 ] = reinterpret_cast< int32_t >( body );
sp = reinterpret_cast< int32_t >( & stack[ int_stack_size - 10 ] );
}
public:
/// pass execution to this coroutine
//
/// Call this function on a coroutine object to activate (start or resume)
/// execution of that coroutine. The current coroutine is suspended.
/// Execution of a coroutine starts by execution its body function
/// (the function that was passed to its constructor).
/// Resuming a coroutine continues
/// execution after the resume() call that suspended it.
///
/// Initially, the main is executing,
/// so it must issue the first resume() call on a coroutine.
/// A coroutine body function must not return.
void resume(){
// this is where the sp must be stored.
// save it, because we must overwrite it in the next statement
auto store_old_sp = coroutine_context::current_coroutine_sp_ptr;
// next time, the SP must store in the sp of the current object
coroutine_context::current_coroutine_sp_ptr = & sp;
// store the old sp and switch to this coroutine
switch_from_to( store_old_sp, sp );
}
/// pass execution to the main 'coroutine'
//
/// Call this function to re-activate (resume) the main 'coroutine'.
/// The current coroutine is suspended.
static void resume_main(){
// this is where the sp must be stored.
// save it, because we must overwrite it for the next resume()
auto store_old_sp = coroutine_context::current_coroutine_sp_ptr;
// next time, the SP must store in the sp of the main
coroutine_context::current_coroutine_sp_ptr = & coroutine_context::main_sp;
// store the old sp and switch to this coroutine
switch_from_to( store_old_sp, coroutine_context::main_sp );
}
/// return number of unused stack bytes
int stack_free(){
// count the unused entries, starting at the bottom
int n = 0;
while( stack[ n ] == marker ){
n++;
}
// return the number of unused bytes
return 4 * n;
}
/// return number of used stack bytes
int stack_used(){
return stack_size - stack_free();
}
};
template< int32_t N > class coroutine : public coroutine<> {
private:
// convert N to a number of integers, round up
static const size_t int_stack_size = ( N + 3 ) / 4;
// the room for the stack
int32_t stack[ int_stack_size ];
public:
/// construct a coroutine from its stack size and main function
//
/// This constructor creates a coroutine from its stack size
/// (this is the template parameter, in bytes),
/// and a function that is its body.
/// The body function must not return.
coroutine( void body( void ) ):
coroutine< 0 >( body, stack, int_stack_size )
{}
};
#endif // coroutine_H