forked from sba1/simplemail
-
Notifications
You must be signed in to change notification settings - Fork 0
/
coroutines.h
150 lines (126 loc) · 4.3 KB
/
coroutines.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
/**
* coroutines.c - simple coroutines for SimpleMail.
* Copyright (C) 2015 Sebastian Bauer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file coroutines.h
*
* This implements the coroutines basic functionality. This is highly inspired
* by http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html.
*/
#ifndef SM__COROUTINES_H
#define SM__COROUTINES_H
#ifndef SM__LISTS_H
#include "lists.h"
#endif
struct coroutine;
struct coroutine_basic_context;
/** The return values of a coroutine */
typedef enum {
COROUTINE_YIELD,
COROUTINE_WAIT,
COROUTINE_DONE
} coroutine_return_t;
/** A single coroutine */
typedef struct coroutine *coroutine_t;
typedef coroutine_return_t (*coroutine_entry_t)(struct coroutine_basic_context *arg);
struct coroutine_scheduler;
typedef struct coroutine_scheduler *coroutine_scheduler_t;
/**
* The basic context of a coroutine. This should be embedded in a higher
* context.
*/
struct coroutine_basic_context
{
/** The scheduler that is responsible for this context */
coroutine_scheduler_t scheduler;
/** The context should be automatically freed when coroutine was done */
int free_after_done;
/** The state that will be executed next for this coroutine */
int next_state;
/** The socket fd for a waiting coroutine */
int socket_fd;
/** Whether we wait for a reading or writing fd */
int write_mode;
/** Another coroutine we are waiting for */
coroutine_t other;
/** Function that checks if a switch from wait to ready is possible */
int (*is_now_ready)(coroutine_scheduler_t scheduler, coroutine_t cor);
};
#define COROUTINE_BEGIN(context) \
switch(context->basic_context.next_state)\
{ \
case 0:
/**
* Insert a simple preemption point. Continue on the next possible event.
*/
#define COROUTINE_YIELD(context)\
context->basic_context.next_state = __LINE__;\
return COROUTINE_YIELD;\
case __LINE__:\
/**
* Insert a preemption point but don't continue until the given coroutine
* is done.
*/
#define COROUTINE_AWAIT_OTHER(context, oth)\
context->basic_context.next_state = __LINE__;\
context->basic_context.other = oth;\
return COROUTINE_WAIT;\
case __LINE__:\
context->basic_context.other = NULL;
#define COROUTINE_END(context) \
}\
return COROUTINE_DONE;
/**
* Create a new scheduler for coroutines with a custom wait for event callback.
*
* @param wait_for_event a function that is called for looking for new events. If
* polling is set to 1, wait_for_event() should not block. Otherwise, wait_for_event()
* may block.
* @return the scheduler nor NULL for an error.
*/
coroutine_scheduler_t coroutine_scheduler_new_custom(int (*wait_for_event)(coroutine_scheduler_t sched, int poll, void *udata), void *udata);
/**
* Execute the current set of ready coroutines.
*
* @param scheduler
* @return 0 if the ready queue is empty, 1 otherwise.
*/
int coroutine_schedule_ready(coroutine_scheduler_t scheduler);
/**
* Dispose the given scheduler. Does not check if there are any coroutines
* left.
*
* @param scheduler defines the scheduler to be disposed.
*/
void coroutine_scheduler_dispose(coroutine_scheduler_t scheduler);
/**
* Add a new coroutine to the scheduler.
*
* @param scheduler the schedule that should take care of the coroutine.
* @param entry the coroutine's entry
* @param context the coroutine's context
* @return the coroutine just added or NULL for an error.
*/
coroutine_t coroutine_add(coroutine_scheduler_t scheduler, coroutine_entry_t entry, struct coroutine_basic_context *context);
/**
* Schedule all coroutines once and execute possible state transitions.
*
* @param scheduler the scheduler
* @return whether there are unfinished coroutines
*/
int coroutine_schedule(coroutine_scheduler_t scheduler);
#endif