[yc]用户态非抢占式线程库实现
平台 i386 , win32 , msvc 2003
代码简单介绍:
调度算法:轮转法。。,可修改
内存模型:每个线程拥有各自独立的堆栈。启动线程的时候,切换到对应的堆栈再启动,使得线程之间的堆栈互不干扰
调度方式:线程调用 schedule 函数, schedule 用 setjmp 保存当前堆栈,选择一个新的线程之后用 longjmp 跳转过去。
线程退出:线程函数的返回即意味着线程的退出,可以在线程函数的任何位置上退出。退出后返回到 start_thread 函数里,此后该函数将退出线程的 slot 从链表里删除,如果还有别的线程那么再继续调度,否则跳转出最外层。
堆栈释放:由于线程退出的时候堆栈还在使用,因此无法释放堆栈,所以采用延后一个调度周期的办法,等线程完全结束之后,下一次调用 schedule 的时候释放。
问题:切换线程的时候, longjmp 不会恢复通用寄存器的值,因此要么函数内的局部变量都加上 volatile ,要么在 setjmp 之前手动保存, longjmp 之后手动恢复(可以在库的实现方完成,但是会增大不可移植的面积,现在暂不考虑加入)。
代码:
1
//
Cothread.h
2
#include
<
setjmp.h
>
3
4
typedef void (
*
thread_func_t)(void
*
);
5
6
typedef struct sched_slot
7
{
8
jmp_buf buf;
9
int
has_set;
10
thread_func_t thread;
11
void
*
arg;
12
char
*
stack;
13
} sched_slot;
14
15
16
typedef struct slot_list
17
{
18
sched_slot slot;
19
struct slot_list
*
next
;
20
} slot_list;
21
22
typedef struct sched_system
23
{
24
slot_list
*
threads;
25
slot_list
*
current;
26
int
main_esp, main_ebp;
27
char
*
unfreed_stack;
28
int
retaddr;
29
} sched_system;
30
31
32
extern sched_system sys;
33
34
void reg_thread(thread_func_t f, void
*
arg);
35
36
void free_unfree_stack();
37
38
void schedule();
39
40
void start_first_thread();
41
42
43
44
45
46
47
//
cothread.c
48
#include
<
assert.h
>
49
#include
<
stdlib.h
>
50
#include
<
setjmp.h
>
51
#include
"
cothread.h
"
52
#define CHANGE_STACK(newaddr) _asm mov esp, newaddr
53
#define STACK_SIZE
65536
54
#define RESERVED_STACK
4
55
56
57
58
59
60
61
sched_system sys;
62
63
64
void reg_thread(thread_func_t f, void
*
arg)
65
{
66
slot_list
*
new_thread
=
(slot_list
*
)malloc(sizeof(slot_list));
67
new_thread
->
next
=
sys.threads;
68
sys.threads
=
new_thread;
69
new_thread
->
slot.arg
=
arg;
70
new_thread
->
slot.has_set
=
0
;
71
new_thread
->
slot.stack
=
0
;
72
new_thread
->
slot.thread
=
f;
73
}
74
75
76
void free_unfree_stack()
77
{
78
if
(sys.unfreed_stack)
79
{
80
free(sys.unfreed_stack);
81
sys.unfreed_stack
=
0
;
82
}
83
}
84
void start_thread(slot_list
*
iter);
85
86
87
void schedule()
88
{
89
slot_list
*
old;
90
free_unfree_stack();
91
old
=
sys.current;
92
sys.current
=
sys.current
->
next
;
93
if
(!sys.current)
94
{
95
sys.current
=
sys.threads;
96
}
97
98
if
(!setjmp(old
->
slot.buf))
99
{
100
old
->
slot.has_set
=
1
;
101
102
if
(sys.current
->
slot.has_set)
103
longjmp(sys.current
->
slot.buf,
1
);
104
else
105
start_thread(sys.current);
106
107
}
108
}
109
110
111
static void exit_thread()
112
{
113
slot_list
*
iter;
114
free_unfree_stack();
115
if
(sys.current
==
sys.threads)
116
{
117
sys.threads
=
sys.threads
->
next
;
118
sys.unfreed_stack
=
sys.current
->
slot.stack;
119
free(sys.current);
120
sys.current
=
sys.threads;
121
}
122
else
123
{
124
125
for
(iter
=
sys.threads; iter
&&
iter
->
next
!
=
sys.current
&&
iter
->
next
!
=
0
; iter
=
iter
->
next
)
126
;
127
assert (iter
&&
iter
->
next
==
sys.current);
128
iter
->
next
=
sys.current
->
next
;
129
sys.unfreed_stack
=
sys.current
->
slot.stack;
130
free(sys.current);
131
sys.current
=
iter
->
next
;
132
}
133
134
if
(sys.current
==
0
)
135
{
136
sys.current
=
sys.threads;
137
}
138
139
if
(sys.current)
140
{
141
142
if
(sys.current
->
slot.has_set)
143
longjmp(sys.current
->
slot.buf,
1
);
144
else
145
start_thread(sys.current);
146
}
147
}
148
149
static jmp_buf buf;
150
151
static void start_thread(slot_list
*
iter)
152
{
153
char
*
stack_btm;
154
static thread_func_t thread;
155
static void
*
arg;
156
157
iter
->
slot.stack
=
(char
*
)malloc(STACK_SIZE
+
RESERVED_STACK);
158
stack_btm
=
iter
->
slot.stack
+
STACK_SIZE;
159
thread
=
iter
->
slot.thread;
160
arg
=
iter
->
slot.arg;
161
CHANGE_STACK(stack_btm);
162
thread(arg);
163
if
(sys.threads
->
next
)
164
exit_thread();
165
else
166
{
167
sys.unfreed_stack
=
sys.threads
->
slot.stack;
168
free(sys.threads);
169
longjmp(buf,
1
);
170
}
171
}
172
173
void start_first_thread()
174
{
175
if
(!setjmp(buf))
176
{
177
sys.current
=
sys.threads;
178
start_thread(sys.current);
179
}
180
free_unfree_stack();
181
}
182
183
184
185
186
187
//
测试代码
188
//
test.c
189
190
191
#include
<
stdio.h
>
192
#include
<
Windows.h
>
193
#include
"
cothread.h
"
194
void f0(void
*
p)
195
{
196
register
int
i;
197
for
(i
=
0
; i
<
3
;
++
i)
198
{
199
printf(
"
%d, %d\n
"
,
0
, i);
200
Sleep(
200
);
201
schedule();
202
}
203
//
exit_thread();
204
}
205
206
void f1(void
*
p)
207
{
208
register
int
i;
209
for
(i
=
0
; ;
++
i)
210
{
211
if
(i
==
6
)
212
return;
213
printf(
"
%d, %d\n
"
,
1
, i);
214
Sleep(
200
);
215
schedule();
216
}
217
}
218
219
void f3(void
*
p)
220
{
221
register
int
i;
222
for
(i
=
0
; i
<
6
;
++
i)
223
{
224
printf(
"
%d, %d\n
"
,
3
, i);
225
Sleep(
200
);
226
schedule();
227
}
228
}
229
230
231
void f2(void
*
p)
232
{
233
register
int
i;
234
for
(i
=
0
;i
<
12
;
++
i)
235
{
236
printf(
"
%d, %d\n
"
,
2
, i);
237
Sleep(
200
);
238
schedule();
239
if
(i
==
8
)
240
{
241
reg_thread(f3,
0
);
242
}
243
}
244
}
245
246
247
248
249
250
int
main()
251
{
252
253
254
reg_thread(f0,
0
);
255
reg_thread(f1,
0
);
256
reg_thread(f2,
0
);
257
258
start_first_thread();
259
printf(
"
finished\n
"
);
260
getchar();
261
262
}
263
264

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

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264
