| /**************************************************************************** |
| * sched/semaphore/sem_holder.c |
| * |
| * 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. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <sched.h> |
| #include <assert.h> |
| #include <debug.h> |
| |
| #include <nuttx/addrenv.h> |
| #include <nuttx/arch.h> |
| |
| #include "sched/sched.h" |
| #include "semaphore/semaphore.h" |
| |
| #ifdef CONFIG_PRIORITY_INHERITANCE |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* Configuration ************************************************************/ |
| |
| #ifndef CONFIG_SEM_PREALLOCHOLDERS |
| # define CONFIG_SEM_PREALLOCHOLDERS 0 |
| #endif |
| |
| /**************************************************************************** |
| * Private Type Declarations |
| ****************************************************************************/ |
| |
| typedef int (*holderhandler_t)(FAR struct semholder_s *pholder, |
| FAR sem_t *sem, FAR void *arg); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* Preallocated holder structures */ |
| |
| #if CONFIG_SEM_PREALLOCHOLDERS > 0 |
| static struct semholder_s g_holderalloc[CONFIG_SEM_PREALLOCHOLDERS]; |
| static FAR struct semholder_s *g_freeholders; |
| #endif |
| |
| /**************************************************************************** |
| * Name: nxsem_allocholder |
| ****************************************************************************/ |
| |
| static inline FAR struct semholder_s * |
| nxsem_allocholder(FAR sem_t *sem, FAR struct tcb_s *htcb) |
| { |
| FAR struct semholder_s *pholder; |
| |
| /* Check if the "built-in" holder is being used. We have this built-in |
| * holder to optimize for the simplest case where semaphores are only |
| * used to implement mutexes. |
| */ |
| |
| #if CONFIG_SEM_PREALLOCHOLDERS > 0 |
| pholder = g_freeholders; |
| if (pholder != NULL) |
| { |
| /* Remove the holder from the free list and |
| * put it into the semaphore's holder list |
| */ |
| |
| g_freeholders = pholder->flink; |
| pholder->flink = sem->hhead; |
| sem->hhead = pholder; |
| } |
| #else |
| if (sem->holder.htcb == NULL) |
| { |
| pholder = &sem->holder; |
| } |
| #endif |
| else |
| { |
| serr("ERROR: Insufficient pre-allocated holders\n"); |
| PANIC(); |
| } |
| |
| pholder->sem = sem; |
| pholder->htcb = htcb; |
| pholder->counts = 0; |
| |
| /* Put it into the task's list */ |
| |
| pholder->tlink = htcb->holdsem; |
| htcb->holdsem = pholder; |
| |
| return pholder; |
| } |
| |
| /**************************************************************************** |
| * Name: nxsem_findholder |
| * |
| * NOTE: htcb may be used only as a look-up key. It certain cases, the task |
| * may have exited and htcb may refer to a stale memory. It must not be |
| * dereferenced. |
| * |
| ****************************************************************************/ |
| |
| static FAR struct semholder_s * |
| nxsem_findholder(FAR sem_t *sem, FAR struct tcb_s *htcb) |
| { |
| FAR struct semholder_s *pholder; |
| |
| #if CONFIG_SEM_PREALLOCHOLDERS > 0 |
| /* Try to find the holder in the list of holders associated with this |
| * semaphore |
| */ |
| |
| for (pholder = sem->hhead; pholder != NULL; pholder = pholder->flink) |
| { |
| if (pholder->htcb == htcb) |
| { |
| /* Got it! */ |
| |
| return pholder; |
| } |
| } |
| #else |
| /* We have one hard-allocated holder structures in sem_t */ |
| |
| pholder = &sem->holder; |
| |
| if (pholder->htcb == htcb) |
| { |
| /* Got it! */ |
| |
| return pholder; |
| } |
| #endif |
| |
| /* The holder does not appear in the list */ |
| |
| return NULL; |
| } |
| |
| /**************************************************************************** |
| * Name: nxsem_findorallocateholder |
| ****************************************************************************/ |
| |
| static inline FAR struct semholder_s * |
| nxsem_findorallocateholder(FAR sem_t *sem, FAR struct tcb_s *htcb) |
| { |
| FAR struct semholder_s *pholder = nxsem_findholder(sem, htcb); |
| if (pholder == NULL) |
| { |
| pholder = nxsem_allocholder(sem, htcb); |
| } |
| |
| return pholder; |
| } |
| |
| /**************************************************************************** |
| * Name: nxsem_freeholder |
| ****************************************************************************/ |
| |
| static inline void nxsem_freeholder(FAR sem_t *sem, |
| FAR struct semholder_s *pholder) |
| { |
| FAR struct semholder_s * FAR *curr; |
| |
| /* Remove the holder from the task's list */ |
| |
| for (curr = &pholder->htcb->holdsem; |
| *curr != NULL; |
| curr = &(*curr)->tlink) |
| { |
| if (*curr == pholder) |
| { |
| *curr = pholder->tlink; |
| break; |
| } |
| } |
| |
| /* Release the holder and counts */ |
| |
| pholder->tlink = NULL; |
| pholder->sem = NULL; |
| pholder->htcb = NULL; |
| pholder->counts = 0; |
| |
| #if CONFIG_SEM_PREALLOCHOLDERS > 0 |
| /* Remove the holder from the semaphore's list */ |
| |
| for (curr = &sem->hhead; |
| *curr != NULL; |
| curr = &(*curr)->flink) |
| { |
| if (*curr == pholder) |
| { |
| *curr = pholder->flink; |
| break; |
| } |
| } |
| |
| /* And put it in the free list */ |
| |
| pholder->flink = g_freeholders; |
| g_freeholders = pholder; |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: nxsem_freecount0holder |
| ****************************************************************************/ |
| |
| #if CONFIG_SEM_PREALLOCHOLDERS > 0 |
| static int nxsem_freecount0holder(FAR struct semholder_s *pholder, |
| FAR sem_t *sem, FAR void *arg) |
| { |
| /* When no more counts are held, remove the holder from the list. The |
| * count was decremented in nxsem_release_holder. |
| */ |
| |
| if (pholder->counts <= 0) |
| { |
| nxsem_freeholder(sem, pholder); |
| return 1; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: nxsem_foreachholder |
| ****************************************************************************/ |
| |
| static int nxsem_foreachholder(FAR sem_t *sem, holderhandler_t handler, |
| FAR void *arg) |
| { |
| FAR struct semholder_s *pholder; |
| int ret = 0; |
| |
| #if CONFIG_SEM_PREALLOCHOLDERS > 0 |
| FAR struct semholder_s *next; |
| |
| for (pholder = sem->hhead; pholder && ret == 0; pholder = next) |
| { |
| /* In case this holder gets deleted */ |
| |
| next = pholder->flink; |
| |
| DEBUGASSERT(pholder->htcb != NULL); |
| |
| /* Call the handler */ |
| |
| ret = handler(pholder, sem, arg); |
| } |
| #else |
| /* We have one hard-allocated holder structures in sem_t */ |
| |
| pholder = &sem->holder; |
| |
| /* The hard-allocated containers may hold a NULL holder */ |
| |
| if (pholder->htcb != NULL) |
| { |
| /* Call the handler */ |
| |
| ret = handler(pholder, sem, arg); |
| } |
| #endif |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nxsem_recoverholders |
| ****************************************************************************/ |
| |
| static int nxsem_recoverholders(FAR struct semholder_s *pholder, |
| FAR sem_t *sem, FAR void *arg) |
| { |
| nxsem_freeholder(sem, pholder); |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: nxsem_boostholderprio |
| ****************************************************************************/ |
| |
| static int nxsem_boostholderprio(FAR struct semholder_s *pholder, |
| FAR sem_t *sem, FAR void *arg) |
| { |
| FAR struct tcb_s *htcb = pholder->htcb; |
| FAR struct tcb_s *rtcb = (FAR struct tcb_s *)arg; |
| |
| /* If the priority of the thread that is waiting for a count is less than |
| * or equal to the priority of the thread holding a count, then do nothing |
| * because the thread is already running at a sufficient priority. |
| */ |
| |
| if (rtcb->sched_priority > htcb->sched_priority) |
| { |
| /* Raise the priority of the holder of the semaphore. This |
| * cannot cause a context switch because we have preemption |
| * disabled. The task will be marked "pending" and the switch |
| * will occur during up_contex_switch() processing. |
| */ |
| |
| nxsched_set_priority(htcb, rtcb->sched_priority); |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: nxsem_verifyholder |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_DEBUG_ASSERTIONS |
| static int nxsem_verifyholder(FAR struct semholder_s *pholder, |
| FAR sem_t *sem, FAR void *arg) |
| { |
| /* Need to revisit this, but these assumptions seem to be untrue -- OR |
| * there is a bug??? |
| */ |
| |
| #if 0 |
| FAR struct tcb_s *htcb = pholder->htcb; |
| |
| /* Called after a semaphore has been released (incremented), the semaphore |
| * could be non-negative, and there is no thread waiting for the count. |
| * In this case, the priority of the holder should not be boosted. |
| */ |
| |
| DEBUGASSERT(htcb->sched_priority == htcb->base_priority); |
| #endif |
| |
| return 0; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: nxsem_dumpholder |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_DEBUG_INFO) && defined(CONFIG_SEM_PHDEBUG) |
| static int nxsem_dumpholder(FAR struct semholder_s *pholder, FAR sem_t *sem, |
| FAR void *arg) |
| { |
| #if CONFIG_SEM_PREALLOCHOLDERS > 0 |
| _info(" %08x: %08x %08x %08x %08x %04x\n", |
| pholder, pholder->flink, |
| #else |
| _info(" %08x: %08x %08x %08x %04x\n", |
| pholder, |
| #endif |
| pholder->tlink, pholder->sem, pholder->htcb, pholder->counts); |
| return 0; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: nxsem_restore_priority |
| ****************************************************************************/ |
| |
| static void nxsem_restore_priority(FAR struct tcb_s *htcb) |
| { |
| int hpriority; |
| |
| /* We attempt to restore thread priority to its base priority. If |
| * there is any thread with the higher priority waiting for the |
| * semaphore held by htcb then this value will be overwritten. |
| */ |
| |
| hpriority = htcb->boost_priority > htcb->base_priority ? |
| htcb->boost_priority : htcb->base_priority; |
| |
| /* Was the priority of the holder thread boosted? If so, then drop its |
| * priority back to the correct level. What is the correct level? |
| */ |
| |
| if (htcb->sched_priority != hpriority) |
| { |
| FAR struct semholder_s *pholder; |
| |
| #ifdef CONFIG_ARCH_ADDRENV |
| FAR struct addrenv_s *oldenv; |
| |
| if (htcb->addrenv_own) |
| { |
| addrenv_select(htcb->addrenv_own, &oldenv); |
| } |
| #endif |
| |
| /* Try to find the highest priority across all the threads that are |
| * waiting for any semaphore held by htcb. |
| */ |
| |
| for (pholder = htcb->holdsem; pholder != NULL; |
| pholder = pholder->tlink) |
| { |
| FAR struct tcb_s *stcb; |
| |
| stcb = (FAR struct tcb_s *)dq_peek(SEM_WAITLIST(pholder->sem)); |
| |
| if (stcb != NULL && stcb->sched_priority > hpriority) |
| { |
| hpriority = stcb->sched_priority; |
| } |
| } |
| |
| #ifdef CONFIG_ARCH_ADDRENV |
| if (htcb->addrenv_own) |
| { |
| addrenv_restore(oldenv); |
| } |
| #endif |
| |
| /* Apply the selected priority to the thread (hopefully back to the |
| * threads base_priority). |
| */ |
| |
| nxsched_set_priority(htcb, hpriority); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: nxsem_restoreholderprio |
| ****************************************************************************/ |
| |
| static int nxsem_restoreholderprio(FAR struct semholder_s *pholder, |
| FAR sem_t *sem, FAR void *arg) |
| { |
| FAR struct tcb_s *htcb = pholder->htcb; |
| |
| /* Release the holder if all counts have been given up |
| * before reprioritizing causes a context switch. |
| */ |
| |
| if (pholder->counts <= 0) |
| { |
| nxsem_freeholder(sem, pholder); |
| } |
| |
| nxsem_restore_priority(htcb); |
| |
| return 0; |
| } |
| |
| #if CONFIG_SEM_PREALLOCHOLDERS > 0 |
| |
| /**************************************************************************** |
| * Name: nxsem_restoreholderprio_others |
| * |
| * Description: |
| * Reprioritize all holders except the currently executing task |
| * |
| ****************************************************************************/ |
| |
| static int nxsem_restoreholderprio_others(FAR struct semholder_s *pholder, |
| FAR sem_t *sem, FAR void *arg) |
| { |
| FAR struct tcb_s *rtcb = this_task(); |
| if (pholder->htcb != rtcb) |
| { |
| return nxsem_restoreholderprio(pholder, sem, arg); |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: nxsem_restoreholderprio_self |
| * |
| * Description: |
| * Reprioritize only the currently executing task |
| * |
| ****************************************************************************/ |
| |
| static int nxsem_restoreholderprio_self(FAR struct semholder_s *pholder, |
| FAR sem_t *sem, FAR void *arg) |
| { |
| FAR struct tcb_s *rtcb = this_task(); |
| |
| if (pholder->htcb == rtcb) |
| { |
| /* The running task has given up a count on the semaphore */ |
| |
| nxsem_restoreholderprio(pholder, sem, arg); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| #endif |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: nxsem_initialize_holders |
| * |
| * Description: |
| * Called from nxsem_initialize() to set up semaphore holder information. |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * None |
| * |
| * Assumptions: |
| * |
| ****************************************************************************/ |
| |
| void nxsem_initialize_holders(void) |
| { |
| #if CONFIG_SEM_PREALLOCHOLDERS > 0 |
| int i; |
| |
| /* Put all of the pre-allocated holder structures into the free list */ |
| |
| g_freeholders = g_holderalloc; |
| for (i = 0; i < (CONFIG_SEM_PREALLOCHOLDERS - 1); i++) |
| { |
| g_holderalloc[i].flink = &g_holderalloc[i + 1]; |
| } |
| |
| g_holderalloc[CONFIG_SEM_PREALLOCHOLDERS - 1].flink = NULL; |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: nxsem_destroyholder |
| * |
| * Description: |
| * Called from nxsem_destroy() to handle any holders of a semaphore |
| * when it is destroyed. |
| * |
| * Input Parameters: |
| * sem - A reference to the semaphore being destroyed |
| * |
| * Returned Value: |
| * None |
| * |
| * Assumptions: |
| * |
| ****************************************************************************/ |
| |
| void nxsem_destroyholder(FAR sem_t *sem) |
| { |
| /* It might be an error if a semaphore is destroyed while there are any |
| * holders of the semaphore (except perhaps the thread that release the |
| * semaphore itself). We actually have to assume that the caller knows |
| * what it is doing because could have killed another thread that is the |
| * actual holder of the semaphore. |
| * |
| * It is also a standard practice to destroy the semaphore while the |
| * caller holds it. Of course, the caller MUST assure that there are no |
| * other holders of the semaphore in this case. This occurs, for example, |
| * when a driver is unlink'ed and the driver instance must be destroyed. |
| * |
| * Therefore, we cannot make any assumptions about the state of the |
| * semaphore or the state of any of the holder threads. So just recover |
| * any stranded holders and hope the task knows what it is doing. |
| */ |
| |
| #if CONFIG_SEM_PREALLOCHOLDERS > 0 |
| if (sem->hhead != NULL) |
| { |
| /* There may be an issue if there are multiple holders of |
| * the semaphore. |
| */ |
| |
| DEBUGASSERT(sem->hhead->flink == NULL); |
| } |
| |
| #else |
| /* There may be an issue if there are multiple holders of the semaphore. */ |
| |
| DEBUGASSERT(sem->holder.htcb == NULL); |
| |
| #endif |
| |
| nxsem_foreachholder(sem, nxsem_recoverholders, NULL); |
| } |
| |
| /**************************************************************************** |
| * Name: nxsem_add_holder_tcb |
| * |
| * Description: |
| * Called from nxsem_wait() when the calling thread obtains the semaphore; |
| * Called from sem_post() when the waiting thread obtains the semaphore. |
| * |
| * Input Parameters: |
| * htcb - TCB of the thread that just obtained the semaphore |
| * sem - A reference to the incremented semaphore |
| * |
| * Returned Value: |
| * 0 (OK) or -1 (ERROR) if unsuccessful |
| * |
| * Assumptions: |
| * Interrupts are disabled. |
| * |
| ****************************************************************************/ |
| |
| void nxsem_add_holder_tcb(FAR struct tcb_s *htcb, FAR sem_t *sem) |
| { |
| FAR struct semholder_s *pholder; |
| uint8_t prioinherit = sem->flags & SEM_PRIO_MASK; |
| |
| /* If priority inheritance is disabled for this thread or it is IDLE |
| * thread, then do not add the holder. |
| * If there are never holders of the semaphore, the priority |
| * inheritance is effectively disabled. |
| */ |
| |
| if (!is_idle_task(htcb) && prioinherit == SEM_PRIO_INHERIT) |
| { |
| /* Find or allocate a container for this new holder */ |
| |
| pholder = nxsem_findorallocateholder(sem, htcb); |
| if (pholder->counts < SEM_VALUE_MAX) |
| { |
| /* Increment the number of counts held by this holder */ |
| |
| pholder->counts++; |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: nxsem_add_holder |
| * |
| * Description: |
| * Called from nxsem_wait() when the calling thread obtains the semaphore |
| * |
| * Input Parameters: |
| * sem - A reference to the incremented semaphore |
| * |
| * Returned Value: |
| * 0 (OK) or -1 (ERROR) if unsuccessful |
| * |
| * Assumptions: |
| * Interrupts are disabled. |
| * |
| ****************************************************************************/ |
| |
| void nxsem_add_holder(FAR sem_t *sem) |
| { |
| nxsem_add_holder_tcb(this_task(), sem); |
| } |
| |
| /**************************************************************************** |
| * Name: void nxsem_boost_priority(sem_t *sem) |
| * |
| * Description: |
| * |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * 0 (OK) or -1 (ERROR) if unsuccessful |
| * |
| * Assumptions: |
| * |
| ****************************************************************************/ |
| |
| void nxsem_boost_priority(FAR sem_t *sem) |
| { |
| FAR struct tcb_s *rtcb = this_task(); |
| |
| /* Boost the priority of every thread holding counts on this semaphore |
| * that are lower in priority than the new thread that is waiting for a |
| * count. |
| */ |
| |
| #if CONFIG_SEM_PREALLOCHOLDERS > 0 |
| nxsem_foreachholder(sem, nxsem_boostholderprio, rtcb); |
| #else |
| nxsem_boostholderprio(&sem->holder, sem, rtcb); |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: nxsem_release_holder |
| * |
| * Description: |
| * Called from sem_post() after a thread releases one count on the |
| * semaphore. |
| * |
| * Input Parameters: |
| * sem - A reference to the semaphore being posted |
| * |
| * Returned Value: |
| * None |
| * |
| * Assumptions: |
| * |
| ****************************************************************************/ |
| |
| void nxsem_release_holder(FAR sem_t *sem) |
| { |
| FAR struct tcb_s *rtcb = this_task(); |
| uint8_t prioinherit = sem->flags & SEM_PRIO_MASK; |
| |
| /* If priority inheritance is disabled for this thread or it is IDLE |
| * thread, then do not add the holder. |
| * If there are never holders of the semaphore, the priority |
| * inheritance is effectively disabled. |
| */ |
| |
| if (!is_idle_task(rtcb) && prioinherit == SEM_PRIO_INHERIT) |
| { |
| FAR struct semholder_s *pholder; |
| |
| DEBUGASSERT(!up_interrupt_context()); |
| |
| /* Find the container for this holder */ |
| |
| #if CONFIG_SEM_PREALLOCHOLDERS > 0 |
| for (pholder = sem->hhead; pholder != NULL; pholder = pholder->flink) |
| { |
| DEBUGASSERT(pholder->counts > 0); |
| |
| if (pholder->htcb == rtcb) |
| { |
| /* Decrement the counts on this holder -- the holder will be |
| * freed later in nxsem_restore_baseprio. |
| */ |
| |
| pholder->counts--; |
| return; |
| } |
| } |
| |
| /* The current task is not a holder */ |
| |
| DEBUGPANIC(); |
| #else |
| pholder = &sem->holder; |
| DEBUGASSERT(pholder->htcb == rtcb); |
| nxsem_freeholder(sem, pholder); |
| #endif |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: nxsem_restore_baseprio |
| * |
| * Description: |
| * This function is called after the current running task releases a |
| * count on the semaphore or an interrupt handler posts a new count. It |
| * will check if we need to drop the priority of any threads holding a |
| * count on the semaphore. Their priority could have been boosted while |
| * they held the count. |
| * |
| * Input Parameters: |
| * stcb - The TCB of the task that was just started (if any). If the |
| * post action caused a count to be given to another thread, then stcb |
| * is the TCB that received the count. Note, just because stcb received |
| * the count, it does not mean that it is higher priority than other |
| * threads. |
| * sem - A reference to the semaphore being posted. |
| * - If the semaphore count is <0 then there are still threads waiting |
| * for a count. stcb should be non-null and will be higher priority |
| * than all of the other threads still waiting. |
| * - If it is ==0 then stcb refers to the thread that got the last count; |
| * no other threads are waiting. |
| * - If it is >0 then there should be no threads waiting for counts and |
| * stcb should be null. |
| * |
| * Returned Value: |
| * None |
| * |
| * Assumptions: |
| * The scheduler is locked. |
| * |
| ****************************************************************************/ |
| |
| void nxsem_restore_baseprio(FAR struct tcb_s *stcb, FAR sem_t *sem) |
| { |
| #if 0 /* DSA: sometimes crashes when Telnet calls external cmd (i.e. 'i2c') */ |
| /* Check our assumptions */ |
| |
| DEBUGASSERT((sem->semcount > 0 && stcb == NULL) || |
| (sem->semcount <= 0 && stcb != NULL)); |
| #endif |
| |
| DEBUGASSERT(!up_interrupt_context()); |
| |
| /* Perform the following actions only if a new thread was given a count. |
| * The thread that received the count should be the highest priority |
| * of all threads waiting for a count from the semaphore. So in that |
| * case, the priority of all holder threads should be dropped to the |
| * next highest pending priority. |
| */ |
| |
| if (stcb != NULL) |
| { |
| #if CONFIG_SEM_PREALLOCHOLDERS > 0 |
| /* The currently executed thread should be the lower priority |
| * thread that just posted the count and caused this action. |
| * However, we cannot drop the priority of the currently running |
| * thread -- because that will cause it to be suspended. |
| * |
| * So, do this in two passes. First, reprioritizing all holders |
| * except for the running thread. |
| */ |
| |
| nxsem_foreachholder(sem, nxsem_restoreholderprio_others, stcb); |
| |
| /* Now, find an reprioritize only the ready to run task */ |
| |
| nxsem_foreachholder(sem, nxsem_restoreholderprio_self, stcb); |
| #else |
| /* New owner is already the highest priority since the wait queue |
| * is priority-based, no need to adjust its priority, only restore |
| * the older owner when posted the count. |
| */ |
| |
| nxsem_restore_priority(this_task()); |
| #endif |
| } |
| else |
| { |
| #if CONFIG_SEM_PREALLOCHOLDERS > 0 |
| /* Remove the holder from the list if it's counts is zero. */ |
| |
| nxsem_foreachholder(sem, nxsem_freecount0holder, NULL); |
| |
| /* If there are no tasks waiting for available counts, then all holders |
| * should be at their base priority. |
| */ |
| #endif |
| |
| #ifdef CONFIG_DEBUG_ASSERTIONS |
| nxsem_foreachholder(sem, nxsem_verifyholder, NULL); |
| #endif |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: nxsem_canceled |
| * |
| * Description: |
| * Called from nxsem_wait_irq() after a thread that was waiting for a |
| * semaphore count was awakened because of a signal and the semaphore wait |
| * has been canceled. This function restores the correct thread priority |
| * of each holder of the semaphore. |
| * |
| * Input Parameters: |
| * sem - A reference to the semaphore no longer being waited for |
| * |
| * Returned Value: |
| * None |
| * |
| * Assumptions: |
| * |
| ****************************************************************************/ |
| |
| void nxsem_canceled(FAR struct tcb_s *stcb, FAR sem_t *sem) |
| { |
| /* Check our assumptions */ |
| |
| DEBUGASSERT(sem->semcount <= 0); |
| |
| /* Adjust the priority of every holder as necessary */ |
| |
| nxsem_foreachholder(sem, nxsem_restoreholderprio, stcb); |
| } |
| |
| /**************************************************************************** |
| * Name: sem_enumholders |
| * |
| * Description: |
| * Show information about threads currently waiting on this semaphore |
| * |
| * Input Parameters: |
| * sem - A reference to the semaphore |
| * |
| * Returned Value: |
| * None |
| * |
| * Assumptions: |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_DEBUG_FEATURES) && defined(CONFIG_SEM_PHDEBUG) |
| void sem_enumholders(FAR sem_t *sem) |
| { |
| #ifdef CONFIG_DEBUG_INFO |
| nxsem_foreachholder(sem, nxsem_dumpholder, NULL); |
| #endif |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: nxsem_nfreeholders |
| * |
| * Description: |
| * Return the number of available holder containers. This is a good way |
| * to find out which threads are not calling sem_destroy. |
| * |
| * Input Parameters: |
| * sem - A reference to the semaphore |
| * |
| * Returned Value: |
| * The number of available holder containers |
| * |
| * Assumptions: |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_DEBUG_FEATURES) && defined(CONFIG_SEM_PHDEBUG) |
| int nxsem_nfreeholders(void) |
| { |
| #if CONFIG_SEM_PREALLOCHOLDERS > 0 |
| FAR struct semholder_s *pholder; |
| int n; |
| |
| for (pholder = g_freeholders, n = 0; pholder; pholder = pholder->flink) |
| { |
| n++; |
| } |
| |
| return n; |
| #else |
| return 0; |
| #endif |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: nxsem_release_all |
| * |
| * Description: |
| * Release all semaphore holders for the task. |
| * |
| * Input Parameters: |
| * htcb - TCB of the task |
| * |
| * Returned Value: |
| * None |
| * |
| * Assumptions: |
| * |
| ****************************************************************************/ |
| |
| void nxsem_release_all(FAR struct tcb_s *htcb) |
| { |
| FAR struct semholder_s *pholder; |
| |
| while ((pholder = htcb->holdsem) != NULL) |
| { |
| FAR sem_t *sem = pholder->sem; |
| |
| nxsem_freeholder(sem, pholder); |
| |
| /* Increment the count on the semaphore, to releases the count |
| * that was taken by sem_wait() or sem_post(). |
| */ |
| |
| sem->semcount++; |
| } |
| } |
| |
| #endif /* CONFIG_PRIORITY_INHERITANCE */ |