blob: 2a7f1a519058efb7115d5c81dacee88607ca27e9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* The taskpool module implements a reusable task pool.
*/
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "os/mynewt.h"
#include "taskpool/taskpool.h"
#define TASKPOOL_STATE_UNUSED 0
#define TASKPOOL_STATE_ACTIVE 1
#define TASKPOOL_STATE_DONE 2
/** Ensures thread safety across the taskpool API. */
static struct os_mutex taskpool_mtx;
/** Signals completion of final task to waiters. */
static struct os_sem taskpool_wait_sem;
/** The number of tasks waiting for all taskpool tasks to complete. */
static int taskpool_waiter_count;
/** Represents a single taskpool task. */
struct taskpool_entry {
OS_TASK_STACK_DEFINE_NOSTATIC(stack, MYNEWT_VAL(TASKPOOL_STACK_SIZE));
os_task_func_t fn;
struct os_task task;
uint8_t state;
char name[sizeof "taskXX"];
};
static struct taskpool_entry taskpool_entries[MYNEWT_VAL(TASKPOOL_NUM_TASKS)];
static void
taskpool_lock(void)
{
int rc;
rc = os_mutex_pend(&taskpool_mtx, OS_TIMEOUT_NEVER);
assert(rc == 0 || rc == OS_NOT_STARTED);
}
static void
taskpool_unlock(void)
{
int rc;
rc = os_mutex_release(&taskpool_mtx);
assert(rc == 0 || rc == OS_NOT_STARTED);
}
static bool
taskpool_locked(void)
{
struct os_task *owner;
owner = taskpool_mtx.mu_owner;
return owner != NULL && owner == os_sched_get_current_task();
}
#define TASKPOOL_ASSERT_LOCKED() do \
{ \
if (os_started()) { \
assert(taskpool_locked()); \
} \
} while (0)
static int
taskpool_find_state(uint8_t state)
{
int i;
TASKPOOL_ASSERT_LOCKED();
for (i = 0; i < MYNEWT_VAL(TASKPOOL_NUM_TASKS); i++) {
if (taskpool_entries[i].state == state) {
return i;
}
}
return -1;
}
static void
taskpool_wrapper(void *arg)
{
struct taskpool_entry *entry;
entry = arg;
/* Execute wrapped task handler. */
entry->fn(NULL);
taskpool_lock();
/* Mark task done. */
entry->state = TASKPOOL_STATE_DONE;
/* If this was the last running task, signal that the test is complete. */
if (taskpool_find_state(TASKPOOL_STATE_ACTIVE) == -1) {
while (taskpool_waiter_count > 0) {
os_sem_release(&taskpool_wait_sem);
taskpool_waiter_count--;
}
}
taskpool_unlock();
/* Block forever. This task can now be collected with os_task_remove(). */
while (1) {
os_time_delay(OS_STIME_MAX);
}
}
int
taskpool_alloc(os_task_func_t task_handler, uint8_t prio,
struct os_task **out_task)
{
struct taskpool_entry *entry;
int idx;
int rc;
taskpool_lock();
idx = taskpool_find_state(TASKPOOL_STATE_UNUSED);
if (idx != -1) {
entry = &taskpool_entries[idx];
entry->state = TASKPOOL_STATE_ACTIVE;
}
taskpool_unlock();
if (idx == -1) {
return SYS_ENOMEM;
}
entry->fn = task_handler;
snprintf(entry->name, sizeof entry->name, "task%02d", idx);
rc = os_task_init(&entry->task, entry->name, taskpool_wrapper,
entry, prio, OS_WAIT_FOREVER, entry->stack,
OS_STACK_ALIGN(MYNEWT_VAL(TASKPOOL_STACK_SIZE)));
if (rc != 0) {
taskpool_lock();
entry->state = TASKPOOL_STATE_UNUSED;
taskpool_unlock();
return rc;
}
*out_task = &entry->task;
return 0;
}
struct os_task *
taskpool_alloc_assert(os_task_func_t task_handler, uint8_t prio)
{
struct os_task *task;
int rc;
rc = taskpool_alloc(task_handler, prio, &task);
assert(rc == 0);
return task;
}
static void
taskpool_reset(void)
{
int rc;
int i;
taskpool_lock();
for (i = 0; i < MYNEWT_VAL(TASKPOOL_NUM_TASKS); i++) {
if (taskpool_entries[i].state != TASKPOOL_STATE_UNUSED) {
rc = os_task_remove(&taskpool_entries[i].task);
assert(rc == OS_OK);
taskpool_entries[i].state = TASKPOOL_STATE_UNUSED;
}
}
taskpool_unlock();
}
int
taskpool_wait(os_time_t max_ticks)
{
int any_tasks;
int rc;
taskpool_lock();
any_tasks = taskpool_find_state(TASKPOOL_STATE_ACTIVE) != -1;
if (any_tasks) {
taskpool_waiter_count++;
}
taskpool_unlock();
if (!any_tasks) {
/* No active tasks; nothing to wait on. */
return 0;
}
rc = os_sem_pend(&taskpool_wait_sem, max_ticks);
if (rc != 0) {
return rc;
}
/* Collect all the completed taskpool tasks. */
taskpool_reset();
return 0;
}
void
taskpool_wait_assert(os_time_t max_ticks)
{
int rc;
rc = taskpool_wait(max_ticks);
assert(rc == 0);
}
void
taskpool_init(void)
{
int rc;
int i;
/* Ensure this function only gets called by sysinit. */
SYSINIT_ASSERT_ACTIVE();
rc = os_mutex_init(&taskpool_mtx);
SYSINIT_PANIC_ASSERT(rc == 0 || rc == OS_NOT_STARTED);
rc = os_sem_init(&taskpool_wait_sem, 0);
SYSINIT_PANIC_ASSERT(rc == 0 || rc == OS_NOT_STARTED);
for (i = 0; i < MYNEWT_VAL(TASKPOOL_NUM_TASKS); i++) {
taskpool_entries[i].state = TASKPOOL_STATE_UNUSED;
}
}