/** * scaffolding for send a signal from ouside the VMhost * * <b>Examples (from test suite):</b> * @snippet spec/vmhost_spec.h testVMHostActivateReceptor */ void _v_send(VMHost *v,ReceptorAddress from,ReceptorAddress to,Aspect aspect,Symbol carrier,T *contents) { T *s = __r_make_signal(from,to,aspect,carrier,contents,0,0); T *x = _r_send(v->r,s); _t_free(x); }
/** * 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; }