-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmulti.fs
206 lines (153 loc) · 4.87 KB
/
multi.fs
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
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
\ ************************************
\ Tasks
\ ************************************
dlist
cell field >rp \ task's saved return pointer
cell field >list \ which task list we're on
cell field >dsize \ size of data space
struct task
l_head runners \ list head for running tasks
{
: mvtask ( head task -- ) \ move task to list
dup >list @ tuck !
l_dequeue 2dup swap l_queue
>list !
;
: restore ( -- ) \ restores a task's state
runners @ dup 0= if ." No Tasks!" cr begin again then
>rp @ rp! \ retore rp
pull sp! \ restore sp
ion
;
public
: myself ( -- a ) \ puts our task struct on stack
runners @ ;
: wait ( head -- ) \ causes current process to sleep
ioff sp@ cell+ push
rp@ myself >rp !
myself mvtask restore
;
: wake ( task -- ) \ wakes another task
runners swap mvtask ;
: yield ( -- ) \ start next running task
ioff
sp@ push
rp@ myself >rp !
runners l_next restore
;
: texit ( -- ) \ exit a task
ioff
runners l_rm \ removes our task from running list
restore
;
}
\ install timer interrupt handler
' yield 2 !
\ *****************************************
\ Binary Semaphore Locking
\ *****************************************
{
0
cell field >flag \ is locked?
cell field >list \ is waiting tasks list
: lrel ( sema -- ) \ release lock
dup >list @ \ test for waiting tasks
if >list l_dequeue wake \ wake task
else false swap ! \ clear lock
then ; \ return
public
struct sema
: lock ( sema -- ) \ aquires the lock
dup ioff @ \ test the lock
if >list wait \ if locked then wait
else true swap ! \ else set lock
then ion ; \ return
: release ( sema -- ) \ releases the lock
ioff lrel ion ;
: swait ( sema -- ) \ release lock and go on waiting list
dup ioff lrel \ release lock and wake waiter
>list wait ; \ place self on sema's waiting list
: semaphore ( "name" -- ) ( -- sema )
here 0 , 0 , constant ;
}
{
400 dchunk dmem \ setup system chunks
semaphore s
public
\ salloc - allocate system memory
: salloc ( u -- a )
s lock
dmem alloc
s release
;
}
\ spawn creates a new task - the new task will
\ start executing ( when it's time comes ) xt
\ and will receive new data stack and return stack spaces
: spawn ( xt -- task )
\ get new stack space
task 40 cells + salloc
tuck task 40 cells + + dup 20 cells - swap ( base xt sp rp )
lit texit -! \ push a return address of texit
rot -! \ push a return address of passed xt
swap -! \ push the new sp
over >rp ! \ store our new stack in task struct
runners over >list ! \ put list head in task struct
dup ioff runners l_queue ion \ add our new task struct to tail of list
;
\ ***************************************
\ Channels
\ one-way asynchronous streaming IPC
\ ***************************************
{
8 constant BUFZ \ size of buffer in cells
BUFZ 1- constant MASK \ offset mask
0 ( this might be a dlist later... )
sema field >sema \ access semaphore
cell field >size \ how many bytes in buffer
cell field >pos \ input data pointer
BUFZ cells field >buffer \ data buffer
struct chan
: full? ( channel -- f ) \ flags true on buffer full
>size @ BUFZ = ;
: empty? ( channel -- f ) \ flags true on buffer empty
>size @ 0= ;
: head ( channel -- a ) \ fetches address to head of buffer
dup >buffer swap >pos @ MASK and cells + ;
: tail ( channel -- a ) \ fetches address to tail of buffer
dup >buffer swap dup >pos @ swap >size @ - MASK and cells + ;
: inc ( channel -- ) \ increments buffer
1 over >size +! 1 swap >pos +! ;
: dec ( channel -- ) \ decrements buffer
-1 swap >size +! ;
public
: ch! ( x channel -- ) \ send cell to channel
dup >sema lock
begin dup full? while dup >sema swait repeat
tuck head ! dup inc
>sema release ;
: ch@ ( channel -- x ) \ get cell from channel
dup >sema lock
begin dup empty? while dup >sema swait repeat
dup tail @ swap dup dec
>sema release ;
;
: channel ( "name" -- ) ( -- a ) \ creates an new channel
chan salloc dup constant
chan for false c!+ next drop ;
}
\ ****************************************
\ Debug and Testing Code
\ ****************************************
\ manually make me - the boot task
here task allot runners over >list ! runners l_add
channel test
: task1 ." task1" cr begin test ch@ emit again ;
: task2 ." task2" cr 41 1a for dup test ch! 1+ next ;
: go
lit task1 spawn .
lit task2 spawn .
quit
;
\ ' go 0 !
bye