klisp

an open source interpreter for the Kernel Programming Language.
git clone http://git.hanabi.in/repos/klisp.git
Log | Files | Refs | README

commit 2ae03fef7b1da19bd6ba56d788c83b05058cedc0
parent 16a7dd113090e31f05d019b9372cc9bb0bfb691a
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Sat, 25 Aug 2012 14:34:41 -0300

Temporarily moved all locking to the main thread loop.  This will limit parallelism for now but will alow gradual rewrite of all operatives and continuations.

Diffstat:
MCHANGES | 5+++--
MTODO | 5+++++
Msrc/imath.c | 10----------
Msrc/imrat.c | 6------
Msrc/kapplicative.c | 2--
Msrc/kbytevector.c | 12++----------
Msrc/kcontinuation.c | 4----
Msrc/kencapsulation.c | 2--
Msrc/kenvironment.c | 14--------------
Msrc/kerror.c | 5-----
Msrc/keval.c | 2--
Msrc/kgerrors.h | 2++
Msrc/kghelpers.c | 8--------
Msrc/kgsystem.c | 6+++++-
Msrc/klimits.h | 1+
Msrc/kmem.c | 2+-
Msrc/kstate.c | 15+++++++++++----
Msrc/kstate.h | 15+--------------
18 files changed, 31 insertions(+), 85 deletions(-)

diff --git a/CHANGES b/CHANGES @@ -2,4 +2,6 @@ v0.4 - Added eq-hashtables (Oto Havle) - Fixed semantics of eval in the presence of continuation capturing - and mutation of the argument or result list -\ No newline at end of file + and mutation of the argument or result list +- Fixed semantics of other combiners in the presence of continuation capturing + and mutation (filter) diff --git a/TODO b/TODO @@ -1,3 +1,8 @@ +- Check lambda for problems with continuation capturing +- Check the let family for problems with continuation capturing +- Check map for problems with continuation capturing + + * Release 0.4+ ** refactor: *** clean stand alone interpreter diff --git a/src/imath.c b/src/imath.c @@ -374,9 +374,7 @@ mp_result mp_int_init(mp_int z) mp_int mp_int_alloc(klisp_State *K) { - klisp_lock(K); mp_int out = klispM_new(K, mpz_t); - klisp_unlock(K); if(out != NULL) mp_int_init(out); @@ -490,9 +488,7 @@ void mp_int_free(klisp_State *K, mp_int z) NRCHECK(z != NULL); mp_int_clear(K, z); - klisp_lock(K); klispM_free(K, z); /* note: NOT s_free() */ - klisp_unlock(K); } /* }}} */ @@ -2212,9 +2208,7 @@ const char *mp_error_string(mp_result res) STATIC mp_digit *s_alloc(klisp_State *K, mp_size num) { - klisp_lock(K); mp_digit *out = klispM_malloc(K, num * sizeof(mp_digit)); - klisp_unlock(K); assert(out != NULL); /* for debugging */ #if DEBUG > 1 @@ -2246,10 +2240,8 @@ STATIC mp_digit *s_realloc(klisp_State *K, mp_digit *old, mp_size osize, memcpy(new, old, osize * sizeof(mp_digit)); #else - klisp_lock(K); mp_digit *new = klispM_realloc_(K, old, osize * sizeof(mp_digit), nsize * sizeof(mp_digit)); - klisp_unlock(K); assert(new != NULL); /* for debugging */ #endif return new; @@ -2261,9 +2253,7 @@ STATIC mp_digit *s_realloc(klisp_State *K, mp_digit *old, mp_size osize, STATIC void s_free(klisp_State *K, void *ptr, mp_size size) { - klisp_lock(K); klispM_freemem(K, ptr, size * sizeof(mp_digit)); - klisp_unlock(K); } /* }}} */ diff --git a/src/imrat.c b/src/imrat.c @@ -59,15 +59,11 @@ mp_result mp_rat_init(klisp_State *K, mp_rat r) mp_rat mp_rat_alloc(klisp_State *K) { - klisp_lock(K); mp_rat out = klispM_new(K, mpq_t); - klisp_unlock(K); if(out != NULL) { if(mp_rat_init(K, out) != MP_OK) { - klisp_lock(K); klispM_free(K, out); - klisp_unlock(K); return NULL; } } @@ -150,9 +146,7 @@ void mp_rat_free(klisp_State *K, mp_rat r) if(r->num.digits != NULL) mp_rat_clear(K, r); - klisp_lock(K); klispM_free(K, r); - klisp_unlock(K); } /* }}} */ diff --git a/src/kapplicative.c b/src/kapplicative.c @@ -13,7 +13,6 @@ /* GC: Assumes underlying is rooted */ TValue kwrap(klisp_State *K, TValue underlying) { - klisp_lock(K); Applicative *new_app = klispM_new(K, Applicative); /* header + gc_fields */ @@ -22,6 +21,5 @@ TValue kwrap(klisp_State *K, TValue underlying) /* applicative specific fields */ new_app->underlying = underlying; - klisp_unlock(K); return gc2app(new_app); } diff --git a/src/kbytevector.c b/src/kbytevector.c @@ -70,12 +70,11 @@ static Bytevector *search_in_bb_table(klisp_State *K, const uint8_t *buf, TValue kbytevector_new_bs_imm(klisp_State *K, const uint8_t *buf, uint32_t size) { uint32_t h = get_bytevector_hash(buf, size); - klisp_lock(K); + /* first check to see if it's in the stringtable */ Bytevector *new_bb = search_in_bb_table(K, buf, size, h); if (new_bb != NULL) { /* found */ - klisp_unlock(K); return gc2bytevector(new_bb); } @@ -118,7 +117,6 @@ TValue kbytevector_new_bs_imm(klisp_State *K, const uint8_t *buf, uint32_t size) krooted_tvs_pop(K); } - klisp_unlock(K); return ret_tv; } @@ -137,7 +135,6 @@ TValue kbytevector_new_s(klisp_State *K, uint32_t size) return G(K)->empty_bytevector; } - klisp_lock(K); new_bb = klispM_malloc(K, sizeof(Bytevector) + size); /* header + gc_fields */ @@ -148,7 +145,6 @@ TValue kbytevector_new_s(klisp_State *K, uint32_t size) new_bb->size = size; /* the buffer is initialized elsewhere */ - klisp_unlock(K); return gc2bytevector(new_bb); } @@ -187,12 +183,8 @@ bool kbytevector_equalp(klisp_State *K, TValue obj1, TValue obj2) Bytevector *bytevector2 = tv2bytevector(obj2); if (bytevector1->size == bytevector2->size) { - bool res; - klisp_lock(K); - res = (bytevector1->size == 0) || + return (bytevector1->size == 0) || (memcmp(bytevector1->b, bytevector2->b, bytevector1->size) == 0); - klisp_unlock(K); - return res; } else { return false; } diff --git a/src/kcontinuation.c b/src/kcontinuation.c @@ -19,7 +19,6 @@ TValue kmake_continuation(klisp_State *K, TValue parent, klisp_CFunction fn, { va_list argp; - klisp_lock(K); Continuation *new_cont = (Continuation *) klispM_malloc(K, sizeof(Continuation) + sizeof(TValue) * xcount); @@ -51,7 +50,6 @@ TValue kmake_continuation(klisp_State *K, TValue parent, klisp_CFunction fn, /* TODO: find all the places where this should be changed (like $and?, $sequence), and change it */ kset_source_info(K, res, kget_csi(K)); - klisp_unlock(K); return res; } @@ -130,7 +128,6 @@ static TValue select_interceptor(TValue guard_ls) TValue create_interception_list(klisp_State *K, TValue src_cont, TValue dst_cont) { - klisp_lock(K); mark_iancestors(dst_cont); TValue ilist = kcons(K, KNIL, KNIL); krooted_vars_push(K, &ilist); @@ -205,7 +202,6 @@ TValue create_interception_list(klisp_State *K, TValue src_cont, /* all interceptions collected, append the two lists and return */ kset_cdr(tail, entry_int); - klisp_unlock(K); krooted_vars_pop(K); krooted_vars_pop(K); return kcdr(ilist); diff --git a/src/kencapsulation.c b/src/kencapsulation.c @@ -19,7 +19,6 @@ bool kis_encapsulation_type(TValue enc, TValue key) /* GC: Assumes that key & val are rooted */ TValue kmake_encapsulation(klisp_State *K, TValue key, TValue val) { - klisp_lock(K); Encapsulation *new_enc = klispM_new(K, Encapsulation); /* header + gc_fields */ @@ -29,7 +28,6 @@ TValue kmake_encapsulation(klisp_State *K, TValue key, TValue val) new_enc->key = key; new_enc->value = val; - klisp_unlock(K); return gc2enc(new_enc); } diff --git a/src/kenvironment.c b/src/kenvironment.c @@ -29,7 +29,6 @@ /* GC: Assumes that parents is rooted */ TValue kmake_environment(klisp_State *K, TValue parents) { - klisp_lock(K); Environment *new_env = klispM_new(K, Environment); /* header + gc_fields */ @@ -88,7 +87,6 @@ TValue kmake_environment(klisp_State *K, TValue parents) kparents = kcar(kparents); } new_env->keyed_parents = kparents; /* overwrite with the proper value */ - klisp_unlock(K); return gc2env(new_env); } @@ -124,7 +122,6 @@ TValue kfind_local_binding(klisp_State *K, TValue bindings, TValue sym) /* GC: Assumes that obj & sym are rooted. */ void ktry_set_name(klisp_State *K, TValue obj, TValue sym) { - klisp_lock(K); if (kcan_have_name(obj) && !khas_name(obj)) { /* TODO: maybe we could have some kind of inheritance so that if this object receives a name it can pass on that @@ -150,7 +147,6 @@ void ktry_set_name(klisp_State *K, TValue obj, TValue sym) } } } - klisp_unlock(K); } /* Assumes obj has a name */ @@ -176,7 +172,6 @@ void kadd_binding(klisp_State *K, TValue env, TValue sym, TValue val) /* lock early because it is possible that even the environment type changes (from list to table) */ - klisp_lock(K); TValue bindings = kenv_bindings(K, env); if (ttistable(bindings)) { TValue *cell = klispH_setsym(K, tv2table(bindings), tv2sym(sym)); @@ -193,7 +188,6 @@ void kadd_binding(klisp_State *K, TValue env, TValue sym, TValue val) kset_cdr(oldb, val); } } - klisp_unlock(K); } /* This works no matter if parents is a list or a single environment */ @@ -201,7 +195,6 @@ void kadd_binding(klisp_State *K, TValue env, TValue sym, TValue val) static inline bool try_get_binding(klisp_State *K, TValue env, TValue sym, TValue *value) { - klisp_lock(K); /* assume the stack may be in use, keep track of pushed objs */ int pushed = 1; ks_spush(K, env); @@ -243,7 +236,6 @@ static inline bool try_get_binding(klisp_State *K, TValue env, TValue sym, *value = KINERT; - klisp_unlock(K); return false; } @@ -276,9 +268,7 @@ TValue kmake_keyed_static_env(klisp_State *K, TValue parent, TValue key, { TValue new_env = kmake_environment(K, parent); krooted_tvs_push(K, new_env); /* keep the env rooted */ - klisp_lock(K); env_keyed_node(new_env) = kcons(K, key, val); - klisp_unlock(K); krooted_tvs_pop(K); return new_env; } @@ -291,7 +281,6 @@ static inline bool try_get_keyed(klisp_State *K, TValue env, TValue key, repetition */ /* assume the stack may be in use, keep track of pushed objs */ - klisp_lock(K); int pushed = 1; if (!env_is_keyed(env)) env = env_keyed_parents(env); @@ -320,7 +309,6 @@ static inline bool try_get_keyed(klisp_State *K, TValue env, TValue key, pushed += 2; } } - klisp_unlock(K); *value = KINERT; return false; } @@ -344,9 +332,7 @@ TValue kmake_table_environment(klisp_State *K, TValue parents) TValue new_env = kmake_environment(K, parents); krooted_tvs_push(K, new_env); TValue new_table = klispH_new(K, 0, ENVTABSIZE, K_FLAG_WEAK_NOTHING); - klisp_lock(K); tv2env(new_env)->bindings = new_table; - klisp_unlock(K); krooted_tvs_pop(K); return new_env; } diff --git a/src/kerror.c b/src/kerror.c @@ -18,7 +18,6 @@ TValue klispE_new(klisp_State *K, TValue who, TValue cont, TValue msg, TValue irritants) { - klisp_lock(K); Error *new_error = klispM_new(K, Error); /* header + gc_fields */ @@ -29,7 +28,6 @@ TValue klispE_new(klisp_State *K, TValue who, TValue cont, TValue msg, new_error->cont = cont; new_error->msg = msg; new_error->irritants = irritants; - klisp_unlock(K); return gc2error(new_error); } @@ -89,7 +87,6 @@ void klispE_throw_simple(klisp_State *K, char *msg) /* clear buffer shouldn't cause GC, but just in case... */ krooted_tvs_push(K, error_obj); clear_buffers(K); /* this pops both error_msg & error_obj */ - klisp_unlock_all(K); /* is this thread holds the GIL release it */ /* call_cont protects error from gc */ kcall_cont(K, G(K)->error_cont, error_obj); } @@ -115,7 +112,6 @@ void klispE_throw_with_irritants(klisp_State *K, char *msg, TValue irritants) /* clear buffer shouldn't cause GC, but just in case... */ krooted_tvs_push(K, error_obj); clear_buffers(K); /* this pops both error_msg & error_obj */ - klisp_unlock_all(K); /* is this thread holds the GIL release it */ /* call_cont protects error from gc */ kcall_cont(K, G(K)->error_cont, error_obj); } @@ -127,7 +123,6 @@ void klispE_throw_system_error_with_irritants( irritants); krooted_tvs_push(K, error_obj); clear_buffers(K); - klisp_unlock_all(K); /* is this thread holds the GIL release it */ kcall_cont(K, G(K)->system_error_cont, error_obj); } diff --git a/src/keval.c b/src/keval.c @@ -173,10 +173,8 @@ void keval_ofn(klisp_State *K) switch(ttype(obj)) { case K_TPAIR: { - klisp_lock(K); TValue operator = kcar(obj); TValue operands = kcdr(obj); - klisp_unlock(K); TValue new_cont = kmake_continuation(K, kget_cc(K), do_combine_operands, 3, operands, denv, ktry_get_si(K, obj)); diff --git a/src/kgerrors.h b/src/kgerrors.h @@ -11,5 +11,7 @@ /* init ground */ void kinit_error_ground_env(klisp_State *K); +/* init cont names */ +void kinit_error_cont_names(klisp_State *K); #endif diff --git a/src/kghelpers.c b/src/kghelpers.c @@ -394,8 +394,6 @@ void check_typed_list(klisp_State *K, bool (*typep)(TValue), bool allow_infp, int32_t p = 0; bool type_errorp = false; - klisp_lock(K); - while(ttispair(tail) && !kis_marked(tail)) { /* even if there is a type error continue checking the structure */ type_errorp |= !(*typep)(kcar(tail)); @@ -410,8 +408,6 @@ void check_typed_list(klisp_State *K, bool (*typep)(TValue), bool allow_infp, unmark_list(K, obj); - klisp_unlock(K); - if (!ttispair(tail) && !ttisnil(tail)) { klispE_throw_simple(K, allow_infp? "expected list" : "expected finite list"); @@ -433,7 +429,6 @@ void check_list(klisp_State *K, bool allow_infp, TValue obj, TValue tail = obj; int32_t p = 0; - klisp_lock(K); while(ttispair(tail) && !kis_marked(tail)) { kset_mark(tail, i2tv(p)); tail = kcdr(tail); @@ -445,7 +440,6 @@ void check_list(klisp_State *K, bool allow_infp, TValue obj, *cpairs = ttispair(tail)? (p - ivalue(kget_mark(tail))) : 0; unmark_list(K, obj); - klisp_unlock(K); if (!ttispair(tail) && !ttisnil(tail)) { klispE_throw_simple(K, allow_infp? "expected list" : @@ -478,7 +472,6 @@ TValue check_copy_list(klisp_State *K, TValue obj, bool force_copy, TValue last_pair = copy; TValue tail = obj; - klisp_lock(K); while(ttispair(tail) && !kis_marked(tail)) { TValue new_pair = kcons(K, kcar(tail), KNIL); /* record the corresponding pair to simplify cycle handling */ @@ -508,7 +501,6 @@ TValue check_copy_list(klisp_State *K, TValue obj, bool force_copy, unmark_list(K, obj); unmark_list(K, kcdr(copy)); - klisp_unlock(K); if (!ttispair(tail) && !ttisnil(tail)) { klispE_throw_simple(K, "expected list"); diff --git a/src/kgsystem.c b/src/kgsystem.c @@ -103,17 +103,21 @@ void delete_file(klisp_State *K) /* TEMP: this should probably be done in a operating system specific manner, but this will do for now */ + /* allow other threads to run while the file is being removed */ + klisp_unlock(K); if (remove(kstring_buf(filename))) { /* At least in Windows, this could have failed if there's a dead (in the gc sense) port still open, should retry once after doing a complete GC. This isn't ideal but... */ klisp_lock(K); klispC_fullgc(K); - klisp_unlock(K); + klisp_unlock(K); if (remove(kstring_buf(filename))) { + klisp_lock(K); klispE_throw_errno_with_irritants(K, "remove", 1, filename); return; } + klisp_lock(K); } kapply_cc(K, KINERT); } diff --git a/src/klimits.h b/src/klimits.h @@ -105,6 +105,7 @@ --K->gil_count; \ }}) +/* this will work no matter how many times (even 0) the lock was acquired */ #define klisp_unlock_all(K) ({ \ if (K->gil_count > 0) { \ K->gil_count = 1; \ diff --git a/src/kmem.c b/src/kmem.c @@ -96,7 +96,7 @@ void *klispM_realloc_ (klisp_State *K, void *block, size_t osize, size_t nsize) if (block == NULL && nsize > 0) { /* TEMP: try GC if there is no more mem */ /* TODO: make this a catchable error */ - klisp_unlock(K); + klisp_unlock_all(K); fprintf(stderr, MEMERRMSG); abort(); } diff --git a/src/kstate.c b/src/kstate.c @@ -402,7 +402,6 @@ klisp_State *klisp_newthread(klisp_State *K) klisp_State *klispT_newthread(klisp_State *K) { - klisp_lock(K); klisp_State *K1 = tostate(klispM_malloc(K, state_size(klisp_State))); klispC_link(K, (GCObject *) K1, K_TTHREAD, 0); preinit_state(K1, G(K)); @@ -417,19 +416,16 @@ klisp_State *klispT_newthread(klisp_State *K) ks_tbidx(K1) = 0; /* buffer is empty */ klisp_assert(iswhite((GCObject *) (K1))); - klisp_unlock(K); return K1; } void klispT_freethread (klisp_State *K, klisp_State *K1) { - klisp_lock(K); klispM_freemem(K, ks_sbuf(K1), ks_ssize(K1) * sizeof(TValue)); klispM_freemem(K, ks_tbuf(K1), ks_tbsize(K1)); /* userstatefree() */ klispM_freemem(K, fromstate(K1), state_size(klisp_State)); - klisp_unlock(K); } void klisp_close (klisp_State *K) @@ -566,20 +562,31 @@ void klispT_init_repl(klisp_State *K) kinit_repl(K); } +/* +** TEMP/LOCK: put lock here, until all operatives and continuations do locking directly +** or a new interface (like lua api) does it for them. +** This has the problem that nothing can be done in parallel (but still has the advantage +** that (unlike coroutines) when one thread is blocked (e.g. waiting for IO) the others +** may continue (provided that the blocked thread unlocks the GIL before blocking...) +*/ void klispT_run(klisp_State *K) { while(true) { if (setjmp(K->error_jb)) { /* continuation called */ /* TEMP: do nothing, the loop will call the continuation */ + klisp_unlock_all(K); } else { + klisp_lock(K); /* all ok, continue with next func */ while (K->next_func) { /* next_func is either operative or continuation but in any case the call is the same */ (*(K->next_func))(K); + klispi_threadyield(K); } /* K->next_func is NULL, this means we should exit already */ + klisp_unlock(K); break; } } diff --git a/src/kstate.h b/src/kstate.h @@ -461,7 +461,6 @@ static inline void klispT_apply_cc(klisp_State *K, TValue val) { /* TODO write barriers */ - klisp_lock(K); /* various assert to check the freeing of gc protection methods */ /* TODO add marks assertions */ klisp_assert(K->rooted_tvs_top == 0); @@ -476,26 +475,20 @@ static inline void klispT_apply_cc(klisp_State *K, TValue val) K->next_xparams = cont->extra; K->curr_cont = cont->parent; K->next_si = ktry_get_si(K, K->next_obj); - klisp_unlock(K); } #define kapply_cc(K_, val_) klispT_apply_cc((K_), (val_)); return static inline TValue klispT_get_cc(klisp_State *K) { - klisp_lock(K); - TValue res = K->curr_cont; - klisp_unlock(K); - return res; + return K->curr_cont; } #define kget_cc(K_) (klispT_get_cc(K_)) static inline void klispT_set_cc(klisp_State *K, TValue new_cont) { - klisp_lock(K); K->curr_cont = new_cont; - klisp_unlock(K); } #define kset_cc(K_, c_) (klispT_set_cc(K_, c_)) @@ -504,7 +497,6 @@ static inline void klispT_tail_call_si(klisp_State *K, TValue top, TValue ptree, TValue env, TValue si) { /* TODO write barriers */ - klisp_lock(K); /* various assert to check the freeing of gc protection methods */ klisp_assert(K->rooted_tvs_top == 0); klisp_assert(K->rooted_vars_top == 0); @@ -518,7 +510,6 @@ static inline void klispT_tail_call_si(klisp_State *K, TValue top, TValue ptree, K->next_env = env; K->next_xparams = op->extra; K->next_si = si; - klisp_unlock(K); } #define ktail_call_si(K_, op_, p_, e_, si_) \ @@ -528,17 +519,13 @@ static inline void klispT_tail_call_si(klisp_State *K, TValue top, TValue ptree, #define ktail_call(K_, op_, p_, e_) \ { klisp_State *K__ = (K_); \ TValue op__ = (op_); \ - klisp_lock(K); \ TValue si__ = ktry_get_si(K__, op__); \ - klisp_unlock(K); \ (ktail_call_si(K__, op__, p_, e_, si__)); } \ #define ktail_eval(K_, p_, e_) \ { klisp_State *K__ = (K_); \ TValue p__ = (p_); \ - klisp_lock(K); \ TValue si__ = ktry_get_si(K__, p__); \ - klisp_unlock(K); \ klispT_tail_call_si(K__, G(K__)->eval_op, p__, (e_), si__); \ return; }