Changeset - 5d69ddcae67e
[Not reviewed]
0 5 0
MH - 4 years ago 2021-11-10 00:19:28
contact@maxhenger.nl
WIP on fixing bug in test
5 files changed with 110 insertions and 5 deletions:
0 comments (0 inline, 0 general)
src/protocol/eval/executor.rs
Show inline comments
 
@@ -860,155 +860,156 @@ impl Prompt {
 
                }
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::EndWhile(stmt) => {
 
                cur_frame.position = stmt.next;
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Break(stmt) => {
 
                cur_frame.position = stmt.target.unwrap().upcast();
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Continue(stmt) => {
 
                cur_frame.position = stmt.target.unwrap().upcast();
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Synchronous(stmt) => {
 
                cur_frame.position = stmt.body.upcast();
 

	
 
                Ok(EvalContinuation::SyncBlockStart)
 
            },
 
            Statement::EndSynchronous(stmt) => {
 
                cur_frame.position = stmt.next;
 

	
 
                Ok(EvalContinuation::SyncBlockEnd)
 
            },
 
            Statement::Return(_stmt) => {
 
                debug_assert!(heap[cur_frame.definition].is_function());
 
                debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for return statement");
 

	
 
                // The preceding frame has executed a call, so is expecting the
 
                // return expression on its expression value stack. Note that
 
                // we may be returning a reference to something on our stack,
 
                // so we need to read that value and clone it.
 
                let return_value = cur_frame.expr_values.pop_back().unwrap();
 
                let return_value = match return_value {
 
                    Value::Ref(value_id) => self.store.read_copy(value_id),
 
                    _ => return_value,
 
                };
 

	
 
                // Pre-emptively pop our stack frame
 
                self.frames.pop();
 

	
 
                // Clean up our section of the stack
 
                self.store.clear_stack(0);
 
                self.store.stack.truncate(self.store.cur_stack_boundary + 1);
 
                let prev_stack_idx = self.store.stack.pop().unwrap().as_stack_boundary();
 

	
 
                // TODO: Temporary hack for testing, remove at some point
 
                if self.frames.is_empty() {
 
                    debug_assert!(prev_stack_idx == -1);
 
                    debug_assert!(self.store.stack.len() == 0);
 
                    self.store.stack.push(return_value);
 
                    return Ok(EvalContinuation::Terminal);
 
                }
 

	
 
                debug_assert!(prev_stack_idx >= 0);
 
                // Return to original state of stack frame
 
                self.store.cur_stack_boundary = prev_stack_idx as usize;
 
                let cur_frame = self.frames.last_mut().unwrap();
 
                cur_frame.expr_values.push_back(return_value);
 

	
 
                // We just returned to the previous frame, which might be in
 
                // the middle of evaluating expressions for a particular
 
                // statement. So we don't want to enter the code below.
 
                return Ok(EvalContinuation::Stepping);
 
            },
 
            Statement::Goto(stmt) => {
 
                cur_frame.position = stmt.target.unwrap().upcast();
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::New(stmt) => {
 
                let call_expr = &heap[stmt.expression];
 
                debug_assert!(heap[call_expr.definition].is_component());
 
                debug_assert_eq!(
 
                    cur_frame.expr_values.len(), heap[call_expr.definition].parameters().len(),
 
                    "mismatch in expr stack size and number of arguments for new statement"
 
                );
 

	
 
                let mono_data = types.get_procedure_expression_data(&cur_frame.definition, cur_frame.monomorph_idx);
 
                let expr_data = &mono_data.expr_data[call_expr.unique_id_in_definition as usize];
 

	
 
                // Note that due to expression value evaluation they exist in
 
                // reverse order on the stack.
 
                // TODO: Revise this code, keep it as is to be compatible with current runtime
 
                let mut args = Vec::new();
 
                while let Some(value) = cur_frame.expr_values.pop_front() {
 
                    args.push(value);
 
                }
 

	
 
                // Construct argument group, thereby copying heap regions
 
                let argument_group = ValueGroup::from_store(&self.store, &args);
 
                // println!("Creating {} with\n{:#?}", heap[call_expr.definition].identifier().value.as_str(), argument_group);
 

	
 
                // Clear any heap regions
 
                for arg in &args {
 
                    self.store.drop_value(arg.get_heap_pos());
 
                }
 

	
 
                cur_frame.position = stmt.next;
 

	
 
                Ok(EvalContinuation::NewComponent(call_expr.definition, expr_data.field_or_monomorph_idx, argument_group))
 
            },
 
            Statement::Expression(stmt) => {
 
                // The expression has just been completely evaluated. Some
 
                // values might have remained on the expression value stack.
 
                // cur_frame.expr_values.clear(); PROPER CLEARING
 
                cur_frame.position = stmt.next;
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
        };
 

	
 
        assert!(
 
            cur_frame.expr_values.is_empty(),
 
            "This is a debugging assertion that will fail if you perform expressions without \
 
            assigning to anything. This should be completely valid, and this assertion should be \
 
            replaced by something that clears the expression values if needed, but I'll keep this \
 
            in for now for debugging purposes."
 
        );
 

	
 
        // If the next statement requires evaluating expressions then we push
 
        // these onto the expression stack. This way we will evaluate this
 
        // stack in the next loop, then evaluate the statement using the result
 
        // from the expression evaluation.
 
        if !cur_frame.position.is_invalid() {
 
            let stmt = &heap[cur_frame.position];
 

	
 
            match stmt {
 
                Statement::If(stmt) => cur_frame.prepare_single_expression(heap, stmt.test),
 
                Statement::While(stmt) => cur_frame.prepare_single_expression(heap, stmt.test),
 
                Statement::Return(stmt) => {
 
                    debug_assert_eq!(stmt.expressions.len(), 1); // TODO: @ReturnValues
 
                    cur_frame.prepare_single_expression(heap, stmt.expressions[0]);
 
                },
 
                Statement::New(stmt) => {
 
                    // Note that we will end up not evaluating the call itself.
 
                    // Rather we will evaluate its expressions and then
 
                    // instantiate the component upon reaching the "new" stmt.
 
                    let call_expr = &heap[stmt.expression];
 
                    cur_frame.prepare_multiple_expressions(heap, &call_expr.arguments);
 
                },
 
                Statement::Expression(stmt) => {
 
                    cur_frame.prepare_single_expression(heap, stmt.expression);
 
                }
 
                _ => {},
 
            }
 
        }
 

	
 
        return_value
 
    }
 
}
 
\ No newline at end of file
src/protocol/parser/pass_definitions.rs
Show inline comments
 
@@ -1795,192 +1795,209 @@ fn consume_parser_type(
 
                    iter.consume(); // consume opening square
 
                    if iter.next() != Some(TokenKind::CloseSquare) {
 
                        return Err(ParseError::new_error_str_at_pos(
 
                            source, iter.last_valid_pos(),
 
                            "unexpected token: expected ']'"
 
                        ));
 
                    }
 
                    let (_, end_pos) = iter.next_positions();
 
                    let array_span = InputSpan::from_positions(start_pos, end_pos);
 
                    insert_array_before(&mut elements, angle_depth, array_span);
 
                } else {
 
                    return Err(ParseError::new_error_str_at_pos(
 
                        source, iter.last_valid_pos(),
 
                        "unexpected token: expected '<', '>', ',' or '['")
 
                    );
 
                }
 

	
 
                iter.consume();
 
            },
 
            State::Open => {
 
                // Just parsed an opening angle bracket, expecting an identifier
 
                let element = consume_parser_type_ident(source, iter, symbols, heap, poly_vars, cur_scope, wrapping_definition, allow_inference)?;
 
                elements.push(Entry{ element, depth: angle_depth });
 
                state = State::Ident;
 
            },
 
            State::Close => {
 
                // Just parsed 1 or 2 closing angle brackets, expecting comma,
 
                // more closing brackets or the tokens indicating an array
 
                if Some(TokenKind::Comma) == next {
 
                    state = State::Comma;
 
                } else if Some(TokenKind::CloseAngle) == next {
 
                    let (_, end_angle_pos) = iter.next_positions();
 
                    last_pos = end_angle_pos;
 
                    angle_depth -= 1;
 
                    state = State::Close;
 
                } else if Some(TokenKind::ShiftRight) == next {
 
                    let (_, end_angle_pos) = iter.next_positions();
 
                    last_pos = end_angle_pos;
 
                    angle_depth -= 2;
 
                    state = State::Close;
 
                } else if Some(TokenKind::OpenSquare) == next {
 
                    let (start_pos, _) = iter.next_positions();
 
                    iter.consume();
 
                    if iter.next() != Some(TokenKind::CloseSquare) {
 
                        return Err(ParseError::new_error_str_at_pos(
 
                            source, iter.last_valid_pos(),
 
                            "unexpected token: expected ']'"
 
                        ));
 
                    }
 
                    let (_, end_pos) = iter.next_positions();
 
                    let array_span = InputSpan::from_positions(start_pos, end_pos);
 
                    insert_array_before(&mut elements, angle_depth, array_span);
 
                } else {
 
                    return Err(ParseError::new_error_str_at_pos(
 
                        source, iter.last_valid_pos(),
 
                        "unexpected token: expected ',', '>', or '['")
 
                    );
 
                }
 

	
 
                iter.consume();
 
            },
 
            State::Comma => {
 
                // Just parsed a comma, expecting an identifier or more closing
 
                // braces
 
                if Some(TokenKind::Ident) == next {
 
                    let element = consume_parser_type_ident(source, iter, symbols, heap, poly_vars, cur_scope, wrapping_definition, allow_inference)?;
 
                    elements.push(Entry{ element, depth: angle_depth });
 
                    state = State::Ident;
 
                } else if Some(TokenKind::CloseAngle) == next {
 
                    let (_, end_angle_pos) = iter.next_positions();
 
                    last_pos = end_angle_pos;
 
                    iter.consume();
 
                    angle_depth -= 1;
 
                    state = State::Close;
 
                } else if Some(TokenKind::ShiftRight) == next {
 
                    let (_, end_angle_pos) = iter.next_positions();
 
                    last_pos = end_angle_pos;
 
                    iter.consume();
 
                    angle_depth -= 2;
 
                    state = State::Close;
 
                } else {
 
                    return Err(ParseError::new_error_str_at_pos(
 
                        source, iter.last_valid_pos(),
 
                        "unexpected token: expected '>' or a type name"
 
                    ));
 
                }
 
            }
 
        }
 

	
 
        if angle_depth < 0 {
 
            return Err(ParseError::new_error_str_at_pos(source, iter.last_valid_pos(), "unmatched '>'"));
 
        } else if angle_depth == 0 {
 
            break;
 
        }
 
    }
 

	
 
    // If here then we have found the correct number of angle braces.
 

	
 
    // Check for trailing array identifiers
 
    while Some(TokenKind::OpenSquare) == iter.next() {
 
        let (array_start, _) = iter.next_positions();
 
        iter.consume();
 
        if Some(TokenKind::CloseSquare) != iter.next() {
 
            return Err(ParseError::new_error_str_at_pos(
 
                source, iter.last_valid_pos(),
 
                "unexpected token: expected ']'"
 
            ));
 
        }
 
        let (_, array_end) = iter.next_positions();
 
        iter.consume();
 
        insert_array_before(&mut elements, 0, InputSpan::from_positions(array_start, array_end))
 
    }
 

	
 
    // If here then we found the correct number of angle braces. But we still
 
    // need to make sure that each encountered type has the correct number of
 
    // embedded types.
 
    for idx in 0..elements.len() {
 
        let cur_element = &elements[idx];
 

	
 
        let expected_subtypes = cur_element.element.variant.num_embedded();
 
        let mut encountered_subtypes = 0;
 
        for peek_idx in idx + 1..elements.len() {
 
            let peek_element = &elements[peek_idx];
 
            if peek_element.depth == cur_element.depth + 1 {
 
                encountered_subtypes += 1;
 
            } else if peek_element.depth <= cur_element.depth {
 
                break;
 
            }
 
        }
 

	
 
        if expected_subtypes != encountered_subtypes {
 
            if encountered_subtypes == 0 {
 
                // Case where we have elided the embedded types, all of them
 
                // should be inferred.
 
                if !allow_inference {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        source, cur_element.element.element_span,
 
                        "type inference is not allowed here"
 
                    ));
 
                }
 

	
 
                // Insert the missing types (in reverse order, but they're all
 
                // of the "inferred" type anyway).
 
                let inserted_span = cur_element.element.element_span;
 
                let inserted_depth = cur_element.depth + 1;
 
                elements.reserve(expected_subtypes);
 
                for _ in 0..expected_subtypes {
 
                    elements.insert(idx + 1, Entry{
 
                        element: ParserTypeElement{ element_span: inserted_span, variant: ParserTypeVariant::Inferred },
 
                        depth: inserted_depth,
 
                    });
 
                }
 
            } else {
 
                // Mismatch in number of embedded types, produce a neat error
 
                // message.
 
                let type_name = String::from_utf8_lossy(source.section_at_span(cur_element.element.element_span));
 
                fn polymorphic_name_text(num: usize) -> &'static str {
 
                    if num == 1 { "polymorphic argument" } else { "polymorphic arguments" }
 
                }
 
                fn were_or_was(num: usize) -> &'static str {
 
                    if num == 1 { "was" } else { "were" }
 
                }
 

	
 
                if expected_subtypes == 0 {
 
                    return Err(ParseError::new_error_at_span(
 
                        source, cur_element.element.element_span,
 
                        format!(
 
                            "the type '{}' is not polymorphic, yet {} {} {} provided",
 
                            type_name, encountered_subtypes, polymorphic_name_text(encountered_subtypes),
 
                            were_or_was(encountered_subtypes)
 
                        )
 
                    ));
 
                }
 

	
 
                let maybe_infer_text = if allow_inference {
 
                    " (or none, to perform implicit type inference)"
 
                } else {
 
                    ""
 
                };
 

	
 
                return Err(ParseError::new_error_at_span(
 
                    source, cur_element.element.element_span,
 
                    format!(
 
                        "expected {} {}{} for the type '{}', but {} {} provided",
 
                        expected_subtypes, polymorphic_name_text(expected_subtypes),
 
                        maybe_infer_text, type_name, encountered_subtypes,
 
                        were_or_was(encountered_subtypes)
 
                    )
 
                ));
 
            }
 
        }
 
    }
 

	
 
    let mut constructed_elements = Vec::with_capacity(elements.len());
 
    for element in elements.into_iter() {
 
        constructed_elements.push(element.element);
 
    }
 

	
 
    Ok(ParserType{
 
        elements: constructed_elements,
 
        full_span: InputSpan::from_positions(first_pos, last_pos)
 
    })
 
}
 

	
 
/// Consumes an identifier for which we assume that it resolves to some kind of
 
/// type. Once we actually arrive at a type we will stop parsing. Hence there
 
/// may be trailing '::' tokens in the iterator, or the subsequent specification
 
/// of polymorphic arguments.
 
fn consume_parser_type_ident(
src/protocol/tests/parser_validation.rs
Show inline comments
 
@@ -257,97 +257,119 @@ fn test_correct_union_instance() {
 

	
 
    Tester::new_single_source_expect_ok(
 
        "single polymorphic var",
 
        "
 
        union Option<T> { None, Some(T) }
 
        func bar() -> Option<s32> { return Option::Some(3); }"
 
    );
 

	
 
    Tester::new_single_source_expect_ok(
 
        "multiple polymorphic vars",
 
        "
 
        union Result<T, E> { Ok(T), Err(E), }
 
        func bar() -> Result<s32, s8> { return Result::Ok(3); }
 
        "
 
    );
 

	
 
    Tester::new_single_source_expect_ok(
 
        "multiple polymorphic in one variant",
 
        "
 
        union MaybePair<T1, T2>{ None, Some(T1, T2) }
 
        func bar() -> MaybePair<s8, s32> { return MaybePair::Some(1, 2); }
 
        "
 
    );
 
}
 

	
 
#[test]
 
fn test_incorrect_union_instance() {
 
    Tester::new_single_source_expect_err(
 
        "tag-variant name reuse",
 
        "
 
        union Foo{ A, A }
 
        "
 
    ).error(|e| { e
 
        .assert_num(2)
 
        .assert_occurs_at(0, "A }")
 
        .assert_msg_has(0, "union variant is defined more than once")
 
        .assert_occurs_at(1, "A, ")
 
        .assert_msg_has(1, "other union variant");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "embedded-variant name reuse",
 
        "
 
        union Foo{ A(s32), A(s8) }
 
        "
 
    ).error(|e| { e 
 
        .assert_num(2)
 
        .assert_occurs_at(0, "A(s8)")
 
        .assert_msg_has(0, "union variant is defined more than once")
 
        .assert_occurs_at(1, "A(s32)")
 
        .assert_msg_has(1, "other union variant");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "undefined variant",
 
        "
 
        union Silly{ Thing(s8) }
 
        func bar() -> Silly { return Silly::Undefined(5); }
 
        "
 
    ).error(|e| { e
 
        .assert_msg_has(0, "variant 'Undefined' does not exist on the union 'Silly'");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "using tag instead of embedded",
 
        "
 
        union Foo{ A(s32) }
 
        func bar() -> Foo { return Foo::A; }
 
        "
 
    ).error(|e| { e
 
        .assert_msg_has(0, "variant 'A' of union 'Foo' expects 1 embedded values, but 0 were");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "using embedded instead of tag",
 
        "
 
        union Foo{ A }
 
        func bar() -> Foo { return Foo::A(3); }
 
        "
 
    ).error(|e| { e 
 
        .assert_msg_has(0, "The variant 'A' of union 'Foo' expects 0");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "wrong embedded value",
 
        "
 
        union Foo{ A(s32) }
 
        func bar() -> Foo { return Foo::A(false); }
 
        "
 
    ).error(|e| { e
 
        .assert_occurs_at(0, "Foo::A")
 
        .assert_msg_has(0, "failed to fully resolve")
 
        .assert_occurs_at(1, "false")
 
        .assert_msg_has(1, "has been resolved to 's32'")
 
        .assert_msg_has(1, "has been resolved to 'bool'");
 
    });
 
}
 

	
 
#[test]
 
fn test_polymorph_array_types() {
 
    Tester::new_single_source_expect_ok(
 
        "array of polymorph in struct",
 
        "
 
        struct Foo<T> { T[] hello }
 
        struct Bar { Foo<u32>[] world }
 
        "
 
    ).for_struct("Bar", |s| { s
 
        .for_field("world", |f| { f.assert_parser_type("Foo<u32>[]"); });
 
    });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "array of port in struct",
 
        "
 
        struct Bar { in<u32>[] inputs }
 
        "
 
    ).for_struct("Bar", |s| { s
 
        .for_field("inputs", |f| { f.assert_parser_type("in<u32>[]"); });
 
    });
 
}
 
\ No newline at end of file
src/runtime2/connector.rs
Show inline comments
 
@@ -261,122 +261,122 @@ impl ConnectorPDL {
 
                    branch.sync_state = SpeculativeState::Inconsistent;
 
                }
 
            }
 
            RunResult::BranchAtSyncEnd => {
 
                let consistency = self.consensus.notify_of_finished_branch(branch_id);
 
                if consistency == Consistency::Valid {
 
                    branch.sync_state = SpeculativeState::ReachedSyncEnd;
 
                    self.tree.push_into_queue(QueueKind::FinishedSync, branch_id);
 
                } else if consistency == Consistency::Inconsistent {
 
                    branch.sync_state = SpeculativeState::Inconsistent;
 
                }
 
            },
 
            RunResult::BranchPut(port_id, content) => {
 
                // Branch is attempting to send data
 
                let port_id = PortIdLocal::new(port_id.0.u32_suffix);
 
                let consistency = self.consensus.notify_of_speculative_mapping(branch_id, port_id, true);
 
                if consistency == Consistency::Valid {
 
                    // `put()` is valid.
 
                    let (sync_header, data_header) = self.consensus.handle_message_to_send(branch_id, port_id, &content, comp_ctx);
 
                    comp_ctx.submit_message(Message::Data(DataMessage {
 
                        sync_header, data_header,
 
                        content: DataContent::Message(content),
 
                    }));
 

	
 
                    self.tree.push_into_queue(QueueKind::Runnable, branch_id);
 
                    return ConnectorScheduling::Immediate;
 
                } else {
 
                    branch.sync_state = SpeculativeState::Inconsistent;
 
                }
 
            },
 
            _ => unreachable!("unexpected run result {:?} in sync mode", run_result),
 
        }
 

	
 
        // If here then the run result did not require a particular action. We
 
        // return whether we have more active branches to run or not.
 
        if self.tree.queue_is_empty(QueueKind::Runnable) {
 
            return ConnectorScheduling::NotNow;
 
        } else {
 
            return ConnectorScheduling::Later;
 
        }
 
    }
 

	
 
    pub fn run_in_deterministic_mode(&mut self, sched_ctx: SchedulerCtx, comp_ctx: &mut ComponentCtx) -> ConnectorScheduling {
 
        debug_assert!(!self.tree.is_in_sync() && !self.consensus.is_in_sync());
 

	
 
        let branch = self.tree.base_branch_mut();
 
        debug_assert!(branch.sync_state == SpeculativeState::RunningNonSync);
 

	
 
        let mut run_context = ConnectorRunContext{
 
            branch_id: branch.id,
 
            consensus: &self.consensus,
 
            received: &branch.inbox,
 
            scheduler: sched_ctx,
 
            prepared_channel: branch.prepared_channel.take(),
 
        };
 
        let run_result = branch.code_state.run(&mut run_context, &sched_ctx.runtime.protocol_description);
 

	
 
        match run_result {
 
            RunResult::ComponentTerminated => {
 
                branch.sync_state = SpeculativeState::Finished;
 

	
 
                return ConnectorScheduling::Exit;
 
            },
 
            RunResult::ComponentAtSyncStart => {
 
                comp_ctx.notify_sync_start();
 
                let sync_branch_id = self.tree.start_sync();
 
                self.consensus.start_sync(comp_ctx);
 
                self.consensus.notify_of_new_branch(BranchId::new_invalid(), sync_branch_id);
 
                self.tree.push_into_queue(QueueKind::Runnable, sync_branch_id);
 

	
 
                return ConnectorScheduling::Immediate;
 
            },
 
            RunResult::NewComponent(definition_id, monomorph_idx, arguments) => {
 
                // Note: we're relinquishing ownership of ports. But because
 
                // we are in non-sync mode the scheduler will handle and check
 
                // port ownership transfer.
 
                debug_assert!(comp_ctx.workspace_ports.is_empty());
 
                find_ports_in_value_group(&arguments, &mut comp_ctx.workspace_ports);
 

	
 
                let new_state = ComponentState {
 
                    prompt: Prompt::new(
 
                        &sched_ctx.runtime.protocol_description.types,
 
                        &sched_ctx.runtime.protocol_description.heap,
 
                        definition_id, monomorph_idx, arguments
 
                    ),
 
                };
 
                let new_component = ConnectorPDL::new(new_state);
 
                comp_ctx.push_component(new_component, comp_ctx.workspace_ports.clone());
 
                comp_ctx.workspace_ports.clear();
 

	
 
                return ConnectorScheduling::Later;
 
            },
 
            RunResult::NewChannel => {
 
                let (getter, putter) = sched_ctx.runtime.create_channel(comp_ctx.id);
 
                debug_assert!(getter.kind == PortKind::Getter && putter.kind == PortKind::Putter);
 
                branch.prepared_channel = Some((
 
                    Value::Input(PortId::new(putter.self_id.index)),
 
                    Value::Output(PortId::new(getter.self_id.index)),
 
                    Value::Output(PortId::new(putter.self_id.index)),
 
                    Value::Input(PortId::new(getter.self_id.index)),
 
                ));
 

	
 
                comp_ctx.push_port(putter);
 
                comp_ctx.push_port(getter);
 

	
 
                return ConnectorScheduling::Immediate;
 
            },
 
            _ => unreachable!("unexpected run result '{:?}' while running in non-sync mode", run_result),
 
        }
 
    }
 

	
 
    pub fn collapse_sync_to_solution_branch(&mut self, solution_branch_id: BranchId, ctx: &mut ComponentCtx) {
 
        let mut fake_vec = Vec::new();
 
        self.tree.end_sync(solution_branch_id);
 
        self.consensus.end_sync(solution_branch_id, &mut fake_vec);
 

	
 
        for port in fake_vec {
 
            // TODO: Handle sent/received ports
 
            debug_assert!(ctx.get_port_by_id(port).is_some());
 
        }
 

	
 
        ctx.notify_sync_end(&[]);
 
    }
 
}
 
\ No newline at end of file
src/runtime2/tests/mod.rs
Show inline comments
 
use super::*;
 
use crate::{PortId, ProtocolDescription};
 
use crate::common::Id;
 
use crate::protocol::eval::*;
 

	
 
const NUM_THREADS: u32 = 1;  // number of threads in runtime
 
const NUM_INSTANCES: u32 = 4;  // number of test instances constructed
 
const NUM_LOOPS: u32 = 4;      // number of loops within a single test (not used by all tests)
 
const NUM_INSTANCES: u32 = 1;  // number of test instances constructed
 
const NUM_LOOPS: u32 = 1500;      // number of loops within a single test (not used by all tests)
 

	
 
fn create_runtime(pdl: &str) -> Runtime {
 
    let protocol = ProtocolDescription::parse(pdl.as_bytes()).expect("parse pdl");
 
    let runtime = Runtime::new(NUM_THREADS, protocol);
 

	
 
    return runtime;
 
}
 

	
 
fn run_test_in_runtime<F: Fn(&mut ApplicationInterface)>(pdl: &str, constructor: F) {
 
    let protocol = ProtocolDescription::parse(pdl.as_bytes())
 
        .expect("parse PDL");
 
    let runtime = Runtime::new(NUM_THREADS, protocol);
 

	
 
    let mut api = runtime.create_interface();
 
    for _ in 0..NUM_INSTANCES {
 
        constructor(&mut api);
 
    }
 

	
 
    // Wait until done :)
 
}
 

	
 
struct TestTimer {
 
    name: &'static str,
 
    started: std::time::Instant
 
}
 

	
 
impl TestTimer {
 
    fn new(name: &'static str) -> Self {
 
        Self{ name, started: std::time::Instant::now() }
 
    }
 
}
 

	
 
impl Drop for TestTimer {
 
    fn drop(&mut self) {
 
        let delta = std::time::Instant::now() - self.started;
 
        let nanos = (delta.as_secs_f64() * 1_000_000.0) as u64;
 
        let millis = nanos / 1000;
 
        let nanos = nanos % 1000;
 
        println!("[{}] Took {:>4}.{:03} ms", self.name, millis, nanos);
 
    }
 
}
 

	
 
#[test]
 
fn test_put_and_get() {
 
    const CODE: &'static str = "
 
    primitive putter(out<bool> sender, u32 loops) {
 
        u32 index = 0;
 
        while (index < loops) {
 
            synchronous {
 
                put(sender, true);
 
            }
 
            index += 1;
 
        }
 
    }
 

	
 
    primitive getter(in<bool> receiver, u32 loops) {
 
        u32 index = 0;
 
        while (index < loops) {
 
            synchronous {
 
                auto result = get(receiver);
 
                assert(result);
 
            }
 
            index += 1;
 
        }
 
    }
 
    ";
 

	
 
    let thing = TestTimer::new("hello");
 
    let thing = TestTimer::new("put_and_get");
 
    run_test_in_runtime(CODE, |api| {
 
        let channel = api.create_channel();
 

	
 
        api.create_connector("", "putter", ValueGroup::new_stack(vec![
 
            Value::Output(PortId(Id{ connector_id: 0, u32_suffix: channel.putter_id.index })),
 
            Value::UInt32(NUM_LOOPS)
 
        ])).expect("create putter");
 

	
 
        api.create_connector("", "getter", ValueGroup::new_stack(vec![
 
            Value::Input(PortId(Id{ connector_id: 0, u32_suffix: channel.getter_id.index })),
 
            Value::UInt32(NUM_LOOPS)
 
        ])).expect("create getter");
 
    });
 
}
 

	
 
#[test]
 
fn test_star_shaped_request() {
 
    const CODE: &'static str = "
 
    primitive edge(in<u32> input, out<u32> output, u32 loops) {
 
        u32 index = 0;
 
        while (index < loops) {
 
            synchronous {
 
                auto req = get(input);
 
                put(output, req * 2);
 
            }
 
            index += 1;
 
        }
 
    }
 

	
 
    primitive center(out<u32>[] requests, in<u32>[] responses, u32 loops) {
 
        u32 loop_index = 0;
 
        auto num_edges = length(requests);
 

	
 
        while (loop_index < loops) {
 
            print(\"starting loop\");
 
            synchronous {
 
                u32 edge_index = 0;
 
                u32 sum = 0;
 
                while (edge_index < num_edges) {
 
                    put(requests[edge_index], edge_index);
 
                    auto response = get(responses[edge_index]);
 
                    sum += response;
 
                    edge_index += 1;
 
                }
 

	
 
                assert(sum == num_edges * (num_edges - 1));
 
            }
 
            print(\"ending loop\");
 
            loop_index += 1;
 
        }
 
    }
 

	
 
    composite constructor(u32 num_edges, u32 num_loops) {
 
        auto requests = {};
 
        auto responses = {};
 

	
 
        u32 edge_index = 0;
 
        while (edge_index < num_edges) {
 
            channel req_put -> req_get;
 
            channel resp_put -> resp_get;
 
            new edge(req_get, resp_put, num_loops);
 
            requests @= { req_put };
 
            responses @= { resp_get };
 

	
 
            edge_index += 1;
 
        }
 

	
 
        new center(requests, responses, num_loops);
 
    }
 
    ";
 

	
 
    let thing = TestTimer::new("star_shaped_request");
 
    run_test_in_runtime(CODE, |api| {
 
        api.create_connector("", "constructor", ValueGroup::new_stack(vec![
 
            Value::UInt32(5),
 
            Value::UInt32(NUM_LOOPS),
 
        ]));
 
    });
 
}
 
\ No newline at end of file
0 comments (0 inline, 0 general)