Data Structures | Defines | Functions

# Semaphore functions
[Pre-emptive, soft real-time scheduler]

Semaphores are resource counters. More...

## Data Structures
struct | BX_SEMA
| Semaphore object. More...
## Defines
#define | BX_SEMA_INIT(policy, count)
| Static initialiser for a semaphore.
## Functions
int | BX_SemaGet (BX_SEMA *sema, unsigned int value, unsigned int timeout)
| Waits for a number of resources on a semaphore.
int | BX_SemaInit (BX_SEMA *sema, BX_POLICY policy, unsigned int value)
| Initialises a semaphore.
int | BX_SemaPut (BX_SEMA *sema, unsigned int value)
| Add resources to a semaphore.

## Detailed Description

Semaphores are resource counters.

A semaphore maintains a count, which is an unsigned integer. A process can request a certain amount of resources. If the semaphore's count is at least as large as the requested amount, the process is let to run and the counter is decremented by the requested amount. As the process finishes with the resources, it can give them back to the semaphore, which basically means incrementing its counter.
If the semaphore does not have enough resources, then the requesting process will be suspended until the semaphore receives resources. As soon as the semaphore's counter reaches the requested amount, the process gets unblocked, that is, its request is satisfied.
Most semaphore implementations allow you to take or give back one resource at the time. This implementation allows you to request and return an arbitrary number of resources. This latter method is more complex to implement, but it has certain advantages.
First, it is faster. If you need 10 resources, then if you have to request them one at a time, you have to call the kernel 10 times. It is obviously much more efficient to call the kernel only once.
Second, the one at a time method can lead to a deadlock. Let's say a semaphore has 10 resources. Process A wants to get 8 of them, so it starts to get them one after the other in a loop. Now let's assume that when it has already acquired 6 of them, process B, which has higher priority than A wakes up. Therefore, B will start to run. Let's assume that it also need the resources maintained by the semaphore, namely 5 of them. So it starts to get them in a loop. Since A has already taken 6 from the semaphore, there are only 4 left. After B gets that 4, it will be suspended on the semaphore, as it is now empty. Due to B being suspended, A starts to run again, trying to obtain the last 2 resources. Alas, the semaphore is now empty, so A gets suspended on its next request. The net effect is that both A and B are suspended and they are also stuck, because neither of them can give back the resources it took. With the version implemented in the kernel A requests 8 items, it gets them in one hit. If B wakes up and asks for its 5 resources, it will be suspended on the semaphore (as it only has 2 left), but A is running, thus it can release the resources it took. There's no deadlock.
Now what happens if more than one process is waiting for resources on the same semaphore? When the semaphore gets enough resources, which of the waiting processes will get them? That depends on the policy of the semaphore. You can use 4 policies in this kernel:

- FIFO
  The requests are satisfied in the order they arrived onto the semaphore. If process A comes first and ask for N resources (and gets suspended because the semaphore doesn't have any) then process B comes and asks for just a single resource, B will be suspended and will not run until the request of A can be satisfied first. Note that B gets suspended even if the semaphore could satisfy its request, because the policy says that the processes must leave the semaphore in the same order as they arrived to it.

- By priority
  The processing order is determined by the effective priority of the waiting processes; the highest priority process gets served first. The arbitration between identical priority processes is random.

- Smallest requests first
  The smallest requested amount is served first. If there are identical amounts, the order is random.

- Largest requests first
  The largest requested amount is served first. If there are identical amounts, the order is random.

In addition, semaphores support a timeout, where the caller can specify a maximum amount of time it is willing to wait for the resources.

Semaphores are represented by the `BX_SEMA` structure. You have to define the smeaphores you want to use, that is, the memory for the semaphore structure is provided by you. You must also initialise them either using a static initialiser (the `BX_SEMA_INIT()` macro) or by calling the initialiser function, `BX_SemaInit()`. The initialisation sets the semaphore's policy and the initial amount of resources it holds.

## Define Documentation

#define BX_SEMA_INIT | ( |  | policy,
|  |  | count |
| ) |  |  |

Value:

    {                                                               \
        .base = {                                                   \
            .type = BX_TYPE_SEMA,                                   \
            .head = NULL,                                           \
            .tail = NULL,                                           \
            .ftab = (struct _bx_poli *) (                           \
                    (policy) == BX_POLICY_SRQF ? &_bx_poli_srqf :   \
                    (policy) == BX_POLICY_LRQF ? &_bx_poli_lrqf :   \
                    (policy) == BX_POLICY_PRIO ? &_bx_poli_prio :   \
                    &_bx_poli_fifo )                                \
        },                                                          \
        .cntr = (count)                                             \
    }

Static initialiser for a semaphore.

Parameters:

  | policy | The semaphore's waiting policy, one of BX_POLICY_FIFO, BX_POLICY_PRIO, BX_POLICY_LRQF or BX_POLICY_SRQF.
  | count | The initial count on the semaphore.

## Function Documentation

int BX_SemaGet | ( | BX_SEMA * | sema,
|  | unsigned int | value,
|  | unsigned int | timeout |
| ) |  |  |

Waits for a number of resources on a semaphore.

Parameters:

  | sema | Pointer to a semaphore
  | value | The number of resources to be taken
  | timeout | The maximum time to wait, in microsecs. If the value is -1 (cast to `unsigned`), then there is no timeout, the function will wait indefinitely. If the timeout value is 0, then the function will not wait, it will return to the caller immediately.

Return values:

  | >0 | Success, the given number of resources were taken
  | =0 | Timeout, no resources were taken
  | BX_ERR_OBJECT | The semaphore was not initialised.
  | BX_ERR_CONTEXT | The function was called from an ISR or delayed ISR.
  | BX_ERR_SUSPEND | The caller would have to wait but it is currently running with IRQ or FIQ disabled and thus can not be suspended.

Note:
  Although the timeout value is specified in microseconds, the actual resolution depends on the frequency of calls to the BX_TimerTick() function.

Attention:
  This function should not be called from an ISR or delayed ISR. This function can suspend the caller.

int BX_SemaInit | ( | BX_SEMA * | sema,
|  | BX_POLICY | policy,
|  | unsigned int | value |
| ) |  |  |

Initialises a semaphore.

Parameters:

  | sema | Pointer to the semaphore structure
  | value | Initial value (resource count) of the semaphore
  | policy | The semaphore queueing policy. It must be one of BX_POLICY_FIFO, BX_POLICY_PRIO, BX_POLICY_LRQF or BX_POLICY_SRQF.

Return values:

  | >0 | Success
  | BX_ERR_PARAM | The policy parameter is not valid for a sempaphore

Attention:
  This function does not suspend the caller. This function should not be called from an ISR or delayed ISR. Note, however, that the function does not check this criteria, to allow initialisation of semaphores before the kernel is started (which must happen in IRQ context). In addition, during the initialisation interrupts are not disabled.

int BX_SemaPut | ( | BX_SEMA * | sema,
|  | unsigned int | value |
| ) |  |  |

Add resources to a semaphore.

The counter of the semaphore will be incremented by the amount specified by the parameter. If the increment would overflow the semaphore's counter, then the counter will be set to its maximum value (0xffffffff).

Parameters:

  | sema | Pointer to a semaphore
  | value | The number of resources to be added to the semaphore

Return values:

  | >0 | Success, the semaphore count was increased
  | BX_ERR_OBJECT | The semaphore was not initialised.

Attention:
  This function can be called from user, ISR or delayed ISR context. This function does not suspend the caller but can cause a context switch if a higher priority process is woken up by the call.