/* DripBack process a record. */ void SNetNodeDripBack(snet_stream_desc_t *desc, snet_record_t *rec) { landing_t *land = desc->landing; dripback_arg_t *darg = LAND_NODE_SPEC(land, dripback); trace(__func__); if (land->type == LAND_dripback1) { landing_dripback1_t *db1 = LAND_SPEC(land, dripback1); landing_dripback2_t *db2 = LAND_SPEC(db1->dripback2, dripback2); SNetFifoPut(db1->recfifo, rec); if (FAA(&db2->queued, 1) == 0) { if (db1->controldesc == NULL) { SNetPushLanding(desc, db1->dripback2); db1->controldesc = SNetStreamOpen(darg->selfref, desc); } rec = SNetRecCreate(REC_wakeup); SNetWrite(&db1->controldesc, rec, true); } } else if (land->type == LAND_dripback2) { landing_dripback2_t *db2 = LAND_SPEC(land, dripback2); bool via_access = (DESC_STREAM(desc) == darg->selfref); assert(via_access || DESC_STREAM(desc) == darg->dripback); while (rec) { switch (REC_DESCR( rec)) { case REC_data: /* Test if record should go to the instance. */ if (SNetFeedbackMatch( rec, darg->back_patterns, darg->guards)) { if (via_access) { /* Because record came from outside add a detref counter. */ assert(db2->detfifo.head->next == NULL); SNetRecDetrefAdd(rec, ++(db2->entered), land, &db2->detfifo); } else { /* Record came from the instance. */ assert(DATA_REC(rec, detref)); } if (db2->instdesc == NULL) { /* Instance should come back to this landing. */ if (SNetTopLanding(desc) != land) { SNetPushLanding(desc, land); } db2->instdesc = SNetStreamOpen(darg->instance, desc); if (SNetTopLanding(desc) == land) { SNetPopLanding(desc); SNetLandingDone(land); } } SNetWrite(&db2->instdesc, rec, false); } else { if (!via_access) { /* Record leaves the dripback loop. */ assert(DATA_REC(rec, detref)); SNetFeedbackLeave(rec, land, &db2->detfifo); } if (db2->outdesc == NULL) { db2->outdesc = SNetStreamOpen(darg->output, desc); } SNetWrite(&db2->outdesc, rec, false); } break; case REC_wakeup: assert(via_access); SNetRecDestroy(rec); break; case REC_terminate: assert(via_access); assert(db2->terminate == DripBackInitial); db2->terminate = DripBackDraining; SNetRecDestroy(rec); break; case REC_detref: if (DETREF_REC( rec, leave) == land && DETREF_REC( rec, location) == SNetDistribGetNodeId()) { assert(!via_access); SNetDetLeaveCheckDetref(rec, &db2->detfifo); if (DETREF_REC( rec, detref) == SNetFifoPeekFirst(&db2->detfifo)) { DripBackCheckBusy(db2); } SNetRecDestroy(rec); } else { assert(via_access); if (db2->outdesc == NULL) { db2->outdesc = SNetStreamOpen(darg->output, desc); } SNetWrite(&db2->outdesc, rec, false); } break; case REC_sync: SNetRecDestroy(rec); break; default: SNetRecUnknownEnt(__func__, rec, darg->entity); } rec = NULL; if (db2->state == DripBackBusy) { if (DripBackCheckBusy(db2) == false) { assert(db2->queued > 0); if (SAF(&db2->queued, 1) == 0) { db2->state = DripBackIdle; } else { rec = SNetFifoGet(&db2->recfifo); assert(rec); via_access = true; } } } else { assert(db2->state == DripBackIdle); assert(SNetFifoPeekFirst(&db2->detfifo) == NULL); if (db2->queued > 0) { rec = SNetFifoGet(&db2->recfifo); assert(rec); via_access = true; db2->state = DripBackBusy; } } } if (db2->terminate == DripBackDraining) { if (db2->state == DripBackIdle) { assert(db2->queued == 0 && DripBackCheckBusy(db2) == false); db2->terminate = DripBackTerminated; } } if (db2->terminate == DripBackTerminated) { if (db2->instdesc) { snet_stream_desc_t *desc = db2->instdesc; db2->instdesc = NULL; SNetDescDone(desc); SNetLandingDone(land); } } } else { assert(0); } }
/* 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; }