blob: 454f6ab404181e41b99afa4fab0da8338f3cbc19 [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.
*/
#include "os/os.h"
#include <assert.h>
/**
* os mutex create
*
* Create a mutex and initialize it.
*
* @param mu Pointer to mutex
*
* @return os_error_t
* OS_INVALID_PARM Mutex passed in was NULL.
* OS_OK no error.
*/
os_error_t
os_mutex_init(struct os_mutex *mu)
{
if (!mu) {
return OS_INVALID_PARM;
}
/* Initialize to 0 */
mu->mu_prio = 0;
mu->mu_level = 0;
mu->mu_owner = NULL;
SLIST_FIRST(&mu->mu_head) = NULL;
return OS_OK;
}
/**
* os mutex release
*
* Release a mutex.
*
* @param mu Pointer to the mutex to be released
*
* @return os_error_t
* OS_INVALID_PARM Mutex passed in was NULL.
* OS_BAD_MUTEX Mutex was not granted to current task (not owner).
* OS_OK No error
*/
os_error_t
os_mutex_release(struct os_mutex *mu)
{
int resched;
os_sr_t sr;
struct os_task *current;
struct os_task *rdy;
/* Check if OS is started */
if (!g_os_started) {
return (OS_NOT_STARTED);
}
/* Check for valid mutex */
if (!mu) {
return OS_INVALID_PARM;
}
/* We better own this mutex! */
current = os_sched_get_current_task();
if ((mu->mu_level == 0) || (mu->mu_owner != current)) {
return (OS_BAD_MUTEX);
}
/* Decrement nesting level by 1. If not zero, nested (so dont release!) */
--mu->mu_level;
if (mu->mu_level != 0) {
return (OS_OK);
}
OS_ENTER_CRITICAL(sr);
/* Restore owner task's priority; resort list if different */
if (current->t_prio != mu->mu_prio) {
current->t_prio = mu->mu_prio;
os_sched_resort(current);
}
/* Check if tasks are waiting for the mutex */
rdy = SLIST_FIRST(&mu->mu_head);
if (rdy) {
/* There is one waiting. Wake it up */
assert(rdy->t_obj);
os_sched_wakeup(rdy);
/* Set mutex internals */
mu->mu_level = 1;
mu->mu_prio = rdy->t_prio;
}
/* Set new owner of mutex (or NULL if not owned) */
mu->mu_owner = rdy;
/* Do we need to re-schedule? */
resched = 0;
rdy = os_sched_next_task();
if (rdy != current) {
resched = 1;
}
OS_EXIT_CRITICAL(sr);
/* Re-schedule if needed */
if (resched) {
os_sched(rdy, 0);
}
return OS_OK;
}
/**
* os mutex pend
*
* Pend (wait) for a mutex.
*
* @param mu Pointer to mutex.
* @param timeout Timeout, in os ticks. A timeout of 0 means do
* not wait if not available. A timeout of
* 0xFFFFFFFF means wait forever.
*
*
* @return os_error_t
* OS_INVALID_PARM Mutex passed in was NULL.
* OS_TIMEOUT Mutex was owned by another task and timeout=0
* OS_OK no error.
*/
os_error_t
os_mutex_pend(struct os_mutex *mu, uint32_t timeout)
{
os_sr_t sr;
os_error_t rc;
struct os_task *current;
struct os_task *entry;
struct os_task *last;
/* OS must be started when calling this function */
if (!g_os_started) {
return (OS_NOT_STARTED);
}
/* Check for valid mutex */
if (!mu) {
return OS_INVALID_PARM;
}
OS_ENTER_CRITICAL(sr);
/* Is this owned? */
current = os_sched_get_current_task();
if (mu->mu_level == 0) {
mu->mu_owner = current;
mu->mu_prio = current->t_prio;
mu->mu_level = 1;
OS_EXIT_CRITICAL(sr);
return OS_OK;
}
/* Are we owner? */
if (mu->mu_owner == current) {
++mu->mu_level;
OS_EXIT_CRITICAL(sr);
return OS_OK;
}
/* Mutex is not owned by us. If timeout is 0, return immediately */
if (timeout == 0) {
OS_EXIT_CRITICAL(sr);
return OS_TIMEOUT;
}
/* Change priority of owner if needed */
if (mu->mu_owner->t_prio > current->t_prio) {
mu->mu_owner->t_prio = current->t_prio;
os_sched_resort(mu->mu_owner);
}
/* Link current task to tasks waiting for mutex */
last = NULL;
if (!SLIST_EMPTY(&mu->mu_head)) {
/* Insert in priority order */
SLIST_FOREACH(entry, &mu->mu_head, t_obj_list) {
if (current->t_prio < entry->t_prio) {
break;
}
last = entry;
}
}
if (last) {
SLIST_INSERT_AFTER(last, current, t_obj_list);
} else {
SLIST_INSERT_HEAD(&mu->mu_head, current, t_obj_list);
}
/* Set mutex pointer in task */
current->t_obj = mu;
current->t_flags |= OS_TASK_FLAG_MUTEX_WAIT;
os_sched_sleep(current, timeout);
OS_EXIT_CRITICAL(sr);
os_sched(NULL, 0);
OS_ENTER_CRITICAL(sr);
current->t_flags &= ~OS_TASK_FLAG_MUTEX_WAIT;
OS_EXIT_CRITICAL(sr);
/* If we are owner we did not time out. */
if (mu->mu_owner == current) {
rc = OS_OK;
} else {
rc = OS_TIMEOUT;
}
return rc;
}
/**
* os mutex delete
*
* Delete a mutex.
*
* @param mu Pointer to mutex to delete
*
* @return os_error_t
* OS_INVALID_PARM Mutex passed in was NULL.
* OS_OK no error.
*/
os_error_t
os_mutex_delete(struct os_mutex *mu)
{
os_sr_t sr;
struct os_task *current;
struct os_task *rdy;
/* OS must be started to call this function */
if (!g_os_started) {
return (OS_NOT_STARTED);
}
/* Check for valid mutex */
if (!mu) {
return OS_INVALID_PARM;
}
/* Get currently running task */
current = os_sched_get_current_task();
OS_ENTER_CRITICAL(sr);
/* Restore owner task's priority. */
if (mu->mu_level != 0) {
if (mu->mu_owner->t_prio != mu->mu_prio) {
/* We have to resort the ready list if this task is ready */
mu->mu_owner->t_prio = mu->mu_prio;
os_sched_resort(mu->mu_owner);
}
}
/* Now, go through all the tasks waiting on the mutex */
while (!SLIST_EMPTY(&mu->mu_head)) {
rdy = SLIST_FIRST(&mu->mu_head);
assert(rdy->t_obj);
os_sched_wakeup(rdy);
}
/* Is there a task that is ready that is higher priority than us? */
rdy = os_sched_next_task();
if (rdy != current) {
/* Re-schedule */
OS_EXIT_CRITICAL(sr);
os_sched(rdy, 0);
} else {
OS_EXIT_CRITICAL(sr);
}
return OS_OK;
}