static void test_call() { char *r[] = { "one_r1" }; char *w[] = { }; char *t[] = { "___param" }; /* simulate a function call whih ignores the return value of a function see stmt_call for implementation details */ Head *head = env_head(env, "one_r1"); Rel *stmts[] = { rel_store("___param", rel_load(head, "one_r1")), rel_store("___param", rel_load(head, "one_r1")) }; Rel *fn = rel_call(r, 1, w, 0, t, 1, stmts, 2, NULL, 0, NULL, "", NULL); Vars *wvars = vars_new(0); long long sid = tx_enter("", rvars, wvars); vars_free(wvars); load_vars(); rel_eval(fn, vars, &arg); rel_free(fn); rel_free(stmts[0]); rel_free(stmts[1]); free_vars(); tx_commit(sid); }
static Rel *pack(char *str, char *names[], Type types[], int len) { char *n[len]; Type t[len]; for (int i = 0; i < len; ++i) { n[i] = names[i]; t[i] = types[i]; } Head *head = head_new(n, t, len); TBuf *body = NULL; Error *err = pack_csv2rel(str, head, &body); if (err != NULL) fail(); Rel *res = NULL; if (body != NULL) { int idx = array_scan(vars->names, vars->len, "___param"); if (idx < 0) fail(); res = rel_load(head, "___param"); vars->vals[idx] = body; } mem_free(head); return res; }
static void processor(const char *tx_addr, int port) { sys_init(1); sys_log('E', "started port=%d, tx=%s\n", port, tx_addr); /* connect to the control thread */ char addr[MAX_ADDR]; sys_address(addr, port); IO *io = sys_connect(addr, IO_CHUNK); tx_attach(tx_addr); /* get env code from the tx */ char *code = tx_program(); char *res = mem_alloc(MAX_BLOCK); while (!io->stop) { sys_iready(io, -1); int status = -1; long long sid = 0LL, time = sys_millis(); Env *env = NULL; Arg *arg = NULL; Vars *v = vars_new(0), *r = NULL, *w = NULL; Http_Req *req = http_parse_req(io); if (io->stop) goto exit; if (req == NULL) { status = http_400(io); goto exit; } if (req->method == OPTIONS) { status = http_opts(io); goto exit; } env = env_new("net", code); if (str_idx(req->path, "/fn") == 0) { int idx = (req->path[3] == '/') ? 4 : 3; int i = 0, len = 1, cnt = 0; Func **fns = env_funcs(env, req->path + idx, &cnt); status = http_200(io); while (status == 200 && len) { len = pack_fn2csv(fns, cnt, res, MAX_BLOCK, &i); status = http_chunk(io, res, len); } mem_free(fns); goto exit; } /* compare the request with the function defintion */ Func *fn = env_func(env, req->path + 1); if (fn == NULL) { Error *err = error_new("unknown function '%s'", req->path + 1); status = http_404(io, err->msg); mem_free(err); goto exit; } if (fn->rp.name != NULL && req->method != POST) { status = http_405(io, POST); goto exit; } if (fn->rp.name == NULL && req->method == POST) { status = http_405(io, GET); goto exit; } /* TODO: think what to do with duplicate parameter values */ for (int i = 0; i < req->args->len; ++i) { char *name = req->args->names[i]; if (array_freq(req->args->names, req->args->len, name) > 1) { Error *err = error_new("duplicate parameter '%s' " "(not supported)", name); status = http_404(io, err->msg); mem_free(err); goto exit; } } if (fn->pp.len != req->args->len) { Error *err = error_new("expected %d primitive parameters, got %d", fn->pp.len, req->args->len); status = http_404(io, err->msg); mem_free(err); goto exit; } arg = mem_alloc(sizeof(Arg)); for (int i = 0; i < fn->pp.len; ++i) { char *name = fn->pp.names[i]; Type t = fn->pp.types[i]; int idx = array_scan(req->args->names, req->args->len, name); if (idx < 0) { Error *err = error_new("unknown parameter '%s'", name); status = http_404(io, err->msg); mem_free(err); goto exit; } char *val = req->args->vals[idx]; int error = 0; if (t == Int) { arg->vals[i].v_int = str_int(val, &error); } else if (t == Real) arg->vals[i].v_real = str_real(val, &error); else if (t == Long) arg->vals[i].v_long = str_long(val, &error); else if (t == String) { error = str_len(val) > MAX_STRING; if (!error) str_cpy(arg->vals[i].v_str, val); } if (error) { Error *err = error_new("value '%s' (parameter '%s') " "is not of type '%s'", val, name, type_to_str(t)); status = http_404(io, err->msg); mem_free(err); goto exit; } } if (fn->rp.name != NULL) { TBuf *body = NULL; if (req->len > 0) { Error *err = pack_csv2rel(req->body, fn->rp.head, &body); if (err != NULL) { status = http_404(io, err->msg); mem_free(err); goto exit; } } else { body = tbuf_new(); } vars_add(v, fn->rp.name, 0, body); /* project the parameter */ Rel *param = rel_project(rel_load(fn->rp.head, fn->rp.name), fn->rp.head->names, fn->rp.head->len); rel_eval(param, v, arg); /* clean the previous version */ tbuf_clean(body); tbuf_free(body); /* replace with the new body */ int vpos = array_scan(v->names, v->len, fn->rp.name); v->vals[vpos] = param->body; param->body = NULL; rel_free(param); } /* start a transaction */ r = vars_new(fn->r.len); w = vars_new(fn->w.len); for (int i = 0; i < fn->r.len; ++i) vars_add(r, fn->r.names[i], 0, NULL); for (int i = 0; i < fn->w.len; ++i) vars_add(w, fn->w.names[i], 0, NULL); sid = tx_enter(addr, r, w); /* prepare variables */ for (int i = 0; i < r->len; ++i) { TBuf *body = vol_read(r->vols[i], r->names[i], r->vers[i]); vars_add(v, r->names[i], 0, body); } for (int i = 0; i < w->len; ++i) { int pos = array_scan(v->names, v->len, w->names[i]); if (pos < 0) vars_add(v, w->names[i], 0, NULL); } for (int i = 0; i < fn->t.len; ++i) vars_add(v, fn->t.names[i], 0, NULL); /* evaluate the function body */ for (int i = 0; i < fn->slen; ++i) rel_eval(fn->stmts[i], v, arg); /* prepare the return value. note, the resulting relation is just a container for the body, so it is not freed */ Rel *ret = NULL; if (fn->ret != NULL) ret = fn->stmts[fn->slen - 1]; /* persist the global variables */ for (int i = 0; i < w->len; ++i) { int idx = array_scan(v->names, v->len, w->names[i]); if (idx < 0) { status = http_500(io); goto exit; } vol_write(w->vols[i], v->vals[idx], w->names[i], w->vers[i]); tbuf_free(v->vals[idx]); v->vals[idx] = NULL; } /* confirm a success and send the result back */ status = http_200(io); if (status != 200) goto exit; tx_commit(sid); /* N.B. there is no explicit revert as the transaction manager handles nested tx_enter and a connectivity failure as a rollback */ int len = 1, i = 0; while (status == 200 && len) { len = pack_rel2csv(ret, res, MAX_BLOCK, i++); status = http_chunk(io, res, len); } exit: if (status != -1) sys_log('E', "%016llX method %c, path %s, time %lldms - %3d\n", sid, (req == NULL) ? '?' : req->method, (req == NULL) ? "malformed" : req->path, sys_millis() - time, status); if (r != NULL) vars_free(r); if (w != NULL) vars_free(w); if (arg != NULL) mem_free(arg); if (req != NULL) http_free_req(req); if (env != NULL) env_free(env); for (int i = 0; i < v->len; ++i) if (v->vals[i] != NULL) { tbuf_clean(v->vals[i]); tbuf_free(v->vals[i]); } vars_free(v); sys_term(io); } mem_free(code); mem_free(res); tx_detach(); sys_close(io); }
static Rel *load(const char *name) { Head *head = env_head(env, name); return rel_load(head, name); }
int main(int argc, char **argv) { argp_parse(&argp, argc, argv, 0, 0, 0); // Init Lace lace_init(workers, 1000000); // auto-detect number of workers, use a 1,000,000 size task queue lace_startup(0, NULL, NULL); // auto-detect program stack, do not use a callback for startup LACE_ME; size_t max = 16LL<<30; if (max > getMaxMemory()) max = getMaxMemory()/10*9; printf("Setting Sylvan main tables memory to "); print_h(max); printf(" max.\n"); // Init Sylvan sylvan_set_limits(max, 1, 10); sylvan_init_package(); sylvan_init_ldd(); sylvan_init_mtbdd(); sylvan_gc_hook_pregc(TASK(gc_start)); sylvan_gc_hook_postgc(TASK(gc_end)); // Obtain operation ids for the operation cache compute_highest_id = cache_next_opid(); compute_highest_action_id = cache_next_opid(); bdd_from_ldd_id = cache_next_opid(); bdd_from_ldd_rel_id = cache_next_opid(); // Open file FILE *f = fopen(model_filename, "r"); if (f == NULL) Abort("Cannot open file '%s'!\n", model_filename); // Read integers per vector if (fread(&vector_size, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n"); // Read initial state if (verbose) printf("Loading initial state.\n"); set_t initial = set_load(f); // Read number of transitions if (fread(&next_count, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n"); next = (rel_t*)malloc(sizeof(rel_t) * next_count); // Read transitions if (verbose) printf("Loading transition relations.\n"); for (int i=0; i<next_count; i++) next[i] = rel_load_proj(f); for (int i=0; i<next_count; i++) rel_load(f, next[i]); // Read whether reachable states are stored int has_reachable = 0; if (fread(&has_reachable, sizeof(int), 1, f) != 1) Abort("Input file missing reachable states!\n"); if (has_reachable == 0) Abort("Input file missing reachable states!\n"); // Read reachable states if (verbose) printf("Loading reachable states.\n"); set_t states = set_load(f); // Read number of action labels int action_labels_count = 0; if (fread(&action_labels_count, sizeof(int), 1, f) != 1) action_labels_count = 0; // ignore: Abort("Input file missing action label count!\n"); // Read action labels char *action_labels[action_labels_count]; for (int i=0; i<action_labels_count; i++) { uint32_t len; if (fread(&len, sizeof(uint32_t), 1, f) != 1) Abort("Invalid input file!\n"); action_labels[i] = (char*)malloc(sizeof(char[len+1])); if (fread(action_labels[i], sizeof(char), len, f) != len) Abort("Invalid input file!\n"); action_labels[i][len] = 0; } // Close file fclose(f); // Report that we have read the input file printf("Read file %s.\n", argv[1]); // Report statistics if (verbose) { printf("%d integers per state, %d transition groups\n", vector_size, next_count); printf("LDD nodes:\n"); printf("Initial states: %zu LDD nodes\n", lddmc_nodecount(initial->dd)); for (int i=0; i<next_count; i++) { printf("Transition %d: %zu LDD nodes\n", i, lddmc_nodecount(next[i]->dd)); } } // Report that we prepare BDD conversion if (verbose) printf("Preparing conversion to BDD...\n"); // Compute highest value at each level (from reachable states) uint32_t highest[vector_size]; for (int i=0; i<vector_size; i++) highest[i] = 0; compute_highest(states->dd, highest); // Compute highest action label value (from transition relations) uint32_t highest_action = 0; for (int i=0; i<next_count; i++) { compute_highest_action(next[i]->dd, next[i]->meta, &highest_action); } // Compute number of bits for each level int bits[vector_size]; for (int i=0; i<vector_size; i++) { bits[i] = 0; while (highest[i] != 0) { bits[i]++; highest[i]>>=1; } if (bits[i] == 0) bits[i] = 1; } // Compute number of bits for action label actionbits = 0; while (highest_action != 0) { actionbits++; highest_action>>=1; } if (actionbits == 0 && has_actions) actionbits = 1; // Report number of bits if (verbose) { printf("Bits per level: "); for (int i=0; i<vector_size; i++) { if (i>0) printf(", "); printf("%d", bits[i]); } printf("\n"); printf("Action bits: %d.\n", actionbits); } // Compute bits MDD MDD bits_dd = lddmc_true; for (int i=0; i<vector_size; i++) { bits_dd = lddmc_makenode(bits[vector_size-i-1], bits_dd, lddmc_false); } lddmc_ref(bits_dd); // Compute total number of bits int totalbits = 0; for (int i=0; i<vector_size; i++) { totalbits += bits[i]; } // Compute state variables MTBDD state_vars = mtbdd_true; for (int i=0; i<totalbits; i++) { state_vars = mtbdd_makenode(2*(totalbits-i-1), mtbdd_false, state_vars); } mtbdd_protect(&state_vars); // Report that we begin the actual conversion if (verbose) printf("Converting to BDD...\n"); // Create BDD file f = fopen(bdd_filename, "w"); if (f == NULL) Abort("Cannot open file '%s'!\n", bdd_filename); // Write domain... fwrite(&vector_size, sizeof(int), 1, f); fwrite(bits, sizeof(int), vector_size, f); fwrite(&actionbits, sizeof(int), 1, f); // Write initial state... MTBDD new_initial = bdd_from_ldd(initial->dd, bits_dd, 0); assert((size_t)mtbdd_satcount(new_initial, totalbits) == (size_t)lddmc_satcount_cached(initial->dd)); mtbdd_refs_push(new_initial); { int k = -1; fwrite(&k, sizeof(int), 1, f); mtbdd_writer_tobinary(f, &new_initial, 1); } // Custom operation that converts to BDD given number of bits for each level MTBDD new_states = bdd_from_ldd(states->dd, bits_dd, 0); assert((size_t)mtbdd_satcount(new_states, totalbits) == (size_t)lddmc_satcount_cached(states->dd)); mtbdd_refs_push(new_states); // Report size of BDD if (verbose) { printf("Initial states: %zu BDD nodes\n", mtbdd_nodecount(new_initial)); printf("Reachable states: %zu BDD nodes\n", mtbdd_nodecount(new_states)); } // Write number of transitions fwrite(&next_count, sizeof(int), 1, f); // Write meta for each transition for (int i=0; i<next_count; i++) { fwrite(&next[i]->r_k, sizeof(int), 1, f); fwrite(&next[i]->w_k, sizeof(int), 1, f); fwrite(next[i]->r_proj, sizeof(int), next[i]->r_k, f); fwrite(next[i]->w_proj, sizeof(int), next[i]->w_k, f); } // Write BDD for each transition for (int i=0; i<next_count; i++) { // Compute new transition relation MTBDD new_rel = bdd_from_ldd_rel(next[i]->dd, bits_dd, 0, next[i]->meta); mtbdd_refs_push(new_rel); mtbdd_writer_tobinary(f, &new_rel, 1); // Report number of nodes if (verbose) printf("Transition %d: %zu BDD nodes\n", i, mtbdd_nodecount(new_rel)); if (check_results) { // Compute new <variables> for the current transition relation MTBDD new_vars = meta_to_bdd(next[i]->meta, bits_dd, 0); mtbdd_refs_push(new_vars); // Test if the transition is correctly converted MTBDD test = sylvan_relnext(new_states, new_rel, new_vars); mtbdd_refs_push(test); MDD succ = lddmc_relprod(states->dd, next[i]->dd, next[i]->meta); lddmc_refs_push(succ); MTBDD test2 = bdd_from_ldd(succ, bits_dd, 0); if (test != test2) Abort("Conversion error!\n"); lddmc_refs_pop(1); mtbdd_refs_pop(2); } mtbdd_refs_pop(1); } // Write reachable states if (no_reachable) has_reachable = 0; fwrite(&has_reachable, sizeof(int), 1, f); if (has_reachable) { int k = -1; fwrite(&k, sizeof(int), 1, f); mtbdd_writer_tobinary(f, &new_states, 1); } mtbdd_refs_pop(1); // new_states // Write action labels fwrite(&action_labels_count, sizeof(int), 1, f); for (int i=0; i<action_labels_count; i++) { uint32_t len = strlen(action_labels[i]); fwrite(&len, sizeof(uint32_t), 1, f); fwrite(action_labels[i], sizeof(char), len, f); } // Close the file fclose(f); // Report to the user printf("Written file %s.\n", bdd_filename); // Report Sylvan statistics (if SYLVAN_STATS is set) if (verbose) sylvan_stats_report(stdout); return 0; }