Skip to content

Instantly share code, notes, and snippets.

@pingwin
Last active February 12, 2018 15:10
Show Gist options
  • Save pingwin/42328c06507ce01554a72115b7b84570 to your computer and use it in GitHub Desktop.
Save pingwin/42328c06507ce01554a72115b7b84570 to your computer and use it in GitHub Desktop.
/**
* This was something to play around with longjmp/setjmp and the stack that it would require.
* This code does not work as the direction this experiment was going would require context
* switching that I didn't want to happen.
*/
#include <setjmp.h>
#include <ucontext.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
enum f_states {
STATE_ERROR = -1,
STATE_NULL = 0,
STATE_NEW,
STATE_RUNNING
};
struct future_t {
jmp_buf *rip; // return to
struct future *msg_from;
};
typedef struct future_t future;
struct message {
uint64_t msg_type;
jmp_buf *rip; // return to
ssize_t data_s;
void *data;
};
#define MSG_ACK (struct message){ .rip = &self->private, .msg_type = 1 }
struct coroutine_t {
ucontext_t ctx;
jmp_buf private; // only cr can change
struct coroutine_t *parent; // only `root` can have parent == NULL
char name[100];
char stack[4096 * 4]; // 4 pages
};
typedef struct coroutine_t coroutine;
#define mkln(prefix, fmt) ({ \
char __t [512]; \
bzero(__t, sizeof(__t)); \
snprintf(__t, sizeof(__t), "%s :: %s\n", prefix, fmt); \
__t; \
})
#define pp(fmt, ...) printf(mkln(self->name, fmt), ##__VA_ARGS__);
intptr_t func_y(coroutine *self) {
enum f_states state = STATE_NULL;
struct message *msg = NULL;
intptr_t rc = 0;
do {
pp("do");
if (STATE_RUNNING == state) {
rc = _setjmp(self->private);
pp("setjmp = %p", rc);
}
if (STATE_ERROR == rc) {
pp("error setjmp");
state = STATE_ERROR;
} else if (STATE_RUNNING == state && STATE_NULL == rc) {
pp("block");
if (self->parent == NULL) {
pp("ERR init w/o parent");
state = STATE_ERROR;
rc = -1;
} else {
pp("setcontext to parent");
_longjmp(self->parent->private, (intptr_t)&MSG_ACK);
}
} else if (STATE_NULL == rc) {
pp("first execution");
state = STATE_RUNNING;
} else /* rc == future */ {
pp("sending reply");
msg = (struct message*)&rc;
msg->data_s ++;
pp("data_t = %d", msg->data_s);
if (NULL != msg->rip) {
// save loc before jump
//rc = _setjmp(self->private);
pp("longjmp to %p", *msg->rip);
//if (rc == 0)
_longjmp(*msg->rip, (intptr_t)msg);
} else {
state = STATE_ERROR;
}
}
} while(state >= STATE_NEW);
pp("exiting");
return rc;
}
coroutine * crinit(const char *name, intptr_t (*func)(coroutine*), coroutine *parent) {
coroutine *new = malloc(sizeof(coroutine));
if (new == NULL) {
perror("coroutine new = malloc()");
return NULL;
}
bzero(new, sizeof(coroutine));
strncpy(new->name, name, sizeof(new->name));
if (-1 == getcontext(&new->ctx)) {
perror("getcontext");
return NULL;
}
new->ctx.uc_stack.ss_sp = new->stack;
new->ctx.uc_stack.ss_size = sizeof(new->stack);
new->parent = parent;
new->ctx.uc_link = &parent->ctx;
makecontext(&new->ctx, func, 1, new);
return new;
}
int main(int argc, char *argv[]) {
int32_t rc = 0;
struct message msg;
bzero(&msg, sizeof(msg));
// setup root context
coroutine root;
bzero(&root, sizeof(root));
coroutine *self = &root;
strncpy(self->name, "root", 4);
getcontext(&root.ctx);
/*
coroutine *self = crinit("root", func_y, NULL);
if (self == NULL)
return -1;
*/
coroutine *A, *B = NULL;
rc = _setjmp(self->private);
if (0 == rc) {
A = crinit("fn_A", func_y, self);
setcontext(&A->ctx);
}
rc = _setjmp(self->private);
if (0 == rc) {
B = crinit("fn_B", func_y, self);
setcontext(&B->ctx);
}
// entering root loop
msg.rip = &self->private;
msg.data_s = 0;
intptr_t ptr = 0;
do {
ptr = _setjmp(self->private);
if (msg.data_s > 5) {
pp("all finished");
break;
}
_longjmp(A->private, (intptr_t)&msg);
} while(1);
pp("Final value = %i", msg.data_s);
return rc;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment