Browse Source

Rewrite request_handler from scratch

Yentl Van Tendeloo 7 years ago
parent
commit
b3487abe8f

+ 1 - 1
hybrid_server/classes/task.xml

@@ -46,7 +46,7 @@
                 import traceback
                 print("Error on requests: " + str(commands))
                 print("For taskname " + str(taskname))
-                stack = [gen['generator'] for gen in mvk.request_handlers[taskname][operation].generator_stack if gen['generator'].__name__ not in ['execute_rule', 'execute_jit', 'execute_jit_internal']]
+                stack = [gen for gen in mvk.request_handlers[taskname][operation].generator_stack if gen is not None and gen.__name__ not in ['execute_rule', 'execute_jit']]
                 printed_stack = []
                 for gen in stack:
                     try:

+ 7 - 11
kernel/modelverse_kernel/main.py

@@ -102,8 +102,7 @@ class ModelverseKernel(object):
     ### Process primitives ###
     ##########################
     def load_primitives(self, taskname):
-        yield [("CALL_ARGS",
-                [self.load_primitives_from, (taskname, 'primitives', primitive_functions)])]
+        yield [("CALL_ARGS", [self.load_primitives_from, (taskname, 'primitives', primitive_functions)])]
 
     def load_primitives_from(self, taskname, source_name, source):
         hierarchy, = yield [("RD", [self.root, "__hierarchy"])]
@@ -240,9 +239,8 @@ class ModelverseKernel(object):
         elif inst_type["value"] == "assign":
             var, val = yield [("RD", [inst, "var"]),
                               ("RD", [inst, "value"])]
-            (prev_var, instruction_var), (prev_val, instruction_val) = \
-                        yield [("CALL_ARGS", [self.print_instruction, (var, 0, indent)]),
-                               ("CALL_ARGS", [self.print_instruction, (val, 0, indent)])]
+            (prev_var, instruction_var), = yield [("CALL_ARGS", [self.print_instruction, (var, 0, indent)])]
+            (prev_val, instruction_val), = yield [("CALL_ARGS", [self.print_instruction, (val, 0, indent)])]
 
             instruction = prev_val + "  " * indent + instruction_var + " = " + instruction_val + "\n"
 
@@ -272,9 +270,8 @@ class ModelverseKernel(object):
                 value, = yield [("RD", [param, "value"])]
                 name, = yield [("RD", [param, "name"])]
                 name, = yield [("RV", [name])]
-                (prev_res, instruction_res), param = \
-                        yield [("CALL_ARGS", [self.print_instruction, (value, 0, nested_indent)]),
-                               ("RD", [param, "next_param"])]
+                (prev_res, instruction_res), = yield [("CALL_ARGS", [self.print_instruction, (value, 0, nested_indent)])]
+                param, = yield [("RD", [param, "next_param"])]
                 computation += prev_res
                 param_list[name] = instruction_res
 
@@ -305,9 +302,8 @@ class ModelverseKernel(object):
         elif inst_type["value"] == "while":
             cond, body = yield [("RD", [inst, "cond"]),
                                 ("RD", [inst, "body"])]
-            (prev_cond, instruction_cond), (prev_body, instruction_body) = \
-                        yield [("CALL_ARGS", [self.print_instruction, (cond, 0, indent+1)]),
-                               ("CALL_ARGS", [self.print_instruction, (body, indent+1)])]
+            (prev_cond, instruction_cond), = yield [("CALL_ARGS", [self.print_instruction, (cond, 0, indent+1)])]
+            (prev_body, instruction_body), = yield [("CALL_ARGS", [self.print_instruction, (body, indent+1)])]
             instruction = "  " * indent + "while 1:\n" + prev_cond + \
                           "  " * (indent + 1) + "if 'value' not in %s:\n" % instruction_cond + \
                           "  " * (indent + 2) + "%s['value'], = yield [('RV', [%s['id']])]\n" % (instruction_cond, instruction_cond) + \

+ 37 - 139
kernel/modelverse_kernel/request_handler.py

@@ -4,163 +4,61 @@ import modelverse_kernel.jit as jit
 from collections import defaultdict
 
 class RequestHandler(object):
-    """A type of object that intercepts logic-related Modelverse requests, and
-       forwards Modelverse state requests."""
     def __init__(self):
-        # generator_stack is a stack of GeneratorStackEntry values.
         self.generator_stack = []
-        # exception_handlers is a stack of
-        # (generator_stack index, [(exception type, handler function)])
-        # tuples.
-        self.produce_stack_trace = True
-        self.handlers = {
-            'CALL' : self.execute_call,
-            'CALL_ARGS' : self.execute_call_args,
-            'CALL_KWARGS' : self.execute_call_kwargs,
-            'SLEEP' : self.execute_sleep,
-        }
+        self.handlers = {"CALL": self.execute_call,
+                         "CALL_ARGS": self.execute_call_args,
+                         "CALL_KWARGS": self.execute_call_kwargs,
+                         "SLEEP": self.execute_sleep}
 
-    def handle_request(self, reply):
-        """Replies to a request from the top-of-stack generator, and returns a new request."""
-        if not self.generator_stack:
-            raise ValueError('handle_request cannot be called with an empty generator stack.')
-
-        # Append the server's replies to the list of replies.
-        if reply is not None:
-            gen = self.generator_stack[-1]
-
-            if gen["replies"]:
-                gen["replies"].extend(reply)
-            else:
-                gen["replies"] = reply
+    def push_generator(self, gen):
+        self.generator_stack.append(gen)
 
-        while 1:
-            # Silence pylint's warning about catching Exception.
-            # pylint: disable=I0011,W0703
+    def handle_request(self, reply):
+        while self.generator_stack:
             try:
                 gen = self.generator_stack[-1]
-                if gen["finished_requests"]:
-                    gen["pending_requests"] = gen["generator"].send(gen["replies"])
-                    gen["finished_requests"] = False
-                    gen["replies"] = None
-                ret = self.pop_requests()
-                if ret[0]:
-                    return ret[1]
+                requests = gen.send(reply)
+
+                # Generated new request, so process
+                if requests and requests[0][0] in self.handlers:
+                        # Known request, so process that one
+                        if len(requests) > 1:
+                            raise Exception("CALL_* and SLEEP operations MUST be split in individual yields")
+
+                        # Try to add a new generator to branch into
+                        reply = None
+                        self.generator_stack.append(None)
+                        # This next command potentially raises a finished message already, meaning that we should stop already
+                        # We avoid an extra try/except block by putting the None on the stack already
+                        self.generator_stack[-1] = self.handlers[requests[0][0]](requests[0][1])
+                else:
+                    # MvS request, so forward that instead
+                    return requests
 
             except StopIteration:
-                # Done, so remove the generator
+                # Exception, so finished execution of this generator, passing on None to the caller
                 del self.generator_stack[-1]
-                if self.generator_stack:
-                    # This generator was called from another generator.
-                    # Append 'None' to the caller's list of replies.
-                    self.append_reply(None)
-                else:
-                    # Looks like we're done here.
-                    return None
+                reply = [None]
+                
             except primitive_functions.PrimitiveFinished as ex:
-                # Done, so remove the generator
+                # Exception, so finished execution of this generator, passing on ex.result to the caller
                 del self.generator_stack[-1]
-                if self.generator_stack:
-                    # This generator was called from another generator.
-                    # Append the callee's result to the caller's list of replies.
-                    self.append_reply(ex.result)
-                else:
-                    # Looks like we're done here.
-                    return None
+                reply = [ex.result]
 
-    def push_generator(self, gen):
-        """Pushes a new generator onto the stack."""
-        dd = defaultdict(lambda : None)
-        dd["generator"] = gen
-        dd["finished_requests"] = True
-        self.generator_stack.append(dd)
-        # print('Pushed generator %s. Generator count: %d' % (gen, len(self.generator_stack)))
-
-    def append_reply(self, new_reply):
-        """Appends a reply to the top-of-stack generator's list of pending replies."""
-        if self.generator_stack[-1]["replies"] is None:
-            self.generator_stack[-1]["replies"] = [new_reply]
-        else:
-            self.generator_stack[-1]["replies"].append(new_reply)
-
-    def pop_requests(self):
-        """Tries to pop a batch of Modelverse _state_ requests from the
-           current list of requests. Known 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."""
-        requests = self.generator_stack[-1]["pending_requests"]
-        if requests:
-            if requests[0][0] in self.handlers:
-                # First element is a known request
-                elem = requests.pop(0)
-
-                # The list of requests might be empty now. If so, then flag this
-                # batch of requests as finished.
-                if not requests:
-                    self.generator_stack[-1]["finished_requests"] = True
-
-                # Handle the request.
-                self.handlers[elem[0]](elem[1])
-                return (False, None)
-            else:
-                for i, elem in enumerate(requests):
-                    if elem[0] in self.handlers:
-                        # Handle any requests that precede the known request first.
-                        pre_requests = requests[:i]
-                        del requests[:i]
-                        return pre_requests
-
-        # We couldn't find a known request in the batch of requests, so we might as well
-        # handle them all at once then.
-        self.generator_stack[-1]["finished_requests"] = True
-        #return requests
-        return (True, requests)
+            except primitive_functions.SleepKernel:
+                # Processing sleep, so pop its generator and reraise
+                del self.generator_stack[-1]
+                raise
 
     def execute_call(self, request_args):
-        """Executes a CALL-request with the given argument list."""
-        # Format: ("CALL", [gen])
-        gen, = request_args
-        self.push_generator(gen)
+        return request_args[0]
 
     def execute_call_kwargs(self, request_args):
-        """Executes a CALL_KWARGS-request with the given argument list."""
-        # Format: ("CALL_KWARGS", [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:
-            self.push_generator(func(**kwargs))
-        except primitive_functions.PrimitiveFinished, StopIteration:
-            self.push_generator(None)
-            raise
-        except:
-            print("EXCEPTION for " + str(locals()))
-            raise
+        return request_args[0](**(request_args[1]))
 
     def execute_call_args(self, request_args):
-        """Executes a CALL_ARGS-request with the given argument list."""
-        # Format: ("CALL_ARGS", [gen, args])
-        func, args = request_args
-        # We need to be extra careful here, because func(*args) 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:
-            self.push_generator(func(*args))
-        except primitive_functions.PrimitiveFinished, StopIteration:
-            self.push_generator(None)
-            raise
+        return request_args[0](*(request_args[1]))
 
     def execute_sleep(self, request_args):
-        """Executes a SLEEP-request with the given argument list."""
-        self.append_reply(None)
         raise primitive_functions.SleepKernel(request_args[0], request_args[1])

+ 1 - 1
wrappers/modelverse_SCCD.py

@@ -1,7 +1,7 @@
 """
 Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
 
-Date:   Fri Apr 27 11:01:30 2018
+Date:   Fri Apr 27 13:39:47 2018
 
 Model author: Yentl Van Tendeloo
 Model name:   MvK Server