forked from ldmud/ldmud
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcoroutine.h
169 lines (150 loc) · 6.24 KB
/
coroutine.h
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*---------------------------------------------------------------------------
* Coroutines
*
*---------------------------------------------------------------------------
* These is the runtime implementation of coroutines.
*/
#ifndef COROUTINE_H__
#define COROUTINE_H__
#include "driver.h"
#include "typedefs.h"
#include "svalue.h"
/* --- Coroutine Configuration --- */
enum
{
CR_RESERVED_EXTRA_VALUES = 5,
/* Reserved space for extra values in the
* coroutine object. Must be at least 2.
*/
};
/* --- Coroutine State ---
*
* The current state of a coroutine.
*/
enum coroutine_state
{
CS_RUNNING, /* Coroutine is currently running
* (i.e. somewhere in the call stack).
*/
CS_AWAITING, /* Coroutine is sleeping and waiting
* for another coroutine to finish.
*/
CS_SLEEPING, /* Coroutine is just sleeping.
*/
CS_FINISHED, /* Coroutine has finished,
* all references should turn to zero.
*/
};
/* --- Coroutine ---
*
* A coroutine encapsulates the state of a suspended function.
*
* Local variables and stack entries are saved in .variables[].
* There is space for .num_variables + CR_RESERVED_EXTRA_VALUES values.
* If more values need to be saved (.num_values > CR_RESERVED_EXTRA_VALUES),
* .variables[.num_variables].u.lvalue will point to an
* memory block that contains all .num_values values,
* .variables[.num_variables+1].u.number contains the size
* of the extra memory block.
*
* All waiting coroutines will form a doubly linked list. They will be
* freed all or none. So even if the reference count of one coroutine
* drops to zero, it will not be deallocated if it's in a list with
* other coroutines that have a non-zero reference count. The list fields
* (.awaiter and .awaitee) are therefore also not counted references.
*
* If pragma save_local_names is active, the compiler will store the names
* of local variables at each suspension point. The number of those is
* saved in .num_variable_names. So to get these names, you'll have to
* look at .pc - 2*.num_variable_names to read the indices (short) into
* the program's strings.
*/
struct coroutine_s
{
int ref; /* Reference count. */
enum coroutine_state state; /* State of the coroutine. */
svalue_t ob; /* The corresponding object. */
program_t *prog; /* Current program. */
lambda_t *closure; /* Inline closure. */
bytecode_p funstart; /* Start of the function code. */
bytecode_p pc; /* Program counter. */
int function_index_offset; /* Index of .prog's function block
* within .ob's program.
*/
int variable_index_offset; /* Same for variables. */
int num_variables; /* Number of local variables. */
int num_values; /* Number of extra values. */
int num_variable_names; /* Number of variable names. */
#ifdef DEBUG
int num_hidden_variables; /* Number of hidden temporary
* variables (eg. in a foreach.
*/
#endif
coroutine_t *awaiter; /* The coroutine waiting for me. */
coroutine_t *awaitee; /* The coroutine I'm waiting for. */
svalue_t *last_frame; /* Last stack frame. */
svalue_t variables[]; /* The variables. */
};
extern long num_coroutines;
/* Number of coroutine_s. */
extern long total_coroutine_size;
/* Size of all coroutine_s. */
extern void _free_coroutine(coroutine_t *cr);
extern coroutine_t *create_coroutine(svalue_t *closure);
extern bool suspend_coroutine(coroutine_t *cr, svalue_t *fp);
extern coroutine_t *get_resumable_coroutine(coroutine_t* cr);
extern bool resume_coroutine(coroutine_t *cr);
extern void await_coroutine(coroutine_t *awaiter, coroutine_t *awaitee);
extern coroutine_t *finish_coroutine(coroutine_t *cr);
extern void abort_coroutine(coroutine_t *cr);
extern bool valid_coroutine(coroutine_t *cr);
extern string_t* coroutine_to_string(coroutine_t *cr);
/* coroutine_t *ref_coroutine(coroutine_t *cr)
* Add another ref to <cr> and return the coroutine_t <cr>.
*/
static INLINE coroutine_t *ref_coroutine(coroutine_t *cr)
{
if (cr && cr->ref >= 0)
cr->ref++;
return cr;
}
/* void free_coroutine(coroutine_t *cr)
* Remove one ref from <cr>, and free the coroutine fully if
* the refcount reaches zero.
*/
static INLINE void free_coroutine(coroutine_t *cr)
{
if (cr && cr->ref && !--(cr->ref))
_free_coroutine(cr);
}
static INLINE void put_ref_coroutine(svalue_t * const dest, coroutine_t * const cr)
__attribute__((nonnull(1,2)));
static INLINE void put_ref_coroutine(svalue_t * const dest, coroutine_t * const cr)
/* Put the coroutine <cr> into <dest>, which is considered empty,
* and increment the refcount of <cr>.
*/
{
*dest = svalue_coroutine(ref_coroutine(cr));
}
static INLINE void put_ref_valid_coroutine(svalue_t * const dest, coroutine_t * const cr)
__attribute__((nonnull(1,2)));
static INLINE void put_ref_valid_coroutine(svalue_t * const dest, coroutine_t * const cr)
/* Put the coroutine <cr> into <dest>, which is considered empty,
* and increment the refcount of <cr>. If <cr> is already finished,
* put the number 0 there instead.
*/
{
if (valid_coroutine(cr))
*dest = svalue_coroutine(ref_coroutine(cr));
else
put_number(dest, 0);
}
#define push_ref_coroutine(sp,val) put_ref_coroutine(++(sp),val)
#define push_ref_valid_coroutine(sp,val) put_ref_valid_coroutine(++(sp),val)
#ifdef GC_SUPPORT
extern coroutine_t* new_sample_coroutine();
extern void free_sample_coroutine(coroutine_t* cr);
extern void clear_coroutine_ref(coroutine_t *cr);
extern void count_coroutine_ref(coroutine_t *cr);
#endif /* GC_SUPPORT */
#endif /* COROUTINE_H__ */