summaryrefslogblamecommitdiffstats
path: root/c/src/exec/rtems/src/sem.c
blob: d810c205eea3127a368b355325f5de7109fff02a (plain) (tree)




























                                                                          
                          




























                                                                           
                             

                       

                                

                              






















                                                                       
                           






                                            
                                     






















































                                                                          
                                                                          
































                                                        
                     



                     
                                                                            






































































































































































































































































































                                                                                
/*
 *  Semaphore Manager
 *
 *  DESCRIPTION:
 *
 *  This package is the implementation of the Semaphore Manager.
 *  This manager utilizes standard Dijkstra counting semaphores to provide
 *  synchronization and mutual exclusion capabilities.
 *
 *  Directives provided are:
 *
 *     + create a semaphore
 *     + get an ID of a semaphore
 *     + delete a semaphore
 *     + acquire a semaphore
 *     + release a semaphore
 *
 *  COPYRIGHT (c) 1989, 1990, 1991, 1992, 1993, 1994.
 *  On-Line Applications Research Corporation (OAR).
 *  All rights assigned to U.S. Government, 1994.
 *
 *  This material may be reproduced by or for the U.S. Government pursuant
 *  to the copyright license under the clause at DFARS 252.227-7013.  This
 *  notice must appear in all copies of this file and its derivatives.
 *
 *  $Id$
 */

#include <rtems/system.h>
#include <rtems/support.h>
#include <rtems/attr.h>
#include <rtems/config.h>
#include <rtems/isr.h>
#include <rtems/object.h>
#include <rtems/options.h>
#include <rtems/sem.h>
#include <rtems/states.h>
#include <rtems/thread.h>
#include <rtems/threadq.h>
#include <rtems/mpci.h>

/*PAGE
 *
 *  _Semaphore_Manager_initialization
 *
 *  This routine initializes all semaphore manager related data structures.
 *
 *  Input parameters:
 *    maximum_semaphores - maximum configured semaphores
 *
 *  Output parameters:  NONE
 */

void _Semaphore_Manager_initialization(
  unsigned32 maximum_semaphores
)
{
  _Objects_Initialize_information(
    &_Semaphore_Information,
    OBJECTS_RTEMS_SEMAPHORES,
    TRUE,
    maximum_semaphores,
    sizeof( Semaphore_Control ),
    FALSE,
    RTEMS_MAXIMUM_NAME_LENGTH,
    FALSE
  );
}

/*PAGE
 *
 *  rtems_semaphore_create
 *
 *  This directive creates a semaphore and sets the initial value based
 *  on the given count.  A semaphore id is returned.
 *
 *  Input parameters:
 *    name          - user defined semaphore name
 *    count         - initial count of semaphore
 *    attribute_set - semaphore attributes
 *    id            - pointer to semaphore id
 *
 *  Output parameters:
 *    id       - semaphore id
 *    RTEMS_SUCCESSFUL - if successful
 *    error code - if unsuccessful
 */

rtems_status_code rtems_semaphore_create(
  rtems_name          name,
  unsigned32          count,
  rtems_attribute  attribute_set,
  Objects_Id         *id
)
{
  register Semaphore_Control *the_semaphore;

  if ( !rtems_is_name_valid( name ) )
    return ( RTEMS_INVALID_NAME );

  if ( _Attributes_Is_global( attribute_set ) ) {

    if ( !_Configuration_Is_multiprocessing() )
      return( RTEMS_MP_NOT_CONFIGURED );

    if ( _Attributes_Is_inherit_priority( attribute_set ) )
      return( RTEMS_NOT_DEFINED );

  } else if ( _Attributes_Is_inherit_priority( attribute_set ) ) {

    if ( ! ( _Attributes_Is_binary_semaphore( attribute_set ) &&
             _Attributes_Is_priority( attribute_set ) ) )
      return( RTEMS_NOT_DEFINED );

  }

  if ( _Attributes_Is_binary_semaphore( attribute_set ) && ( count > 1 ) )
    return( RTEMS_INVALID_NUMBER );

  _Thread_Disable_dispatch();             /* prevents deletion */

  the_semaphore = _Semaphore_Allocate();

  if ( !the_semaphore ) {
    _Thread_Enable_dispatch();
    return( RTEMS_TOO_MANY );
  }

  if ( _Attributes_Is_global( attribute_set ) &&
       !( _Objects_MP_Open( &_Semaphore_Information, name,
                            the_semaphore->Object.id, FALSE ) ) ) {
    _Semaphore_Free( the_semaphore );
    _Thread_Enable_dispatch();
    return( RTEMS_TOO_MANY );
  }

  the_semaphore->attribute_set = attribute_set;
  the_semaphore->count         = count;

  if ( _Attributes_Is_binary_semaphore( attribute_set ) && count == 0 ) {
    the_semaphore->nest_count = 1;
    the_semaphore->holder     = _Thread_Executing;
    the_semaphore->holder_id  = _Thread_Executing->Object.id;
    _Thread_Executing->resource_count++;
  } else {
    the_semaphore->nest_count = 0;
    the_semaphore->holder     = NULL;
    the_semaphore->holder_id  = 0;
  }

  _Thread_queue_Initialize( &the_semaphore->Wait_queue,
                            attribute_set, STATES_WAITING_FOR_SEMAPHORE );

  _Objects_Open( &_Semaphore_Information, &the_semaphore->Object, &name );

  *id = the_semaphore->Object.id;

  if ( _Attributes_Is_global( attribute_set ) )
    _Semaphore_MP_Send_process_packet(
      SEMAPHORE_MP_ANNOUNCE_CREATE,
      the_semaphore->Object.id,
      name,
      0                          /* Not used */
    );
  _Thread_Enable_dispatch();
  return( RTEMS_SUCCESSFUL );
}

/*PAGE
 *
 *  rtems_semaphore_ident
 *
 *  This directive returns the system ID associated with
 *  the semaphore name.
 *
 *  Input parameters:
 *    name - user defined semaphore name
 *    node - node(s) to be searched
 *    id   - pointer to semaphore id
 *
 *  Output parameters:
 *    *id      - semaphore id
 *    RTEMS_SUCCESSFUL - if successful
 *    error code - if unsuccessful
 */

rtems_status_code rtems_semaphore_ident(
  rtems_name    name,
  unsigned32    node,
  Objects_Id   *id
)
{
  return( _Objects_Name_to_id( &_Semaphore_Information, &name, node, id ) );
}

/*PAGE
 *
 *  rtems_semaphore_delete
 *
 *  This directive allows a thread to delete a semaphore specified by
 *  the semaphore id.  The semaphore is freed back to the inactive
 *  semaphore chain.
 *
 *  Input parameters:
 *    id - semaphore id
 *
 *  Output parameters:
 *    RTEMS_SUCCESSFUL - if successful
 *    error code        - if unsuccessful
 */

rtems_status_code rtems_semaphore_delete(
  Objects_Id id
)
{
  register Semaphore_Control *the_semaphore;
  Objects_Locations           location;

  the_semaphore = _Semaphore_Get( id, &location );
  switch ( location ) {
    case OBJECTS_ERROR:
      return( RTEMS_INVALID_ID );
    case OBJECTS_REMOTE:
      _Thread_Dispatch();
      return( RTEMS_ILLEGAL_ON_REMOTE_OBJECT );
    case OBJECTS_LOCAL:
      if ( _Attributes_Is_binary_semaphore( the_semaphore->attribute_set) &&
                       ( the_semaphore->count == 0 ) ) {
        _Thread_Enable_dispatch();
        return( RTEMS_RESOURCE_IN_USE );
      }

      _Objects_Close( &_Semaphore_Information, &the_semaphore->Object );

      _Thread_queue_Flush(
        &the_semaphore->Wait_queue,
        _Semaphore_MP_Send_object_was_deleted
      );

      _Semaphore_Free( the_semaphore );

      if ( _Attributes_Is_global( the_semaphore->attribute_set ) ) {

        _Objects_MP_Close( &_Semaphore_Information, the_semaphore->Object.id );

        _Semaphore_MP_Send_process_packet(
          SEMAPHORE_MP_ANNOUNCE_DELETE,
          the_semaphore->Object.id,
          0,                         /* Not used */
          0                          /* Not used */
        );
      }
      _Thread_Enable_dispatch();
      return( RTEMS_SUCCESSFUL );
  }

  return( RTEMS_INTERNAL_ERROR );   /* unreached - only to remove warnings */
}

/*PAGE
 *
 *  rtems_semaphore_obtain
 *
 *  This directive allows a thread to acquire a semaphore.
 *
 *  Input parameters:
 *    id         - semaphore id
 *    option_set - wait option
 *    timeout    - number of ticks to wait (0 means wait forever)
 *
 *  Output parameters:
 *    RTEMS_SUCCESSFUL - if successful
 *    error code        - if unsuccessful
 */

rtems_status_code rtems_semaphore_obtain(
  Objects_Id        id,
  unsigned32        option_set,
  rtems_interval timeout
)
{
  register Semaphore_Control *the_semaphore;
  Objects_Locations           location;

  the_semaphore = _Semaphore_Get( id, &location );
  switch ( location ) {
    case OBJECTS_ERROR:
      return( RTEMS_INVALID_ID );
    case OBJECTS_REMOTE:
      return _Semaphore_MP_Send_request_packet(
          SEMAPHORE_MP_OBTAIN_REQUEST,
          id,
          option_set,
          timeout
      );
    case OBJECTS_LOCAL:
      if ( !_Semaphore_Seize( the_semaphore, option_set ) ) {
        if ( _Attributes_Is_inherit_priority( the_semaphore->attribute_set ) &&
             the_semaphore->holder->current_priority >
               _Thread_Executing->current_priority ) {
            _Thread_Change_priority(
              the_semaphore->holder, _Thread_Executing->current_priority );
         }
        _Thread_queue_Enqueue( &the_semaphore->Wait_queue, timeout );
      }
      _Thread_Enable_dispatch();
      return( _Thread_Executing->Wait.return_code );
  }

  return( RTEMS_INTERNAL_ERROR );   /* unreached - only to remove warnings */
}

/*PAGE
 *
 *  rtems_semaphore_release
 *
 *  This directive allows a thread to release a semaphore.
 *
 *  Input parameters:
 *    id - semaphore id
 *
 *  Output parameters:
 *    RTEMS_SUCCESSFUL - if successful
 *    error code        - if unsuccessful
 */

rtems_status_code rtems_semaphore_release(
  Objects_Id id
)
{
  register Semaphore_Control *the_semaphore;
  Objects_Locations           location;
  Thread_Control             *the_thread;

  the_semaphore = _Semaphore_Get( id, &location );
  switch ( location ) {
    case OBJECTS_ERROR:
      return( RTEMS_INVALID_ID );
    case OBJECTS_REMOTE:
      return(
        _Semaphore_MP_Send_request_packet(
          SEMAPHORE_MP_RELEASE_REQUEST,
          id,
          0,                               /* Not used */
          MPCI_DEFAULT_TIMEOUT
        )
      );
    case OBJECTS_LOCAL:
      if ( _Attributes_Is_binary_semaphore( the_semaphore->attribute_set)) {

        if ( !_Objects_Are_ids_equal(
               _Thread_Executing->Object.id, the_semaphore->holder_id ) ) {
          _Thread_Enable_dispatch();
          return( RTEMS_NOT_OWNER_OF_RESOURCE );
        }

        the_semaphore->nest_count--;

        if ( the_semaphore->nest_count != 0 ) {
          _Thread_Enable_dispatch();
          return( RTEMS_SUCCESSFUL );
        }

        _Thread_Executing->resource_count--;
        the_semaphore->holder    = NULL;
        the_semaphore->holder_id = 0;

        /*
         *  Whether or not someone is waiting for the semaphore, an
         *  inherited priority must be lowered if this is the last
         *  semaphore (i.e. resource) this task has.
         */

        if ( _Attributes_Is_inherit_priority(the_semaphore->attribute_set) &&
             _Thread_Executing->resource_count == 0 &&
             _Thread_Executing->real_priority !=
                _Thread_Executing->current_priority ) {
           _Thread_Change_priority(
              _Thread_Executing, _Thread_Executing->real_priority );
        }

      }

      if ( (the_thread = _Thread_queue_Dequeue(&the_semaphore->Wait_queue)) ) {

        if ( !_Objects_Is_local_id( the_thread->Object.id ) ) {
          the_thread->receive_packet->return_code = RTEMS_SUCCESSFUL;

          if ( _Attributes_Is_binary_semaphore(the_semaphore->attribute_set) ) {
            the_semaphore->holder    = NULL;
            the_semaphore->holder_id = the_thread->Object.id;
            the_semaphore->nest_count = 1;
          }

          _Semaphore_MP_Send_response_packet(
            SEMAPHORE_MP_OBTAIN_RESPONSE,
            id,
            the_thread
          );
        } else {

          if ( _Attributes_Is_binary_semaphore(the_semaphore->attribute_set) ) {
            the_semaphore->holder    = the_thread;
            the_semaphore->holder_id = the_thread->Object.id;
            the_thread->resource_count++;
            the_semaphore->nest_count = 1;
          }

          /*
           *  No special action for priority inheritance because the_thread
           *  is guaranteed to be the highest priority thread waiting for
           *  the semaphore.
           */
        }
      } else
        the_semaphore->count += 1;

      _Thread_Enable_dispatch();
      return( RTEMS_SUCCESSFUL );
  }

  return( RTEMS_INTERNAL_ERROR );   /* unreached - only to remove warnings */
}

/*PAGE
 *
 *  _Semaphore_Seize
 *
 *  This routine attempts to allocate a semaphore to the calling thread.
 *
 *  Input parameters:
 *    the_semaphore - pointer to semaphore control block
 *    option_set    - acquire semaphore options
 *
 *  Output parameters:
 *    TRUE  - if semaphore allocated
 *    FALSE - if semaphore NOT allocated
 *
 *  INTERRUPT LATENCY:
 *    available
 *    wait
 */

boolean _Semaphore_Seize(
  Semaphore_Control *the_semaphore,
  rtems_option    option_set
)
{
  Thread_Control *executing;
  ISR_Level       level;

  executing = _Thread_Executing;
  executing->Wait.return_code = RTEMS_SUCCESSFUL;
  _ISR_Disable( level );
  if ( the_semaphore->count != 0 ) {
    the_semaphore->count -= 1;
    if ( _Attributes_Is_binary_semaphore( the_semaphore->attribute_set ) ) {
      the_semaphore->holder     = executing;
      the_semaphore->holder_id  = executing->Object.id;
      the_semaphore->nest_count = 1;
      executing->resource_count++;
    }
    _ISR_Enable( level );
    return( TRUE );
  }

  if ( _Options_Is_no_wait( option_set ) ) {
    _ISR_Enable( level );
    executing->Wait.return_code = RTEMS_UNSATISFIED;
    return( TRUE );
  }

  if ( _Attributes_Is_binary_semaphore( the_semaphore->attribute_set ) ) {
    if ( _Objects_Are_ids_equal(
            _Thread_Executing->Object.id, the_semaphore->holder_id ) ) {
      the_semaphore->nest_count++;
      _ISR_Enable( level );
      return( TRUE );
    }
  }

  the_semaphore->Wait_queue.sync = TRUE;
  executing->Wait.queue      = &the_semaphore->Wait_queue;
  executing->Wait.id         = the_semaphore->Object.id;
  executing->Wait.option_set = option_set;
  _ISR_Enable( level );
  return( FALSE );
}