|
@@ -1,5 +1,6 @@
|
|
|
import modelverse_kernel.primitives as primitive_functions
|
|
|
import modelverse_kernel.compiled as compiled_functions
|
|
|
+from modelverse_kernel.request_handler import RequestHandler
|
|
|
import modelverse_jit.jit as jit
|
|
|
import modelverse_jit.intrinsics as jit_intrinsics
|
|
|
from collections import defaultdict
|
|
@@ -11,94 +12,21 @@ if sys.version > '3': # pragma: no cover
|
|
|
else:
|
|
|
string_types = (str, unicode)
|
|
|
|
|
|
-class RunRequest(Exception):
|
|
|
- pass
|
|
|
-
|
|
|
-def set_finished_requests_flag(opStack):
|
|
|
- """Sets the finished_requests flag in the top-of-stack tuple."""
|
|
|
- current_generator, requests, finished_requests, replies, has_reply = opStack[-1]
|
|
|
- opStack[-1] = (current_generator, requests, True, replies, has_reply)
|
|
|
-
|
|
|
-def push_generator(gen, opStack):
|
|
|
- """Pushes a new generator onto the given stack."""
|
|
|
- opStack.append((gen, None, True, [], False))
|
|
|
-
|
|
|
-def pop_requests(requests, opStack):
|
|
|
- """Tries to pop a batch of Modelverse _state_ requests from the
|
|
|
- given list of requests. RUN-requests are executed immediately.
|
|
|
-
|
|
|
- A list of requests and a Boolean are returned. The latter is True
|
|
|
- if there are no more requests to process, and false otherwise."""
|
|
|
- if requests is None or len(requests) == 0:
|
|
|
- # Couldn't find a request for the state to handle.
|
|
|
- set_finished_requests_flag(opStack)
|
|
|
- return requests
|
|
|
-
|
|
|
- for i, elem in enumerate(requests):
|
|
|
- if elem[0] == "RUN":
|
|
|
- # The kernel should handle RUN-requests.
|
|
|
- if i > 0:
|
|
|
- # Handle any requests that precede the RUN-request first.
|
|
|
- pre_requests = requests[:i]
|
|
|
- del requests[:i]
|
|
|
- return pre_requests
|
|
|
-
|
|
|
- # The RUN-request must be the first element in the list. Pop it.
|
|
|
- requests.pop(0)
|
|
|
-
|
|
|
- # The list of requests might be empty now. If so, then flag this
|
|
|
- # batch of requests as finished.
|
|
|
- if len(requests) == 0:
|
|
|
- set_finished_requests_flag(opStack)
|
|
|
-
|
|
|
- _, request_args = elem
|
|
|
- if len(request_args) == 1:
|
|
|
- # Format: ("RUN", [gen])
|
|
|
- gen, = request_args
|
|
|
- push_generator(gen, opStack)
|
|
|
- raise RunRequest()
|
|
|
- else:
|
|
|
- # Format: ("RUN", [func, kwargs])
|
|
|
- # This format is useful because it also works for functions that
|
|
|
- # throw an exception but never yield.
|
|
|
- func, kwargs = request_args
|
|
|
- # We need to be extra careful here, because func(**kwargs) might
|
|
|
- # not be a generator at all: it might simply be a method that
|
|
|
- # raises an exception. To cope with this we need to push a dummy
|
|
|
- # entry onto the stack if a StopIteration or PrimtiveFinished
|
|
|
- # exception is thrown. The logic in execute_yields will then pop
|
|
|
- # that dummy entry.
|
|
|
- try:
|
|
|
- push_generator(func(**kwargs), opStack)
|
|
|
- raise RunRequest()
|
|
|
- except StopIteration:
|
|
|
- push_generator(None, opStack)
|
|
|
- raise
|
|
|
- except primitive_functions.PrimitiveFinished as ex:
|
|
|
- push_generator(None, opStack)
|
|
|
- raise
|
|
|
-
|
|
|
- # We couldn't find a RUN-request in the batch of requests, so we might as well
|
|
|
- # handle them all at once then.
|
|
|
- set_finished_requests_flag(opStack)
|
|
|
- return requests
|
|
|
-
|
|
|
class ModelverseKernel(object):
|
|
|
def __init__(self, root):
|
|
|
self.root = root
|
|
|
self.returnvalue = None
|
|
|
self.success = True
|
|
|
+ # request_handlers is a dictionary of usernames to dictionaries of operations
|
|
|
+ # to request handlers. In generics notation:
|
|
|
#
|
|
|
- # generators is a dictionary of usernames to dictionaries of operations
|
|
|
- # to stacks of (generator, pending requests, request replies, has-reply) tuples.
|
|
|
- # In generics notation:
|
|
|
# Dictionary<
|
|
|
# Username,
|
|
|
# Dictionary<
|
|
|
# Operation,
|
|
|
- # Stack<(Generator, Queue<List<Request>>, List<Replies>, Boolean)>>
|
|
|
+ # RequestHandler>>
|
|
|
#
|
|
|
- self.generators = {}
|
|
|
+ self.request_handlers = {}
|
|
|
self.allow_compiled = True
|
|
|
#self.allow_compiled = False
|
|
|
|
|
@@ -130,78 +58,16 @@ class ModelverseKernel(object):
|
|
|
def execute_yields(self, username, operation, params, reply):
|
|
|
self.success = True
|
|
|
self.username = username
|
|
|
- if username not in self.generators:
|
|
|
- self.generators[username] = {}
|
|
|
- if operation not in self.generators[username]:
|
|
|
+ if username not in self.request_handlers:
|
|
|
+ self.request_handlers[username] = {}
|
|
|
+ if operation not in self.request_handlers[username]:
|
|
|
# Create the generator for the function to execute
|
|
|
- self.generators[username][operation] = []
|
|
|
- opStack = self.generators[username][operation]
|
|
|
- if len(opStack) == 0:
|
|
|
- push_generator(getattr(self, operation)(username, *params), opStack)
|
|
|
-
|
|
|
- current_generator, requests, finished_requests, replies, has_reply = opStack[-1]
|
|
|
- # Append the server's replies to the list of replies.
|
|
|
- if reply is not None:
|
|
|
- replies.extend(reply)
|
|
|
- has_reply = True
|
|
|
- opStack[-1] = (current_generator, requests, finished_requests, replies, has_reply)
|
|
|
- while 1:
|
|
|
- try:
|
|
|
- if not finished_requests:
|
|
|
- try:
|
|
|
- # Try to pop a request for the modelverse state.
|
|
|
- result = pop_requests(requests, opStack)
|
|
|
- return result
|
|
|
- except RunRequest:
|
|
|
- # Carry on.
|
|
|
- pass
|
|
|
-
|
|
|
- # The call to pop_requests may have appended a tuple to the stack,
|
|
|
- # so we need to reload the top-of-stack tuple.
|
|
|
- current_generator, requests, finished_requests, replies, has_reply = opStack[-1]
|
|
|
-
|
|
|
- # Send the replies to the generator, and ask for new requests.
|
|
|
- requests = current_generator.send(replies if has_reply else None)
|
|
|
- finished_requests = False
|
|
|
-
|
|
|
- # Update the entry in the stack.
|
|
|
- opStack[-1] = (current_generator, requests, finished_requests, [], False)
|
|
|
- except StopIteration:
|
|
|
- # Done, so remove the generator
|
|
|
- opStack.pop()
|
|
|
- if len(opStack) == 0:
|
|
|
- # Looks like we're done here.
|
|
|
- # Maybe remove the stack of generators as well?
|
|
|
- # del self.generators[username][operation]
|
|
|
- return None
|
|
|
-
|
|
|
- # Update the current generator, requests, and replies.
|
|
|
- current_generator, requests, finished_requests, replies, has_reply = opStack[-1]
|
|
|
- # Append 'None' to the list of replies.
|
|
|
- replies.append(None)
|
|
|
- if not has_reply:
|
|
|
- has_reply = True
|
|
|
- # Update the stack.
|
|
|
- opStack[-1] = current_generator, requests, finished_requests, replies, has_reply
|
|
|
- except primitive_functions.PrimitiveFinished as ex:
|
|
|
- # Done, so remove the generator.
|
|
|
- opStack.pop()
|
|
|
- if len(opStack) == 0:
|
|
|
- # Looks like we're done here.
|
|
|
- # Maybe remove the stack of generators as well?
|
|
|
- # del self.generators[username][operation]
|
|
|
- return None
|
|
|
-
|
|
|
- # Update the current generator, requests, and replies.
|
|
|
- current_generator, requests, finished_requests, replies, has_reply = opStack[-1]
|
|
|
- # Append the generator's result to the list of replies.
|
|
|
- replies.append(ex.result)
|
|
|
- if not has_reply:
|
|
|
- has_reply = True
|
|
|
- # Update the stack.
|
|
|
- opStack[-1] = current_generator, requests, finished_requests, replies, has_reply
|
|
|
- except:
|
|
|
- raise
|
|
|
+ self.request_handlers[username][operation] = RequestHandler()
|
|
|
+ handler = self.request_handlers[username][operation]
|
|
|
+ if not handler.is_active():
|
|
|
+ handler.push_generator(getattr(self, operation)(username, *params))
|
|
|
+
|
|
|
+ return handler.handle_request(reply)
|
|
|
|
|
|
def execute_rule(self, username):
|
|
|
user_root, = yield [("RD", [self.root, username])]
|
|
@@ -245,7 +111,11 @@ class ModelverseKernel(object):
|
|
|
raise Exception("%s: error understanding command (%s, %s)" % (self.debug_info[username], inst_v, self.phase_v))
|
|
|
|
|
|
try:
|
|
|
- yield [("RUN", [gen])]
|
|
|
+ inp = None
|
|
|
+ while 1:
|
|
|
+ inp = yield gen.send(inp)
|
|
|
+ except StopIteration:
|
|
|
+ pass
|
|
|
except jit.JitCompilationFailedException as e:
|
|
|
# Try again, but this time without the JIT.
|
|
|
# print(e.message)
|