-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathprocess.go
198 lines (176 loc) · 5.8 KB
/
process.go
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
package sham
import (
"context"
"fmt"
log "github.com/sirupsen/logrus"
"time"
)
// Runnable 程序:应用程序的具体的代码就写在这里面
// 每一次返回就代表“一条指令”(一个原子操作)执行完毕,返回值为状态:
// - StatusRunning 继续运行(如果时间片未用尽)
// - StatusReady 会进入就绪队列(即 yield,主动让出 CPU)
// - StatusBlocked 会进入阻塞状态(无法恢复,所以一般不用。需要阻塞时一般通过中断请求)
// - StatusDone 进程运行结束。
type Runnable func(contextual *Contextual) int
// Thread 线程:是一个可以在 CPU 里跑的东西。
type Thread struct {
// runnable 是实际要运行的内容,应该自己在内部保存状态。
runnable Runnable
// contextual 是 Thread 的环境
contextual *Contextual
// 预计剩余时间
remainingTime uint
}
// Run 包装并运行 Thread 的 runnable。
// 该函数返回的 done、cancel 让 runnable 变得可控:
// - 当 runnable 返回,即 Thread 结束时,done 会接收到 Process/Thread 的状态。
// - 当外部需要强制终止 runnable 的运行(调度),调用 cancel() 即可。
func (t *Thread) Run() (done chan int, cancel context.CancelFunc) {
done = make(chan int)
_ctx, cancel := context.WithCancel(context.Background())
go func() {
for { // 一条条代码不停跑,直到阻塞|退出|被取消
select {
case <-_ctx.Done(): // 被取消,取消由 CPU 发起
// 取消时 CPU 会临时置 Status 为需要转到的状态,
// 这里获取并把这个值传给操作系统
// 同时把状态重置为 StatusRunning
// (真正的状态转化需由操作系统完成,这里只是暂时借用了这个值,故要还原)
log.WithField("process", t.contextual.Process).Info("Thread Run Cancel")
s := t.contextual.Process.Status
t.contextual.Process.Status = StatusRunning
done <- s
return
default:
ret := t.runnable(t.contextual)
t.contextual.Commit()
if ret != StatusRunning { // 结束了,交给调度器处理
done <- ret
return
}
}
}
}()
return done, cancel
}
// 进程的状态
const (
StatusBlocked = -1
StatusReady = 0
StatusRunning = 1
StatusDone = 2
)
// Process 进程:一个可运行(其中的 Thread 可以运行),集合了资源的东西。
// 为了简化设计,一个 Process 只能持有且必须持有一个 Thread。
type Process struct {
Id string
// Precedence 优先级,数字越大越优先
Precedence uint
Thread *Thread
Memory Memory
Devices map[string]Device
// Status 状态:one of -1, 0, 1, 2 分别代表 阻塞,就绪,运行,已结束
Status int
}
// TODO: Contextual.Commit: after a time_cost (an operation): remainingTime--, schedule.
// Contextual 上下文:线程的上下文。
// 其实就是包含一个指向 Process 的指针。
// 后面还可以往这里加东西:用来保存各种值。
type Contextual struct {
Process *Process
// 通过 Contextual.OS.XX 调系统调用
OS OSInterface
// 程序计数器
PC uint
}
func (c *Contextual) Commit() {
c.PC += 1
if c.Process != nil {
c.Process.Thread.remainingTime -= 1
}
if c.OS != nil {
c.OS.clockTick()
} else {
log.WithField("Contextual", c).Warn("Commit: no clock to tick: do time.Sleep(time.Second)")
time.Sleep(time.Second)
}
}
// 👇VAR POOL👇
// 由于放变量是最常用的使用内存的方式,所以这里提供一组方法来方便变量的使用。
// 这里所谓的变量池是放在 Process.Memory[0] 的一个 map[string]interface{}
// InitVarPool 把 Contextual.Process.Memory[0] 开辟为变量池
func (c *Contextual) InitVarPool() bool {
if c.Process != nil {
mem := &c.Process.Memory[0]
if mem.Content == nil {
mem.Content = map[string]interface{}{}
return true
}
}
return false
}
// GetVar 获取一个名为 name 的变量
func (c *Contextual) GetVar(name string) interface{} {
mem := &c.Process.Memory[0]
if _, ok := mem.Content.(map[string]interface{}); !ok {
log.WithFields(log.Fields{
"targetVarName": name,
"mem": c.Process.Memory,
}).Error("[CTX] GetVar Failed: mem[0] is not a VarPool")
return nil
}
if c.Process != nil {
return mem.Content.(map[string]interface{})[name]
}
return nil
}
// TryGetVar 获取一个名为 name 的变量。类似于 GetVar,但如果不成功会返回 nil, false
func (c *Contextual) TryGetVar(name string) (interface{}, bool) {
mem := &c.Process.Memory[0]
if _, ok := mem.Content.(map[string]interface{}); !ok {
log.WithFields(log.Fields{
"targetVarName": name,
"mem": c.Process.Memory,
}).Error("[CTX] GetVar Failed: mem[0] is not a VarPool")
return nil, false
}
if c.Process != nil {
v, ok := mem.Content.(map[string]interface{})[name]
return v, ok
}
return nil, false
}
// SetVar 为名为 name 的变量赋值,不存在会新建,存在会复写
func (c *Contextual) SetVar(name string, value interface{}) bool {
mem := &c.Process.Memory[0]
if _, ok := mem.Content.(map[string]interface{}); !ok {
log.WithFields(log.Fields{
"targetVarName": name,
"mem": c.Process.Memory,
}).Error("[CTX] GetVar Failed: mem[0] is not a VarPool")
return false
}
mem.Content.(map[string]interface{})[name] = value
return true
}
// 👆VAR POOL👆
// Noop 是一个基本的进程,运行时会使用 fmt.Println 打印 "no-op"。
// 这个东西不需要 IO 设备,不需要内存。
// 运行需要的时间是 0,优先级为最低 (0)。
var Noop = Process{
Id: "no-op",
Precedence: 0,
Thread: &Thread{
runnable: func(contextual *Contextual) int {
fmt.Println("no-op")
return StatusDone
},
contextual: &Contextual{},
remainingTime: 0,
},
Memory: Memory{},
Devices: map[string]Device{},
}
func init() {
Noop.Thread.contextual.Process = &Noop
}