Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow tail resumptions to be discarded #11

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ set(test_mpe_main_sources
test/src/nqueens.c
test/src/rehandle.c
test/src/triples.c
test/src/sphinx.c
test/test_mpe_main.c)

if (NOT MP_USE_C)
Expand Down
26 changes: 22 additions & 4 deletions src/mpeff/mpeff.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ struct mpe_resume_s {
void** plocal; // kind == MPE_RESUMPTION_INPLACE
mp_resume_t* resume; // kind == MPE_RESUMPTION_SCOPED_ONCE || MPE_RESUMPTION_ONCE || MPE_RESUME_MULTI
} mp;
bool resumed;
};


Expand Down Expand Up @@ -302,18 +303,27 @@ static void* mpe_perform_yield_to_abort(mpe_frame_handle_t* h, const mpe_operati
return mp_yield(h->prompt, &mpe_perform_op_clause_abort, &env);
}

// Id function for resuming
static void* id(mp_resume_t* mpr, void* arg) {
return arg;
}

// Tail resumptive under an "under" frame
static void* mpe_perform_under(mpe_frame_handle_t* h, const mpe_operation_t* op, void* arg) {
mpe_frame_under_t f;
f.frame.effect = MPE_EFFECT(mpe_frame_under);
f.under = h->frame.effect;
void* result = NULL;
mpe_resume_t resume = { MPE_RESUMPTION_INPLACE, { &h->local }, false };
{mpe_with_frame(&f.frame) {
mpe_resume_t resume = { MPE_RESUMPTION_INPLACE, { &h->local } };
result = (op->opfun)(&resume, h->local, arg);
}}
return result;

if (resume.resumed) {
return result;
} else {
return mp_yield(h->prompt, &id, result);
}
}

// ------------------------------------------------------------------------------
Expand All @@ -325,8 +335,13 @@ static void* mpe_perform_at(mpe_frame_handle_t* h, const mpe_operation_t* op, vo
mpe_assert_internal(opkind == op->opkind);
if (mpe_likely(opkind == MPE_OP_TAIL_NOOP)) {
// tail resumptive, calls no operations, execute in place
mpe_resume_t resume = { MPE_RESUMPTION_INPLACE, { &h->local } };
return (op->opfun)(&resume, h->local, arg);
mpe_resume_t resume = { MPE_RESUMPTION_INPLACE, { &h->local }, false };
void* result = (op->opfun)(&resume, h->local, arg);
if (resume.resumed) {
return result;
} else {
return mp_yield(h->prompt, &id, result);
}
}
else if (mpe_likely(opkind == MPE_OP_TAIL)) {
// tail resumptive; execute in place under an "under" frame
Expand Down Expand Up @@ -497,16 +512,19 @@ static void mpe_resume_unwind(mpe_resume_t* r) {

// Last use of a resumption
void* mpe_resume_final(mpe_resume_t* resume, void* local, void* arg) {
resume->resumed = true;
return mpe_resume_internal(true, resume, local, arg, false);
}

// Regular resume
void* mpe_resume(mpe_resume_t* resume, void* local, void* arg) {
resume->resumed = true;
return mpe_resume_internal(false, resume, local, arg, false);
}

// Last resume in tail-position
void* mpe_resume_tail(mpe_resume_t* resume, void* local, void* arg) {
resume->resumed = true;
if (mpe_likely(resume->kind == MPE_RESUMPTION_INPLACE)) {
*resume->mp.plocal = local;
return arg;
Expand Down
107 changes: 107 additions & 0 deletions test/src/sphinx.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/* ----------------------------------------------------------------------------
Copyright (c) 2021, Microsoft Research, Daan Leijen
This is free software; you can redistribute it and/or modify it
under the terms of the MIT License. A copy of the license can be
found in the "LICENSE" file at the root of this distribution.

The sphinx effect will only allow performers to continue execution
when they provide the correct answer to their riddle
-----------------------------------------------------------------------------*/
#include "test.h"
#include <string.h>


/*-----------------------------------------------------------------
Define operations
-----------------------------------------------------------------*/
MPE_DEFINE_EFFECT1(sphinx, answer)
MPE_DEFINE_VOIDOP1(sphinx, answer, mpe_string_t)

/*-----------------------------------------------------------------
Example programs
-----------------------------------------------------------------*/

void* brian(void* arg){
UNUSED(arg);
// Arrive at Thebes
sphinx_answer("Scooters");
// Die
mpt_assert(false, "Brian should have been eaten by the sphinx");
return mpe_voidp_int(1);
}


void* oedipus(void* arg){
UNUSED(arg);
// Arrive at Thebes
sphinx_answer("Person");
// Free Thebes
return mpe_voidp_int(1);
}

/*-----------------------------------------------------------------
Sphinx handler
-----------------------------------------------------------------*/

static void* _sphinx_answer(mpe_resume_t* r, void* local, void* arg){
if (strcmp(mpe_mpe_string_t_voidp(arg), "Person") == 0) {
return mpe_resume_tail(r, local, NULL);
} else {
// I couldn't find a way to release it, this one is not intended for MPE_RESUMPTION_INPLACE
// mpe_resume_release(r);
return mpe_voidp_int(0);
}
}

static const mpe_handlerdef_t sphinx_hdef = { MPE_EFFECT(sphinx), NULL, {
{ MPE_OP_TAIL, MPE_OPTAG(sphinx, answer), &_sphinx_answer },
{ MPE_OP_NULL, mpe_op_null, NULL}
}};

static void* sphinx_handle(mpe_actionfun_t action) {
return mpe_handle(&sphinx_hdef, NULL, action, NULL);
}

static const mpe_handlerdef_t sphinx_noop_hdef = { MPE_EFFECT(sphinx), NULL, {
{ MPE_OP_TAIL_NOOP, MPE_OPTAG(sphinx, answer), &_sphinx_answer },
{ MPE_OP_NULL, mpe_op_null, NULL}
}};

static void* sphinx_noop_handle(mpe_actionfun_t action) {
return mpe_handle(&sphinx_noop_hdef, NULL, action, NULL);
}

/*-----------------------------------------------------------------
testing
-----------------------------------------------------------------*/

static void oedipus_tail() {
int res = mpe_int_voidp(sphinx_handle(&oedipus));
mpt_printf("state: %d\n", res);
mpt_assert(res == 1, "Oedipus should pass");
}

static void oedipus_tail_noop() {
int res = mpe_int_voidp(sphinx_noop_handle(&oedipus));
mpt_printf("state: %d\n", res);
mpt_assert(res == 1, "Oedipus should pass");
}

static void brian_tail() {
int res = mpe_int_voidp(sphinx_handle(&brian));
mpt_printf("state: %d\n", res);
mpt_assert(res == 0, "Brian shouldn't pass");
}

static void brian_tail_noop() {
int res = mpe_int_voidp(sphinx_noop_handle(&brian));
mpt_printf("state: %d\n", res);
mpt_assert(res == 0, "Brian shouldn't pass");
}

void sphinx_run(void) {
oedipus_tail();
oedipus_tail_noop();
brian_tail();
brian_tail_noop();
}
2 changes: 1 addition & 1 deletion test/test.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ void triples_run(void);
void amb_run(void);
void amb_state_run(void);
void rehandle_run(void);

void sphinx_run(void);

#ifdef __cplusplus
void throw_run(void);
Expand Down
3 changes: 2 additions & 1 deletion test/test_mpe_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ static void test_c(void) {
countern_run();
mstate_run();
rehandle_run();

sphinx_run();

// multi-shot tests
amb_run();
amb_state_run();
Expand Down