Add simple Promise type.
A bit unstable. Almost certainly needs some locking on struct Promise fields.
This commit is contained in:
parent
fc4f0bef28
commit
b54b93c7d1
|
@ -1,5 +1,5 @@
|
||||||
files = main.c pebblisp.c tokens.c object.c env.c web.c plfunc.c hash.c
|
files = main.c pebblisp.c tokens.c object.c env.c web.c plfunc.c hash.c threads.c
|
||||||
libs = -lreadline -lmicrohttpd
|
libs = -lreadline -lmicrohttpd -lpthread
|
||||||
exe = pl
|
exe = pl
|
||||||
|
|
||||||
file_libs := $(files) $(libs)
|
file_libs := $(files) $(libs)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "pebblisp.h"
|
#include "pebblisp.h"
|
||||||
#include "web.h"
|
#include "web.h"
|
||||||
|
#include "threads.h"
|
||||||
#include "plfunc.h"
|
#include "plfunc.h"
|
||||||
|
|
||||||
struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*), const char* help,
|
struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*), const char* help,
|
||||||
|
@ -57,6 +58,7 @@ struct symFunc {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef HASHLESS_ENV // Hash-based env
|
#ifndef HASHLESS_ENV // Hash-based env
|
||||||
|
|
||||||
Object* fetch(const char* name, struct Environment* env)
|
Object* fetch(const char* name, struct Environment* env)
|
||||||
{
|
{
|
||||||
while (env) {
|
while (env) {
|
||||||
|
@ -207,6 +209,8 @@ struct Environment defaultEnv()
|
||||||
pf(takeInput),
|
pf(takeInput),
|
||||||
pf(readFileToObject),
|
pf(readFileToObject),
|
||||||
pf(getEnvVar),
|
pf(getEnvVar),
|
||||||
|
pf(async),
|
||||||
|
pf(await),
|
||||||
pf(help)
|
pf(help)
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
@ -471,6 +475,8 @@ struct Environment defaultEnvPreAllocated(struct EnvElement* elements)
|
||||||
pf(takeInput),
|
pf(takeInput),
|
||||||
pf(readFileToObject),
|
pf(readFileToObject),
|
||||||
pf(getEnvVar),
|
pf(getEnvVar),
|
||||||
|
pf(async),
|
||||||
|
pf(await),
|
||||||
pf(help)
|
pf(help)
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
@ -554,6 +560,7 @@ void shredDictionary()
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef STANDALONE
|
#ifdef STANDALONE
|
||||||
|
|
||||||
struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*), const char* help,
|
struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*), const char* help,
|
||||||
const char* const* tests, size_t testLength)
|
const char* const* tests, size_t testLength)
|
||||||
{
|
{
|
||||||
|
@ -570,6 +577,7 @@ struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, st
|
||||||
.sym = symbol,
|
.sym = symbol,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*))
|
struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*))
|
||||||
{
|
{
|
||||||
|
@ -733,6 +741,7 @@ int runTests(int detailed)
|
||||||
// fprintf(stderr, "TOTAL BYTES: %zu\n", getBytes());
|
// fprintf(stderr, "TOTAL BYTES: %zu\n", getBytes());
|
||||||
return failureCount;
|
return failureCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int getStructIndex(const char* name)
|
int getStructIndex(const char* name)
|
||||||
|
|
13
src/object.c
13
src/object.c
|
@ -6,6 +6,9 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef STANDALONE
|
#ifdef STANDALONE
|
||||||
|
|
||||||
|
#include "threads.h"
|
||||||
|
|
||||||
size_t bytes = 0;
|
size_t bytes = 0;
|
||||||
|
|
||||||
size_t getBytes()
|
size_t getBytes()
|
||||||
|
@ -263,6 +266,9 @@ int stringNObj(struct string* s, const Object* obj)
|
||||||
case TYPE_LIST:
|
case TYPE_LIST:
|
||||||
stringList(s, obj);
|
stringList(s, obj);
|
||||||
break;
|
break;
|
||||||
|
case TYPE_PROMISE:
|
||||||
|
s->cursor += sprintf(s->cursor, isDone(obj->promise) ? "<DONE>" : "<PENDING>");
|
||||||
|
break;
|
||||||
case TYPE_ERROR: {
|
case TYPE_ERROR: {
|
||||||
int code = getErrorCode(*obj);
|
int code = getErrorCode(*obj);
|
||||||
#ifdef SIMPLE_ERRORS
|
#ifdef SIMPLE_ERRORS
|
||||||
|
@ -344,6 +350,7 @@ void printType(const Object* obj)
|
||||||
SIMPLE_TYPE(TYPE_FUNC);
|
SIMPLE_TYPE(TYPE_FUNC);
|
||||||
SIMPLE_TYPE(TYPE_SYMBOL);
|
SIMPLE_TYPE(TYPE_SYMBOL);
|
||||||
SIMPLE_TYPE(TYPE_STRING);
|
SIMPLE_TYPE(TYPE_STRING);
|
||||||
|
SIMPLE_TYPE(TYPE_PROMISE);
|
||||||
SIMPLE_TYPE(TYPE_OTHER);
|
SIMPLE_TYPE(TYPE_OTHER);
|
||||||
SIMPLE_TYPE(TYPE_ERROR);
|
SIMPLE_TYPE(TYPE_ERROR);
|
||||||
case TYPE_LAMBDA:
|
case TYPE_LAMBDA:
|
||||||
|
@ -426,6 +433,9 @@ void cleanObject(Object* target)
|
||||||
free(target->structObject->fields);
|
free(target->structObject->fields);
|
||||||
free(target->structObject);
|
free(target->structObject);
|
||||||
break;
|
break;
|
||||||
|
case TYPE_PROMISE:
|
||||||
|
cleanPromise(target->promise);
|
||||||
|
break;
|
||||||
case TYPE_ERROR:
|
case TYPE_ERROR:
|
||||||
#ifndef SIMPLE_ERRORS
|
#ifndef SIMPLE_ERRORS
|
||||||
free(target->error->plContext);
|
free(target->error->plContext);
|
||||||
|
@ -603,6 +613,7 @@ inline int isValidType(const Object test)
|
||||||
case TYPE_SYMBOL:
|
case TYPE_SYMBOL:
|
||||||
case TYPE_LAMBDA:
|
case TYPE_LAMBDA:
|
||||||
case TYPE_STRING:
|
case TYPE_STRING:
|
||||||
|
case TYPE_PROMISE:
|
||||||
case TYPE_OTHER:
|
case TYPE_OTHER:
|
||||||
case TYPE_ERROR:
|
case TYPE_ERROR:
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -648,6 +659,8 @@ inline Object cloneObject(const Object src)
|
||||||
case TYPE_STRING:
|
case TYPE_STRING:
|
||||||
case TYPE_SYMBOL:
|
case TYPE_SYMBOL:
|
||||||
return cloneString(src);
|
return cloneString(src);
|
||||||
|
case TYPE_PROMISE:
|
||||||
|
return clonePromise(src);
|
||||||
case TYPE_ERROR:
|
case TYPE_ERROR:
|
||||||
return errorWithContext(getErrorCode(src), src.error->context);
|
return errorWithContext(getErrorCode(src), src.error->context);
|
||||||
case TYPE_OTHER:
|
case TYPE_OTHER:
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#define sprintf(_dest, args...) snprintf(_dest, 999, args)
|
#define sprintf(_dest, args...) snprintf(_dest, 999, args)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#define printd(...) printf(__VA_ARGS__)
|
#define printd(...) printf(__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
|
@ -67,6 +68,7 @@ typedef enum Type {
|
||||||
TYPE_SYMBOL,
|
TYPE_SYMBOL,
|
||||||
TYPE_LAMBDA,
|
TYPE_LAMBDA,
|
||||||
TYPE_STRING,
|
TYPE_STRING,
|
||||||
|
TYPE_PROMISE,
|
||||||
TYPE_OTHER,
|
TYPE_OTHER,
|
||||||
TYPE_ERROR
|
TYPE_ERROR
|
||||||
} Type;
|
} Type;
|
||||||
|
@ -75,6 +77,7 @@ typedef struct Object Object;
|
||||||
|
|
||||||
struct Lambda;
|
struct Lambda;
|
||||||
struct Environment;
|
struct Environment;
|
||||||
|
struct Promise;
|
||||||
struct Other;
|
struct Other;
|
||||||
struct Error {
|
struct Error {
|
||||||
enum errorCode code;
|
enum errorCode code;
|
||||||
|
@ -95,6 +98,7 @@ struct Object {
|
||||||
struct StructObject* structObject;
|
struct StructObject* structObject;
|
||||||
struct Lambda* lambda;
|
struct Lambda* lambda;
|
||||||
struct Other* other;
|
struct Other* other;
|
||||||
|
struct Promise* promise;
|
||||||
#ifdef SIMPLE_ERRORS
|
#ifdef SIMPLE_ERRORS
|
||||||
enum errorCode error;
|
enum errorCode error;
|
||||||
#else
|
#else
|
||||||
|
@ -246,6 +250,7 @@ struct Error noError();
|
||||||
Object constructLambda(const Object* params, const Object* body, struct Environment* env);
|
Object constructLambda(const Object* params, const Object* body, struct Environment* env);
|
||||||
|
|
||||||
#ifdef STANDALONE
|
#ifdef STANDALONE
|
||||||
|
|
||||||
int getAllocations();
|
int getAllocations();
|
||||||
|
|
||||||
size_t getBytes();
|
size_t getBytes();
|
||||||
|
|
|
@ -287,6 +287,7 @@ Object eval(const Object* obj, struct Environment* env)
|
||||||
case TYPE_STRING:
|
case TYPE_STRING:
|
||||||
case TYPE_STRUCT:
|
case TYPE_STRUCT:
|
||||||
case TYPE_SLIST:
|
case TYPE_SLIST:
|
||||||
|
case TYPE_PROMISE:
|
||||||
return cloneObject(*obj);
|
return cloneObject(*obj);
|
||||||
|
|
||||||
case TYPE_SYMBOL:
|
case TYPE_SYMBOL:
|
||||||
|
|
|
@ -457,7 +457,7 @@ Object numToChar(Object* params, unused int length, unused struct Environment* e
|
||||||
checkTypes(numToChar)
|
checkTypes(numToChar)
|
||||||
Object c = params[0];
|
Object c = params[0];
|
||||||
|
|
||||||
if (c.type != TYPE_NUMBER) {
|
if (c.type != TYPE_NUMBER || c.number > 255 || c.number < 0) {
|
||||||
return errorObject(BAD_NUMBER);
|
return errorObject(BAD_NUMBER);
|
||||||
}
|
}
|
||||||
char ch[1] = { c.number };
|
char ch[1] = { c.number };
|
||||||
|
@ -561,4 +561,5 @@ Object getEnvVar(Object* params, unused int length, unused struct Environment* e
|
||||||
checkTypes(getEnvVar)
|
checkTypes(getEnvVar)
|
||||||
return nullTerminated(getenv(params[0].string));
|
return nullTerminated(getenv(params[0].string));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // STANDALONE
|
#endif // STANDALONE
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
#include "threads.h"
|
||||||
|
#include "object.h"
|
||||||
|
#include "pebblisp.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -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", "<PENDING>",
|
||||||
|
"(def sleepy (fn () ((sys \"sleep 0.1\") \"Hiya\"))) (def x (async sleepy)) (await x)", "Hiya",
|
||||||
|
);
|
||||||
|
|
||||||
|
fn(await, "await",
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif // PEBBLISP_THREADS_H
|
||||||
|
|
||||||
|
#endif // STANDALONE
|
Loading…
Reference in New Issue