/** * Creates a new message spscq. * @param name Name of the new spscq interface * @param max_msg Positive integer. Maximum number of messages that can be inserted in the spscq * @param msg_sz Positive integer. Maximum message size * @return non-null value on success, zero on error */ r_itf_t rtdal_itfspscq_new(int max_msg, int max_msg_sz, int delay) { hdebug("max_msg=%d,max_msg_sz=%d\n",max_msg,max_msg_sz); assert(context); RTDAL_ASSERT_PARAM_P(max_msg>=0); RTDAL_ASSERT_PARAM_P(max_msg_sz>=0); int i; pthread_mutex_lock(&context->mutex); for (i=0;i<MAX(rtdal_itfspscq);i++) { if (!context->spscqs[i].parent.id) break; } if (i == MAX(rtdal_itfspscq)) { RTDAL_SETERROR(RTDAL_ERROR_NOTFOUND); return NULL; } hdebug("i=%d\n",i); context->spscqs[i].max_msg = max_msg; context->spscqs[i].max_msg_sz = max_msg_sz; context->spscqs[i].parent.delay = delay; context->spscqs[i].parent.id = i+1; if (rtdal_itfspscq_init(&context->spscqs[i])) { context->spscqs[i].parent.id = 0; pthread_mutex_unlock(&context->mutex); return NULL; } pthread_mutex_unlock(&context->mutex); return (r_itf_t) &context->spscqs[i]; }
int rtdal_itfspscq_recv(r_itf_t obj, void* buffer, int len) { RTDAL_ASSERT_PARAM(buffer); RTDAL_ASSERT_PARAM(len>=0); int n, plen; void *ptr; if ((n = rtdal_itfspscq_pop(obj, &ptr, &plen, rtdal_time_slot())) != 1) { return n; } if (plen > len) { plen = len; } hdebug("obj=0x%x, rcv pkt=0x%x\n",obj,ptr); memcpy(buffer, ptr, (size_t) plen); if ((n = rtdal_itfspscq_release(obj)) == 1) { return n; } hdebug("release pkt 0x%x\n",ptr); return plen; }
inline static void pipeline_run_time_slot(pipeline_t *obj) { int idx; rtdal_process_t *run_proc; hdebug("pipeid=%d, tslot=%d, nof_process=%d thread=%d\n",obj->id,obj->ts_counter, obj->nof_processes, obj->thread); obj->finished = 0; pipeline_run_thread_print_time(obj); run_proc = obj->first_process; idx = 0; while(run_proc) { hdebug("%d/%d: run=%d code=%d next=0x%x\n",idx,obj->nof_processes,run_proc->runnable, run_proc->finish_code,run_proc->next); if (idx > obj->nof_processes) { aerror_msg("Fatal error. Corrupted pipeline-%d process list at process %d\n", obj->id, idx); kill(getpid(),SIGTERM); pthread_exit(NULL); } pipeline_run_thread_check_status(obj,run_proc); pipeline_run_thread_run_module(obj,run_proc, idx); run_proc = run_proc->next; idx++; } obj->ts_counter++; obj->finished = 1; }
/** * Removes the process proc from the pipeline pointed by obj. * @param obj Pointer to the pipeline object * @param proc Pointer to the process to remove * @return Zero on success, -1 on error */ int pipeline_remove(pipeline_t *obj, rtdal_process_t *proc) { hdebug("pipeid=%d, nof_process=%d, pid=%d, pid_pos=%d\n",obj->id,obj->nof_processes, proc->pid,proc->attributes.exec_position); RTDAL_ASSERT_PARAM(obj); RTDAL_ASSERT_PARAM(proc); rtdal_process_t *cur, *prev; prev = NULL; cur = obj->first_process; while(cur != proc && cur) { hdebug("pipeid=%d, prev=0x%x, cur=0x%x\n", obj->id, prev,cur); prev = cur; cur = cur->next; } if (!cur) { RTDAL_SETERROR(RTDAL_ERROR_NOTFOUND); return -1; } if (prev) { hdebug("pipeid=%d remove middle/end\n",obj->id); prev->next = cur->next; } else { hdebug("pipeid=%d remove first\n",obj->id); obj->first_process = cur->next; } obj->nof_processes--; proc->next = NULL; return 0; }
/** * Creates a new process to be executed by one of the kernel pipelines. * The structure pointed by attr is defined in rtdal_process_attr. * * rtdal_process_new() configures the process parameters and loads it into the * pipeline. The process won't execute until a success call to rtdal_process_run() * is performed. * * The address pointed by arg will be passed to the process as a parameter. * * Notes: The shared library must conform with the rtdal_process_t requirements. * * @param attr Configures the process attributes * @param arg Argument to pass to the process each execution cycle * @return non-null value on success, zero on error */ r_proc_t rtdal_process_new(struct rtdal_process_attr *attr, void *arg) { hdebug("binary=%s, proc=%d, pos=%d, finish=0x%x, arg=0x%x\n",attr->binary_path,attr->pipeline_id, attr->exec_position,attr->finish_callback,arg); assert(context); RTDAL_ASSERT_PARAM_P(attr); RTDAL_ASSERT_PARAM_P(arg); pthread_mutex_lock(&context->mutex); int i=0; /* find empty space on process db */ for (i=0;i<MAX(rtdal_process);i++) { if (!context->processes[i].pid) break; } if (i == MAX(rtdal_process)) { RTDAL_SETERROR(RTDAL_ERROR_NOSPACE); goto out; } if (attr->process_group_id > MAX_PROCESS_GROUP_ID) { RTDAL_SETERROR(RTDAL_ERROR_NOSPACE); goto out; } if (attr->process_group_id < 0) { RTDAL_SETERROR(RTDAL_ERROR_INVAL); goto out; } pgroup_notified_failure[attr->process_group_id] = 0; memset(&context->processes[i],0,sizeof(rtdal_process_t)); context->processes[i].pid=i+1; hdebug("i=%d, pid=%d\n",context->processes[i].pid); memcpy(&context->processes[i].attributes, attr, sizeof(struct rtdal_process_attr)); context->processes[i].arg = arg; context->processes[i].finish_code = FINISH_OK; if (pipeline_add(&context->pipelines[attr->pipeline_id], &context->processes[i]) == -1) { goto out; } if (rtdal_process_launch(&context->processes[i])) { pipeline_remove(&context->pipelines[attr->pipeline_id], &context->processes[i]); goto out; } pthread_mutex_unlock(&context->mutex); return (r_proc_t) &context->processes[i]; out: pthread_mutex_unlock(&context->mutex); return NULL; }
/** * Loads a process binary into memory. The process must have been created using * rtdal_process_new(). This function loads the library defined in the process * attributes during the call to rtdal_process_new(). * @param obj Pointer to the rtdal_process_t object. * @return Zero on success, -1 on error. */ int rtdal_process_launch(rtdal_process_t *obj) { hdebug("path=%s\n",obj->attributes.binary_path); RTDAL_ASSERT_PARAM(obj); char *error; snprintf(tmp,LSTR_LEN,"/tmp/am_%d.so",obj->pid); snprintf(tmp2,LSTR_LEN,"cp %s/%s %s",libs_path,obj->attributes.binary_path,tmp); if (system(tmp2) == -1) { aerror("Error removing file\n"); return -1; } obj->dl_handle = dlopen(tmp,RTLD_NOW); if (!obj->dl_handle) { RTDAL_DLERROR(dlerror()); return -1; } dlerror(); *(void**) (&obj->run_point) = dlsym(obj->dl_handle, "_run_cycle"); if ((error = dlerror()) != NULL) { RTDAL_DLERROR(error); return -1; } return 0; }
/** * Creates a new low-priority periodic function. If it succeeds, the function callback * will be called every period timeslots with low priority. * * @param callback Pointer to the periodic function * @param period Positive integer, in time slots * @return zero on success, -1 on error */ int rtdal_periodic_add(void (*callback)(void), int period) { assert(context); RTDAL_ASSERT_PARAM(callback); RTDAL_ASSERT_PARAM(period>0); pthread_mutex_lock(&context->mutex); int i; for (i=0;i<MAX(rtdal_periodic);i++) { if (!context->periodic[i].callback) break; } if (i == MAX(rtdal_periodic)) { RTDAL_SETERROR(RTDAL_ERROR_NOSPACE); pthread_mutex_unlock(&context->mutex); return -1; } context->periodic[i].counter = 0; context->periodic[i].period = period; context->periodic[i].callback = callback; pthread_mutex_unlock(&context->mutex); hdebug("i=%d, period=%d, callback=0x%x\n",i,period,callback); return 0; }
/** * Disables the execution of the process identified by the first argument. * The process must have been previously loaded using rtdal_process_new(). * @param process Process handler given by rtdal_process_new() * @returns zero on success, -1 on error */ int rtdal_process_stop(r_proc_t process) { RTDAL_ASSERT_PARAM(process); rtdal_process_t *obj = (rtdal_process_t*) process; hdebug("pid=%d\n",obj->pid); obj->runnable = 0; return 0; }
/** * Sets an error code for a process. * \param proc Process handler given by rtdal_process_new() * \returns zero on success, -1 on error */ int rtdal_process_seterror(r_proc_t proc, rtdal_processerrors_t code) { RTDAL_ASSERT_PARAM(proc); rtdal_process_t *obj = (rtdal_process_t*) proc; hdebug("pid=%d, code=%d\n",obj->pid,(int)code); obj->finish_code = code; return 0; }
/** * Runs one process after another (calling process[i].run_point()) and then * sleeps waiting for the semaphore */ void *pipeline_run_thread(void *self) { assert(self); pipeline_t *obj = (pipeline_t*) self; assert(obj->id>=0); hdebug("pipeid=%d waiting\n",obj->id); obj->stop = 0; pipeline_sync_thread_waits(obj->id); hdebug("pipeid=%d start\n",obj->id); while(!obj->stop) { pipeline_run_time_slot(obj); pipeline_sync_thread_waits(obj->id); } hdebug("pipeid=%d exiting\n",obj->id); return NULL; }
/** sleep the calling thread for the time specified by the time_t structure. */ int rtdal_sleep(time_t *t) { assert(context); RTDAL_ASSERT_PARAM(t); hdebug("sleep_for=%d:%d\n",t->tv_sec,t->tv_usec); struct timespec sleep; sleep.tv_sec = t->tv_sec; sleep.tv_nsec = t->tv_usec*1000; if (clock_nanosleep(CLOCK_REALTIME,0,&sleep,NULL)) { RTDAL_SYSERROR("clock_nanosleep"); return -1; } hdebug("waking up at %d\n",rtdal_time_slot()); return 0; }
/** * Returns a handler to the first physical interface matching the id. * @param id Id of the physical interface * @return non-null value on success, zero on error */ r_itf_t rtdal_itfphysic_get_id(int id) { hdebug("id=%d\n",id); assert(context); RTDAL_ASSERT_PARAM_P(id); int i; for (i=0;i<MAX(rtdal_itfphysic);i++) { if (context->physic_itfs[i].parent.id == id) break; } hdebug("i=%d\n",i); if (i == MAX(rtdal_itfphysic)) { RTDAL_SETERROR(RTDAL_ERROR_NOTFOUND); return NULL; } return (r_itf_t) &context->physic_itfs[i]; }
/** * Returns a handler to the first physical interface matching the name. * @param name Name of the physical interface * @return non-null value on success, zero on error */ r_itf_t rtdal_itfphysic_get(string name) { hdebug("name=%s\n",name); assert(context); RTDAL_ASSERT_PARAM_P(name); int i; for (i=0;i<MAX(rtdal_itfphysic);i++) { if (!strcmp(context->physic_itfs[i].parent.name, name)) break; } hdebug("i=%d\n",i); if (i == MAX(rtdal_itfphysic)) { RTDAL_SETERROR(RTDAL_ERROR_NOTFOUND); return NULL; } return (r_itf_t) &context->physic_itfs[i]; }
inline static void pipeline_run_thread_check_status(pipeline_t *pipe, rtdal_process_t *proc) { hdebug("pipeid=%d, pid=%d, run=%d, code=%d, waveform_notify=%d\n",pipe->id,proc->pid, proc->runnable,proc->finish_code, pgroup_notified_failure[proc->attributes.process_group_id]); if (proc->runnable && proc->finish_code != FINISH_OK && !pgroup_notified_failure[proc->attributes.process_group_id]) { if (proc->attributes.finish_callback) { hdebug("calling finish 0x%x arg=0x%x\n",proc->attributes.finish_callback, proc->arg); pgroup_notified_failure[proc->attributes.process_group_id] = 1; rtdal_task_new(NULL, proc->attributes.finish_callback,proc->arg); } else { aerror_msg("Abnormal pid=%d termination but no callback was defined\n", proc->pid); } } }
/** * Adds a process to the pipeline. It is inserted in the position * min(n,exec_position) where n is the number of * elements in the spscq and exec_position is defined in the process attributes * used in the call to rtdal_process_new(). * Returns the position it has finally been inserted. * * @param obj Pointer to the pipeline_t object where the process is inserted * @param process pointer to the rtdal_process_t object to insert. * @returns non-negative integer number indicating the position it has been * inserted, or -1 on error. */ int pipeline_add(pipeline_t *obj, rtdal_process_t *process) { hdebug("pipeid=%d, nof_process=%d, pid=%d, exec_pos=%d\n",obj->id,obj->nof_processes, process->pid,process->attributes.exec_position); RTDAL_ASSERT_PARAM(obj); RTDAL_ASSERT_PARAM(process); int exec_pos, i; rtdal_process_t *p = NULL; exec_pos = process->attributes.exec_position; /* head because empty list */ if (!obj->first_process) { hdebug("pipeid=%d add pid=%d to head\n", obj->id, process->pid); obj->first_process = process; process->next = NULL; goto end; } /* head because first exec position */ if (exec_pos < obj->first_process->attributes.exec_position) { hdebug("pipeid=%d add pid=%d to head\n", obj->id, process->pid); process->next = obj->first_process; obj->first_process = process; goto end; } /* middle */ i=0; p = obj->first_process; while(p->next && exec_pos < p->next->attributes.exec_position) { p=p->next; i++; } process->next = p->next; p->next = process; hdebug("pipeid=%d, add pid=%d to pos=%d\n", obj->id, process->pid,i); end: obj->nof_processes++; /* assign pipeline to object */ process->pipeline = obj; return i; }
/** * Acknowledges that the process group error notification has been processed, enabling another * future call to the finish_callback function. * \returns 0 on success, -1 on error */ int rtdal_process_group_notified(r_proc_t proc) { RTDAL_ASSERT_PARAM(proc); rtdal_process_t *obj = (rtdal_process_t*) proc; hdebug("pid=%d, running=%d\n",obj->pid,obj->runnable); if (obj->attributes.process_group_id < 0 || obj->attributes.process_group_id > MAX_PROCESS_GROUP_ID) { RTDAL_SETERROR(RTDAL_ERROR_INVAL); return -1; } pgroup_notified_failure[obj->attributes.process_group_id] = 0; return 0; }
void pipeline_run_from_timer(void *arg, struct timespec *time) { pipeline_t *obj = (pipeline_t*) arg; time_t realtime; #ifdef KERNEL_DEB_TIME rtdal_time_get(&realtime); hdebug("now is %d:%d\n",realtime.tv_sec,realtime.tv_usec); #else hdebug("now is %d:%d\n",time->tv_sec,time->tv_nsec); #endif if (!timer_first_cycle && time) { rtdal_time_reset_realtime(time); timer_first_cycle = 1; } if (!is_first_in_cycle()) { kernel_tslot_run(); } pipeline_run_time_slot(obj); }
/** Called from the sigwait kernel thread after a pipeline thread has died. * Set the process that caused the fault as non-runnable and create a new pipeline thread. */ int pipeline_recover_thread(pipeline_t *obj) { hdebug("pipeline_id=%d\n",obj->id); obj->finished = 1; if (rtdal_process_seterror((r_proc_t) obj->running_process, SIG_RECV)) { aerror("setting process error\n"); return -1; } if (kernel_initialize_create_pipeline(obj, NULL)) { aerror("creating pipeline thread\n"); return -1; } return 0; }
int rtdal_itfspscq_send(r_itf_t obj, void* buffer, int len) { cast(obj,itf); RTDAL_ASSERT_PARAM(buffer); RTDAL_ASSERT_PARAM(len>=0); int n; void *ptr; if (len > itf->max_msg_sz) { RTDAL_SETERROR(RTDAL_ERROR_LARGE); return -1; } hdebug("requesting pkt for 0x%x\n",obj); if ((n = rtdal_itfspscq_request(obj, &ptr)) != 1) { return n; } memcpy(ptr, buffer, (size_t) len); hdebug("put pkt for 0x%x pkt 0x%x\n",obj,ptr); return rtdal_itfspscq_push(obj,len,rtdal_time_slot()); }
inline static void pipeline_run_thread_run_module(pipeline_t *pipe, rtdal_process_t *proc, int idx) { hdebug("pipeid=%d, pid=%d, idx=%d, run=%d\n",pipe->id,proc->pid,idx,proc->runnable); if (proc->runnable) { pipe->running_process = proc; pipe->running_process_idx = idx; proc->is_running = 1; if (proc->run_point(proc->arg)) { aerror_msg("Error running module %d:%d\n", pipe->id,pipe->running_process_idx); pipeline_remove(pipe,proc); proc->is_running = 0; } proc->is_running = 0; } }
/** Called when there is an rtfault in the pipeline */ int pipeline_rt_fault(pipeline_t *obj) { #ifdef KILL_RT_FAULT hdebug("pipeline_id=%d, process_idx=%d\n",obj->id,obj->running_process_idx,obj->running_process_idx); obj->finished = 1; aerror_msg("RT-Fault detected at pipeline %d, process %d\n",obj->id, obj->running_process_idx); if (obj->thread) { int s = pthread_kill(obj->thread,SIGUSR1); if (s) { rtdal_POSERROR(s, "pthread_kill"); return -1; } } #else obj->finished = 1; printf("+++[ts=%d]+++ RT-Fault: Process %d/%d still running in pipeline %d\n", obj->ts_counter, obj->running_process_idx, obj->nof_processes, obj->id); #endif return 0; }
/** * Removes the function pointed by callback from the kernel periodic callback functions, * previously added with rtdal_periodic_add(). * * @param callback Pointer to the periodic function * @returns zero on success, -1 on error */ int rtdal_periodic_remove(void (*callback)(void)) { assert(context); RTDAL_ASSERT_PARAM(callback); int i; pthread_mutex_lock(&context->mutex); for (i=0;i<MAX(rtdal_periodic);i++) { if (context->periodic[i].callback == callback) break; } if (i == MAX(rtdal_periodic)) { RTDAL_SETERROR(RTDAL_ERROR_NOTFOUND); return -1; } context->periodic[i].counter = 0; context->periodic[i].period = 0; context->periodic[i].callback = NULL; pthread_mutex_unlock(&context->mutex); hdebug("i=%d\n",i); return 0; }
/** * This is a thread with priority kernel_prio that synchronously waits for * rtdal_pipeline signals (usign sigwaitinfo). All signals except thread-specific * ones (SIGSEGV,SIGILL,SIGBUS,SIGFPE) are blocked by all threads except this one. * Thread-specific signals are handled by ProcThreads which send a SIGRTMIN+1, * SIGRTMIN+2,SIGRTMIN+3,SIGRTMIN+4 (respectively) to this thread, which takes * actions accordingly. * * for signals SIGRTMIN to SIGRTMIN+4, cast the rtdal_pipeline object from this * si_value pointer and call rtdal_pipeline_recover_thread(pipeline, * pipeline->running_process, TRUE) */ void sigwait_loop(void) { int signum; sigset_t set; siginfo_t info; sigfillset(&set); sigdelset(&set,TASK_TERMINATION_SIGNAL); while(!sigwait_stops) { do { signum = sigwaitinfo(&set, &info); } while (signum == -1 && errno == EINTR); if (signum == -1) { poserror(errno, "sigwaitinfo"); goto out; } hdebug("detected signal %d\n",signum); if (signum == KERNEL_SIG_THREAD_SPECIFIC) { printf("[rtdal]: Caught thread-specific signal\n"); #ifdef EXIT_ON_THREADSIG fflush(stdout); goto out; #else sigwait_loop_process_thread_signal(&info); #endif } else if (signum == SIGINT) { printf("Caught SIGINT, exiting\n"); fflush(stdout); goto out; } else if (signum != SIGWINCH && signum != SIGCHLD) { printf("Got signal %d, exiting\n", signum); fflush(stdout); goto out; } } out: kernel_exit(); }
static int sigwait_loop_process_thread_signal(siginfo_t *info) { int thread_id, signum; signum = info->si_value.sival_int & 0xffff; thread_id = info->si_value.sival_int >> 16; hdebug("signum=%d, thread_id=%d\n",signum,thread_id); if (signum < N_THREAD_SPECIFIC_SIGNALS) { snprintf(tmp_msg, STR_LEN, "[rtdal]: Got signal %s from ", thread_specific_signals_name[signum]); } else { snprintf(tmp_msg, STR_LEN, "[rtdal]: Got unknown signal from "); } /* now try to restore the pipeline, if the thread was a pipeline */ if (thread_id > -1) { if (strnlen(tmp_msg,STR_LEN)>1) { snprintf(&tmp_msg[strlen(tmp_msg)-1],STR_LEN, " pipeline thread %d. Current running process idx is %d\n", thread_id, rtdal.pipelines[thread_id].running_process_idx); } #ifdef RECOVER_THREAD if (pipeline_recover_thread( &rtdal.pipelines[thread_id])) { aerror("recovering pipeline thread\n"); } #endif } else if (info->si_value.sival_int == -1) { strcat(tmp_msg, " the kernel thread\n"); kernel_timer_recover_thread(); } else { strcat(tmp_msg, " an unkown thread\n"); } write(0,tmp_msg,strlen(tmp_msg)); return 1; }
void pipeline_initialize(int _num_pipelines) { hdebug("num_pipelines=%d\n",_num_pipelines); num_pipelines = _num_pipelines; pipeline_sync_initialize_; }
/** * Returns 1 if the process is running or zero otherwise */ int rtdal_process_isrunning(r_proc_t proc) { RTDAL_ASSERT_PARAM(proc); rtdal_process_t *obj = (rtdal_process_t*) proc; hdebug("pid=%d, running=%d\n",obj->pid,obj->runnable); return obj->is_running; }
/** * Handler for thread-specific signals (SIGSEGV,SIGILL,SIGFPE,SIGBUS). * Forwards a signal above SIGRTMIN to myself. Since it is blocked, it will * be received by sigwait_loop(), which is runs in the main kernel thread. * * The thread terminates after exiting the handler. */ void thread_signal_handler(int signum, siginfo_t *info, void *ctx) { union sigval value; int thread_id; int i; #ifdef PRINT_BT_ON_SIGSEGV if (signum == SIGSEGV) { signal_segv(signum,info,ctx); } #endif signal_received++; thread_id = -1; hdebug("[ts=%d] signal %d received\n",rtdal_time_slot(),signum); /* try to find the thread that caused the signal */ /** todo: Caution!! is pthread_self() safe in the handler? * it is not async-signal-safe by the standard, * but the signals are synchronous. */ pthread_t thisthread = pthread_self(); /* if signum is SIGUSR2, its a task termination signal, just exit */ if (signum == TASK_TERMINATION_SIGNAL) { hdebug("sigusr2 signal. thread=%d\n",thisthread); goto cancel_and_exit; } /* is it a pipeline thread? */ for (i=0;i<rtdal.machine.nof_cores;i++) { if (thisthread == rtdal.pipelines[i].thread) { break; } } if (i < rtdal.machine.nof_cores) { hdebug("pipeline_idx=%d\n",i); thread_id = i; /* set the thread to 0 because is terminating */ rtdal.pipelines[thread_id].thread = 0; } else { /* it is not, may be it is the kernel timer */ if (thisthread == single_timer_thread) { hdebug("timer thread=%d\n",thisthread); thread_id = -1; } else { /* @TODO: check if it is a status or init thread of any module */ hdebug("other thread=%d\n",thisthread); goto cancel_and_exit; } } /* Now send a signal to the kernel */ for (i=0;i<N_THREAD_SPECIFIC_SIGNALS;i++) { if (thread_specific_signals[i] == signum) break; } hdebug("signal=%d, thread=%d\n",i,thread_id); value.sival_int = thread_id<<16 | i; if (sigqueue(kernel_pid, KERNEL_SIG_THREAD_SPECIFIC, value)) { poserror(errno, "sigqueue"); } cancel_and_exit: if (signum != SIGABRT || thread_id == -1) { pthread_exit(NULL); } else { rtdal.pipelines[thread_id].waiting=1; while(rtdal.pipelines[thread_id].waiting) { hdebug("waiting\n",0); usleep(1000); } } }