From b54b93c7d101d261c862791a02d3e2cf752f45f4 Mon Sep 17 00:00:00 2001 From: Sage Vaillancourt Date: Tue, 5 Apr 2022 23:53:17 -0400 Subject: [PATCH] Add simple Promise type. A bit unstable. Almost certainly needs some locking on struct Promise fields. --- src/Makefile | 4 +-- src/env.c | 11 +++++++- src/object.c | 17 ++++++++++-- src/object.h | 5 ++++ src/pebblisp.c | 1 + src/plfunc.c | 7 +++-- src/threads.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/threads.h | 26 ++++++++++++++++++ 8 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 src/threads.c create mode 100644 src/threads.h diff --git a/src/Makefile b/src/Makefile index a56ab83..5786cdb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,5 +1,5 @@ -files = main.c pebblisp.c tokens.c object.c env.c web.c plfunc.c hash.c -libs = -lreadline -lmicrohttpd +files = main.c pebblisp.c tokens.c object.c env.c web.c plfunc.c hash.c threads.c +libs = -lreadline -lmicrohttpd -lpthread exe = pl file_libs := $(files) $(libs) diff --git a/src/env.c b/src/env.c index fc3d6b2..c433f90 100644 --- a/src/env.c +++ b/src/env.c @@ -6,6 +6,7 @@ #include "pebblisp.h" #include "web.h" +#include "threads.h" #include "plfunc.h" struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*), const char* help, @@ -57,6 +58,7 @@ struct symFunc { #endif #ifndef HASHLESS_ENV // Hash-based env + Object* fetch(const char* name, struct Environment* env) { while (env) { @@ -207,6 +209,8 @@ struct Environment defaultEnv() pf(takeInput), pf(readFileToObject), pf(getEnvVar), + pf(async), + pf(await), pf(help) #endif }; @@ -471,6 +475,8 @@ struct Environment defaultEnvPreAllocated(struct EnvElement* elements) pf(takeInput), pf(readFileToObject), pf(getEnvVar), + pf(async), + pf(await), pf(help) #endif }; @@ -554,6 +560,7 @@ void shredDictionary() } #ifdef STANDALONE + struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*), const char* help, const char* const* tests, size_t testLength) { @@ -570,6 +577,7 @@ struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, st .sym = symbol, }; } + #else struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*)) { @@ -699,7 +707,7 @@ int runTests(int detailed) char* result = stringObj(&o, &length); cleanObject(&o); int expectedLen = 0; - while(expected[expectedLen] && expected[expectedLen] != ';') { + while (expected[expectedLen] && expected[expectedLen] != ';') { expectedLen++; } if (strncmp(result, expected, expectedLen) != 0) { @@ -733,6 +741,7 @@ int runTests(int detailed) // fprintf(stderr, "TOTAL BYTES: %zu\n", getBytes()); return failureCount; } + #endif int getStructIndex(const char* name) diff --git a/src/object.c b/src/object.c index 82127f4..79ef2ba 100644 --- a/src/object.c +++ b/src/object.c @@ -6,6 +6,9 @@ #include #ifdef STANDALONE + +#include "threads.h" + size_t bytes = 0; size_t getBytes() @@ -148,7 +151,7 @@ static const char* errorText[] = { "NO_CLONE_SPECIFIED", "CAN_ONLY_EVAL_STRINGS", "UNEXPECTED_EOF", - "INDEX_PAST_END"}; + "INDEX_PAST_END" }; #endif size_t inflate(struct string* s, size_t additional) @@ -172,7 +175,7 @@ size_t inflate(struct string* s, size_t additional) return s->capacity; } -int stringNObj(struct string *s, const Object* obj); +int stringNObj(struct string* s, const Object* obj); /** * Creates a string from a given list Object @@ -263,6 +266,9 @@ int stringNObj(struct string* s, const Object* obj) case TYPE_LIST: stringList(s, obj); break; + case TYPE_PROMISE: + s->cursor += sprintf(s->cursor, isDone(obj->promise) ? "" : ""); + break; case TYPE_ERROR: { int code = getErrorCode(*obj); #ifdef SIMPLE_ERRORS @@ -344,6 +350,7 @@ void printType(const Object* obj) SIMPLE_TYPE(TYPE_FUNC); SIMPLE_TYPE(TYPE_SYMBOL); SIMPLE_TYPE(TYPE_STRING); + SIMPLE_TYPE(TYPE_PROMISE); SIMPLE_TYPE(TYPE_OTHER); SIMPLE_TYPE(TYPE_ERROR); case TYPE_LAMBDA: @@ -426,6 +433,9 @@ void cleanObject(Object* target) free(target->structObject->fields); free(target->structObject); break; + case TYPE_PROMISE: + cleanPromise(target->promise); + break; case TYPE_ERROR: #ifndef SIMPLE_ERRORS free(target->error->plContext); @@ -603,6 +613,7 @@ inline int isValidType(const Object test) case TYPE_SYMBOL: case TYPE_LAMBDA: case TYPE_STRING: + case TYPE_PROMISE: case TYPE_OTHER: case TYPE_ERROR: return 1; @@ -648,6 +659,8 @@ inline Object cloneObject(const Object src) case TYPE_STRING: case TYPE_SYMBOL: return cloneString(src); + case TYPE_PROMISE: + return clonePromise(src); case TYPE_ERROR: return errorWithContext(getErrorCode(src), src.error->context); case TYPE_OTHER: diff --git a/src/object.h b/src/object.h index 6f4d46e..a0a78e2 100644 --- a/src/object.h +++ b/src/object.h @@ -9,6 +9,7 @@ #define sprintf(_dest, args...) snprintf(_dest, 999, args) #else + #ifdef DEBUG #define printd(...) printf(__VA_ARGS__) #else @@ -67,6 +68,7 @@ typedef enum Type { TYPE_SYMBOL, TYPE_LAMBDA, TYPE_STRING, + TYPE_PROMISE, TYPE_OTHER, TYPE_ERROR } Type; @@ -75,6 +77,7 @@ typedef struct Object Object; struct Lambda; struct Environment; +struct Promise; struct Other; struct Error { enum errorCode code; @@ -95,6 +98,7 @@ struct Object { struct StructObject* structObject; struct Lambda* lambda; struct Other* other; + struct Promise* promise; #ifdef SIMPLE_ERRORS enum errorCode error; #else @@ -246,6 +250,7 @@ struct Error noError(); Object constructLambda(const Object* params, const Object* body, struct Environment* env); #ifdef STANDALONE + int getAllocations(); size_t getBytes(); diff --git a/src/pebblisp.c b/src/pebblisp.c index 7309eaf..6fb5006 100644 --- a/src/pebblisp.c +++ b/src/pebblisp.c @@ -287,6 +287,7 @@ Object eval(const Object* obj, struct Environment* env) case TYPE_STRING: case TYPE_STRUCT: case TYPE_SLIST: + case TYPE_PROMISE: return cloneObject(*obj); case TYPE_SYMBOL: diff --git a/src/plfunc.c b/src/plfunc.c index 8c32741..d14e3e0 100644 --- a/src/plfunc.c +++ b/src/plfunc.c @@ -187,7 +187,7 @@ Object chars(Object* params, unused int length, unused struct Environment* env) c[1] = '\0'; int i = 0; Object list = listObject(); - while(params[0].string[i]) { + while (params[0].string[i]) { c[0] = params[0].string[i]; nf_addToList(&list, stringFromSlice(c, 1)); i++; @@ -198,7 +198,7 @@ Object chars(Object* params, unused int length, unused struct Environment* env) size_t literalLen(const char* pattern) { const char* cursor = pattern; - while(*cursor != '*' && *cursor != '\0') { + while (*cursor != '*' && *cursor != '\0') { cursor++; } return cursor - pattern; @@ -457,7 +457,7 @@ Object numToChar(Object* params, unused int length, unused struct Environment* e checkTypes(numToChar) Object c = params[0]; - if (c.type != TYPE_NUMBER) { + if (c.type != TYPE_NUMBER || c.number > 255 || c.number < 0) { return errorObject(BAD_NUMBER); } char ch[1] = { c.number }; @@ -561,4 +561,5 @@ Object getEnvVar(Object* params, unused int length, unused struct Environment* e checkTypes(getEnvVar) return nullTerminated(getenv(params[0].string)); } + #endif // STANDALONE diff --git a/src/threads.c b/src/threads.c new file mode 100644 index 0000000..e90d3ff --- /dev/null +++ b/src/threads.c @@ -0,0 +1,74 @@ +#include "threads.h" +#include "object.h" +#include "pebblisp.h" + +#include +#include +#include + +struct Promise { + int done; + int refs; + struct Environment* env; + Object object; + pthread_t thread; +}; + +Object clonePromise(Object src) +{ + src.promise->refs += 1; + return src; +} + +int isDone(struct Promise* promise) +{ + return promise->done; +} + +void cleanPromise(struct Promise* promise) +{ + promise->refs -= 1; + if (promise->refs == 0) { + free(promise); + } +} + +Object await(Object* params, int length, struct Environment* env) +{ + struct Promise* promise = params[0].promise; + if (!promise->done) { // TODO: Does `done` need a mutex or other lock? + pthread_join(promise->thread, NULL); + } + return cloneObject(promise->object); +} + +void* doAsync(void* args) +{ + struct Promise* promise = args; + + Object cloned = cloneObject(promise->object); + Object first_eval = eval(&cloned, promise->env); + Object e = listEvalLambda(&first_eval, NULL, 1, promise->env); + + promise->object = e; + promise->done = 1; + cleanPromise(promise); + + cleanObject(&cloned); + return NULL; +} + +Object async(Object* params, int length, struct Environment* env) +{ + Object promise = newObject(TYPE_PROMISE); + promise.promise = malloc(sizeof(struct Promise)); + *promise.promise = (struct Promise) { + .refs = 2, + .done = 0, + .env = env, + .object = params[0], // TODO: Clone? + }; + + pthread_create(&promise.promise->thread, NULL, doAsync, promise.promise); + return promise; +} diff --git a/src/threads.h b/src/threads.h new file mode 100644 index 0000000..8d5ce3b --- /dev/null +++ b/src/threads.h @@ -0,0 +1,26 @@ +#ifdef STANDALONE + +#ifndef THREADS_H +#define THREADS_H + +#include "pebblisp.h" + +Object clonePromise(Object src); + +void cleanPromise(struct Promise* promise); + +int isDone(struct Promise* promise); + +fn(async, "async", + "Run the given lambda on a separate thread.", + "(def sleepy (fn () ((sys \"sleep 0.1\") \"Hiya\"))) (def x (async sleepy)) x", "", + "(def sleepy (fn () ((sys \"sleep 0.1\") \"Hiya\"))) (def x (async sleepy)) (await x)", "Hiya", +); + +fn(await, "await", + "" +); + +#endif // PEBBLISP_THREADS_H + +#endif // STANDALONE \ No newline at end of file