void run () { TScopedLock del_lock( _del_mutex ); while ( ! _end ) { _pool->append_idle( this ); { TScopedLock work_lock( _work_cond ); while (( _job == NULL ) && ! _end ) _work_cond.wait(); } if ( _job != NULL ) { _job->run( _data_ptr ); _job->unlock(); if ( _del_job ) delete _job; TScopedLock work_lock( _work_cond ); _job = NULL; _data_ptr = NULL; } } }
int work_queue(int qid, struct work_s *work, worker_t worker, void *arg, uint32_t delay) { struct wqueue_s *wqueue = &g_work[qid]; //DEBUGASSERT(work != NULL && (unsigned)qid < NWORKERS); /* First, initialize the work structure */ work->worker = worker; /* Work callback */ work->arg = arg; /* Callback argument */ work->delay = delay; /* Delay until work performed */ /* Now, time-tag that entry and put it in the work queue. This must be * done with interrupts disabled. This permits this function to be called * from with task logic or interrupt handlers. */ work_lock(qid); work->qtime = clock_systimer(); /* Time work queued */ dq_addlast((dq_entry_t *)work, &wqueue->q); #ifdef __PX4_QURT px4_task_kill(wqueue->pid, SIGALRM); /* Wake up the worker thread */ #else px4_task_kill(wqueue->pid, SIGCONT); /* Wake up the worker thread */ #endif work_unlock(qid); return PX4_OK; }
static int work_qcancel(FAR struct usr_wqueue_s *wqueue, FAR struct work_s *work) { int ret = -ENOENT; DEBUGASSERT(work != NULL); /* Get exclusive access to the work queue */ while (work_lock() < 0); /* Cancelling the work is simply a matter of removing the work structure * from the work queue. This must be done with interrupts disabled because * new work is typically added to the work queue from interrupt handlers. */ if (work->worker != NULL) { /* A little test of the integrity of the work queue */ DEBUGASSERT(work->dq.flink || (FAR dq_entry_t *)work == wqueue->q.tail); DEBUGASSERT(work->dq.blink || (FAR dq_entry_t *)work == wqueue->q.head); /* Remove the entry from the work queue and make sure that it is * mark as available (i.e., the worker field is nullified). */ dq_rem((FAR dq_entry_t *)work, &wqueue->q); work->worker = NULL; ret = OK; } work_unlock(); return ret; }
static int work_qqueue(FAR struct usr_wqueue_s *wqueue, FAR struct work_s *work, worker_t worker, FAR void *arg, uint32_t delay) { DEBUGASSERT(work != NULL); /* First, initialize the work structure */ work->worker = worker; /* Work callback */ work->arg = arg; /* Callback argument */ work->delay = delay; /* Delay until work performed */ /* Get exclusive access to the work queue */ while (work_lock() < 0); /* Now, time-tag that entry and put it in the work queue. */ work->qtime = clock_systimer(); /* Time work queued */ dq_addlast((FAR dq_entry_t *)work, &wqueue->q); kill(wqueue->pid, SIGWORK); /* Wake up the worker thread */ work_unlock(); return OK; }
static int work_qqueue(FAR struct usr_wqueue_s *wqueue, FAR struct work_s *work, worker_t worker, FAR void *arg, clock_t delay) { DEBUGASSERT(work != NULL); /* Get exclusive access to the work queue */ while (work_lock() < 0); /* Is there already pending work? */ if (work->worker != NULL) { /* Remove the entry from the work queue. It will re requeued at the * end of the work queue. */ dq_rem((FAR dq_entry_t *)work, &wqueue->q); } /* Initialize the work structure */ work->worker = worker; /* Work callback. non-NULL means queued */ work->arg = arg; /* Callback argument */ work->delay = delay; /* Delay until work performed */ /* Now, time-tag that entry and put it in the work queue. */ work->qtime = clock(); /* Time work queued */ dq_addlast((FAR dq_entry_t *)work, &wqueue->q); kill(wqueue->pid, SIGWORK); /* Wake up the worker thread */ work_unlock(); return OK; }
void work_process(FAR struct usr_wqueue_s *wqueue) { volatile FAR struct work_s *work; worker_t worker; FAR void *arg; uint32_t elapsed; uint32_t remaining; uint32_t stick; uint32_t ctick; uint32_t next; int ret; /* Then process queued work. Lock the work queue while we process items * in the work list. */ next = wqueue->delay; ret = work_lock(); if (ret < 0) { /* Break out earlier if we were awakened by a signal */ return; } /* Get the time that we started this polling cycle in clock ticks. */ stick = clock_systimer(); /* And check each entry in the work queue. Since we have locked the * work queue we know: (1) we will not be suspended unless we do * so ourselves, and (2) there will be no changes to the work queue */ work = (FAR struct work_s *)wqueue->q.head; while (work) { /* Is this work ready? It is ready if there is no delay or if * the delay has elapsed. qtime is the time that the work was added * to the work queue. It will always be greater than or equal to * zero. Therefore a delay of zero will always execute immediately. */ ctick = clock_systimer(); elapsed = ctick - work->qtime; if (elapsed >= work->delay) { /* Remove the ready-to-execute work from the list */ (void)dq_rem((struct dq_entry_s *)work, &wqueue->q); /* Extract the work description from the entry (in case the work * instance by the re-used after it has been de-queued). */ worker = work->worker; /* Check for a race condition where the work may be nullified * before it is removed from the queue. */ if (worker != NULL) { /* Extract the work argument (before unlocking the work queue) */ arg = work->arg; /* Mark the work as no longer being queued */ work->worker = NULL; /* Do the work. Unlock the the work queue while the work is being * performed... we don't have any idea how long this will take! */ work_unlock(); worker(arg); /* Now, unfortunately, since we unlocked the work queue we don't * know the state of the work list and we will have to start * back at the head of the list. */ ret = work_lock(); if (ret < 0) { /* Break out earlier if we were awakened by a signal */ return; } work = (FAR struct work_s *)wqueue->q.head; } else { /* Cancelled.. Just move to the next work in the list with * the work queue still locked. */ work = (FAR struct work_s *)work->dq.flink; } } else /* elapsed < work->delay */ { /* This one is not ready. * * NOTE that elapsed is relative to the the current time, * not the time of beginning of this queue processing pass. * So it may need an adjustment. */ elapsed += (ctick - stick); if (elapsed > work->delay) { /* The delay has expired while we are processing */ elapsed = work->delay; } /* Will it be ready before the next scheduled wakeup interval? */ remaining = work->delay - elapsed; if (remaining < next) { /* Yes.. Then schedule to wake up when the work is ready */ next = remaining; } /* Then try the next in the list. */ work = (FAR struct work_s *)work->dq.flink; } } /* Get the delay (in clock ticks) since we started the sampling */ elapsed = clock_systimer() - stick; if (elapsed < wqueue->delay && next > 0) { /* How must time would we need to delay to get to the end of the * sampling period? The amount of time we delay should be the smaller * of the time to the end of the sampling period and the time to the * next work expiry. */ remaining = wqueue->delay - elapsed; next = MIN(next, remaining); /* Wait awhile to check the work list. We will wait here until * either the time elapses or until we are awakened by a signal. * Interrupts will be re-enabled while we wait. */ usleep(next * USEC_PER_TICK); } work_unlock(); }
static void work_process(struct wqueue_s *wqueue, int lock_id) { volatile struct work_s *work; worker_t worker; void *arg; uint64_t elapsed; uint32_t remaining; uint32_t next; /* Then process queued work. We need to keep interrupts disabled while * we process items in the work list. */ next = CONFIG_SCHED_WORKPERIOD; work_lock(lock_id); work = (struct work_s *)wqueue->q.head; while (work) { /* Is this work ready? It is ready if there is no delay or if * the delay has elapsed. qtime is the time that the work was added * to the work queue. It will always be greater than or equal to * zero. Therefore a delay of zero will always execute immediately. */ elapsed = USEC2TICK(clock_systimer() - work->qtime); //printf("work_process: in ticks elapsed=%lu delay=%u\n", elapsed, work->delay); if (elapsed >= work->delay) { /* Remove the ready-to-execute work from the list */ (void)dq_rem((struct dq_entry_s *)work, &wqueue->q); /* Extract the work description from the entry (in case the work * instance by the re-used after it has been de-queued). */ worker = work->worker; arg = work->arg; /* Mark the work as no longer being queued */ work->worker = NULL; /* Do the work. Re-enable interrupts while the work is being * performed... we don't have any idea how long that will take! */ work_unlock(lock_id); if (!worker) { PX4_WARN("MESSED UP: worker = 0\n"); } else { worker(arg); } /* Now, unfortunately, since we re-enabled interrupts we don't * know the state of the work list and we will have to start * back at the head of the list. */ work_lock(lock_id); work = (struct work_s *)wqueue->q.head; } else { /* This one is not ready.. will it be ready before the next * scheduled wakeup interval? */ /* Here: elapsed < work->delay */ remaining = USEC_PER_TICK * (work->delay - elapsed); if (remaining < next) { /* Yes.. Then schedule to wake up when the work is ready */ next = remaining; } /* Then try the next in the list. */ work = (struct work_s *)work->dq.flink; } } /* Wait awhile to check the work list. We will wait here until either * the time elapses or until we are awakened by a signal. */ work_unlock(lock_id); usleep(next); }