-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathprocessor.go
149 lines (137 loc) · 4.53 KB
/
processor.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
package qsim
import (
"errors"
)
// A Processor is the piece of the queueing system that processes jobs.
type Processor struct {
// The current job being processed. If the processor is idle, this
// will be nil.
CurrentJob *Job
// A unique identifier for the Processor. Useful for debugging, as it
// will be printed in debug output for events involving the Processor.
// The implementor must set this value if it's going to be used –
// otherwise it will be 0 (and thus not unique)
ProcessorId int
procTimeGenerator func(j *Job) int
// Callback lists
cbBeforeStart []func(p *Processor, j *Job)
cbAfterStart []func(p *Processor, j *Job, procTime int)
cbBeforeFinish []func(p *Processor, j *Job)
cbAfterFinish []func(p *Processor, j *Job)
}
// SetProcTimeGenerator sets the function that will generate processing
// times for jobs.
//
// For example, if you wanted half of the jobs to take 10 ticks to
// process, and the other half to take 20 ticks, you could do this:
//
// ptg := func(j *Job) int {
// if rand.Float32() < 0.5 {
// return 10
// } else {
// return 20
// }
// }
// p.SetProcTimeGenerator(ptg)
func (p *Processor) SetProcTimeGenerator(ptg func(j *Job) int) {
p.procTimeGenerator = ptg
}
// Start begins processing a given job.
//
// The return value is the amount of time it'll take to process the job.
// This method will throw an error if called when there's already a job
// being processed: that job needs to be finished first.
func (p *Processor) Start(j *Job) (procTime int, err error) {
p.beforeStart(j)
if p.CurrentJob != nil {
p.afterStart(nil, 0)
return 0, errors.New("Tried to start job on busy processor; call Finish() first")
}
p.CurrentJob = j
procTime = p.procTimeGenerator(j)
if procTime == 0 {
p.Finish()
} else {
p.afterStart(j, procTime)
}
return procTime, nil
}
// Finish empties the current job out of the Processor and returns it.
//
// If Finish is called on an idle processor, j will be nil.
func (p *Processor) Finish() (j *Job) {
j = p.CurrentJob
p.beforeFinish(j)
p.CurrentJob = nil
p.afterFinish(j)
return j
}
// IsIdle returns a boolean indicating whether the Processor is available to
// start a new Job.
func (p *Processor) IsIdle() bool {
return p.CurrentJob == nil
}
// BeforeStart adds a callback to be run immediately before a Job is started
// on the processor.
//
// The callback will be passed the processor itself and the job that's about
// to be started. If Start is called on a busy Processor, there is no change
// in the callback's behavior: it's still passed the new job, but the job won't
// actually get started.
func (p *Processor) BeforeStart(f func(p *Processor, j *Job)) {
p.cbBeforeStart = append(p.cbBeforeStart, f)
}
func (p *Processor) beforeStart(j *Job) {
for _, cb := range p.cbBeforeStart {
cb(p, j)
}
}
// AfterStart adds a callback to be run immediately after a Job is
// started on the processor.
//
// The callback will be passed the processor itself, the job that was
// just started, and the processing time that was decided upon for the
// job. If Start is called on a busy processor, this callback will
// run but j will be nil.
func (p *Processor) AfterStart(f func(p *Processor, j *Job, procTime int)) {
p.cbAfterStart = append(p.cbAfterStart, f)
}
func (p *Processor) afterStart(j *Job, procTime int) {
for _, cb := range p.cbAfterStart {
cb(p, j, procTime)
}
}
// BeforeFinish adds a callback to be run immediately before a Job is finished
// on the processor.
//
// The callback will be passed the processor itself and the job that's about
// to be finished. If Finish is called on an idle Processor, the callback still
// runs but j is nil.
func (p *Processor) BeforeFinish(f func(p *Processor, j *Job)) {
p.cbBeforeFinish = append(p.cbBeforeFinish, f)
}
func (p *Processor) beforeFinish(j *Job) {
for _, cb := range p.cbBeforeFinish {
cb(p, j)
}
}
// AfterFinish adds a callback to be run immediately after a Job is
// finished on the processor.
//
// The callback will be passed the processor itself and the job that was
// just finished. If Finish is called on a idle processor, this callback
// will run but j will be nil.
func (p *Processor) AfterFinish(f func(p *Processor, j *Job)) {
p.cbAfterFinish = append(p.cbAfterFinish, f)
}
func (p *Processor) afterFinish(j *Job) {
for _, cb := range p.cbAfterFinish {
cb(p, j)
}
}
// NewProcessor creates a new Processor struct.
func NewProcessor(procTimeGenerator func(j *Job) int) (p *Processor) {
p = new(Processor)
p.SetProcTimeGenerator(procTimeGenerator)
return
}