You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2019/04/06 00:51:23 UTC

[mynewt-core] branch master updated: util/taskpool: New package

This is an automated email from the ASF dual-hosted git repository.

ccollins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git


The following commit(s) were added to refs/heads/master by this push:
     new b4b8865  util/taskpool: New package
b4b8865 is described below

commit b4b886519540e1770e0af9b625d7279b3e95f92b
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Mon Mar 25 09:54:47 2019 -0700

    util/taskpool: New package
    
    This package allows you to create and remove generic tasks at runtime.
    The initial use case is multithreaded unit tests, but this package can
    be used in other applications as well.
    
    This package creates a single global task pool.  Each allocated task
    uses the same size stack.  The task count and stack size settings are
    specified via syscfg.
    
    It might be useful to allow multiple task pools and / or variable stack
    sizes.  This would add quite a bit of complexity to the API, and I did
    not deem it useful enough to justify the expense.  If we need these
    features, we can add them to the API later.
---
 util/taskpool/include/taskpool/taskpool.h |  84 ++++++++++
 util/taskpool/pkg.yml                     |  28 ++++
 util/taskpool/src/taskpool.c              | 270 ++++++++++++++++++++++++++++++
 util/taskpool/syscfg.yml                  |  25 +++
 4 files changed, 407 insertions(+)

diff --git a/util/taskpool/include/taskpool/taskpool.h b/util/taskpool/include/taskpool/taskpool.h
new file mode 100644
index 0000000..3c37d40
--- /dev/null
+++ b/util/taskpool/include/taskpool/taskpool.h
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#ifndef H_TASKPOOL_
+#define H_TASKPOOL_
+
+#include "os/mynewt.h"
+
+/**
+ * @file taskpool.h
+ * @brief Allows you to create and remove generic tasks at runtime.
+ *
+ * This package creates a single global task pool.  Each allocated task
+ * uses the same size stack.  The task count and stack size settings are
+ * specified via syscfg.
+ */
+
+/**
+ * @brief Allocates a new task from the global task pool.
+ *
+ * A task allocated with this function is allowed to terminate via return.
+ * That is, if its handler function runs to completion, the task complete and
+ * can be collected.
+ *
+ * @param task_handler          The function to be executed by the new task.
+ * @param prio                  The priority to assign to the new task.
+ * @param out_task              On success, the address ofthe newly allocated
+ *                                  task gets written here.  Pass NULL if you
+ *                                  don't require this information.
+ *
+ * @return                      0 on success; SYS_E[...] error code on failure.
+ */
+int taskpool_alloc(os_task_func_t task_handler, uint8_t prio,
+                   struct os_task **out_task);
+
+/**
+ * @brief Allocates a new task and asserts success.
+ *
+ * See taskpool_alloc() for details.
+ *
+ * @param task_handler          The function to be executed by the new task.
+ * @param prio                  The priority to assign to the new task.
+ *
+ * @return                      The newly allocateed task.
+ */
+struct os_task *taskpool_alloc_assert(os_task_func_t task_handler,
+                                      uint8_t prio);
+
+/**
+ * @brief Waits for all allocated tasks to complete.
+ *
+ * @param max_ticks             The maximum duration to wait before the wait
+ *                                  operation times out.  Units are OS ticks.
+ *
+ * @return                      0 on success; 
+ *                              OS_TIMEOUT on timeout.
+ */
+int taskpool_wait(os_time_t max_ticks);
+
+/**
+ * @brief Waits for all allocated tasks to complete and asserts success.
+ *
+ * @param max_ticks             The maximum duration to wait before the wait
+ *                                  operation times out.  Units are OS ticks.
+ */
+void taskpool_wait_assert(os_time_t max_ticks);
+
+#endif
diff --git a/util/taskpool/pkg.yml b/util/taskpool/pkg.yml
new file mode 100644
index 0000000..5936137
--- /dev/null
+++ b/util/taskpool/pkg.yml
@@ -0,0 +1,28 @@
+# 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.
+
+pkg.name: util/taskpool
+pkg.description: "Reusable task pool"
+pkg.author: "Apache Mynewt <de...@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps: 
+    - "@apache-mynewt-core/kernel/os"
+
+pkg.init:
+    taskpool_init: 1000
diff --git a/util/taskpool/src/taskpool.c b/util/taskpool/src/taskpool.c
new file mode 100644
index 0000000..1690c67
--- /dev/null
+++ b/util/taskpool/src/taskpool.c
@@ -0,0 +1,270 @@
+/*
+ * 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,
+                      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);
+}
+
+/*
+ * Package init routine to register newtmgr "run" commands
+ */
+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;
+    }
+}
diff --git a/util/taskpool/syscfg.yml b/util/taskpool/syscfg.yml
new file mode 100644
index 0000000..6225f60
--- /dev/null
+++ b/util/taskpool/syscfg.yml
@@ -0,0 +1,25 @@
+# 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.
+#
+
+syscfg.defs:
+    TASKPOOL_NUM_TASKS:
+        description: 'The number of tasks available in the task pool.'
+        value: 4
+    TASKPOOL_STACK_SIZE:
+        description: 'The stack size, in words, of each task pool task.'
+        value: 256