/** * Open a stream for reading/writing * * @param s pointer to stream * @param mode either 'r' for reading or 'w' for writing * @return a stream descriptor * @pre only one task may open it for reading resp. writing * at any given point in time */ lpel_stream_desc_t *LpelStreamOpen( lpel_stream_t *s, char mode) { lpel_stream_desc_t *sd; lpel_task_t *ct = LpelTaskSelf(); assert( mode == 'r' || mode == 'w' ); sd = (lpel_stream_desc_t *) malloc( sizeof( lpel_stream_desc_t)); sd->task = ct; sd->stream = s; sd->mode = mode; sd->next = NULL; #ifdef USE_TASK_EVENT_LOGGING /* create monitoring object, or NULL if stream * is not going to be monitored (depends on ct->mon) */ if (ct->mon && MON_CB(stream_open)) { sd->mon = MON_CB(stream_open)( ct->mon, s->uid, mode); } else { sd->mon = NULL; } #else sd->mon = NULL; #endif switch(mode) { case 'r': s->cons_sd = sd; break; case 'w': s->prod_sd = sd; break; } return sd; }
/** * Blocking, consuming read from a stream * * If the stream is empty, the task is suspended until * a producer writes an item to the stream. * * @param sd stream descriptor * @return the next item of the stream * @pre current task is single reader */ void *LpelStreamRead( lpel_stream_desc_t *sd) { void *item; sd->task = LpelTaskSelf(); assert( sd->mode == 'r'); /* MONITORING CALLBACK */ #ifdef USE_TASK_EVENT_LOGGING if (sd->mon && MON_CB(stream_readprepare)) { MON_CB(stream_readprepare)(sd->mon); } #endif /* quasi P(n_sem) */ if ( atomic_fetch_sub( &sd->stream->n_sem, 1) == 0) { #ifdef USE_TASK_EVENT_LOGGING /* MONITORING CALLBACK */ if (sd->mon && MON_CB(stream_blockon)) { MON_CB(stream_blockon)(sd->mon); } #endif /* wait on stream: */ LpelTaskBlockStream( sd->task); } /* read the top element */ item = LpelBufferTop( &sd->stream->buffer); assert( item != NULL); /* pop off the top element */ LpelBufferPop( &sd->stream->buffer); /* quasi V(e_sem) */ if ( atomic_fetch_add( &sd->stream->e_sem, 1) < 0) { /* e_sem was -1 */ lpel_task_t *prod = sd->stream->prod_sd->task; /* wakeup producer: make ready */ LpelTaskUnblock( sd->task, prod); /* MONITORING CALLBACK */ #ifdef USE_TASK_EVENT_LOGGING if (sd->mon && MON_CB(stream_wakeup)) { MON_CB(stream_wakeup)(sd->mon); } #endif } /* MONITORING CALLBACK */ #ifdef USE_TASK_EVENT_LOGGING if (sd->mon && MON_CB(stream_readfinish)) { MON_CB(stream_readfinish)(sd->mon, item); } #endif return item; }
/** * Signal an user event */ void SNetThreadingEventSignal(snet_moninfo_t *moninfo) { lpel_task_t *t = LpelTaskSelf(); assert(t != NULL); mon_task_t *mt = LpelTaskGetMon(t); if (mt != NULL) { SNetThreadingMonEvent(mt, moninfo); } }
/** * Yield execution back to scheduler voluntarily * * @pre This call must be made from within a LPEL task! */ void LpelTaskYield(void) { lpel_task_t *ct = LpelTaskSelf(); assert( ct->state == TASK_RUNNING ); ct->state = TASK_READY; LpelWorkerSelfTaskYield(ct); TaskStop( ct); LpelWorkerDispatcher( ct); TaskStart( ct); }
/** * Exit the current task * * @param outarg output argument of the task * @pre This call must be made within a LPEL task! */ void LpelTaskExit(void *outarg) { lpel_task_t *ct = LpelTaskSelf(); assert( ct->state == TASK_RUNNING ); ct->outarg = outarg; /* context switch happens, this task is cleaned up then */ ct->state = TASK_ZOMBIE; LpelWorkerSelfTaskExit(ct); TaskStop( ct); LpelWorkerDispatcher( ct); /* execution never comes back here */ assert(0); }
/** * Open a stream for reading/writing * * @param s pointer to stream * @param mode either 'r' for reading or 'w' for writing * @return a stream descriptor * @pre only one task may open it for reading resp. writing * at any given point in time */ lpel_stream_desc_t *LpelStreamOpen( lpel_stream_t *s, char mode) { lpel_stream_desc_t *sd; lpel_task_t *ct = LpelTaskSelf(); assert( mode == 'r' || mode == 'w' ); sd = LpelWorkerGetSd(ct->worker_context); // try to get from the free list if (sd == NULL) sd = (lpel_stream_desc_t *) malloc( sizeof( lpel_stream_desc_t)); sd->task = ct; sd->stream = s; sd->mode = mode; sd->next = NULL; #ifdef USE_TASK_EVENT_LOGGING /* create monitoring object, or NULL if stream * is not going to be monitored (depends on ct->mon) */ if (ct->mon && MON_CB(stream_open)) { sd->mon = MON_CB(stream_open)( ct->mon, s->uid, mode); } else { sd->mon = NULL; } #else sd->mon = NULL; #endif switch(mode) { case 'r': s->cons_sd = sd; break; case 'w': s->prod_sd = sd; break; } /* set entry/exit stream */ if (LpelTaskIsWrapper(ct)) s->type = (mode == 'r' ? LPEL_STREAM_EXIT : LPEL_STREAM_ENTRY); STREAM_DBG("task %d open stream %d, mode %c\n", ct->uid, s->uid, mode); LpelTaskAddStream(ct, sd, mode); return sd; }
const char *SNetThreadingGetName(void) { return LpelGetName(LpelTaskSelf()); }
unsigned long SNetThreadingGetId() { /* FIXME more convenient way */ /* returns the thread id */ return (unsigned long) LpelTaskSelf(); }
/** * Poll a set of streams * * This is a blocking function called by a consumer which wants to wait * for arrival of data on any of a specified set of streams. * The consumer task is suspended while there is no new data on all streams. * * @param set a stream descriptor set the task wants to poll * @pre set must not be empty (*set != NULL) * * @post The first element when iterating through the set after * LpelStreamPoll() will be the one after the one which * caused the task to wakeup, * i.e., the first stream where data arrived. */ lpel_stream_desc_t *LpelStreamPoll( lpel_streamset_t *set) { lpel_task_t *self; lpel_stream_iter_t *iter; int do_ctx_switch = 1; int cnt = 0; assert( *set != NULL); /* get 'self', i.e. the task calling LpelStreamPoll() */ (*set)->task = LpelTaskSelf(); self = (*set)->task; iter = LpelStreamIterCreate( set); /* fast path*/ while( LpelStreamIterHasNext( iter)) { lpel_stream_desc_t *sd = LpelStreamIterNext( iter); lpel_stream_t *s = sd->stream; if ( LpelBufferTop( &s->buffer) != NULL) { LpelStreamIterDestroy(iter); *set = sd; return sd; } } /* place a poll token */ atomic_store( &self->poll_token, 1); /* for each stream in the set */ LpelStreamIterReset(iter, set); while( LpelStreamIterHasNext( iter)) { lpel_stream_desc_t *sd = LpelStreamIterNext( iter); sd->task = LpelTaskSelf(); lpel_stream_t *s = sd->stream; /* lock stream (prod-side) */ PRODLOCK_LOCK( &s->prod_lock); { /* CS BEGIN */ /* check if there is something in the buffer */ if ( LpelBufferTop( &s->buffer) != NULL) { /* yes, we can stop iterating through streams. * determine, if we have been woken up by another producer: */ int tok = atomic_exchange( &self->poll_token, 0); if (tok) { /* we have not been woken yet, no need for ctx switch */ do_ctx_switch = 0; self->wakeup_sd = sd; } /* unlock stream */ PRODLOCK_UNLOCK( &s->prod_lock); /* exit loop */ break; } else { /* nothing in the buffer, register stream as activator */ s->is_poll = 1; cnt++; //sd->event_flags |= STDESC_WAITON; /* TODO marking all streams does potentially flood the log-files - is it desired to have anyway? MarkDirty( sd); */ } } /* CS END */ /* unlock stream */ PRODLOCK_UNLOCK( &s->prod_lock); } /* end for each stream */ /* context switch */ if (do_ctx_switch) { /* set task as blocked */ LpelTaskBlockStream( self); } assert( atomic_load( &self->poll_token) == 0); /* unregister activators * - would only be necessary, if the consumer task closes the stream * while the producer is in an is_poll state, * as this could result in a SEGFAULT when the producer * is trying to dereference sd->stream->cons_sd * - a consumer closes the stream if it reads * a terminate record or a sync record, and between reading the record * and closing the stream the consumer issues no LpelStreamPoll() * and no entity writes a record on the stream after these records. * UPDATE: with static/dynamc collectors in S-Net, this is possible! */ LpelStreamIterReset(iter, set); while( LpelStreamIterHasNext( iter)) { lpel_stream_t *s = (LpelStreamIterNext(iter))->stream; PRODLOCK_LOCK( &s->prod_lock); s->is_poll = 0; PRODLOCK_UNLOCK( &s->prod_lock); if (--cnt == 0) break; } LpelStreamIterDestroy(iter); /* 'rotate' set to stream descriptor for non-empty buffer */ *set = self->wakeup_sd; return self->wakeup_sd; }
/** * Blocking write to a stream * * If the stream is full, the task is suspended until the consumer * reads items from the stream, freeing space for more items. * * @param sd stream descriptor * @param item data item (a pointer) to write * @pre current task is single writer * @pre item != NULL */ void LpelStreamWrite( lpel_stream_desc_t *sd, void *item) { int poll_wakeup = 0; sd->task = LpelTaskSelf(); /* check if opened for writing */ assert( sd->mode == 'w' ); assert( item != NULL ); /* MONITORING CALLBACK */ #ifdef USE_TASK_EVENT_LOGGING if (sd->mon && MON_CB(stream_writeprepare)) { MON_CB(stream_writeprepare)(sd->mon, item); } #endif /* quasi P(e_sem) */ if ( atomic_fetch_sub( &sd->stream->e_sem, 1)== 0) { /* MONITORING CALLBACK */ #ifdef USE_TASK_EVENT_LOGGING if (sd->mon && MON_CB(stream_blockon)) { MON_CB(stream_blockon)(sd->mon); } #endif /* wait on stream: */ LpelTaskBlockStream( sd->task); } /* writing to the buffer and checking if consumer polls must be atomic */ PRODLOCK_LOCK( &sd->stream->prod_lock); { /* there must be space now in buffer */ assert( LpelBufferIsSpace( &sd->stream->buffer) ); /* put item into buffer */ LpelBufferPut( &sd->stream->buffer, item); if ( sd->stream->is_poll) { /* get consumer's poll token */ poll_wakeup = atomic_exchange( &sd->stream->cons_sd->task->poll_token, 0); sd->stream->is_poll = 0; } } PRODLOCK_UNLOCK( &sd->stream->prod_lock); /* quasi V(n_sem) */ if ( atomic_fetch_add( &sd->stream->n_sem, 1) < 0) { /* n_sem was -1 */ lpel_task_t *cons = sd->stream->cons_sd->task; /* wakeup consumer: make ready */ LpelTaskUnblock( sd->task, cons); /* MONITORING CALLBACK */ #ifdef USE_TASK_EVENT_LOGGING if (sd->mon && MON_CB(stream_wakeup)) { MON_CB(stream_wakeup)(sd->mon); } #endif } else { /* we are the sole producer task waking the polling consumer up */ if (poll_wakeup) { lpel_task_t *cons = sd->stream->cons_sd->task; cons->wakeup_sd = sd->stream->cons_sd; LpelTaskUnblock( sd->task, cons); /* MONITORING CALLBACK */ #ifdef USE_TASK_EVENT_LOGGING if (sd->mon && MON_CB(stream_wakeup)) { MON_CB(stream_wakeup)(sd->mon); } #endif } } /* MONITORING CALLBACK */ #ifdef USE_TASK_EVENT_LOGGING if (sd->mon && MON_CB(stream_writefinish)) { MON_CB(stream_writefinish)(sd->mon); } #endif }