/** * @brief Creates a new receptor * * allocates all the memory needed in the heap * * @param[in] r semantic ID for this receptor * @returns pointer to a newly allocated Receptor * * <b>Examples (from test suite):</b> * @snippet spec/receptor_spec.h testReceptorCreate */ Receptor *_r_new(SemTable *sem,SemanticID r) { T *t = _t_new_root(RECEPTOR_INSTANCE); _t_news(t,INSTANCE_OF,r); if (semeq(r,SYS_RECEPTOR)) { _t_newi(t,CONTEXT_NUM,0); _t_newi(t,PARENT_CONTEXT_NUM,-1); } else { _t_newi(t,CONTEXT_NUM,_d_get_receptor_context(sem,r)); _t_newi(t,PARENT_CONTEXT_NUM,r.context); } T *state = _r_make_state(); _t_add(t,state); return __r_init(t,sem); }
// create a TREE_DELTA tree T *makeDelta(Symbol sym,int *path,T *t,int count) { T *d = _t_new_root(sym); _t_new(d,TREE_DELTA_PATH,path,sizeof(int)*(_t_path_depth(path)+1)); _t_add(_t_newr(d,TREE_DELTA_VALUE),_t_clone(t)); if (count) _t_newi(d,TREE_DELTA_COUNT,count); return d; }
T *_r_make_state() { T *t = _t_new_root(RECEPTOR_STATE); T *f = _t_newr(t,FLUX); __r_add_aspect(f,DEFAULT_ASPECT); _t_newr(t,PENDING_SIGNALS); _t_newr(t,PENDING_RESPONSES); _t_newr(t,CONVERSATIONS); _t_newi(t,RECEPTOR_ELAPSED_TIME,0); return t; }
Receptor *__r_new(Symbol s,T *defs,T *aspects) { T *t = _t_new_root(s); _t_add(t,defs); _t_add(t,aspects); T *f = _t_newr(t,FLUX); T *a = _t_newi(f,ASPECT,DEFAULT_ASPECT); _t_newr(a,LISTENERS); _t_newr(a,SIGNALS); _t_newr(t,RECEPTOR_STATE); _t_newr(t,PENDING_SIGNALS); _t_newr(t,PENDING_RESPONSES); return __r_init(t); }
void makeShell(VMHost *v,FILE *input, FILE *output,Receptor **irp,Receptor **orp,Stream **isp,Stream **osp) { // define and then create the shell receptor Symbol shell = _d_define_receptor(v->r->sem,"shell",__r_make_definitions(),DEV_COMPOSITORY_CONTEXT); Receptor *r = _r_new(v->sem,shell); Xaddr shellx = _v_new_receptor(v,v->r,shell,r); _v_activate(v,shellx); // create stdin/out receptors Stream *output_stream = *osp = _st_new_unix_stream(output,0); Stream *input_stream = *isp = _st_new_unix_stream(input,1); Receptor *i_r = *irp = _r_makeStreamEdgeReceptor(v->sem); _r_addReader(i_r,input_stream,r->addr,DEFAULT_ASPECT,parse_line,LINE,false); Xaddr ix = _v_new_receptor(v,v->r,STREAM_EDGE,i_r); _v_activate(v,ix); Receptor *o_r = *orp = _r_makeStreamEdgeReceptor(v->sem); _r_addWriter(o_r,output_stream,DEFAULT_ASPECT); Xaddr ox = _v_new_receptor(v,v->r,STREAM_EDGE,o_r); _v_activate(v,ox); // set up shell to express the line parsing protocol when it receives LINES from the stream reader Protocol clp; __sem_get_by_label(v->sem,"PARSE_COMMAND_FROM_LINE",&clp,DEV_COMPOSITORY_CONTEXT); T *bindings = _t_new_root(PROTOCOL_BINDINGS); T *res = _t_newr(bindings,RESOLUTION); T *w = _t_newr(res,WHICH_RECEPTOR); _t_news(w,ROLE,LINE_SENDER); __r_make_addr(w,ACTUAL_RECEPTOR,i_r->addr); res = _t_newr(bindings,RESOLUTION); w = _t_newr(res,WHICH_RECEPTOR); _t_news(w,ROLE,COMMAND_RECEIVER); __r_make_addr(w,ACTUAL_RECEPTOR,r->addr); res = _t_newr(bindings,RESOLUTION); w = _t_newr(res,WHICH_SYMBOL); _t_news(w,USAGE,COMMAND_TYPE); _t_news(w,ACTUAL_SYMBOL,SHELL_COMMAND); _o_express_role(r,clp,COMMAND_RECEIVER,DEFAULT_ASPECT,bindings); _t_free(bindings); // set up shell to use the CLOCK TELL_TIME protocol for the time command Protocol time; __sem_get_by_label(v->sem,"time",&time,CLOCK_CONTEXT); T *code = _t_new_root(INITIATE_PROTOCOL); _t_news(code,PNAME,time); _t_news(code,WHICH_INTERACTION,tell_time); bindings = _t_newr(code,PROTOCOL_BINDINGS); res = _t_newr(bindings,RESOLUTION); w = _t_newr(res,WHICH_RECEPTOR); _t_news(w,ROLE,TIME_HEARER); __r_make_addr(w,ACTUAL_RECEPTOR,r->addr); res = _t_newr(bindings,RESOLUTION); w = _t_newr(res,WHICH_RECEPTOR); _t_news(w,ROLE,TIME_TELLER); ReceptorAddress clock_addr = {3}; // @todo bogus!!! fix getting clock address somehow __r_make_addr(w,ACTUAL_RECEPTOR,clock_addr); res = _t_newr(bindings,RESOLUTION); w = _t_newr(res,WHICH_PROCESS); _t_news(w,GOAL,RESPONSE_HANDLER); addCommand(r,o_r->addr,"time","get time",code,w); // (expect (on flux SHELL_COMMAND:receptor) action (send std_out (convert_to_lines (send vmhost receptor-list)))) code = _t_newi(0,MAGIC,MagicReceptors); addCommand(r,o_r->addr,"receptors","get receptor list",code,NULL); // (expect (on flux SHELL_COMMAND:receptor) action (send std_out (convert_to_lines (send vmhost shutdown))) code = _t_newi(0,MAGIC,MagicQuit); addCommand(r,o_r->addr,"quit","shut down the vmhost",code,NULL); // (expect (on flux SHELL_COMMAND:debug) action (send std_out (convert_to_lines (magic toggle debug))) code = _t_newi(0,MAGIC,MagicDebug); addCommand(r,o_r->addr,"debug","toggle debug mode",code,NULL); }
/** * reduce system level processes in a run tree. Assumes that the children have already been * reduced and all parameters have been filled in * * these system level processes are the equivalent of the instruction set of the ceptr virtual machine */ Error __p_reduce_sys_proc(R *context,Symbol s,T *code) { int b,c; char *str; Symbol sy; T *x,*t,*match_results,*match_tree; Error err = noReductionErr; switch(s.id) { case NOOP_ID: // noop simply replaces itself with it's own child x = _t_detach_by_idx(code,1); break; case IF_ID: t = _t_child(code,1); b = (*(int *)_t_surface(t)) ? 2 : 3; x = _t_detach_by_idx(code,b); break; case ADD_INT_ID: x = _t_detach_by_idx(code,1); c = *(int *)_t_surface(_t_child(code,1)); *((int *)&x->contents.surface) = c+*((int *)&x->contents.surface); break; case SUB_INT_ID: x = _t_detach_by_idx(code,1); c = *(int *)_t_surface(_t_child(code,1)); *((int *)&x->contents.surface) = *((int *)&x->contents.surface)-c; break; case MULT_INT_ID: x = _t_detach_by_idx(code,1); c = *(int *)_t_surface(_t_child(code,1)); *((int *)&x->contents.surface) = *((int *)&x->contents.surface)*c; break; case DIV_INT_ID: x = _t_detach_by_idx(code,1); c = *(int *)_t_surface(_t_child(code,1)); if (!c) { _t_free(x); return divideByZeroReductionErr; } *((int *)&x->contents.surface) = *((int *)&x->contents.surface)/c; break; case MOD_INT_ID: x = _t_detach_by_idx(code,1); c = *(int *)_t_surface(_t_child(code,1)); if (!c) { _t_free(x); return divideByZeroReductionErr; } *((int *)&x->contents.surface) = *((int *)&x->contents.surface)%c; break; case EQ_INT_ID: x = _t_detach_by_idx(code,1); c = *(int *)_t_surface(_t_child(code,1)); *((int *)&x->contents.surface) = *((int *)&x->contents.surface)==c; x->contents.symbol = BOOLEAN; break; case LT_INT_ID: x = _t_detach_by_idx(code,1); c = *(int *)_t_surface(_t_child(code,1)); *((int *)&x->contents.surface) = *((int *)&x->contents.surface)<c; x->contents.symbol = BOOLEAN; break; case GT_INT_ID: x = _t_detach_by_idx(code,1); c = *(int *)_t_surface(_t_child(code,1)); *((int *)&x->contents.surface) = *((int *)&x->contents.surface)>c; x->contents.symbol = BOOLEAN; break; case LTE_INT_ID: x = _t_detach_by_idx(code,1); c = *(int *)_t_surface(_t_child(code,1)); *((int *)&x->contents.surface) = *((int *)&x->contents.surface)<=c; x->contents.symbol = BOOLEAN; break; case GTE_INT_ID: x = _t_detach_by_idx(code,1); c = *(int *)_t_surface(_t_child(code,1)); *((int *)&x->contents.surface) = *((int *)&x->contents.surface)>=c; x->contents.symbol = BOOLEAN; break; case CONCAT_STR_ID: // if the first parameter is a RESULT SYMBOL then we use that as the symbol type for the result tree. x = _t_detach_by_idx(code,1); sy = _t_symbol(x); if (semeq(RESULT_SYMBOL,sy)) { sy = *(Symbol *)_t_surface(x); _t_free(x); x = _t_detach_by_idx(code,1); } //@todo, add a bunch of sanity checking here to make sure the // parameters are all CSTRINGS c = _t_children(code); // make sure the surface was allocated and if not, converted to an alloced surface if (c > 0) { if (!(x->context.flags & TFLAG_ALLOCATED)) { int v = *((int *)&x->contents.surface); // copy the string as an integer str = (char *)&v; // calculate the length int size = strlen(str)+1; x->contents.surface = malloc(size); memcpy(x->contents.surface,str,size); t->context.flags = TFLAG_ALLOCATED; } } // @todo this would probably be faster with just one total realloc for all children for(b=1;b<=c;b++) { str = (char *)_t_surface(_t_child(code,b)); int size = strlen(str); x->contents.surface = realloc(x->contents.surface,x->contents.size+size); memcpy(x->contents.surface+x->contents.size-1,str,size); x->contents.size+=size; *( (char *)x->contents.surface + x->contents.size -1) = 0; } x->contents.symbol = sy; break; case RESPOND_ID: { T *signal = _t_parent(context->run_tree); if (!signal || !semeq(_t_symbol(signal),SIGNAL)) return notInSignalContextReductionError; T *response_contents = _t_detach_by_idx(code,1); T *envelope = _t_child(signal,1); Xaddr to = *(Xaddr *)_t_surface(_t_child(envelope,1)); // reverse the from and to Xaddr from = *(Xaddr *)_t_surface(_t_child(envelope,2)); Aspect a = *(Aspect *)_t_surface(_t_child(envelope,3)); // add the response signal into the outgoing signals list of the root // run-tree (which is always the last child) R *root = context; while (context->caller) root = context->caller; int kids = _t_children(root->run_tree); T *signals; if (kids == 1 || (!semeq(SIGNALS,_t_symbol(signals = _t_child(root->run_tree,kids))))) signals = _t_newr(root->run_tree,SIGNALS); // make signals list if it's not there T *response = __r_make_signal(from,to,a,response_contents); _t_add(signals,response); x = _t_newi(0,TEST_INT_SYMBOL,0); } // @todo figure what RESPOND should return, since really it's a side-effect instruction // perhaps some kind of signal context symbol or something. Right now using TEST_INT_SYMBOL // as a bogus placeholder. break; case QUOTE_ID: x = _t_detach_by_idx(code,1); break; case EXPECT_ACT_ID: // detach the carrier and expectation and construction params, and enqueue the expectation and action // on the carrier { T *carrier_param = _t_detach_by_idx(code,1); T *carrier = *(T **)_t_surface(carrier_param); _t_free(carrier_param); T *ex = _t_detach_by_idx(code,1); T *expectation = _t_new_root(EXPECTATION); _t_add(expectation,ex); T *params = _t_detach_by_idx(code,1); //@todo: this is a fake way to add an expectation to a carrier (as a c pointer // out of the params) // we probably actually need a system representation for carriers and an API // that will also make this thread safe. For example, in the case of carrier being // a receptor's aspect/flux then we should be using _r_add_listener here, but // unfortunately we don't want to have to know about receptors this far down in the // stack... But it's not clear yet how we do know about the listening context as // I don't think it should be copied into every execution context (the R struct) _t_add(carrier,expectation); _t_add(carrier,params); // the action is a pointer back to this context for now were using a EXPECT_ACT // with the c pointer as the surface because I don't know what else to do... @fixme // perhaps this should be a BLOCKED_EXPECT_ACTION process or something... _t_new(carrier,EXPECT_ACT,&context,sizeof(context)); } rt_cur_child(code) = 1; // reset the current child count on the code x = _t_detach_by_idx(code,1); // the actually blocking happens in redcueq which can remove the process from the // round-robin err = Block; break; case SEND_ID: { T *t = _t_detach_by_idx(code,1); Xaddr to = *(Xaddr *)_t_surface(t); _t_free(t); T* signal_contents = _t_detach_by_idx(code,1); Xaddr from = {RECEPTOR_XADDR,0}; //@todo how do we say SELF?? x = __r_make_signal(from,to,DEFAULT_ASPECT,signal_contents); } err = Send; break; case INTERPOLATE_FROM_MATCH_ID: match_results = _t_child(code,2); match_tree = _t_child(code,3); x = _t_detach_by_idx(code,1); // @todo interpolation errors? _p_interpolate_from_match(x,match_results,match_tree); break; case RAISE_ID: return raiseReductionErr; break; case READ_STREAM_ID: { T *s = _t_detach_by_idx(code,1); FILE *stream =*(FILE**)_t_surface(s); _t_free(s); s = _t_detach_by_idx(code,1); sy = _t_symbol(s); if (semeq(RESULT_SYMBOL,sy)) { sy = *(Symbol *)_t_surface(s); _t_free(s); int ch; char buf[1000]; //@todo handle buffer dynamically int i = 0; while ((ch = fgetc (stream)) != EOF && ch != '\n' && i < 1000) buf[i++] = ch; if (i>=1000) {raise_error0("buffer overrun in READ_STREAM");} buf[i++]=0; x = _t_new(0,sy,buf,i); } else {raise_error0("expecting RESULT_SYMBOL");} } break; default: raise_error("unknown sys-process id: %d",s.id); } // any remaining children of 'code' are the parameters which have all now been "used up" // so we can call the low-level __t_free the clean them up and then replace the contents of // the 'code' node with the contents of the 'x' node that was either detached or produced // by the the process that just ran __t_free(code); code->structure.child_count = x->structure.child_count; code->structure.children = x->structure.children; code->contents = x->contents; code->context = x->context; free(x); return err; }
/** * reduce all the processes in a queue * * @param[in] q the queue to be processed */ Error _p_reduceq(Q *q) { #ifdef debug_reduce printf("\n\nStarting reduce:\n"); #endif Qe *qe = q->active; Error next_state; struct timespec start, end; while (q->contexts_count) { #ifdef debug_reduce R *context = qe->context; char *sn[]={"Ascend","Descend","Pushed","Pop","Eval","Block","Send","Done"}; char *s = context->state <= 0 ? sn[-context->state -1] : "Error"; printf("ID:%p -- State %s : %d\n",qe,s,context->state); printf(" idx:%d\n",context->idx); puts(_t2s(q->defs,context->run_tree)); if (context) { if (context->node_pointer == 0) { printf("Node Pointer: NULL!\n"); } else { printf("rt_cur_child:%d\n",rt_cur_child(context->node_pointer)); int *path = _t_get_path(context->node_pointer); char pp[255]; _t_sprint_path(path,pp); printf("Node Pointer:%s\n",pp); free(path); } } printf("\n"); #endif clock_gettime(CLOCK_MONOTONIC, &start); next_state = _p_step(q->defs, &qe->context); // next state is set in directly in the context clock_gettime(CLOCK_MONOTONIC, &end); qe->accounts.elapsed_time += diff_micro(&start, &end); Qe *next = qe->next; if (next_state == Done) { // remove from the round-robin __p_dequeue(q->active,qe); // add to the completed list __p_enqueue(q->completed,qe); q->contexts_count--; } else if (next_state == Block) { // remove from the round-robin __p_dequeue(q->active,qe); // add to the blocked list __p_enqueue(q->blocked,qe); q->contexts_count--; } else if (next_state == Send) { // remove from the round-robin __p_dequeue(q->active,qe); // take the signal off the run tree and send it, adding a send result in it's place T *signal = qe->context->node_pointer; T *parent = _t_parent(signal); //@todo figure out what that return value should be. Probably some result from // the actual signal sending machinery, or at least what ever is going to // evaluate the destination address for validity. T *result = _t_newi(0,TEST_INT_SYMBOL,0); //@todo refactor this into a version of _t_replace that swaps out the given child and returns it // rather than freeing it. parent->structure.children[0] = result; // 0 is the first child result->structure.parent = parent; signal->structure.parent = NULL; _t_add(q->pending_signals,signal); // add to the blocked list __p_enqueue(q->blocked,qe); q->contexts_count--; } qe = next ? next : q->active; // next in round robing or wrap }; // @todo figure out what error we should be sending back here, i.e. what if // one process ended ok, but one did not. What's the error? Probably // the errors here would be at a different level, and the caller would be // expected to inspect the errors of the reduced processes. return 0; }