/* Identity node: forward all records as is. */ static void SNetNodeIdentity(snet_stream_desc_t *desc, snet_record_t *rec) { landing_identity_t *land = DESC_LAND_SPEC(desc, identity); assert(land->outdesc); SNetWrite(&land->outdesc, rec, true); }
void SNetCloseInput(node_t* node) { input_arg_t *iarg = NODE_SPEC(node, input); landing_input_t *linp = DESC_LAND_SPEC(iarg->indesc, input); trace(__func__); assert(iarg->state == INPUT_terminating); iarg->state = INPUT_terminated; SNetDescDone(linp->outdesc); SNetInParserDestroy(); }
static bool SNetInputAllowed(worker_t *worker) { bool allowed = true; if (SNetInputThrottle()) { /* Check if output has advanced enough for us to input more. */ size_t outs = NODE_SPEC(worker->output_node, output)->num_outputs; size_t ins = DESC_LAND_SPEC(worker->input_desc, input)->num_inputs; double limit = SNetInputOffset() + SNetInputFactor() * outs; allowed = (ins < limit); } return allowed; }
/* Merge a stream to an identity landing with the subsequent stream. */ static snet_stream_desc_t *SNetMergeStreams(snet_stream_desc_t **desc_ptr) { snet_stream_desc_t *desc = *desc_ptr; snet_stream_desc_t *next = DESC_LAND_SPEC(desc, identity)->outdesc; fifo_node_t *fifo_tail_start; fifo_node_t *fifo_tail_end; fifo_node_t *node; int count = 0; /* Remove all data from the queue towards the garbage landing. */ fifo_tail_start = SNetFifoGetTail(&desc->fifo, &fifo_tail_end); /* Count the number of data items in the captured list. */ for (node = fifo_tail_start; node; node = node->next) { ++count; } /* Append the captured list onto the subsequent stream. */ SNetFifoPutTail(&next->fifo, fifo_tail_start, fifo_tail_end); /* Reconnect the source landing of the next landing. */ next->source = desc->source; /* Increase the reference count by the number of added records. */ AAF(&(next->refs), count); /* Report statistics. */ if (SNetDebug()) { printf("%s: collecting %d recs, %d drefs, %d nrefs\n", __func__, count, desc->refs, next->refs); } /* Convert the identity landing into garbage. */ SNetBecomeGarbage(desc->landing); /* Make sure no one ever attempts to write to the dissolved stream. */ *desc_ptr = next; /* Unlock the garbage landing: some worker todo items may still need it. */ unlock_landing(desc->landing); /* Decrease reference count to the garbage collected stream. */ SNetDescDone(desc); /* Return the subsequent stream. */ return next; }
/* Star component: forward record to instance or to collector. */ void SNetNodeStar(snet_stream_desc_t *desc, snet_record_t *rec) { const star_arg_t *sarg = DESC_NODE_SPEC(desc, star); landing_star_t *land = DESC_LAND_SPEC(desc, star); trace(__func__); switch (REC_DESCR(rec)) { case REC_data: if (land->star_leader && (sarg->is_det | sarg->is_detsup)) { SNetDetEnter(rec, &land->detenter, sarg->is_det, sarg->entity); } if (MatchesExitPattern( rec, sarg->exit_patterns, sarg->guards)) { StarWrite(WriteCollector, rec, desc); } else { StarWrite(WriteInstance, rec, desc); } break; case REC_detref: /* Forward record */ StarWrite(WriteCollector, rec, desc); break; case REC_sync: SNetRecDestroy(rec); break; case REC_star_leader: land->star_leader = true; land->detenter.counter = LEADER_REC(rec, counter); land->detenter.seqnr = LEADER_REC(rec, seqnr ); SNetRecDestroy(rec); break; default: SNetRecUnknownEnt(__func__, rec, sarg->entity); } }
/* Create the node which reads records from an input file via the parser. * This is called from networkinterface.c to initialize the input module. */ void SNetInInputInit( FILE *file, snetin_label_t *labels, snetin_interface_t *interfaces, snet_stream_t *output) { input_arg_t *iarg; node_t *node; landing_input_t *linp; const int first_worker_id = 1; trace(__func__); /* Create input node in the fixed network. */ node = SNetNodeNew(NODE_input, (snet_stream_t **)NULL, 0, &output, 1, SNetNodeInput, SNetStopInput, SNetTermInput); iarg = NODE_SPEC(node, input); iarg->output = output; iarg->state = INPUT_reading; /* Create an empty input descriptor: needed for SNetStreamOpen. */ iarg->indesc = SNetNewAlign(snet_stream_desc_t); memset(iarg->indesc, 0, sizeof(snet_stream_desc_t)); /* Create landing: needed for locking the input node before use. */ iarg->indesc->landing = SNetNewLanding(node, NULL, LAND_input); linp = DESC_LAND_SPEC(iarg->indesc, input); linp->num_inputs = 0; /* Create output descriptor: needed by parser for writing. */ iarg->indesc->landing->id = first_worker_id; linp->outdesc = SNetStreamOpen(output, iarg->indesc); iarg->indesc->landing->id = 0; /* Initialize the parser */ SNetInParserInit(file, labels, interfaces, NULL, NULL); }
/* Open a descriptor to an output stream. * * The source landing which opens the stream is determined by parameter 'prev'. * The new descriptor holds a pointer to a landing which instantiates * the destination node as determined by the stream parameter 'stream'. * * Special cases: * (1) Dispatchers should create an additional landing for a future collector * and push this landing onto a stack of future landings. * (2) Collectors should retrieve their designated landing from this stack * and verify that it is theirs. */ snet_stream_desc_t *SNetStreamOpen( snet_stream_t *stream, snet_stream_desc_t *prev) { snet_stream_desc_t *desc; trace(__func__); desc = SNetNewAlign(snet_stream_desc_t); DESC_STREAM(desc) = stream; desc->source = prev->landing; desc->refs = 1; SNetFifoInit(&desc->fifo); switch (NODE_TYPE(stream->dest)) { case NODE_filter: case NODE_nameshift: case NODE_output: desc->landing = SNetNewLanding(DESC_DEST(desc), prev, LAND_siso); DESC_LAND_SPEC(desc, siso)->outdesc = NULL; break; case NODE_box: SNetNewBoxLanding(desc, prev); break; case NODE_parallel: SNetNewParallelLanding(desc, prev); break; case NODE_star: SNetNewStarLanding(desc, prev); break; case NODE_split: SNetNewSplitLanding(desc, prev); break; case NODE_feedback: SNetNewFeedbackLanding(desc, prev); break; case NODE_dripback: SNetNewDripBackLanding(desc, prev); break; case NODE_sync: desc->landing = SNetNewLanding(STREAM_DEST(stream), prev, LAND_sync); SNetSyncInitDesc(desc, stream); break; case NODE_zipper: /* Init the landing state of a fused sync-star node */ desc->landing = SNetNewLanding(STREAM_DEST(stream), prev, LAND_zipper); DESC_LAND_SPEC(desc, zipper)->outdesc = NULL; DESC_LAND_SPEC(desc, zipper)->head = NULL; break; case NODE_collector: /* Collectors receive the previously created landing from the stack */ desc->landing = SNetPopLanding(prev); assert(desc->landing); assert(desc->landing->type == LAND_collector); assert(DESC_NODE(desc) == STREAM_DEST(stream)); assert(desc->landing->refs > 0); break; case NODE_observer: desc->landing = SNetNewLanding(DESC_DEST(desc), prev, LAND_observer); DESC_LAND_SPEC(desc, observer)->outdesc = NULL; DESC_LAND_SPEC(desc, observer)->oid = 0; break; case NODE_observer2: desc->landing = SNetPopLanding(prev); assert(desc->landing); assert(desc->landing->type == LAND_empty); break; case NODE_input: case NODE_identity: case NODE_garbage: default: desc->landing = NULL; assert(0); break; } /* if (SNetVerbose() && SNetNodeGetWorkerCount() <= 1) { printf("%s: %s, %s\n", __func__, SNetNodeName(desc->landing->node), SNetLandingName(desc->landing)); } */ return desc; }
/* Work on an item. * * In case of a dissolved garbage stream update the source * stream of the work item. * * Return true iff a record was processed. * * If the contents of the item was merged with another item then * reset the item descriptor to NULL. */ static bool SNetWorkerWorkItem(work_item_t *const item, worker_t *worker) { work_item_t *lookup; /* Item must be owned and non-empty. */ assert(item->lock == worker->id); assert(item->count > 0); /* Claim destination landing. */ if (trylock_landing(item->desc->landing, worker) == false) { /* Nothing can be done. */ return false; } /* Bring item descriptors past any garbage collectable landings. */ while (item->desc->landing->type == LAND_garbage) { /* Get subsequent descriptor. */ snet_stream_desc_t *next_desc = DESC_LAND_SPEC(item->desc, siso)->outdesc; /* Release landing claim. */ unlock_landing(item->desc->landing); /* Decrease reference counts to descriptor. */ SNetDescRelease(item->desc, item->count); /* Take item out of hash table. */ SNetHashPtrRemove(worker->hash_ptab, item->desc); /* Update item descriptor. */ item->desc = next_desc; /* Also advance past subsequent garbage landings. */ while (next_desc->landing->type == LAND_garbage) { /* Get subsequent descriptor. */ item->desc = DESC_LAND_SPEC(next_desc, siso)->outdesc; /* Test if current descriptor is also in our hash table. */ lookup = (work_item_t *)SNetHashPtrLookup(worker->hash_ptab, next_desc); if (lookup && trylock_work_item(lookup, worker)) { /* Merge both descriptor counts into one. */ item->count += lookup->count; lookup->count = 0; unlock_work_item(lookup, worker); } /* Decrease reference counts to garbage descriptor. */ SNetDescRelease(next_desc, item->count); /* Advance to subsequent descriptor and repeat. */ next_desc = item->desc; } /* The new descriptor may already exist in hash table. */ lookup = (work_item_t *)SNetHashPtrLookup(worker->hash_ptab, next_desc); if (lookup) { /* Merge the two counts. */ AAF(&lookup->count, item->count); /* Reset item. */ item->count = 0; /* We already have this desciptor in lookup, so reset it. */ item->desc = NULL; /* We made progress. */ return true; } else /* (lookup == NULL) */ { /* Add new descriptor to hash table. */ SNetHashPtrStore(worker->hash_ptab, item->desc, item); /* Claim destination landing. */ if (trylock_landing(item->desc->landing, worker) == false) { /* We made progress anyway. */ return true; } } } /* Subtract one read license. */ --item->count; /* Unlock item so thieves can steal it while we work. */ unlock_work_item(item, worker); /* Finally, do the work by delegating to the streams layer. */ SNetStreamWork(item->desc, worker); /* We definitely made progress. */ return true; }
/* Send a record to a stream which may need to be opened */ static void StarWrite(star_write_t kind, snet_record_t *rec, snet_stream_desc_t *desc) { const star_arg_t *sarg = DESC_NODE_SPEC(desc, star); landing_star_t *land = DESC_LAND_SPEC(desc, star); switch (kind) { case WriteCollector: /* check if open */ if (!land->colldesc) { /* first landing must be collector */ if (SNetTopLanding(desc) != land->collland) { SNetPushLanding(desc, land->collland); } land->colldesc = SNetStreamOpen(sarg->collector, desc); } SNetWrite(&land->colldesc, rec, true); break; case WriteInstance: /* check if open */ if (!land->instdesc) { /* first landing must be instance */ if (SNetTopLanding(desc) != land->instland) { SNetPushLanding(desc, land->instland); } land->instdesc = SNetStreamOpen(sarg->instance, desc); /* remove instance landing */ if (SNetTopLanding(desc) == land->instland) { SNetPopLanding(desc); SNetLandingDone(land->instland); } /* prevent double deallocation */ land->instland = NULL; /* emit output record to instance */ SNetWrite(&land->instdesc, rec, true); } else { /* A star is superfluous when the next landing is its incarnation. */ bool superfluous = (desc->landing->node == land->instdesc->landing->node); /* emit output record to instance */ SNetWrite(&land->instdesc, rec, (superfluous == false)); /* If instance has been garbage collected then dissolve. */ if (superfluous) { /* Possibly forward leader status to next incarnation. */ rec = (land->star_leader && (sarg->is_det | sarg->is_detsup)) ? SNetRecCreate(REC_star_leader, land->detenter.counter, land->detenter.seqnr) : NULL; land->star_leader = false; /* Discard stream towards collector. */ if (land->colldesc) { SNetDescDone(land->colldesc); } /* Discard collector landing. */ SNetLandingDone(land->collland); /* Change landing into identity, which can be garbage collected. */ SNetBecomeIdentity(desc->landing, land->instdesc); /* Finally, notify incarnation of new leader status. */ if (rec) { SNetWrite(&(DESC_LAND_SPEC(desc, identity)->outdesc), rec, true); } } } break; default: assert(0); } }