-
Notifications
You must be signed in to change notification settings - Fork 2
/
agents.py
168 lines (135 loc) · 5.51 KB
/
agents.py
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
from trace import BaseTracer
from util import RingBuffer
from common_messages import *
from common_processes import *
from SimPy.Simulation import now
class JobAgent(object):
def __init__(self, job):
self.start_time = now()
self.job = job
self.id = job.id
self.accept_process = None
self.confirm_process = None
self.listen_process = None
self.trace = BaseTracer()
# use sets for job agents for the lookup speed, don't
# worry about memory as they're only around for a bit
self.cancelled = set()
self.timedout = set()
self.nrejected = 0
def start_accept_process(self, other, value, timeout):
accept = Accept(self, other, value)
accept.send_msg(self.node, other.node)
self.accept_process = AcceptProcess(self, value, timeout)
self.accept_process.start(self.accept_process.accept())
# clean up
def cancel_all(self):
if self.listen_process:
cancel_process(self.listen_process)
if self.accept_process:
cancel_process(self.accept_process)
if self.confirm_process:
cancel_process(self.confirm_process)
# public AcceptProcess message interface
# buyers only ever have one accept process
def confirm(self, confirm):
if self.accept_process:
self.accept_process.signal("confirm", confirm)
else:
self.trace("WARNING: no accept process to receive confirm")
def reject(self, reject):
if self.accept_process:
self.accept_process.signal("reject", reject)
else:
self.trace("WARNING: no accept process to receive reject")
# public ConfirmProcess message interface
# buyers only ever have one confirm process
def cancel(self, cancel):
if self.confirm_process:
self.confirm_process.signal("cancel", cancel)
else:
self.trace("WARNING: no confirm_process to receive cancel")
def complete(self, complete):
if self.confirm_process:
self.confirm_process.signal("complete", complete)
else:
self.trace("WARNING: no confirm_process to receive complete")
class ResourceAgent(object):
def __init__(self, node):
self.node = node
self.id = node.id
self.trace = BaseTracer()
# sellers are trading on multiple jobs
self.accept_processes = {}
self.confirm_processes = {}
# use ring buffers for space saving since
# resource agents are long lived
self.nrejected = 0
self.cancelled = RingBuffer(400)
self.timedout = RingBuffer(400)
@property
def resource(self):
return self.node.resource
# utility functions
def confirm_and_start_job(self, job, other, value):
# sending confirmation message
confirm = Confirm(self, other, value)
confirm.send_msg(self.node, other.node)
# start procsses to listen for cancellations
confirm_process = ConfirmProcess(self, value)
activate(confirm_process, confirm_process.confirm())
self.confirm_processes[job.id] = confirm_process
# start the job
self.resource.start(job, confirm_process)
def send_reject(self, other, value):
reject = Reject(self, other, value)
reject.send_msg(self.node, other.node)
# public AcceptProcess message interface
def confirm(self, confirm):
if confirm.id in self.accept_processes:
process = self.accept_processes[confirm.id]
process.signal("confirm", confirm)
else:
self.trace("WARNING: no accept process for confirm %s"
% reject.str(self))
def reject(self, reject):
if reject.id in self.accept_processes:
process = self.accept_processes[reject.id]
process.signal("reject", reject)
else:
self.trace("WARNING: no accept process for reject %s"
% reject.str(self))
# public ConfirmProcess message interface
def cancel(self, cancel):
trace = self.trace.add("j%-5s" % cancel.id)
self.cancelled.add(cancel.id)
if cancel.id in self.confirm_processes:
process = self.confirm_processes[cancel.id]
process.signal("cancel", cancel)
else:
trace and trace("no confirm process for cancel, "
"probably out of sync")
def complete(self, complete):
trace = self.trace.add("j%-5s" % cancel.id)
if complete.id in self.confirm_processes:
process = self.confirm_processes[complete.id]
process.signal("complete", complete)
else:
trace("WARNING: no confirm process for complete")
#internal ConfirmProcess interface
# this should be the same for all resource agents
def cancel_received(self, cancel):
trace = self.trace.add('j%-5s' % cancel.job)
if cancel.job in self.resource.jobs:
trace and trace("got cancel, cancelling job %s"
% cancel.job.id)
self.resource.cancel(cancel.job);
del self.confirm_processes[cancel.job.id]
else:
trace("WARNING: got cancel for job not running (%s)"
% cancel.job.id)
def complete_received(self, complete):
trace = self.trace.add('j%-5s' % complete.job.id)
trace and trace("job %s completed, cleaning up"
% complete.job.id)
del self.confirm_processes[complete.job.id]