Skip to content

Commit 257dc68

Browse files
Merge pull request #5 from icsi-berkeley/process_killing
kills agents gracefully
2 parents 5591f19 + 338aecf commit 257dc68

File tree

6 files changed

+54
-47
lines changed

6 files changed

+54
-47
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ __pycache__/
44

55
*.pyc
66
*.pyc
7+
*.DS_Store

src/main/nluas/Transport.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class TransportSecurityError(TransportError):
9494
######################################################################
9595
#
9696
# The main class for Transport. On creation, sets up a thread to
97-
# listen for incoming messages.
97+
# listen for incoming messages.
9898
#
9999

100100
class Transport():
@@ -107,6 +107,11 @@ def send(self, dest, ntuple):
107107
self._pyre.shout(dest, json.dumps(ntuple).encode('utf-8'))
108108
# send()
109109

110+
def broadcast(self, ntuple):
111+
'''Send given ntuple to Transport all destinations. If the destination isn't listening then the message will (currently) be silently ignored.'''
112+
self._pyre.shout(self._globalchannel, json.dumps(ntuple).encode('utf-8'))
113+
# broadcast()
114+
110115
# Notes on subscribe
111116
#
112117
# The callback is called in the same thread that listens for pyre
@@ -150,7 +155,7 @@ def unsubscribe_all(self):
150155
# unsubscribe_all()
151156

152157
# Notes on get()
153-
#
158+
#
154159
# If you already subscribe to remote, temporarly overrides
155160
# the subscribe. The subscribed callback will NOT be called.
156161
# The subscription is replaced after get() returns.
@@ -187,7 +192,7 @@ def get_callback(tup, **kw):
187192

188193
# Set the subscription
189194
self._subscribers[remote] = get_callback
190-
195+
191196
# Wait for the callback to be called.
192197
e.wait()
193198

@@ -225,7 +230,7 @@ def __init__(self, myname, port=None, prefix=None):
225230

226231
# dict of remote name to callback. See subscribe method above.
227232
self._subscribers = {}
228-
233+
229234
# Callback for all message (or None if none registered)
230235
self._subscribe_all = None
231236

src/main/nluas/app/core_solver.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
"""
2-
Simple solver "core". Contains capabilities for unpacking
3-
a JSON n-tuple, as well as routing this n-tuple based
4-
on the predicate_type (command, query, assertion, etc.).
5-
Other general capabilities can be added. The design
6-
is general enough that the same "unpacking" and "routing"
2+
Simple solver "core". Contains capabilities for unpacking
3+
a JSON n-tuple, as well as routing this n-tuple based
4+
on the predicate_type (command, query, assertion, etc.).
5+
Other general capabilities can be added. The design
6+
is general enough that the same "unpacking" and "routing"
77
method can be used, as long as a new method is written for a given
8-
predicate_type.
8+
predicate_type.
99
1010
"Route_action" can be called by command/query/assertion methods,
1111
to route each parameter to the task-specific method. E.g., "solve_move",
@@ -56,7 +56,6 @@ def __init__(self, args):
5656
self.eventFeatures=None
5757
self.parameter_templates = OrderedDict()
5858
#self.initialize_templates()
59-
6059

6160

6261
def setup_solver_parser(self):
@@ -65,6 +64,8 @@ def setup_solver_parser(self):
6564
return parser
6665

6766
def callback(self, ntuple):
67+
if self.is_quit(ntuple):
68+
return self.close()
6869
self.solve(ntuple)
6970

7071
def initialize_templates(self):
@@ -109,7 +110,7 @@ def solve(self, ntuple):
109110
def broadcast(self):
110111
""" Here, does nothing. Later, an AgentSolver will broadcast information back to BossSolver. """
111112
pass
112-
113+
113114
def update_world(self, discovered=[]):
114115
for item in discovered:
115116
self.world.append(item)
@@ -189,9 +190,6 @@ def route_dispatch(self, dispatch_function, parameters):
189190
""" Simply runs dispatch_function on PARAMETERS. """
190191
return dispatch_function(parameters)
191192

192-
def close(self):
193-
return
194-
195193
def check_for_clarification(self, ntuple):
196194
""" Will need to be replaced by a process that checks whether ntuple needs clarification.
197195
Requires some sort of context/world model. """

src/main/nluas/core_agent.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Author: seantrott <[email protected]>
33
44
Defines a CoreAgent, which uses the Transport module. Can be initialized
5-
by just feeding it a channel name. All "Agents" inherit from the CoreAgent.
5+
by just feeding it a channel name. All "Agents" inherit from the CoreAgent.
66
77
------
88
See LICENSE.txt for licensing information.
@@ -14,6 +14,8 @@
1414
import os
1515
import sys
1616
import logging
17+
import json
18+
import time
1719

1820
from collections import OrderedDict
1921

@@ -52,7 +54,7 @@ def read_templates(self, filename):
5254
return base
5355

5456
def unify_templates(self, child, parent):
55-
""" Unifies a child and parent template. Adds all parent key-value pairs
57+
""" Unifies a child and parent template. Adds all parent key-value pairs
5658
unless the key already exists in the child. """
5759
child.update({key:value for (key, value) in parent.items() if key not in child})
5860
return child
@@ -69,6 +71,8 @@ def initialize(self, args):
6971
self.logfile = args.logfile
7072
self.loglevel = args.loglevel
7173
self.logagent = args.logagent
74+
self._keep_alive = True
75+
self._broadcasted = False
7276

7377
def setup_parser(self):
7478
parser = argparse.ArgumentParser()
@@ -78,17 +82,31 @@ def setup_parser(self):
7882
parser.add_argument("-logagent", type=str, help="indicate agent responsible for logging output")
7983
return parser
8084

81-
def close(self):
82-
#self.transport.join()
83-
print("Transport needs a QUIT procedure.")
84-
sys.exit()
85+
def close(self, quit_federation=False):
86+
if not self._broadcasted:
87+
self._broadcasted = True
88+
self.transport.broadcast({"text": "QUIT", "type": "QUIT"}) # application-level quit
8589

90+
if quit_federation:
91+
time.sleep(0.5)
92+
self.transport.quit_federation() # transport-level quit
93+
94+
self._keep_alive = False
95+
96+
def keep_alive(self, func=None):
97+
while self._keep_alive:
98+
if func:
99+
func()
100+
else:
101+
time.sleep(0.1)
102+
103+
def is_quit(self, ntuple):
104+
""" Checks if an ntuple is the application quit message """
105+
return "type" in ntuple and ntuple["type"] == 'QUIT'
86106

87107
def callback(self, ntuple):
88108
print("{} received {}.".format(self.name, ntuple))
89109

90110
def subscribe_mass(self, ports):
91111
for port in ports:
92112
self.transport.subscribe(port, self.callback)
93-
94-

src/main/nluas/language/text_agent.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ def prompt(self):
3939
#print("Clarification is: {}".format(self.clarification))
4040
msg = input("> ")
4141
if msg == "q":
42-
self.transport.quit_federation()
43-
quit()
42+
self.close(True)
4443
elif msg == None or msg =="":
4544
pass
4645
else:
@@ -67,6 +66,4 @@ def output_stream(self, tag, message):
6766

6867
if __name__ == "__main__":
6968
text = TextAgent(sys.argv[1:])
70-
while True:
71-
text.prompt()
72-
69+
text.keep_alive(text.prompt())

src/main/nluas/language/user_agent.py

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11

22
"""
33
The User-Agent (also called UI-Agent, Agent-UI) receives text/speech
4-
as input, and produces an n-tuple, which it sends to a ProblemSolver.
4+
as input, and produces an n-tuple, which it sends to a ProblemSolver.
55
It feeds the text through the ECG Analyzer (running on a local server)
66
to produce a SemSpec, which it then runs through the CoreSpecializer to produce
7-
the n-tuple.
7+
the n-tuple.
88
99
Interaction with the user is modulated through the output_stream method, which
1010
allows designers to subclass the User-Agent and define a new mode of interaction.
@@ -78,7 +78,7 @@ def initialize_UI(self):
7878

7979
def initialize_analyzer(self):
8080
self.analyzer = Analyzer(self.analyzer_port)
81-
81+
8282
def initialize_specializer(self):
8383
try:
8484
self.specializer=CoreSpecializer(self.analyzer)
@@ -130,7 +130,7 @@ def speech_callback(self, ntuple):
130130
#ntuple = json.loads(ntuple)
131131
text = ntuple['text'].lower()
132132
print("Got {}".format(text))
133-
new_ntuple = self.process_input(text)
133+
new_ntuple = self.process_input(text)
134134
if new_ntuple and new_ntuple != "null" and "predicate_type" in new_ntuple:
135135
self.transport.send(self.solve_destination, new_ntuple)
136136

@@ -141,7 +141,9 @@ def text_callback(self, ntuple):
141141
specialize = True
142142
#ntuple = json.loads(ntuple)
143143
msg = ntuple['text']
144-
if ntuple['type'] == "standard":
144+
if self.is_quit(ntuple):
145+
self.close()
146+
elif ntuple['type'] == "standard":
145147
if msg == None or msg == "":
146148
specialize = False
147149
elif msg.lower() == "d":
@@ -159,7 +161,6 @@ def text_callback(self, ntuple):
159161
self.clarification = False
160162

161163

162-
163164
def callback(self, ntuple):
164165
print(ntuple)
165166
#ntuple = self.decoder.convert_JSON_to_ntuple(ntuple)
@@ -182,7 +183,6 @@ def write_file(self, json_ntuple, msg):
182183

183184

184185

185-
186186
def process_clarification(self, tag, msg, ntuple):
187187
self.clarification = True
188188
#self.output_stream(tag, msg)
@@ -203,15 +203,6 @@ def clarify_ntuple(self, ntuple, descriptor):
203203
new[key] = value
204204
return new
205205

206-
207-
def prompt(self):
208-
while True:
209-
s = input("> ")
210-
if s == "q":
211-
self.transport.quit_federation()
212-
quit()
213-
214-
215206
def check_spelling(self, msg):
216207
table = self.spell_checker.spell_check(msg)
217208
if table:
@@ -229,6 +220,3 @@ def check_spelling(self, msg):
229220

230221
if __name__ == "__main__":
231222
ui = UserAgent(sys.argv[1:])
232-
ui.prompt()
233-
234-

0 commit comments

Comments
 (0)