/** * Peeks the top record of all streams in waitingset, * if there is a terminate record, the stream is closed * and removed from the waitingset */ static int DestroyTermInWaitingSet(snet_stream_iter_t *wait_iter, snet_streamset_t *waitingset) { int destroy_cnt=0; if ( !SNetStreamsetIsEmpty( waitingset)) { SNetStreamIterReset( wait_iter, waitingset); while( SNetStreamIterHasNext( wait_iter)) { snet_stream_desc_t *sd = SNetStreamIterNext( wait_iter); snet_record_t *wait_rec = SNetStreamPeek( sd); /* for this stream, check if there is a termination record next */ if ( wait_rec != NULL && SNetRecGetDescriptor( wait_rec) == REC_terminate ) { /* consume, remove from waiting set and free the stream */ (void) SNetStreamRead( sd); SNetStreamIterRemove( wait_iter); SNetStreamClose( sd, true); /* update destroyed counter */ destroy_cnt++; /* destroy record */ SNetRecDestroy( wait_rec); } /* else do nothing */ } } return destroy_cnt; }
static void FbCollReadFbi(struct fbcoll_state *state) { snet_record_t *rec; assert( false == state->terminate ); /* read from feedback stream */ rec = SNetStreamRead( state->backstream); switch( SNetRecGetDescriptor( rec)) { case REC_data: /* relay data record */ SNetStreamWrite( state->outstream, rec); /* mode switch to FB0 (there is a next iteration) */ state->mode = FBCOLL_FB0; break; case REC_sort_end: assert( 0 == SNetRecGetLevel(rec) ); switch(state->mode) { case FBCOLL_FB0: state->mode = FBCOLL_FB1; /* increase counter (non-functional) */ SNetRecSetNum( rec, SNetRecGetNum(rec)+1); SNetStreamWrite( state->outstream, rec); break; case FBCOLL_FB1: state->mode = FBCOLL_IN; /* kill the sort record */ SNetRecDestroy( rec); break; default: assert(0); } break; case REC_sync: SNetStreamReplace( state->backstream, SNetRecGetStream( rec)); SNetRecDestroy( rec); break; case REC_terminate: case REC_collect: default: assert(0); /* if ignoring, at least destroy ... */ SNetRecDestroy( rec); break; } }
/** * Get a record due to the collector setting */ static snet_record_t *GetRecord( snet_streamset_t *readyset, int incount, snet_stream_desc_t **cur_stream) { assert(incount >= 1 && *readyset != NULL); if (*cur_stream == NULL || SNetStreamPeek(*cur_stream) == NULL) { if (incount == 1) { *cur_stream = *readyset; } else { *cur_stream = SNetStreamPoll(readyset); } } return SNetStreamRead(*cur_stream); }
/* helper functions to handle mode the feedback collector is in */ static void FbCollReadIn(struct fbcoll_state *state) { snet_record_t *rec; assert( false == state->terminate ); /* read from input stream */ rec = SNetStreamRead( state->instream); switch( SNetRecGetDescriptor( rec)) { case REC_data: /* relay data record */ SNetStreamWrite( state->outstream, rec); /* append a sort record */ SNetStreamWrite( state->outstream, SNetRecCreate( REC_sort_end, 0, 1 ) /*type, lvl, num*/ ); /* mode switch to FB1 */ state->mode = FBCOLL_FB1; break; case REC_sort_end: /* increase the level and forward */ SNetRecSetLevel( rec, SNetRecGetLevel(rec)+1); SNetStreamWrite( state->outstream, rec); break; case REC_terminate: state->terminate = true; SNetStreamWrite( state->outstream, rec); /* note that no sort record has to be appended */ break; case REC_sync: SNetStreamReplace( state->instream, SNetRecGetStream( rec)); SNetRecDestroy( rec); break; case REC_collect: default: assert(0); /* if ignoring, at least destroy ... */ SNetRecDestroy( rec); break; } }
static void FeedbackBufTask(snet_entity_t *, void *arg) { fbbuf_arg_t *fbbarg = (fbbuf_arg_t *)arg; snet_stream_desc_t *instream; snet_stream_desc_t *outstream; snet_record_t *rec; int out_counter = 0; int out_capacity; instream = SNetStreamOpen(fbbarg->in, 'r'); outstream = SNetStreamOpen(fbbarg->out, 'w'); out_capacity = fbbarg->out_capacity; SNetMemFree( fbbarg); /* MAIN LOOP */ while(1) { rec = SNetStreamRead(instream); if( SNetRecGetDescriptor(rec) == REC_terminate ) { /* this means, the outstream does not exist anymore! */ SNetRecDestroy(rec); break; /* exit main loop */ } if (out_counter+1 >= out_capacity) { /* new stream */ snet_stream_t *new_stream = SNetStreamCreate(out_capacity); SNetStreamWrite(outstream, SNetRecCreate(REC_sync, new_stream) ); SNetStreamClose(outstream, false); outstream = SNetStreamOpen(new_stream, 'w'); out_counter = 0; } /* write the record to the stream */ SNetStreamWrite(outstream, rec); out_counter++; } /* END OF MAIN LOOP */ SNetStreamClose(instream, true); SNetStreamClose(outstream, false); }
/** * Split box task. * * Implements both the non-deterministic and deterministic variants. */ static void SplitBoxTask(snet_entity_t *ent, void *arg) { int i; split_arg_t *sarg = (split_arg_t *)arg; snet_stream_desc_t *initial, *instream; int ltag_val, utag_val; snet_info_t *info; snet_record_t *rec; snet_locvec_t *locvec; bool terminate = false; /* a list of all outstreams for all yet created instances */ snet_streamset_t repos_set = NULL; snet_stream_iter_t *iter = SNetStreamIterCreate( &repos_set); /* a hashtable for fast lookup, initial capacity = 2^4 = 16 */ hashtab_t *repos_tab = HashtabCreate( 4); (void) ent; /* NOT USED */ /* for deterministic variant: */ int counter = 0; initial = SNetStreamOpen(sarg->output, 'w'); instream = SNetStreamOpen(sarg->input, 'r'); /* MAIN LOOP START */ while( !terminate) { /* read from input stream */ rec = SNetStreamRead( instream); switch( SNetRecGetDescriptor( rec)) { case REC_data: /* get lower and upper tag values */ ltag_val = SNetRecGetTag( rec, sarg->ltag); utag_val = SNetRecGetTag( rec, sarg->utag); /* for all tag values */ for( i = ltag_val; i <= utag_val; i++) { snet_stream_desc_t *outstream = HashtabGet( repos_tab, i); if( outstream == NULL) { snet_stream_t *temp_stream; snet_stream_t *newstream_addr = SNetStreamCreate(0); /* instance does not exist yet, create it */ outstream = SNetStreamOpen(newstream_addr, 'w'); /* add to lookup table */ HashtabPut( repos_tab, i, outstream); /* add to list */ SNetStreamsetPut( &repos_set, outstream); /* create info and location vector for creation of this replica */ info = SNetInfoCopy(sarg->info); locvec = SNetLocvecSplitSpawn(SNetLocvecGet(sarg->info), i); SNetLocvecSet(info, locvec); if( sarg->is_byloc) { SNetRouteDynamicEnter(info, i, i, sarg->boxfun); temp_stream = sarg->boxfun(newstream_addr, info, i); temp_stream = SNetRouteUpdate(info, temp_stream, sarg->location); SNetRouteDynamicExit(info, i, i, sarg->boxfun); } else { SNetRouteDynamicEnter(info, i, sarg->location, sarg->boxfun); temp_stream = sarg->boxfun(newstream_addr, info, sarg->location); temp_stream = SNetRouteUpdate(info, temp_stream, sarg->location); SNetRouteDynamicExit(info, i, sarg->location, sarg->boxfun); } /* destroy info and location vector */ SNetLocvecDestroy(locvec); SNetInfoDestroy(info); if(temp_stream != NULL) { /* notify collector about the new instance via initial */ SNetStreamWrite( initial, SNetRecCreate( REC_collect, temp_stream)); } } /* end if (outstream==NULL) */ /* multicast the record */ SNetStreamWrite( outstream, /* copy record for all but the last tag value */ (i!=utag_val) ? SNetRecCopy( rec) : rec ); } /* end for all tags ltag_val <= i <= utag_val */ /* If deterministic, append a sort record to *all* registered * instances and the initial stream. */ if( sarg->is_det ) { /* reset iterator */ SNetStreamIterReset( iter, &repos_set); while( SNetStreamIterHasNext( iter)) { snet_stream_desc_t *cur_stream = SNetStreamIterNext( iter); SNetStreamWrite( cur_stream, SNetRecCreate( REC_sort_end, 0, counter)); } /* Now also send a sort record to initial, after the collect records for new instances have been sent */ SNetStreamWrite( initial, SNetRecCreate( REC_sort_end, 0, counter)); } /* increment counter for deterministic variant */ counter += 1; break; case REC_sync: { snet_stream_t *newstream = SNetRecGetStream( rec); SNetStreamReplace( instream, newstream); SNetRecDestroy( rec); } break; case REC_sort_end: /* broadcast the sort record */ SNetStreamIterReset( iter, &repos_set); /* all instances receive copies of the record */ while( SNetStreamIterHasNext( iter)) { snet_stream_desc_t *cur_stream = SNetStreamIterNext( iter); SNetStreamWrite( cur_stream, SNetRecCreate( REC_sort_end, /* we have to increase level */ SNetRecGetLevel( rec)+1, SNetRecGetNum( rec)) ); } /* send the original record to the initial stream, but with increased level */ SNetRecSetLevel( rec, SNetRecGetLevel( rec) + 1); SNetStreamWrite( initial, rec); break; case REC_terminate: SNetStreamIterReset( iter, &repos_set); /* all instances receive copies of the record */ while( SNetStreamIterHasNext( iter)) { snet_stream_desc_t *cur_stream = SNetStreamIterNext( iter); SNetStreamWrite( cur_stream, SNetRecCopy( rec)); SNetStreamIterRemove( iter); /* close the stream to the instance */ SNetStreamClose( cur_stream, false); } /* send the original record to the initial stream */ SNetStreamWrite( initial, rec); /* note that no sort record has to be appended */ terminate = true; break; case REC_collect: /* invalid control record */ default: assert( 0); /* if ignore, at least destroy it */ SNetRecDestroy( rec); } } /* MAIN LOOP END */ /* destroy repository */ HashtabDestroy( repos_tab); SNetStreamIterDestroy( iter); /* close and destroy initial stream */ SNetStreamClose( initial, false); /* close instream */ SNetStreamClose( instream, true); SNetLocvecDestroy(SNetLocvecGet(sarg->info)); SNetInfoDestroy(sarg->info); /* destroy the argument */ SNetMemFree( sarg); } /* END of SPLIT BOX TASK */
/** * Star component task */ static void StarBoxTask(void *arg) { star_arg_t *sarg = arg; snet_record_t *rec; /* read from input stream */ rec = SNetStreamRead( sarg->instream); switch( SNetRecGetDescriptor( rec)) { case REC_data: if( MatchesExitPattern( rec, sarg->exit_patterns, sarg->guards)) { assert(!sarg->sync_cleanup); #ifdef DEBUG_PRINT_GC SNetUtilDebugNoticeEnt( ent, "[STAR] Notice: Data leaves replication network."); #endif /* send rec to collector */ SNetStreamWrite( sarg->outstream, rec); } else { /* if instance has not been created yet, create it */ if( sarg->nextstream == NULL) { CreateOperandNetwork(&sarg->nextstream, sarg, sarg->outstream); } /* send the record to the instance */ SNetStreamWrite( sarg->nextstream, rec); } /* end if not matches exit pattern */ /* deterministic non-incarnate has to append control records */ if (sarg->is_det && !sarg->is_incarnate) { /* send new sort record to collector level=0, counter=0*/ SNetStreamWrite( sarg->outstream, SNetRecCreate( REC_sort_end, 0, sarg->counter) ); /* if has next instance, send new sort record */ if (sarg->nextstream != NULL) { SNetStreamWrite( sarg->nextstream, SNetRecCreate( REC_sort_end, 0, sarg->counter) ); } /* increment counter */ sarg->counter++; } #ifdef ENABLE_GC else if (sarg->sync_cleanup) { snet_record_t *term_rec; /* * If sync_cleanup is set, we decided to postpone termination * due to garbage collection triggered by a sync record until now. * Postponing was done in order not to create the operand network unnecessarily * only to be able to forward the sync record. */ assert( sarg->nextstream != NULL); /* first send a sync record to the next instance */ SNetStreamWrite( sarg->nextstream, SNetRecCreate( REC_sync, SNetStreamGet(sarg->instream)) ); /* send a terminate record to collector, it will close and destroy the stream */ term_rec = SNetRecCreate(REC_terminate); SNetRecSetFlag(term_rec); SNetStreamWrite( sarg->outstream, term_rec); #ifdef DEBUG_PRINT_GC /* terminating due to GC */ SNetUtilDebugNoticeEnt( ent, "[STAR] Notice: Destroying star dispatcher due to GC, " "delayed until new data record!" ); #endif SNetStreamClose(sarg->nextstream, false); SNetStreamClose(sarg->instream, false); TerminateStarBoxTask(sarg->outstream,sarg); return; } #endif /* ENABLE_GC */ break; case REC_sync: { snet_stream_t *newstream = SNetRecGetStream( rec); #ifdef ENABLE_GC snet_locvec_t *loc = SNetStreamGetSource( newstream); #ifdef DEBUG_PRINT_GC if (loc != NULL) { int size = SNetLocvecPrintSize(loc) + 1; char srecloc[size]; srecloc[size - 1] = '\0'; SNetLocvecPrint(srecloc, loc); SNetUtilDebugNoticeTask( "[STAR] Notice: Received sync record with a stream with source %s.", srecloc ); } #endif /* TODO * It is not necessary to carry the whole location vector in the * next stream of a star-entity, only a flag. As a prerequisite, * non_incarnates must not clean themselves up! */ /* * Only incarnates are eligible for cleanup! * check if the source (location) of the stream and the own location are * (subsequent) star dispatcher entities of the same star combinator network * -> if so, we can clean-up ourselves */ if ( sarg->is_incarnate && loc != NULL ) { assert( true == SNetLocvecEqualParent(loc, SNetLocvecGet(sarg->info)) ); /* If the next instance is already created, we can forward the sync-record * immediately and terminate. * Otherwise we postpone termination to the point when a next data record * is received, as we create the operand network then. */ if (sarg->nextstream != NULL) { snet_record_t *term_rec; /* forward the sync record */ SNetStreamWrite( sarg->nextstream, rec); /* send a terminate record to collector, it will close and destroy the stream */ term_rec = SNetRecCreate(REC_terminate); SNetRecSetFlag(term_rec); SNetStreamWrite( sarg->outstream, term_rec); #ifdef DEBUG_PRINT_GC /* terminating due to GC */ SNetUtilDebugNoticeEnt( ent, "[STAR] Notice: Destroying star dispatcher due to GC, " "immediately on sync!" ); #endif SNetStreamClose(sarg->nextstream, false); SNetStreamClose(sarg->instream, true); TerminateStarBoxTask(sarg->outstream,sarg); return; } else { sarg->sync_cleanup = true; #ifdef DEBUG_PRINT_GC SNetUtilDebugNoticeEnt( ent, "[STAR] Notice: Remembering delayed destruction."); #endif /* handle sync record as usual */ SNetStreamReplace( sarg->instream, newstream); SNetRecDestroy( rec); } } else #endif /* ENABLE_GC */ { /* handle sync record as usual */ SNetStreamReplace( sarg->instream, newstream); SNetRecDestroy( rec); } } break; case REC_sort_end: { int rec_lvl = SNetRecGetLevel(rec); /* send a copy to the box, if exists */ if( sarg->nextstream != NULL) { SNetStreamWrite( sarg->nextstream, SNetRecCreate( REC_sort_end, (!sarg->is_incarnate)? rec_lvl+1 : rec_lvl, SNetRecGetNum(rec) ) ); } /* send the original one to the collector */ if (!sarg->is_incarnate) { /* if non-incarnate, we have to increase level */ SNetRecSetLevel( rec, rec_lvl+1); } SNetStreamWrite( sarg->outstream, rec); } break; case REC_terminate: if( sarg->nextstream != NULL) { SNetStreamWrite( sarg->nextstream, SNetRecCopy( rec)); SNetStreamClose( sarg->nextstream, false); } SNetStreamWrite( sarg->outstream, rec); /* note that no sort record has to be appended */ SNetStreamClose(sarg->instream, true); TerminateStarBoxTask(sarg->outstream,sarg); return; case REC_collect: default: SNetUtilDebugFatal("Unknown record type!"); /* if ignore, at least destroy ... */ SNetRecDestroy( rec); } SNetThreadingRespawn(NULL); }
static void BoxTask(snet_entity_t *ent, void *arg) { #ifdef DBG_RT_TRACE_BOX_TIMINGS static struct timeval tv_in; static struct timeval tv_out; #endif #ifdef SNET_DEBUG_COUNTERS snet_time_t time_in; snet_time_t time_out; long mseconds; #endif /* SNET_DEBUG_COUNTERS */ box_arg_t *barg = (box_arg_t *)arg; snet_record_t *rec; snet_stream_desc_t *instream, *outstream; bool terminate = false; instream = SNetStreamOpen(barg->input, 'r'); outstream = SNetStreamOpen(barg->output, 'w'); /* set out descriptor */ barg->hnd.out_sd = outstream; /* set entity */ barg->hnd.ent = ent; /* MAIN LOOP */ while(!terminate) { /* read from input stream */ rec = SNetStreamRead(instream); switch(SNetRecGetDescriptor(rec)) { case REC_trigger_initialiser: case REC_data: barg->hnd.rec = rec; #ifdef DBG_RT_TRACE_BOX_TIMINGS gettimeofday(&tv_in, NULL); SNetUtilDebugNoticeEnt(ent, "[BOX] Firing box function at %lf.", tv_in.tv_sec + tv_in.tv_usec / 1000000.0 ); #endif #ifdef SNET_DEBUG_COUNTERS SNetDebugTimeGetTime(&time_in); #endif /* SNET_DEBUG_COUNTERS */ #ifdef USE_USER_EVENT_LOGGING /* Emit a monitoring message of a record read to be processed by a box */ if (SNetRecGetDescriptor(rec) == REC_data) { SNetThreadingEventSignal(ent, SNetMonInfoCreate(EV_MESSAGE_IN, MON_RECORD, rec) ); } #endif /* execute box function and update execution realm */ barg->hnd = *barg->boxfun(&barg->hnd); barg->hnd = *barg->exerealm_update(&barg->hnd); /* * Emit an event here? * SNetMonInfoEvent(EV_BOX_???, MON_RECORD, rec); */ #ifdef DBG_RT_TRACE_BOX_TIMINGS gettimeofday(&tv_out, NULL); SNetUtilDebugNoticeEnt(ent, "[BOX] Return from box function after %lf sec.", (tv_out.tv_sec - tv_in.tv_sec) + (tv_out.tv_usec - tv_in.tv_usec) / 1000000.0 ); #endif #ifdef SNET_DEBUG_COUNTERS SNetDebugTimeGetTime(&time_out); mseconds = SNetDebugTimeDifferenceInMilliseconds(&time_in, &time_out); SNetDebugCountersIncreaseCounter(mseconds, SNET_COUNTER_TIME_BOX); #endif /* SNET_DEBUG_COUNTERS */ SNetRecDestroy(rec); /* restrict to one data record per execution */ //SNetThreadingYield(); /* check the box task should be migrated after one record execution */ SNetThreadingCheckMigrate(); break; case REC_sync: { snet_stream_t *newstream = SNetRecGetStream(rec); SNetStreamReplace(instream, newstream); SNetRecDestroy(rec); } break; case REC_sort_end: /* forward the sort record */ SNetStreamWrite(outstream, rec); break; case REC_terminate: barg->hnd = *barg->exerealm_destroy(&barg->hnd); SNetStreamWrite(outstream, rec); terminate = true; break; case REC_collect: default: assert(0); } } /* MAIN LOOP END */ barg->hnd = *barg->exerealm_destroy(&barg->hnd); SNetStreamClose(instream, true); SNetStreamClose(outstream, false); /* destroy box arg */ SNetVariantListDestroy(barg->hnd.vars); SNetIntListListDestroy(barg->hnd.sign); SNetMemFree( barg); }
/** * The feedback buffer, in the back-loop */ static void FeedbackBufTask(snet_entity_t *ent, void *arg) { fbbuf_arg_t *fbbarg = (fbbuf_arg_t *)arg; snet_stream_desc_t *instream; snet_stream_desc_t *outstream; snet_queue_t *internal_buffer; snet_record_t *rec; int out_capacity; int max_read; (void) ent; /* NOT USED */ instream = SNetStreamOpen(fbbarg->in, 'r'); outstream = SNetStreamOpen(fbbarg->out, 'w'); out_capacity = fbbarg->out_capacity; SNetMemFree( fbbarg); internal_buffer = SNetQueueCreate(); max_read = out_capacity; /* TODO better usual stream capacity */ /* MAIN LOOP */ while(1) { int n = 0; rec = NULL; /* STEP 1: read n=min(available,max_read) records from input stream */ /* read first record of the actual dispatch */ if (0 == SNetQueueSize(internal_buffer)) { rec = SNetStreamRead(instream); /* only in empty mode! */ if( REC_terminate == SNetRecGetDescriptor( rec)) { /* this means, the outstream does not exist anymore! */ SNetRecDestroy(rec); goto feedback_buf_epilogue; } } else { SNetThreadingYield(); if ( SNetStreamPeek(instream) != NULL ) { rec = SNetStreamRead(instream); assert( REC_terminate != SNetRecGetDescriptor( rec) ); } } if (rec != NULL) { n = 1; /* put record into internal buffer */ (void) SNetQueuePut(internal_buffer, rec); } while ( n<=max_read && SNetStreamPeek(instream)!=NULL ) { rec = SNetStreamRead(instream); /* check if we will need a larger outstream, and if so, * create a larger stream */ if (SNetQueueSize(internal_buffer)+1 >= out_capacity) { snet_stream_t *new_stream; out_capacity *= 2; new_stream = SNetStreamCreate(out_capacity); (void) SNetQueuePut(internal_buffer, SNetRecCreate(REC_sync, new_stream)); } /* put record into internal buffer */ (void) SNetQueuePut(internal_buffer, rec); n++; } /* STEP 2: try to empty the internal buffer */ rec = SNetQueuePeek(internal_buffer); while (rec != NULL) { snet_stream_t *new_stream = NULL; if( REC_sync == SNetRecGetDescriptor( rec)) { new_stream = SNetRecGetStream(rec); } if (0 == SNetStreamTryWrite(outstream, rec)) { snet_record_t *rem; /* success, also remove from queue */ rem = SNetQueueGet(internal_buffer); assert( rem == rec ); if (new_stream != NULL) { /* written sync record, now change stream */ SNetStreamClose(outstream, false); outstream = SNetStreamOpen(new_stream, 'w'); } } else { /* there remain elements in the buffer */ break; } /* for the next iteration */ rec = SNetQueuePeek(internal_buffer); } } /* END OF MAIN LOOP */ feedback_buf_epilogue: SNetQueueDestroy(internal_buffer); SNetStreamClose(instream, true); SNetStreamClose(outstream, false); }
/** * The feedback dispatcher, at the end of the * feedback combinator loop */ static void FeedbackDispTask(snet_entity_t *ent, void *arg) { fbdisp_arg_t *fbdarg = (fbdisp_arg_t *)arg; snet_stream_desc_t *instream; snet_stream_desc_t *outstream; snet_stream_desc_t *backstream; bool terminate = false; snet_record_t *rec; (void) ent; /* NOT USED */ instream = SNetStreamOpen(fbdarg->in, 'r'); outstream = SNetStreamOpen(fbdarg->out, 'w'); backstream = SNetStreamOpen(fbdarg->fbo, 'w'); /* MAIN LOOP */ while( !terminate) { /* read from input stream */ rec = SNetStreamRead( instream); switch( SNetRecGetDescriptor( rec)) { case REC_data: /* route data record */ if( MatchesBackPattern( rec, fbdarg->back_patterns, fbdarg->guards)) { /* send rec back into the loop */ SNetStreamWrite( backstream, rec); } else { /* send to output */ SNetStreamWrite( outstream, rec); } break; case REC_sort_end: { int lvl = SNetRecGetLevel(rec); if ( 0 == lvl ) { SNetStreamWrite( backstream, rec); } else { assert( lvl > 0 ); SNetRecSetLevel( rec, lvl-1); SNetStreamWrite( outstream, rec); } } break; case REC_terminate: terminate = true; #ifndef FEEDBACK_OMIT_BUFFER /* a terminate record is sent in the backloop for the buffer */ SNetStreamWrite( backstream, SNetRecCopy( rec)); #endif SNetStreamWrite( outstream, rec); break; case REC_sync: SNetStreamReplace( instream, SNetRecGetStream( rec)); SNetRecDestroy( rec); break; case REC_collect: default: assert(0); /* if ignoring, at least destroy ... */ SNetRecDestroy( rec); break; } } /* END OF MAIN LOOP */ SNetStreamClose(instream, true); SNetStreamClose(outstream, false); SNetStreamClose(backstream, false); SNetVariantListDestroy( fbdarg->back_patterns); SNetExprListDestroy( fbdarg->guards); SNetMemFree( fbdarg); }