Files @ 97217a7b2d18
Branch filter:

Location: CSY/reowolf/testdata/examples/02_error_handling.pdl

Max Henger
feat: examples
// Although in an unstable state, there is an initial implementation for error
// handling. Roughly speaking: if a component has failed then it cannot complete
// any current or future synchronous rounds anymore. Hence, apart from some edge
// cases, any received message by a peer should cause a failure at that peer as
// well. We may have a look at the various places where a component with respect
// to a peer that is receiving its messages

enum ErrorLocation {
    BeforeSync,
    DuringSyncBeforeFirstInteraction,
    DuringSyncBeforeSecondInteraction,
    DuringSyncAfterInteractions,
    AfterSync,
}

func error_location_to_string(ErrorLocation loc) -> string {
    if (let ErrorLocation::BeforeSync = loc) {
        return "before sync";
    } else if (let ErrorLocation::DuringSyncBeforeFirstInteraction = loc) {
        return "during sync before first interaction";
    } else if (let ErrorLocation::DuringSyncBeforeSecondInteraction = loc) {
        return "during sync before second interaction";
    } else if (let ErrorLocation::DuringSyncAfterInteractions = loc) {
        return "during sync after interactions";
    } else { return "after sync"; }
}

func crash() -> u8 {
    return {}[0]; // access index 1 of an empty array
}

comp sender_and_crasher(out<u32> value, ErrorLocation loc) {
    print("sender: will crash " @ error_location_to_string(loc));
    if (loc == ErrorLocation::BeforeSync) { crash(); }
    sync {
        if (loc == ErrorLocation::DuringSyncBeforeFirstInteraction) { crash(); }
        print("sender: sending first value");
        put(value, 0);
        if (loc == ErrorLocation::DuringSyncBeforeSecondInteraction) { crash(); }
        print("sender: sending second value");
        put(value, 1);
        if (loc == ErrorLocation::DuringSyncAfterInteractions) { crash(); }
    }
    if (loc == ErrorLocation::AfterSync) { crash(); }
}

comp receiver(in<u32> value) {
    sync {
        auto a = get(value);
        auto b = get(value);
    }
}

// Note that when we run the example with the error location before sync, or
// during sync, that the receiver always crashes. However the location where it
// will crash is somewhat random! Due to the asynchronous nature of the runtime
// a sender of messages will always just `put` the value onto the port and
// continue execution. So even though the sender component might already be done
// with its sync round, the receiver officially still has to receive its first
// message. In any case, a neat error message should be displayed in the
// console.
//
// Note especially, given the asynchronous nature of the runtime, that the
// receiver should figure out when the peer component has crashed, but it can
// still finish the current synchronous round. This might happen if the peer
// component crashes *just* after the synchronous round. There may be a case
// where the peer receives the information that the peer crashed *before* it
// receives the information that the synchronous round has succeeded.

comp main() {
    channel tx -> rx;

    new sender_and_crasher(tx, ErrorLocation::AfterSync);
    new receiver(rx);
}