summaryrefslogtreecommitdiffstats
path: root/src/ssx/ssx/ssx_semaphore_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ssx/ssx/ssx_semaphore_core.c')
-rwxr-xr-xsrc/ssx/ssx/ssx_semaphore_core.c331
1 files changed, 331 insertions, 0 deletions
diff --git a/src/ssx/ssx/ssx_semaphore_core.c b/src/ssx/ssx/ssx_semaphore_core.c
new file mode 100755
index 0000000..f1f64e8
--- /dev/null
+++ b/src/ssx/ssx/ssx_semaphore_core.c
@@ -0,0 +1,331 @@
+// $Id: ssx_semaphore_core.c,v 1.2 2014/02/03 01:30:44 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/ssx/ssx_semaphore_core.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file ssx_semaphore_core.c
+/// \brief SSX semaphore APIs
+///
+/// The entry points in this file are considered 'core' routines that will
+/// always be present at runtime in any SSX application that enables
+/// semaphores.
+
+#include "ssx.h"
+
+/// Post a count to a semaphore
+///
+/// \param semaphore A pointer to the semaphore
+///
+/// If any thread is pending on the semaphore, the highest priority thread
+/// will be made runnable and the internal count will remain 0.
+///
+/// If no thread is pending on the semaphore then the internal count will be
+/// incremented by 1, with overflow wrapping the internal count through 0. If
+/// the \a max_count argument supplied when the semaphore was created is
+/// non-zero and the new internal count is greater than the \a max_count, an
+/// overflow error will be signalled.
+///
+/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors
+///
+/// \retval 0 Successful completion
+///
+/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical interrupt
+/// context.
+///
+/// \retval -SSX_INVALID_SEMAPHORE_AT_POST The \a semaphore is a null (0) pointer.
+///
+/// \retval -SSX_SEMAPHORE_OVERFLOW The \a max_count argument supplied when
+/// the semaphore was created is non-zero and the new internal count is
+/// greater than the \a max_count.
+
+int
+ssx_semaphore_post(SsxSemaphore *semaphore)
+{
+ SsxMachineContext ctx;
+ SsxThreadPriority priority;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT();
+ SSX_ERROR_IF(semaphore == 0, SSX_INVALID_SEMAPHORE_AT_POST);
+ }
+
+ ssx_critical_section_enter(SSX_NONCRITICAL, &ctx);
+
+ priority = __ssx_thread_queue_min(&(semaphore->pending_threads));
+
+ if (priority != SSX_IDLE_THREAD_PRIORITY) {
+
+ __ssx_thread_queue_delete(&(semaphore->pending_threads), priority);
+ __ssx_thread_queue_insert(&__ssx_run_queue, priority);
+
+ SSX_TRACE_THREAD_SEMAPHORE_POST(priority);
+
+ __ssx_schedule();
+
+ } else {
+
+ semaphore->count++;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF((semaphore->max_count > 0) &&
+ (semaphore->count > semaphore->max_count),
+ SSX_SEMAPHORE_OVERFLOW);
+ }
+ }
+
+ ssx_critical_section_exit(&ctx);
+
+ return SSX_OK;
+}
+
+
+/// Pend on a semaphore with timeout
+///
+/// \param semaphore A pointer to the semaphore
+///
+/// \param timeout A relative timeout in SSX timebase ticks, including the
+/// special values SSX_NO_WAIT and SSX_WAIT_FOREVER
+///
+/// This API is normally called from threads, and can only be successfully
+/// called from interupt handlers under special conditions.
+///
+/// If the internal count of the \a semaphore is non-zero, the internal count
+/// is decremented by one and execution of the caller continues.
+///
+/// If the internal count of the \a semaphore is zero and the \a timeout is
+/// SSX_NO_WAIT (0) then the call returns immediately with the informational
+/// code -SSX_SEMAPHORE_PEND_NO_WAIT.
+///
+/// If the internal count of the \a semaphore is zero and the \a timeout is
+/// non-zero then a thread will block until either a semaphore count is
+/// acquired or the relative timeout expires. If this condition occurs in a
+/// call from an interrupt context or before threads have been started then
+/// the call will fail with the error \c -SSX_SEMAPHORE_PEND_WOULD_BLOCK.
+///
+/// Once timed out the thread is removed from the semaphore pending queue and
+/// made runnable, and the ssx_semaphore_pend() operation will fail, even if
+/// the semaphore count becomes available before the thread runs again. The
+/// ssx_semaphore_pend() API returns the informational code
+/// -SSX_SEMAPHORE_PEND_TIMED_OUT in this case.
+///
+/// By convention, a timeout interval equal to the maximum possible value of
+/// the \c SsxInterval type is taken to mean "wait forever". A thread blocked
+/// on a semaphore in this mode will never time out. SSX provides this
+/// constant as \c SSX_WAIT_FOREVER.
+///
+/// Return values other than SSX_OK (0) are not necessarily errors; see \ref
+/// ssx_errors
+///
+/// The following return codes are non-error codes:
+///
+/// \retval 0 Successful completion
+///
+/// \retval -SSX_SEMAPHORE_PEND_NO_WAIT timeout is set to SSX_NO_WAIT
+///
+/// \retval -SSX_SEMAPHORE_PEND_TIMED_OUT The semaphore was not acquired
+/// before the timeout expired.
+///
+/// The following return codes are error codes:
+///
+/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical interrupt
+/// context.
+///
+/// \retval -SSX_INVALID_SEMAPHORE_AT_PEND The \a semaphore is a null (0)
+/// pointer.
+///
+/// \retval -SSX_SEMAPHORE_PEND_WOULD_BLOCK The call was made from an
+/// interrupt context (or before threads have been started), the semaphore
+/// internal count was 0 and a non-zero timeout was specified.
+
+// Note: Casting __ssx_current_thread removes the 'volatile' attribute.
+
+int
+ssx_semaphore_pend(SsxSemaphore *semaphore,
+ SsxInterval timeout)
+{
+ SsxMachineContext ctx;
+ SsxThreadPriority priority;
+ SsxThread *thread;
+ SsxTimer *timer = 0;
+ int rc = SSX_OK;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT();
+ SSX_ERROR_IF(semaphore == 0, SSX_INVALID_SEMAPHORE_AT_PEND);
+ }
+
+ ssx_critical_section_enter(SSX_NONCRITICAL, &ctx);
+
+ if (semaphore->count != 0) {
+
+ semaphore->count--;
+
+ } else if (timeout == SSX_NO_WAIT) {
+
+ rc = -SSX_SEMAPHORE_PEND_NO_WAIT;
+
+ } else {
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF_CRITICAL(!__ssx_kernel_context_thread(),
+ SSX_SEMAPHORE_PEND_WOULD_BLOCK,
+ &ctx);
+ }
+
+ thread = (SsxThread *)__ssx_current_thread;
+ priority = thread->priority;
+
+ __ssx_thread_queue_insert(&(semaphore->pending_threads), priority);
+
+ thread->semaphore = semaphore;
+ thread->flags |= SSX_THREAD_FLAG_SEMAPHORE_PEND;
+
+ SSX_TRACE_THREAD_SEMAPHORE_PEND(priority);
+
+ if (timeout != SSX_WAIT_FOREVER) {
+ timer = &(thread->timer);
+ timer->timeout = ssx_timebase_get() + timeout;
+ __ssx_timer_schedule(timer);
+ thread->flags |= SSX_THREAD_FLAG_TIMER_PEND;
+ }
+
+ __ssx_thread_queue_delete(&__ssx_run_queue, priority);
+ __ssx_schedule();
+
+ thread->flags &= ~SSX_THREAD_FLAG_SEMAPHORE_PEND;
+
+ if (thread->flags & SSX_THREAD_FLAG_TIMER_PEND) {
+ if (thread->flags & SSX_THREAD_FLAG_TIMED_OUT) {
+ rc = -SSX_SEMAPHORE_PEND_TIMED_OUT;
+ } else {
+ __ssx_timer_cancel(timer);
+ }
+ thread->flags &=
+ ~(SSX_THREAD_FLAG_TIMER_PEND | SSX_THREAD_FLAG_TIMED_OUT);
+ }
+ }
+
+ ssx_critical_section_exit(&ctx);
+
+ return rc;
+}
+
+
+/// Release all threads blocked on a semaphore
+///
+/// \param semaphore A pointer to a semaphore
+///
+/// This API is provided to allow an SSX semaphore to be used as a thread
+/// barrier. ssx_semaphore_release_all() simultaneously unblocks all threads
+/// (if any) currently pending on a semaphore. A semaphore to be used as a
+/// thread barrier will typically be initialized with
+/// ssx_semaphore_create(\a sem, 0, 0), and sxx_semaphore_post() would never be
+/// called on the \a sem.
+///
+/// This API never modifies the \a count field of the semaphore; If any
+/// threads are blocked on a semaphore the semaphore count is 0 by definition.
+///
+/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors
+///
+/// \retval 0 Successful completion
+///
+/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical interrupt
+/// context.
+///
+/// \retval -SSX_INVALID_SEMAPHORE_AT_RELEASE The \a semaphore is a null (0)
+/// pointer.
+
+int
+ssx_semaphore_release_all(SsxSemaphore* semaphore)
+{
+ SsxMachineContext ctx;
+
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT();
+ SSX_ERROR_IF(semaphore == 0, SSX_INVALID_SEMAPHORE_AT_RELEASE);
+ }
+
+ ssx_critical_section_enter(SSX_NONCRITICAL, &ctx);
+
+ __ssx_thread_queue_union(&__ssx_run_queue, &(semaphore->pending_threads));
+ __ssx_thread_queue_clear(&(semaphore->pending_threads));
+ __ssx_schedule();
+
+ ssx_critical_section_exit(&ctx);
+
+ return SSX_OK;
+}
+
+
+/// Get information about a semaphore.
+///
+/// \param semaphore A pointer to the SsxSemaphore to query
+///
+/// \param count The value returned through this pointer is the current count
+/// of the semaphore. The caller can set this parameter to the null pointer
+/// (0) if this information is not required.
+///
+/// \param pending The value returned through this pointer is the current
+/// number of threads pending on the semaphore. The caller can set this
+/// parameter to the null pointer (0) if this information is not required.
+///
+/// The information returned by this API can only be guaranteed consistent if
+/// the API is called from an SSX_NONCRITICAL critical section. Since the
+/// implementation of this API does not require a critical section, it is not
+/// an error to call this API from a critical interrupt context.
+///
+/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors
+///
+/// \retval 0 Successful completion
+///
+/// \retval -SSX_INVALID_SEMAPHORE_AT_INFO The \a semaphore is a null (0)
+/// pointer.
+
+int
+ssx_semaphore_info_get(SsxSemaphore* semaphore,
+ SsxSemaphoreCount* count,
+ int* pending)
+
+{
+ if (SSX_ERROR_CHECK_API) {
+ SSX_ERROR_IF(semaphore == 0, SSX_INVALID_SEMAPHORE_AT_INFO);
+ }
+
+ if (count) {
+ *count = semaphore->count;
+ }
+ if (pending) {
+ *pending = __ssx_thread_queue_count(&(semaphore->pending_threads));
+ }
+
+ return SSX_OK;
+}
+
+
+/// An simple interrupt handler that posts to a semaphore.
+///
+/// To implement basic event-driven blocking of a thread, install
+/// ssx_semaphore_post_handler() as the handler for a non-critical interrupt
+/// and provide a pointer to the semaphore as the \a arg argument in
+/// ssx_irq_handler_set(). The semaphore should be initialized with
+/// ssx_semaphore_create(&sem, 0, 1). This handler simply disables (masks)
+/// the interrupt, clears the status and calls ssx_semaphore_post() on the
+/// semaphore.
+///
+/// Note that clearing the status in the interrupt controller as done here is
+/// effectively a no-op for level-sensitive interrupts. In the level-sensitive
+/// case any thread pending on the semaphore must reset the interrupt
+/// condition in the device before re-enabling the interrupt.
+
+void
+ssx_semaphore_post_handler_full(void *arg, SsxIrqId irq, int priority)
+{
+ ssx_irq_disable(irq);
+ ssx_irq_status_clear(irq);
+ ssx_semaphore_post((SsxSemaphore *)arg);
+}
+
+SSX_IRQ_FAST2FULL(ssx_semaphore_post_handler, ssx_semaphore_post_handler_full);
OpenPOWER on IntegriCloud