void buzzvm_vstig_onconflictlost(buzzvm_t vm, buzzvstig_t vs, buzzobj_t k, buzzvstig_elem_t lv) { /* Was a conflict manager defined? */ if(vs->onconflictlost) { /* Push closure */ buzzvm_push(vm, vs->onconflictlost); /* Push key */ buzzvm_push(vm, k); /* Make table for local value */ buzzvm_pusht(vm); buzzobj_t loc = buzzvm_stack_at(vm, 1); buzzvm_pushs(vm, buzzvm_string_register(vm, "robot")); buzzvm_pushi(vm, lv->robot); buzzvm_tput(vm); buzzvm_push(vm, loc); buzzvm_pushs(vm, buzzvm_string_register(vm, "data")); buzzvm_push(vm, lv->data); buzzvm_tput(vm); buzzvm_push(vm, loc); buzzvm_pushs(vm, buzzvm_string_register(vm, "timestamp")); buzzvm_pushi(vm, lv->timestamp); buzzvm_tput(vm); /* Call closure with 2 arguments */ buzzvm_push(vm, loc); buzzvm_closure_call(vm, 2); } }
buzzvm_state buzzvm_function_call(buzzvm_t vm, const char* fname, uint32_t argc) { /* Reset the VM state if it's DONE */ if(vm->state == BUZZVM_STATE_DONE) vm->state = BUZZVM_STATE_READY; /* Don't continue if the VM has an error */ if(vm->state != BUZZVM_STATE_READY) return vm->state; /* Push the function name (return with error if not found) */ buzzvm_pushs(vm, buzzvm_string_register(vm, fname, 0)); /* Get associated symbol */ buzzvm_gload(vm); /* Make sure it's a closure */ buzzvm_type_assert(vm, 1, BUZZTYPE_CLOSURE); /* Move closure before arguments */ if(argc > 0) { buzzdarray_insert(vm->stack, buzzdarray_size(vm->stack) - argc - 1, buzzvm_stack_at(vm, 1)); buzzvm_pop(vm); } /* Call the closure */ return buzzvm_closure_call(vm, argc); }
void buzzvstig_foreach_entry(const void* key, void* data, void* params) { /* Cast params */ struct buzzvstig_foreach_params* p = (struct buzzvstig_foreach_params*)params; if(p->vm->state != BUZZVM_STATE_READY) return; /* Push closure and params (key, value, robot) */ buzzvm_push(p->vm, p->fun); buzzvm_push(p->vm, *(buzzobj_t*)key); buzzvm_push(p->vm, (*(buzzvstig_elem_t*)data)->data); buzzvm_pushi(p->vm, (*(buzzvstig_elem_t*)data)->robot); /* Call closure */ p->vm->state = buzzvm_closure_call(p->vm, 3); }
void buzzvstig_onconflictlost_call(buzzvm_t vm, buzzvstig_t vs, buzzobj_t k, buzzvstig_elem_t lv) { /* Was a conflict manager defined? */ if(vs->onconflictlost) { /* Push closure */ buzzvm_push(vm, vs->onconflictlost); /* Push key */ buzzvm_push(vm, k); /* Make table for local value */ buzzvm_pusht(vm); add_field(robot, lv, pushi); add_field(data, lv, push); add_field(timestamp, lv, pushi); /* Call closure (key and table are on the stack) */ buzzvm_closure_call(vm, 2); } }
buzzvstig_elem_t buzzvstig_onconflict_call(buzzvm_t vm, buzzvstig_t vs, buzzobj_t k, buzzvstig_elem_t lv, buzzvstig_elem_t rv) { /* Was a conflict manager defined? */ if(vs->onconflict) { /* Push closure */ buzzvm_push(vm, vs->onconflict); /* Push key */ buzzvm_push(vm, k); /* Make table for local value */ buzzvm_pusht(vm); add_field(robot, lv, pushi); add_field(data, lv, push); add_field(timestamp, lv, pushi); /* Make table for remote value */ buzzvm_pusht(vm); add_field(robot, rv, pushi); add_field(data, rv, push); add_field(timestamp, rv, pushi); /* Call closure (key, lv, rv on the stack) */ buzzvm_closure_call(vm, 3); /* Make new entry with return value */ /* Make sure it's a table */ if(buzzvm_stack_at(vm, 1)->o.type != BUZZTYPE_TABLE) { fprintf(stderr, "[WARNING] [ROBOT %u] virtual stigmergy onconflict(): Return value type is %s, expected table\n", vm->robot, buzztype_desc[buzzvm_stack_at(vm, 1)->o.type]); return NULL; } /* Get the robot id */ buzzobj_t ret = buzzvm_stack_at(vm, 1); buzzvm_pushs(vm, buzzvm_string_register(vm, "robot", 1)); buzzvm_tget(vm); if(buzzvm_stack_at(vm, 1)->o.type != BUZZTYPE_INT) return NULL; uint16_t robot = buzzvm_stack_at(vm, 1)->i.value; buzzvm_pop(vm); /* Get the data */ buzzvm_push(vm, ret); buzzvm_pushs(vm, buzzvm_string_register(vm, "data", 1)); buzzvm_tget(vm); buzzobj_t data = buzzvm_stack_at(vm, 1); buzzvm_pop(vm); /* Make new entry */ return buzzvstig_elem_new(data, lv->timestamp, robot); } else { /* No conflict manager, use default behavior */ /* If both values are not nil, keep the value of the robot with the higher id */ if(((lv->data->o.type == BUZZTYPE_NIL) && (rv->data->o.type == BUZZTYPE_NIL)) || ((lv->data->o.type != BUZZTYPE_NIL) && (rv->data->o.type != BUZZTYPE_NIL))) { if(lv->robot > rv->robot) return buzzvstig_elem_clone(vm, lv); else return buzzvstig_elem_clone(vm, rv); } /* If my value is not nil, keep mine */ if(lv->data->o.type != BUZZTYPE_NIL) return buzzvstig_elem_clone(vm, lv); /* If the other value is not nil, keep that one */ if(rv->data->o.type != BUZZTYPE_NIL) return buzzvstig_elem_clone(vm, rv); /* Otherwise nothing to do */ return NULL; } }
buzzvstig_elem_t buzzvm_vstig_onconflict(buzzvm_t vm, buzzvstig_t vs, buzzobj_t k, buzzvstig_elem_t lv, buzzvstig_elem_t rv) { /* Was a conflict manager defined? */ if(vs->onconflict) { /* Push closure */ buzzvm_push(vm, vs->onconflict); /* Push key */ buzzvm_push(vm, k); /* Make table for local value */ buzzvm_pusht(vm); buzzobj_t loc = buzzvm_stack_at(vm, 1); buzzvm_pushs(vm, buzzvm_string_register(vm, "robot")); buzzvm_pushi(vm, lv->robot); buzzvm_tput(vm); buzzvm_push(vm, loc); buzzvm_pushs(vm, buzzvm_string_register(vm, "data")); buzzvm_push(vm, lv->data); buzzvm_tput(vm); buzzvm_push(vm, loc); buzzvm_pushs(vm, buzzvm_string_register(vm, "timestamp")); buzzvm_pushi(vm, lv->timestamp); buzzvm_tput(vm); /* Make table for remote value */ buzzvm_pusht(vm); buzzobj_t rem = buzzvm_stack_at(vm, 1); buzzvm_pushs(vm, buzzvm_string_register(vm, "robot")); buzzvm_pushi(vm, rv->robot); buzzvm_tput(vm); buzzvm_push(vm, rem); buzzvm_pushs(vm, buzzvm_string_register(vm, "data")); buzzvm_push(vm, rv->data); buzzvm_tput(vm); buzzvm_push(vm, rem); buzzvm_pushs(vm, buzzvm_string_register(vm, "timestamp")); buzzvm_pushi(vm, rv->timestamp); buzzvm_tput(vm); /* Call closure with 3 arguments */ buzzvm_push(vm, loc); buzzvm_push(vm, rem); buzzvm_closure_call(vm, 3); /* Make new entry with return value */ /* Make sure it's a table */ if(buzzvm_stack_at(vm, 1)->o.type != BUZZTYPE_TABLE) { fprintf(stderr, "[WARNING] [ROBOT %u] Return value type is %d\n", vm->robot, buzzvm_stack_at(vm, 1)->o.type); return NULL; } /* Get the robot id */ buzzobj_t ret = buzzvm_stack_at(vm, 1); buzzvm_pushs(vm, buzzvm_string_register(vm, "robot")); buzzvm_tget(vm); if(buzzvm_stack_at(vm, 1)->o.type != BUZZTYPE_INT) return NULL; uint16_t robot = buzzvm_stack_at(vm, 1)->i.value; buzzvm_pop(vm); /* Get the data */ buzzvm_push(vm, ret); buzzvm_pushs(vm, buzzvm_string_register(vm, "data")); buzzvm_tget(vm); buzzobj_t data = buzzvm_stack_at(vm, 1); buzzvm_pop(vm); /* Make new entry */ return buzzvstig_elem_new(data, lv->timestamp, robot); } else { /* No conflict manager, use default behavior */ if(lv->robot > rv->robot) return buzzvstig_elem_clone(lv); else return buzzvstig_elem_clone(rv); } }
void buzzvm_process_inmsgs(buzzvm_t vm) { /* Go through the messages */ while(!buzzinmsg_queue_isempty(vm->inmsgs)) { /* Make sure the VM is in the right state */ if(vm->state != BUZZVM_STATE_READY) return; /* Extract the message data */ uint16_t rid; buzzmsg_payload_t msg; buzzinmsg_queue_extract(vm, &rid, &msg); /* Dispatch the message wrt its type in msg->payload[0] */ switch(buzzmsg_payload_get(msg, 0)) { case BUZZMSG_BROADCAST: { /* Deserialize the topic */ buzzobj_t topic; int64_t pos = buzzobj_deserialize(&topic, msg, 1, vm); /* Make sure there's a listener to call */ const buzzobj_t* l = buzzdict_get(vm->listeners, &topic->s.value.sid, buzzobj_t); if(!l) { /* No listener, ignore message */ break; } /* Deserialize value */ buzzobj_t value; pos = buzzobj_deserialize(&value, msg, pos, vm); /* Make an object for the robot id */ buzzobj_t rido = buzzheap_newobj(vm, BUZZTYPE_INT); rido->i.value = rid; /* Call listener */ buzzvm_push(vm, *l); buzzvm_push(vm, topic); buzzvm_push(vm, value); buzzvm_push(vm, rido); buzzvm_closure_call(vm, 3); break; } case BUZZMSG_VSTIG_PUT: { /* Deserialize the vstig id */ uint16_t id; int64_t pos = buzzmsg_deserialize_u16(&id, msg, 1); if(pos < 0) { fprintf(stderr, "[WARNING] [ROBOT %u] Malformed BUZZMSG_VSTIG_PUT message received\n", vm->robot); break; } /* Look for virtual stigmergy */ const buzzvstig_t* vs = buzzdict_get(vm->vstigs, &id, buzzvstig_t); if(!vs) break; /* Virtual stigmergy found */ /* Deserialize key and value from msg */ buzzobj_t k; // key buzzvstig_elem_t v = // value (buzzvstig_elem_t)malloc(sizeof(struct buzzvstig_elem_s)); if(buzzvstig_elem_deserialize(&k, &v, msg, pos, vm) < 0) { fprintf(stderr, "[WARNING] [ROBOT %u] Malformed BUZZMSG_VSTIG_PUT message received\n", vm->robot); free(v); break; } /* Deserialization successful */ /* Fetch local vstig element */ const buzzvstig_elem_t* l = buzzvstig_fetch(*vs, &k); if((!l) || /* Element not found */ ((*l)->timestamp < v->timestamp)) { /* Local element is older */ /* Local element must be updated */ /* Store element */ buzzvstig_store(*vs, &k, &v); buzzoutmsg_queue_append_vstig(vm, BUZZMSG_VSTIG_PUT, id, k, v); } else if(((*l)->timestamp == v->timestamp) && /* Same timestamp */ ((*l)->robot != v->robot)) { /* Different robot */ /* Conflict! */ /* Call conflict manager */ buzzvstig_elem_t c = buzzvstig_onconflict_call(vm, *vs, k, *l, v); if(!c) { fprintf(stderr, "[WARNING] [ROBOT %u] Error resolving PUT conflict\n", vm->robot); break; } /* Get rid of useless vstig element */ free(v); /* Did this robot lose the conflict? */ if((c->robot != vm->robot) && ((*l)->robot == vm->robot)) { /* Yes */ /* Save current local entry */ buzzvstig_elem_t ol = buzzvstig_elem_clone(vm, *l); /* Store winning value */ buzzvstig_store(*vs, &k, &c); /* Call conflict lost manager */ buzzvstig_onconflictlost_call(vm, *vs, k, ol); } else { /* This robot did not lose the conflict */ /* Just propagate the PUT message */ buzzvstig_store(*vs, &k, &c); } buzzoutmsg_queue_append_vstig(vm, BUZZMSG_VSTIG_PUT, id, k, c); } else { /* Remote element is older, ignore it */ /* Get rid of useless vstig element */ free(v); } break; } case BUZZMSG_VSTIG_QUERY: { /* Deserialize the vstig id */ uint16_t id; int64_t pos = buzzmsg_deserialize_u16(&id, msg, 1); if(pos < 0) { fprintf(stderr, "[WARNING] [ROBOT %u] Malformed BUZZMSG_VSTIG_QUERY message received (1)\n", vm->robot); break; } /* Deserialize key and value from msg */ buzzobj_t k; // key buzzvstig_elem_t v = // value (buzzvstig_elem_t)malloc(sizeof(struct buzzvstig_elem_s)); if(buzzvstig_elem_deserialize(&k, &v, msg, pos, vm) < 0) { fprintf(stderr, "[WARNING] [ROBOT %u] Malformed BUZZMSG_VSTIG_QUERY message received (2)\n", vm->robot); free(v); break; } /* Look for virtual stigmergy */ const buzzvstig_t* vs = buzzdict_get(vm->vstigs, &id, buzzvstig_t); if(!vs) { /* Virtual stigmergy not found, simply propagate the message */ buzzoutmsg_queue_append_vstig(vm, BUZZMSG_VSTIG_QUERY, id, k, v); free(v); break; } /* Virtual stigmergy found */ /* Fetch local vstig element */ const buzzvstig_elem_t* l = buzzvstig_fetch(*vs, &k); if(!l) { /* Element not found */ if(v->data->o.type == BUZZTYPE_NIL) { /* This robot knows nothing about the query, just propagate it */ buzzoutmsg_queue_append_vstig(vm, BUZZMSG_VSTIG_QUERY, id, k, v); free(v); } else { /* Store element and propagate PUT message */ buzzvstig_store(*vs, &k, &v); buzzoutmsg_queue_append_vstig(vm, BUZZMSG_VSTIG_PUT, id, k, v); } break; } /* Element found */ if((*l)->timestamp < v->timestamp) { /* Local element is older */ /* Store element */ buzzvstig_store(*vs, &k, &v); buzzoutmsg_queue_append_vstig(vm, BUZZMSG_VSTIG_PUT, id, k, v); } else if((*l)->timestamp > v->timestamp) { /* Local element is newer */ /* Append a PUT message to the out message queue */ buzzoutmsg_queue_append_vstig(vm, BUZZMSG_VSTIG_PUT, id, k, *l); free(v); } else if(((*l)->timestamp == v->timestamp) && /* Same timestamp */ ((*l)->robot != v->robot)) { /* Different robot */ /* Conflict! */ /* Call conflict manager */ buzzvstig_elem_t c = buzzvstig_onconflict_call(vm, *vs, k, *l, v); free(v); /* Make sure conflict manager returned with an element to process */ if(!c) break; /* Did this robot lose the conflict? */ if((c->robot != vm->robot) && ((*l)->robot == vm->robot)) { /* Yes */ /* Save current local entry */ buzzvstig_elem_t ol = buzzvstig_elem_clone(vm, *l); /* Store winning value */ buzzvstig_store(*vs, &k, &c); /* Call conflict lost manager */ buzzvstig_onconflictlost_call(vm, *vs, k, ol); } else { /* This robot did not lose the conflict */ /* Just propagate the PUT message */ buzzvstig_store(*vs, &k, &c); } buzzoutmsg_queue_append_vstig(vm, BUZZMSG_VSTIG_PUT, id, k, c); } else { /* Remote element is same as local, ignore it */ /* Get rid of useless vstig element */ free(v); } break; } case BUZZMSG_SWARM_LIST: { /* Deserialize number of swarm ids */ uint16_t nsids; int64_t pos = buzzmsg_deserialize_u16(&nsids, msg, 1); if(pos < 0) { fprintf(stderr, "[WARNING] [ROBOT %u] Malformed BUZZMSG_SWARM_LIST message received\n", vm->robot); break; } if(nsids < 1) break; /* Deserialize swarm ids */ buzzdarray_t sids = buzzdarray_new(nsids, sizeof(uint16_t), NULL); uint16_t i; for(i = 0; i < nsids; ++i) { pos = buzzmsg_deserialize_u16(buzzdarray_makeslot(sids, i), msg, pos); if(pos < 0) { fprintf(stderr, "[WARNING] [ROBOT %u] Malformed BUZZMSG_SWARM_LIST message received\n", vm->robot); break; } } /* Update the information */ buzzswarm_members_refresh(vm->swarmmembers, rid, sids); break; } case BUZZMSG_SWARM_JOIN: { /* Deserialize swarm id */ uint16_t sid; int64_t pos = buzzmsg_deserialize_u16(&sid, msg, 1); if(pos < 0) { fprintf(stderr, "[WARNING] [ROBOT %u] Malformed BUZZMSG_SWARM_JOIN message received\n", vm->robot); break; } /* Update the information */ buzzswarm_members_join(vm->swarmmembers, rid, sid); break; } case BUZZMSG_SWARM_LEAVE: { /* Deserialize swarm id */ uint16_t sid; int64_t pos = buzzmsg_deserialize_u16(&sid, msg, 1); if(pos < 0) { fprintf(stderr, "[WARNING] [ROBOT %u] Malformed BUZZMSG_SWARM_LEAVE message received\n", vm->robot); break; } /* Update the information */ buzzswarm_members_leave(vm->swarmmembers, rid, sid); break; } } /* Get rid of the message */ buzzmsg_payload_destroy(&msg); } /* Update swarm membership */ buzzswarm_members_update(vm->swarmmembers); }