static FFTW_WORKER worker(void *arg) { struct worker *ego = (struct worker *)arg; struct work *w; for (;;) { /* wait until work becomes available */ os_sem_down(&ego->ready); w = ego->w; /* !w->proc ==> terminate worker */ if (!w->proc) break; /* do the work */ w->proc(&w->d); /* signal that work is done */ os_sem_up(&ego->done); } /* termination protocol */ os_sem_up(&termination_semaphore); os_destroy_thread(); /* UNREACHABLE */ return 0; }
/** * Test scenario: * This is the same scenario as in scen5 but here we use mtx1 instead of mtx0. * This makes situation where mutexes are unlocked in the same order as locking * order, which additionally checks is if OS does not force the unlock sequence. * * L locks mtx0 and mtx1 * H locks mtx1 * L unlock mtx0 (critical moment) * We should see context switch to H even if L holds some other mtx * IMPORTANT: unlocking mtx0 before mtx1 creates same unlock order as locking * order. Additional check is that M should not be scheduled until H exits */ int test_scen6_workerH(void *OS_UNUSED(param)) { int ret; /* block on sem, allow L to progress */ ret = os_sem_down(&test_sem[1], OS_TIMEOUT_INFINITE); test_assert(0 == ret); test_assert(3 == test_atomic[0]); test_atomic[0] = 4; /* try to lock mtx0, we should switch to L */ ret = os_mtx_lock(&test_mtx[0]); test_assert(0 == ret); /* we should now get mtx0 locked in L and we have mtx1 */ test_assert(5 == test_atomic[0]); test_atomic[0] = 6; /* finish test we should switch to M */ os_mtx_unlock(&test_mtx[0]); test_assert(6 == test_atomic[0]); test_atomic[0] = 7; return 0; }
int test_scen4_workerHM(void *OS_UNUSED(param)) { int ret; /* block on sem, allow L to progress */ ret = os_sem_down(&test_sem[1], OS_TIMEOUT_INFINITE); test_assert(0 == ret); /* we will be woken up from L */ test_assert(5 == test_atomic[0]); test_atomic[0] = 6; /* lock mtx1, this will switch back to L */ ret = os_mtx_lock(&test_mtx[1]); test_assert(0 == ret); /* we should switch from H */ test_assert(14 == test_atomic[0]); test_atomic[0] = 15; /* prepare to finish task (no ctx switch this time) */ os_mtx_unlock(&test_mtx[1]); test_assert(15 == test_atomic[0]); test_atomic[0] = 16; /* by finishing the task we should switch to M */ return 0; }
static void* run(void* h) { DEBUGOUT("%p: logw__run", h); HANDLE; os_sem_down(); LOCK; while(1) { if (handle->shared.buf == NULL) { DEBUGOUT("%p: logw__run: i'm waiting...", h); WAIT; DEBUGOUT("%p: logw__run: i woke up", h); } assert (handle->shared.buf); int fd = handle->shared.fd; void* buf = handle->shared.buf; uint8_t len = handle->shared.len; handle->shared.buf = NULL; UNLOCK; hs_send(fd, (unsigned char*)buf, len); LOCK; cb_lock_acquire(); handle->callback->appendDone(handle, buf, len, false, SUCCESS); cb_lock_release(); } UNLOCK; return NULL; }
/** * Test scenario: * Recursive bounded priority inversion problem * Here we focus on os_unlock_mtx() implementation. We check if during call of * this function the task at the end of recursive locking chain gets the right * priority while prio_reset() operation. * * Task L locks mtx0 and mtx1 * Task M locks mtx2 and mtx0 * Task HM lock mtx1 * Task H locks mtx2 * Task L unlocks mtx1 (critical moment) * Test succeeds if during os_mtx_unlock() done by task L context is not * switched to task MH. */ int test_scen4_workerH(void *OS_UNUSED(param)) { int ret; /* block on sem, allow L to progress */ ret = os_sem_down(&test_sem[2], OS_TIMEOUT_INFINITE); test_assert(0 == ret); /* we will be woken up from L */ test_assert(7 == test_atomic[0]); test_atomic[0] = 8; /* lock mtx2, this will switch back to L */ ret = os_mtx_lock(&test_mtx[2]); test_assert(0 == ret); /* we should switch from M */ test_assert(12 == test_atomic[0]); test_atomic[0] = 13; /* prepare to finish task */ os_mtx_unlock(&test_mtx[2]); test_assert(13 == test_atomic[0]); test_atomic[0] = 14; /* by finishing the task we should switch to HM */ return 0; }
/** * test procedure for test1 task1 */ int test1_task_proc1(void *OS_UNUSED(param)) { int ret; ret = os_sem_down(&(worker_tasks[0].sem), OS_TIMEOUT_INFINITE); test_assert(0 == ret); return 0; }
/** * test procedure for os_sem_down with timeout */ int task_proc(void *param) { int ret; task_data_t *data = (task_data_t*)param; data->result = false; /* reseting the test result */ /* checking if sem have 0 */ ret = os_sem_down(&(data->sem), OS_TIMEOUT_TRY); test_assert(OS_WOULDBLOCK == ret); /* sleeping for task assigned time */ ret = os_sem_down(&(data->sem), data->idx); test_assert(OS_TIMEOUT == ret); data->result = true; return 0; }
/** * test procedure for test1 task2 */ int test1_task_proc2(void *OS_UNUSED(param)) { int ret; /* wait until timeout - (if still exist the bug will not update the priomax * while wakup by timeout) */ ret = os_sem_down(&(worker_tasks[0].sem), 10); test_assert(OS_TIMEOUT == ret); /* signalize the same sem to wake up the task1 */ os_sem_up(&(worker_tasks[0].sem)); return 0; }
int test_scen3_workerM(void *OS_UNUSED(param)) { int ret; /* block on sem, allow L to progress */ ret = os_sem_down(&test_sem[0], OS_TIMEOUT_INFINITE); test_assert(0 == ret); /* we should return here when H will finish */ test_assert(11 == test_atomic[0]); test_atomic[0] = 12; return 0; }
int task_proc(void* param) { int ret; unsigned idx = (unsigned)(size_t)param; while(task_data[idx].loop < TEST_CYCLES) { ++(task_data[idx].loop); os_sem_up(&(task_data[(idx + 1) % 2].sem)); ret = os_sem_down(&(task_data[idx].sem), OS_TIMEOUT_INFINITE); test_assert(0 == ret); } return 0; }
int test_scen4_workerM(void *OS_UNUSED(param)) { int ret; /* block on sem, allow L to progress */ ret = os_sem_down(&test_sem[0], OS_TIMEOUT_INFINITE); test_assert(0 == ret); /* we will return here from L */ test_assert(2 == test_atomic[0]); test_atomic[0] = 3; /* lock mtx2 */ ret = os_mtx_lock(&test_mtx[2]); test_assert(0 == ret); test_assert(3 == test_atomic[0]); test_atomic[0] = 4; /* lock mtx0 this will switch to L */ ret = os_mtx_lock(&test_mtx[0]); test_assert(0 == ret); /* we should switch from L */ test_assert(10 == test_atomic[0]); test_atomic[0] = 11; /* the effective priority of this task should still be on H level */ test_assert(4 == task_worker[2].prio_current); /* unlock mtx0, no context switch yet */ os_mtx_unlock(&test_mtx[0]); test_assert(11 == test_atomic[0]); test_atomic[0] = 12; /* unlock mtx2, we should switch to H */ os_mtx_unlock(&test_mtx[2]); /* we should return from HM */ test_assert(16 == test_atomic[0]); test_atomic[0] = 17; /* prepare to finish task */ /* by finishing the task we should switch to L */ return 0; }
int test_scen2_workerM(void *OS_UNUSED(param)) { int ret; /* M also need goes to sleep to allow L to lock the mutex, * after it will be woken up (simultaneously with L) it should not schedule * until L will unlock the mtx */ ret = os_sem_down(&test_sem[1], OS_TIMEOUT_INFINITE); test_assert(0 == ret); /* B should be scheduled after H * Verify that H finishes his task function */ test_assert(1 == test_atomic[0]); test_atomic[0] = 2; /* if test_assertion was meet this is the end of the test */ return 0; }
/** * Test scenario: * Classic priority inversion problem * Three tasks with different prio H (high prio) M (mid prio) L (low prio) * L locks the mtx as a first, then we force context switch to H which tries to * lock the same mtx. H should boost the prio of L and block on mtx. * Next L should be scheduled instead of M (which will happened it prio boost *will * not work). Sequence of scheduling is verified by tracking execution sequence * in global variable. */ int test_scen2_workerH(void *OS_UNUSED(param)) { int ret; /* we need to sleep to allow the L to lock the mutex */ ret = os_sem_down(&test_sem[0], OS_TIMEOUT_INFINITE); test_assert(0 == ret); /* try to lock the mtx * this should boost the prio of L since it already lock it */ ret = os_mtx_lock(&test_mtx[0]); test_assert(0 == ret); /* signalize that M can be scheduled now * finish the task */ test_atomic[0] = 1; return 0; }
int test_scen3_workerLM(void *OS_UNUSED(param)) { int ret; /* block on sem, allow LM and L to progress */ ret = os_sem_down(&test_sem[1], OS_TIMEOUT_INFINITE); test_assert(0 == ret); test_assert(2 == test_atomic[0]); test_atomic[0] = 3; /* lock the mtx0 then mtx1 (second will switch the context to L)*/ ret = os_mtx_lock(&test_mtx[0]); test_assert(0 == ret); test_assert(3 == test_atomic[0]); test_atomic[0] = 4; ret = os_mtx_lock(&test_mtx[1]); test_assert(0 == ret); /* we should return here when L will unlock mtx1 */ test_assert(8 == test_atomic[0]); test_atomic[0] = 9; /* also this means that LM should still have p(H) */ test_assert(4 == task_worker[2].prio_current); /* while L should have its original prio */ test_assert(1 == task_worker[3].prio_current); /* unlock mtx1 and mtx0, last one should cause switch to H */ os_mtx_unlock(&test_mtx[1]); /* no context switch */ test_assert(9 == test_atomic[0]); test_atomic[0] = 10; os_mtx_unlock(&test_mtx[0]); /* we should return here when H and M will finish */ test_assert(12 == test_atomic[0]); test_atomic[0] = 13; /* also this means that LM should have original prio */ test_assert(2 == task_worker[2].prio_current); return 0; }
int test_scen6_workerM(void *OS_UNUSED(param)) { int ret; /* block on sem, allow L to progress */ ret = os_sem_down(&test_sem[0], OS_TIMEOUT_INFINITE); test_assert(0 == ret); test_assert(2 == test_atomic[0]); test_atomic[0] = 3; /* switch context to H */ os_sem_up(&test_sem[1]); /* finish test */ test_assert(7 == test_atomic[0]); test_atomic[0] = 8; return 0; }
/** * Test scenario: * Recursive bounded priority inversion problem * To not duplicate description of the problem please refer to Comment 2 in * os_mtx.c * * Task L locks only mtx2 * Task LN locks both mtx1 and mtx2 * Task H locks only mtx1 * Task M is woken up (critical moment) * Test succeeds if task L is scheduled instead task M */ int test_scen3_workerH(void *OS_UNUSED(param)) { int ret; /* block on sem, allow M, LM and L to progress */ ret = os_sem_down(&test_sem[0], OS_TIMEOUT_INFINITE); test_assert(0 == ret); /* we should return here from L */ test_assert(5 == test_atomic[0]); test_atomic[0] = 6; /* try to lock mtx0, this will preempt and switch to L */ ret = os_mtx_lock(&test_mtx[0]); test_assert(0 == ret); /* we should return here when LM will unlock mtx0 */ test_assert(10 == test_atomic[0]); test_atomic[0] = 11; return 0; }