text: // $Id: OS_NS_Thread.cpp 97326 2013-09-11 07:52:09Z johnnyw $
  
  #include "ace/OS_NS_Thread.h"
  
  #if !defined (ACE_HAS_INLINED_OSCALLS)
  # include "ace/OS_NS_Thread.inl"
  #endif /* ACE_HAS_INLINED_OSCALLS */
  
  #include "ace/OS_NS_stdio.h"
  #include "ace/Sched_Params.h"
  #include "ace/OS_Memory.h"
  #include "ace/OS_Thread_Adapter.h"
  #include "ace/Min_Max.h"
  #include "ace/Object_Manager_Base.h"
  #include "ace/OS_NS_errno.h"
  #include "ace/OS_NS_ctype.h"
  #include "ace/Log_Category.h" // for ACE_ASSERT
  // This is necessary to work around nasty problems with MVS C++.
  #include "ace/Auto_Ptr.h"
  #include "ace/Thread_Mutex.h"
  #include "ace/Condition_Thread_Mutex.h"
  #include "ace/Guard_T.h"
  
  extern "C" void
  ACE_MUTEX_LOCK_CLEANUP_ADAPTER_NAME (void *args)
  {
    ACE_VERSIONED_NAMESPACE_NAME::ACE_OS::mutex_lock_cleanup (args);
  }
  
  
  #if !defined(ACE_WIN32) && defined (__IBMCPP__) && (__IBMCPP__ >= 400)
  # define ACE_BEGINTHREADEX(STACK, STACKSIZE, ENTRY_POINT, ARGS, FLAGS, THR_ID) \
         (*THR_ID = ::_beginthreadex ((void(_Optlink*)(void*))ENTRY_POINT, STACK, STACKSIZE, ARGS), *THR_ID)
  #elif defined (ACE_HAS_WINCE)
  # define ACE_BEGINTHREADEX(STACK, STACKSIZE, ENTRY_POINT, ARGS, FLAGS, THR_ID) \
        CreateThread (0, STACKSIZE, (unsigned long (__stdcall *) (void *)) ENTRY_POINT, ARGS, (FLAGS) & (CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION), (unsigned long *) THR_ID)
  #elif defined(ACE_HAS_WTHREADS)
    // Green Hills compiler gets confused when __stdcall is imbedded in
    // parameter list, so we define the type ACE_WIN32THRFUNC_T and use it
    // instead.
    typedef unsigned (__stdcall *ACE_WIN32THRFUNC_T)(void*);
  # define ACE_BEGINTHREADEX(STACK, STACKSIZE, ENTRY_POINT, ARGS, FLAGS, THR_ID) \
        ::_beginthreadex (STACK, STACKSIZE, (ACE_WIN32THRFUNC_T) ENTRY_POINT, ARGS, FLAGS, (unsigned int *) THR_ID)
  #endif /* defined (__IBMCPP__) && (__IBMCPP__ >= 400) */
  
  /*****************************************************************************/
  
  ACE_BEGIN_VERSIONED_NAMESPACE_DECL
  
  void
  ACE_Thread_ID::to_string (char *thr_string) const
  {
  #if defined (ACE_WIN32)
    ACE_OS::sprintf (thr_string, "%u",
                     static_cast <unsigned> (this->thread_id_));
  #else
    // Yes, this is an ugly C-style cast, but the correct C++ cast is
    // different depending on whether the t_id is an integral type or a
    // pointer type. FreeBSD uses a pointer type, but doesn't have a _np
    // function to get an integral type like other OSes, so use the
    // bigger hammer.
    ACE_OS::sprintf (thr_string, "%lu",
                     (unsigned long) thread_handle_);
  #endif /* ACE_WIN32 */
  }
  
  /*****************************************************************************/
  
  #if defined (ACE_WIN32) || defined (ACE_HAS_TSS_EMULATION)
  
  #if defined (ACE_HAS_TSS_EMULATION)
  u_int ACE_TSS_Emulation::total_keys_ = 0;
  
  ACE_TSS_Keys* ACE_TSS_Emulation::tss_keys_used_ = 0;
  
  ACE_TSS_Emulation::ACE_TSS_DESTRUCTOR
  ACE_TSS_Emulation::tss_destructor_[ACE_TSS_Emulation::ACE_TSS_THREAD_KEYS_MAX]
   = { 0 };
  
  #  if defined (ACE_HAS_THREAD_SPECIFIC_STORAGE)
  
  bool ACE_TSS_Emulation::key_created_ = false;
  
  ACE_OS_thread_key_t ACE_TSS_Emulation::native_tss_key_;
  
  /* static */
  #    if defined (ACE_HAS_THR_C_FUNC)
  extern "C"
  void
  ACE_TSS_Emulation_cleanup (void *)
  {
     // Really this must be used for ACE_TSS_Emulation code to make the TSS
     // cleanup
  }
  #    else
  void
  ACE_TSS_Emulation_cleanup (void *)
  {
     // Really this must be used for ACE_TSS_Emulation code to make the TSS
     // cleanup
  }
  #    endif /* ACE_HAS_THR_C_FUNC */
  
  void **
  ACE_TSS_Emulation::tss_base (void* ts_storage[], u_int *ts_created)
  {
    // TSS Singleton implementation.
  
    // Create the one native TSS key, if necessary.
    if (!key_created_)
      {
        // Double-checked lock . . .
        ACE_TSS_BASE_GUARD
  
        if (!key_created_)
          {
            ACE_NO_HEAP_CHECK;
            if (ACE_OS::thr_keycreate_native (&native_tss_key_,
                                       &ACE_TSS_Emulation_cleanup) != 0)
              {
                ACE_ASSERT (0);
                return 0; // Major problems, this should *never* happen!
              }
            key_created_ = true;
          }
      }
  
    void **old_ts_storage = 0;
  
    // Get the tss_storage from thread-OS specific storage.
    if (ACE_OS::thr_getspecific_native (native_tss_key_,
                                 (void **) &old_ts_storage) == -1)
      {
        ACE_ASSERT (false);
        return 0; // This should not happen!
      }
  
    // Check to see if this is the first time in for this thread.
    // This block can also be entered after a fork () in the child process.
    if (old_ts_storage == 0)
      {
        if (ts_created)
          *ts_created = 1u;
  
        // Use the ts_storage passed as argument, if non-zero.  It is
        // possible that this has been implemented in the stack. At the
        // moment, this is unknown.  The cleanup must not do nothing.
        // If ts_storage is zero, allocate (and eventually leak) the
        // storage array.
        if (ts_storage == 0)
          {
            ACE_NO_HEAP_CHECK;
  
            ACE_NEW_RETURN (ts_storage,
                            void*[ACE_TSS_THREAD_KEYS_MAX],
                            0);
  
            // Zero the entire TSS array.  Do it manually instead of
            // using memset, for optimum speed.  Though, memset may be
            // faster :-)
            void **tss_base_p = ts_storage;
  
            for (u_int i = 0;
                 i < ACE_TSS_THREAD_KEYS_MAX;
                 ++i)
              *tss_base_p++ = 0;
          }
  
         // Store the pointer in thread-specific storage.  It gets
         // deleted via the ACE_TSS_Emulation_cleanup function when the
         // thread terminates.
         if (ACE_OS::thr_setspecific_native (native_tss_key_,
                                      (void *) ts_storage) != 0)
            {
              ACE_ASSERT (false);
              return 0; // This should not happen!
            }
      }
    else
      if (ts_created)
        ts_created = 0;
  
    return ts_storage  ?  ts_storage  :  old_ts_storage;
  }
  #  endif /* ACE_HAS_THREAD_SPECIFIC_STORAGE */
  
  u_int
  ACE_TSS_Emulation::total_keys ()
  {
    ACE_OS_Recursive_Thread_Mutex_Guard (
      *static_cast <ACE_recursive_thread_mutex_t *>
                        (ACE_OS_Object_Manager::preallocated_object[
                          ACE_OS_Object_Manager::ACE_TSS_KEY_LOCK]));
  
    return total_keys_;
  }
  
  int
  ACE_TSS_Emulation::next_key (ACE_thread_key_t &key)
  {
    ACE_OS_Recursive_Thread_Mutex_Guard (
      *static_cast <ACE_recursive_thread_mutex_t *>
                        (ACE_OS_Object_Manager::preallocated_object[
                          ACE_OS_Object_Manager::ACE_TSS_KEY_LOCK]));
  
    // Initialize the tss_keys_used_ pointer on first use.
    if (tss_keys_used_ == 0)
      {
        ACE_NEW_RETURN (tss_keys_used_, ACE_TSS_Keys, -1);
      }
  
    if (total_keys_ < ACE_TSS_THREAD_KEYS_MAX)
      {
         u_int counter = 0;
         // Loop through all possible keys and check whether a key is free
         for ( ;counter < ACE_TSS_THREAD_KEYS_MAX; counter++)
           {
              ACE_thread_key_t localkey = counter;
              // If the key is not set as used, we can give out this key, if not
              // we have to search further
              if (tss_keys_used_->is_set(localkey) == 0)
              {
                 tss_keys_used_->test_and_set(localkey);
                 key = localkey;
                 break;
              }
           }
  
        ++total_keys_;
        return 0;
      }
    else
      {
        key = ACE_OS::NULL_key;
        return -1;
      }
  }
  
  int
  ACE_TSS_Emulation::release_key (ACE_thread_key_t key)
  {
    ACE_OS_Recursive_Thread_Mutex_Guard (
      *static_cast <ACE_recursive_thread_mutex_t *>
                        (ACE_OS_Object_Manager::preallocated_object[
                          ACE_OS_Object_Manager::ACE_TSS_KEY_LOCK]));
  
    if (tss_keys_used_ != 0 &&
        tss_keys_used_->test_and_clear (key) == 0)
    {
      --total_keys_;
      return 0;
    }
    return 1;
  }
  
  int
  ACE_TSS_Emulation::is_key (ACE_thread_key_t key)
  {
    ACE_OS_Recursive_Thread_Mutex_Guard (
      *static_cast <ACE_recursive_thread_mutex_t *>
                        (ACE_OS_Object_Manager::preallocated_object[
                          ACE_OS_Object_Manager::ACE_TSS_KEY_LOCK]));
  
    if (tss_keys_used_ != 0 &&
        tss_keys_used_->is_set (key) == 1)
    {
      return 1;
    }
    return 0;
  }
  
  void *
  ACE_TSS_Emulation::tss_open (void *ts_storage[ACE_TSS_THREAD_KEYS_MAX])
  {
  #    if defined (ACE_HAS_THREAD_SPECIFIC_STORAGE)
          // On VxWorks, in particular, don't check to see if the field
          // is 0.  It isn't always, specifically, when a program is run
          // directly by the shell (without spawning a new task) after
          // another program has been run.
  
    u_int ts_created = 0;
    tss_base (ts_storage, &ts_created);
    if (ts_created)
      {
  #    else  /* ! ACE_HAS_THREAD_SPECIFIC_STORAGE */
        tss_base () = ts_storage;
  #    endif
  
        // Zero the entire TSS array.  Do it manually instead of using
        // memset, for optimum speed.  Though, memset may be faster :-)
        void **tss_base_p = tss_base ();
        for (u_int i = 0; i < ACE_TSS_THREAD_KEYS_MAX; ++i, ++tss_base_p)
          {
            *tss_base_p = 0;
          }
  
        return tss_base ();
  #    if defined (ACE_HAS_THREAD_SPECIFIC_STORAGE)
      }
    else
      {
        return 0;
      }
  #    endif /* ACE_HAS_THREAD_SPECIFIC_STORAGE */
  }
  
  void
  ACE_TSS_Emulation::tss_close ()
  {
  #if defined (ACE_HAS_THREAD_SPECIFIC_STORAGE)
    ACE_OS::thr_keyfree_native (native_tss_key_);
  #endif /* ACE_HAS_THREAD_SPECIFIC_STORAGE */
  }
  
  #endif /* ACE_HAS_TSS_EMULATION */
  
  #endif /* WIN32 || ACE_HAS_TSS_EMULATION */
  
  /*****************************************************************************/
  
  #if defined (ACE_WIN32) || defined (ACE_HAS_TSS_EMULATION)
  
  // Moved class ACE_TSS_Ref declaration to OS.h so it can be visible to
  // the single file of template instantiations.
  
  ACE_TSS_Ref::ACE_TSS_Ref (ACE_thread_t id)
    : tid_(id)
  {
    ACE_OS_TRACE ("ACE_TSS_Ref::ACE_TSS_Ref");
  }
  
  ACE_TSS_Ref::ACE_TSS_Ref (void)
  {
    ACE_OS_TRACE ("ACE_TSS_Ref::ACE_TSS_Ref");
  }
  
  // Check for equality.
  bool
  ACE_TSS_Ref::operator== (const ACE_TSS_Ref &info) const
  {
    ACE_OS_TRACE ("ACE_TSS_Ref::operator==");
  
    return this->tid_ == info.tid_;
  }
  
  // Check for inequality.
  ACE_SPECIAL_INLINE
  bool
  ACE_TSS_Ref::operator != (const ACE_TSS_Ref &tss_ref) const
  {
    ACE_OS_TRACE ("ACE_TSS_Ref::operator !=");
  
    return !(*this == tss_ref);
  }
  
  // moved class ACE_TSS_Info declaration
  // to OS.h so it can be visible to the
  // single file of template instantiations
  
  ACE_TSS_Info::ACE_TSS_Info (ACE_thread_key_t key,
                              ACE_TSS_Info::Destructor dest)
    : key_ (key),
      destructor_ (dest),
      thread_count_ (-1)
  {
    ACE_OS_TRACE ("ACE_TSS_Info::ACE_TSS_Info");
  }
  
  ACE_TSS_Info::ACE_TSS_Info (void)
    : key_ (ACE_OS::NULL_key),
      destructor_ (0),
      thread_count_ (-1)
  {
    ACE_OS_TRACE ("ACE_TSS_Info::ACE_TSS_Info");
  }
  
  // Check for equality.
  bool
  ACE_TSS_Info::operator== (const ACE_TSS_Info &info) const
  {
    ACE_OS_TRACE ("ACE_TSS_Info::operator==");
  
    return this->key_ == info.key_;
  }
  
  // Check for inequality.
  bool
  ACE_TSS_Info::operator != (const ACE_TSS_Info &info) const
  {
    ACE_OS_TRACE ("ACE_TSS_Info::operator !=");
  
    return !(*this == info);
  }
  
  void
  ACE_TSS_Info::dump (void)
  {
  # if defined (ACE_HAS_DUMP)
    //  ACE_OS_TRACE ("ACE_TSS_Info::dump");
  
  #   if 0
    ACELIB_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this));
    ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("key_ = %u\n"), this->key_));
    ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("destructor_ = %u\n"), this->destructor_));
    ACELIB_DEBUG ((LM_DEBUG, ACE_END_DUMP));
  #   endif /* 0 */
  # endif /* ACE_HAS_DUMP */
  }
  
  // Moved class ACE_TSS_Keys declaration to OS.h so it can be visible
  // to the single file of template instantiations.
  
  ACE_TSS_Keys::ACE_TSS_Keys (void)
  {
    for (u_int i = 0; i < ACE_WORDS; ++i)
      {
        key_bit_words_[i] = 0;
      }
  }
  
  ACE_SPECIAL_INLINE
  void
  ACE_TSS_Keys::find (const u_int key, u_int &word, u_int &bit)
  {
    word = key / ACE_BITS_PER_WORD;
    bit = key % ACE_BITS_PER_WORD;
  }
  
  int
  ACE_TSS_Keys::test_and_set (const ACE_thread_key_t key)
  {
    u_int word, bit;
    find (key, word, bit);
  
    if (ACE_BIT_ENABLED (key_bit_words_[word], 1 << bit))
      {
        return 1;
      }
    else
      {
        ACE_SET_BITS (key_bit_words_[word], 1 << bit);
        return 0;
      }
  }
  
  int
  ACE_TSS_Keys::test_and_clear (const ACE_thread_key_t key)
  {
    u_int word, bit;
    find (key, word, bit);
  
    if (word < ACE_WORDS && ACE_BIT_ENABLED (key_bit_words_[word], 1 << bit))
      {
        ACE_CLR_BITS (key_bit_words_[word], 1 << bit);
        return 0;
      }
    else
      {
        return 1;
      }
  }
  
  int
  ACE_TSS_Keys::is_set (const ACE_thread_key_t key) const
  {
    u_int word, bit;
    find (key, word, bit);
  
    return word < ACE_WORDS ? ACE_BIT_ENABLED (key_bit_words_[word], 1 << bit) : 0;
  }
  
  /**
   * @class ACE_TSS_Cleanup
   * @brief Singleton that helps to manage the lifetime of TSS objects and keys.
   */
  class ACE_TSS_Cleanup
  {
  public:
    /// Register a newly-allocated key
    /// @param key the key to be monitored
    /// @param destructor the function to call to delete objects stored via this key
    int insert (ACE_thread_key_t key, void (*destructor)(void *));
  
    /// Mark a key as being used by this thread.
    void thread_use_key (ACE_thread_key_t key);
  
    /// This thread is no longer using this key
    /// call destructor if appropriate
    int thread_detach_key (ACE_thread_key_t key);
  
    /// This key is no longer used
    ///   Release key if use count == 0
    ///   fail if use_count != 0;
    /// @param key the key to be released
    int free_key (ACE_thread_key_t key);
  
    /// Cleanup the thread-specific objects.  Does _NOT_ exit the thread.
    /// For each used key perform the same actions as free_key.
    void thread_exit (void);
  
  private:
    void dump (void);
  
    /// Release a key used by this thread
    /// @param info reference to the info for this key
    /// @param destructor out arg to receive destructor function ptr
    /// @param tss_obj out arg to receive pointer to deletable object
    void thread_release (
            ACE_TSS_Info &info,
            ACE_TSS_Info::Destructor & destructor,
            void *& tss_obj);
  
    /// remove key if it's unused (thread_count == 0)
    /// @param info reference to the info for this key
    int remove_key (ACE_TSS_Info &info);
  
    /// Find the TSS keys (if any) for this thread.
    /// @param thread_keys reference to pointer to be filled in by this function.
    /// @return false if keys don't exist.
    bool find_tss_keys (ACE_TSS_Keys *& thread_keys) const;
  
    /// Accessor for this threads ACE_TSS_Keys instance.
    /// Creates the keys if necessary.
    ACE_TSS_Keys *tss_keys ();
  
    /// Ensure singleton.
    ACE_TSS_Cleanup (void);
    ~ACE_TSS_Cleanup (void);
  
    /// ACE_TSS_Cleanup access only via TSS_Cleanup_Instance
    friend class TSS_Cleanup_Instance;
  
  private:
    // Array of <ACE_TSS_Info> objects.
    typedef ACE_TSS_Info ACE_TSS_TABLE[ACE_DEFAULT_THREAD_KEYS];
    typedef ACE_TSS_Info *ACE_TSS_TABLE_ITERATOR;
  
    /// Table of <ACE_TSS_Info>'s.
    ACE_TSS_TABLE table_;
  
    /// Key for the thread-specific ACE_TSS_Keys
    /// Used by find_tss_keys() or tss_keys() to find the
    /// bit array that records whether each TSS key is in
    /// use by this thread.
    ACE_thread_key_t in_use_;
  };
  
  
  /*****************************************************************************/
  /**
   * @class TSS_Cleanup_Instance
   * @A class to manage an instance pointer to ACE_TSS_Cleanup.
   * Note: that the double checked locking pattern doesn't allow
   * safe deletion.
   * Callers who wish to access the singleton ACE_TSS_Cleanup must
   * do so by instantiating a TSS_Cleanup_Instance, calling the valid
   * method to be sure the ACE_TSS_Cleanup is available, then using
   * the TSS_Cleanup_Instance as a pointer to the instance.
   * Construction argument to the TSS_Cleanup_Instance determines how
   * it is to be used:
   *   CREATE means allow this call to create an ACE_TSS_Cleanup if necessary.
   *   USE means use the existing ACE_TSS_Cleanup, but do not create a new one.
   *   DESTROY means provide exclusive access to the ACE_TSS_Cleanup, then
   *   delete it when the TSS_Cleanup_Instance goes out of scope.
   */
  
  class TSS_Cleanup_Instance
  {
  public:
    enum Purpose
    {
      CREATE,
      USE,
      DESTROY
    };
    TSS_Cleanup_Instance (Purpose purpose = USE);
    ~TSS_Cleanup_Instance();
  
    bool valid();
    ACE_TSS_Cleanup * operator ->();
  
  private:
  
    ACE_TSS_Cleanup * operator *();
  
  private:
    static unsigned int reference_count_;
    static ACE_TSS_Cleanup * instance_;
    static ACE_Thread_Mutex* mutex_;
    static ACE_Condition_Thread_Mutex* condition_;
  
  private:
    ACE_TSS_Cleanup * ptr_;
    unsigned short flags_;
    enum
    {
      FLAG_DELETING = 1,
      FLAG_VALID_CHECKED = 2
    };
  };
  
  TSS_Cleanup_Instance::TSS_Cleanup_Instance (Purpose purpose)
    : ptr_(0)
    , flags_(0)
  {
    // During static construction or construction of the ACE_Object_Manager,
    // there can be only one thread in this constructor at any one time, so
    // it's safe to check for a zero mutex_.  If it's zero, we create a new
    // mutex and condition variable.
    if (mutex_ == 0)
      {
        ACE_NEW (mutex_, ACE_Thread_Mutex ());
        ACE_NEW (condition_, ACE_Condition_Thread_Mutex (*mutex_));
      }
  
    ACE_GUARD (ACE_Thread_Mutex, m, *mutex_);
  
    if (purpose == CREATE)
    {
      if (instance_ == 0)
      {
        instance_ = new ACE_TSS_Cleanup();
      }
      ptr_ = instance_;
      ++reference_count_;
    }
    else if(purpose == DESTROY)
    {
      if (instance_ != 0)
      {
        ptr_ = instance_;
        instance_ = 0;
        ACE_SET_BITS(flags_, FLAG_DELETING);
        while (reference_count_ > 0)
        {
          condition_->wait();
        }
      }
    }
    else // must be normal use
    {
      ACE_ASSERT(purpose == USE);
      if (instance_ != 0)
        {
          ptr_ = instance_;
          ++reference_count_;
        }
    }
  }
  
  TSS_Cleanup_Instance::~TSS_Cleanup_Instance (void)
  {
    // Variable to hold the mutex_ to delete outside the scope of the
    // guard.
    ACE_Thread_Mutex *del_mutex = 0;
  
    // scope the guard
    {
      ACE_GUARD (ACE_Thread_Mutex, guard, *mutex_);
      if (ptr_ != 0)
        {
          if (ACE_BIT_ENABLED (flags_, FLAG_DELETING))
            {
              ACE_ASSERT(instance_ == 0);
              ACE_ASSERT(reference_count_ == 0);
              delete ptr_;
              del_mutex = mutex_ ;
              mutex_ = 0;
            }
          else
            {
              ACE_ASSERT (reference_count_ > 0);
              --reference_count_;
              if (reference_count_ == 0 && instance_ == 0)
                condition_->signal ();
            }
        }
    }// end of guard scope
  
    if (del_mutex != 0)
      {
        delete condition_;
        condition_ = 0;
        delete del_mutex;
      }
  }
  
  bool
  TSS_Cleanup_Instance::valid()
  {
    ACE_SET_BITS(flags_, FLAG_VALID_CHECKED);
    return (this->instance_ != 0);
  }
  
  ACE_TSS_Cleanup *
  TSS_Cleanup_Instance::operator *()
  {
    ACE_ASSERT(ACE_BIT_ENABLED(flags_, FLAG_VALID_CHECKED));
    return instance_;
  }
  
  ACE_TSS_Cleanup *
  TSS_Cleanup_Instance::operator ->()
  {
    ACE_ASSERT(ACE_BIT_ENABLED(flags_, FLAG_VALID_CHECKED));
    return instance_;
  }
  
  // = Static object initialization.
  unsigned int TSS_Cleanup_Instance::reference_count_ = 0;
  ACE_TSS_Cleanup * TSS_Cleanup_Instance::instance_ = 0;
  ACE_Thread_Mutex* TSS_Cleanup_Instance::mutex_ = 0;
  ACE_Condition_Thread_Mutex* TSS_Cleanup_Instance::condition_ = 0;
  
  ACE_TSS_Cleanup::~ACE_TSS_Cleanup (void)
  {
  }
  
  void
  ACE_TSS_Cleanup::thread_exit (void)
  {
    ACE_OS_TRACE ("ACE_TSS_Cleanup::thread_exit");
    // variables to hold the destructors, keys
    // and pointers to the object to be destructed
    // the actual destruction is deferred until the guard is released
    ACE_TSS_Info::Destructor destructor[ACE_DEFAULT_THREAD_KEYS];
    void * tss_obj[ACE_DEFAULT_THREAD_KEYS];
    ACE_thread_key_t keys[ACE_DEFAULT_THREAD_KEYS];
    // count of items to be destroyed
    unsigned int d_count = 0;
  
    // scope the guard
    {
      ACE_TSS_CLEANUP_GUARD
  
      // if not initialized or already cleaned up
      ACE_TSS_Keys *this_thread_keys = 0;
      if (! find_tss_keys (this_thread_keys) )
        {
          return;
        }
  
      // Minor hack: Iterating in reverse order means the LOG buffer which is
      // accidentally allocated first will be accidentally deallocated (almost)
      // last -- in case someone logs something from the other destructors.
      // applications should not count on this behavior because platforms which
      // do not use ACE_TSS_Cleanup may delete objects in other orders.
      unsigned int key_index = ACE_DEFAULT_THREAD_KEYS;
      while( key_index > 0)
        {
          --key_index;
          ACE_TSS_Info & info = this->table_[key_index];
          // if this key is in use by this thread
          if (info.key_in_use () && this_thread_keys->is_set(info.key_))
            {
              // defer deleting the in-use key until all others have been deleted
              if(info.key_ != this->in_use_)
                {
                  destructor[d_count] = 0;
                  tss_obj[d_count] = 0;
                  keys[d_count] = 0;
                  this->thread_release (info, destructor[d_count], tss_obj[d_count]);
                  if (destructor[d_count] != 0 && tss_obj[d_count] != 0)
                    {
                      keys[d_count] = info.key_;
                      ++d_count;
                    }
                }
            }
        }
  
      // remove the in_use bit vector last
      u_int use_index = this->in_use_;
      ACE_TSS_Info & info = this->table_[use_index];
      destructor[d_count] = 0;
      tss_obj[d_count] = 0;
      keys[d_count] = 0;
      this->thread_release (info, destructor[d_count], tss_obj[d_count]);
      if (destructor[d_count] != 0 &&  tss_obj[d_count] != 0)
        {
          keys[d_count] = info.key_;
          ++d_count;
        }
    } // end of guard scope
    for (unsigned int d_index = 0; d_index < d_count; ++d_index)
      {
        (*destructor[d_index])(tss_obj[d_index]);
  #if defined (ACE_HAS_TSS_EMULATION)
        ACE_TSS_Emulation::ts_object (keys[d_index]) = 0;
  #else // defined (ACE_HAS_TSS_EMULATION)
        ACE_OS::thr_setspecific_native (keys[d_index], 0);
  #endif // defined (ACE_HAS_TSS_EMULATION)
      }
  }
  
  extern "C" void
  ACE_TSS_Cleanup_keys_destroyer (void *tss_keys)
  {
    delete static_cast <ACE_TSS_Keys *> (tss_keys);
  }
  
  ACE_TSS_Cleanup::ACE_TSS_Cleanup (void)
    : in_use_ (ACE_OS::NULL_key)
  {
    ACE_OS_TRACE ("ACE_TSS_Cleanup::ACE_TSS_Cleanup");
  }
  
  int
  ACE_TSS_Cleanup::insert (ACE_thread_key_t key,
                           void (*destructor)(void *))
  {
    ACE_OS_TRACE ("ACE_TSS_Cleanup::insert");
    ACE_TSS_CLEANUP_GUARD
  
    u_int key_index = key;
    ACE_ASSERT (key_index < ACE_DEFAULT_THREAD_KEYS);
    if (key_index < ACE_DEFAULT_THREAD_KEYS)
      {
        ACE_ASSERT (table_[key_index].thread_count_ == -1);
        table_[key_index] = ACE_TSS_Info (key, destructor);
        table_[key_index].thread_count_ = 0; // inserting it does not use it
                                             // but it does "allocate" it
        return 0;
      }
    else
      {
        return -1;
      }
  }
  
  int
  ACE_TSS_Cleanup::free_key (ACE_thread_key_t key)
  {
    ACE_OS_TRACE ("ACE_TSS_Cleanup::free_key");
    ACE_TSS_CLEANUP_GUARD
    u_int key_index = key;
    if (key_index < ACE_DEFAULT_THREAD_KEYS)
      {
        return remove_key (this->table_ [key_index]);
      }
    return -1;
  }
  
  int
  ACE_TSS_Cleanup::remove_key (ACE_TSS_Info &info)
  {
    // assume CLEANUP_GUARD is held by caller
    ACE_OS_TRACE ("ACE_TSS_Cleanup::remove_key");
  
  #if 0 // This was a good idea, but POSIX says it's legal to delete used keys.
        // When this is done, any existing TSS objects controlled by this key are leaked
        // There is no "right thing" to do in this case
  
    // only remove it if all threads are done with it
    if (info.thread_count_ != 0)
      {
        return -1;
      }
  #endif // 0
  
  #if !defined (ACE_HAS_TSS_EMULATION)
    ACE_OS_thread_key_t temp_key = info.key_;
    ACE_OS::thr_keyfree_native (temp_key);
  #endif /* !ACE_HAS_TSS_EMULATION */
    if (info.key_ == this->in_use_)
      {
        this->in_use_ = ACE_OS::NULL_key;
      }
    info.key_in_use (0);
    info.destructor_ = 0;
    return 0;
  }
  
  int
  ACE_TSS_Cleanup::thread_detach_key (ACE_thread_key_t key)
  {
    // variables to hold the destructor and the object to be destructed
    // the actual call is deferred until the guard is released
    ACE_TSS_Info::Destructor destructor = 0;
    void * tss_obj = 0;
  
    // scope the guard
    {
      ACE_TSS_CLEANUP_GUARD
  
      u_int key_index = key;
      ACE_ASSERT (key_index < sizeof(this->table_)/sizeof(this->table_[0]));
      // If this entry was never set, just bug out. If it is set, but is the
      // wrong key, assert.
      if (this->table_[key_index].key_ == 0)
        return 0;
      ACE_ASSERT (this->table_[key_index].key_ == key);
      ACE_TSS_Info &info = this->table_ [key_index];
  
      // sanity check
      if (!info.key_in_use ())
        {
          return -1;
        }
  
      this->thread_release (info, destructor, tss_obj);
    } // end of scope for the Guard
    // if there's a destructor and an object to be destroyed
    if (destructor != 0 && tss_obj != 0)
      {
        (*destructor) (tss_obj);
      }
    return 0;
  }
  
  void
  ACE_TSS_Cleanup::thread_release (
          ACE_TSS_Info &info,
          ACE_TSS_Info::Destructor & destructor,
          void *& tss_obj)
  {
    // assume guard is held by caller
    // Find the TSS keys (if any) for this thread
    // do not create them if they don't exist
    ACE_TSS_Keys * thread_keys = 0;
    if (find_tss_keys (thread_keys))
      {
          // if this key is in use by this thread
        if (thread_keys->test_and_clear(info.key_) == 0)
        {
          // save destructor & pointer to tss object
          // until after the guard is released
          destructor = info.destructor_;
          ACE_OS::thr_getspecific (info.key_, &tss_obj);
          ACE_ASSERT (info.thread_count_ > 0);
          --info.thread_count_;
        }
      }
  }
  
  void
  ACE_TSS_Cleanup::thread_use_key (ACE_thread_key_t key)
  {
    // If the key's ACE_TSS_Info in-use bit for this thread is not set,
    // set it and increment the key's thread_count_.
    if (! tss_keys ()->test_and_set (key))
      {
        ACE_TSS_CLEANUP_GUARD
  
        // Retrieve the key's ACE_TSS_Info and increment its thread_count_.
        u_int key_index = key;
        ACE_TSS_Info &key_info = this->table_ [key_index];
  
        ACE_ASSERT (key_info.key_in_use ());
        ++key_info.thread_count_;
      }
  }
  
  void
  ACE_TSS_Cleanup::dump (void)
  {
  # if defined (ACE_HAS_DUMP)
    // Iterate through all the thread-specific items and dump them all.
  
    ACE_TSS_TABLE_ITERATOR key_info = table_;
    for (unsigned int i = 0;
         i < ACE_DEFAULT_THREAD_KEYS;
         ++key_info, ++i)
      key_info->dump ();
  # endif /* ACE_HAS_DUMP */
  }
  
  bool
  ACE_TSS_Cleanup::find_tss_keys (ACE_TSS_Keys *& tss_keys) const
  {
    if (this->in_use_ == ACE_OS::NULL_key)
      return false;
    if (ACE_OS::thr_getspecific (in_use_,
            reinterpret_cast<void **> (&tss_keys)) == -1)
      {
        ACE_ASSERT (false);
        return false; // This should not happen!
      }
    return tss_keys != 0;
  }
  
  ACE_TSS_Keys *
  ACE_TSS_Cleanup::tss_keys ()
  {
    if (this->in_use_ == ACE_OS::NULL_key)
      {
        ACE_TSS_CLEANUP_GUARD
        // Double-check;
        if (in_use_ == ACE_OS::NULL_key)
          {
            // Initialize in_use_ with a new key.
            if (ACE_OS::thr_keycreate (&in_use_,
                                       &ACE_TSS_Cleanup_keys_destroyer))
              {
                ACE_ASSERT (false);
                return 0; // Major problems, this should *never* happen!
              }
          }
      }
  
    void *ts_keys = 0;
    if (ACE_OS::thr_getspecific (in_use_, &ts_keys) == -1)
      {
        ACE_ASSERT (false);
        return 0; // This should not happen!
      }
  
    if (ts_keys == 0)
      {
        ACE_NEW_RETURN (ts_keys,
                        ACE_TSS_Keys,
                        0);
        // Store the dynamically allocated pointer in thread-specific
        // storage.
        if (ACE_OS::thr_setspecific (in_use_, ts_keys) == -1)
          {
            ACE_ASSERT (false);
            delete reinterpret_cast <ACE_TSS_Keys*> (ts_keys);
            return 0; // Major problems, this should *never* happen!
          }
      }
  
    return reinterpret_cast <ACE_TSS_Keys*>(ts_keys);
  }
  
  #endif /* ACE_WIN32 || ACE_HAS_TSS_EMULATION */
  
  /*****************************************************************************/
  
  // = Static initialization.
  
  // This is necessary to deal with POSIX pthreads insanity.  This
  // guarantees that we've got a "zero'd" thread id even when
  // ACE_thread_t, ACE_hthread_t, and ACE_thread_key_t are implemented
  // as structures...  Under no circumstances should these be given
  // initial values.
  // Note: these three objects require static construction.
  ACE_thread_t ACE_OS::NULL_thread;
  ACE_hthread_t ACE_OS::NULL_hthread;
  #if defined (ACE_HAS_TSS_EMULATION)
    ACE_thread_key_t ACE_OS::NULL_key = static_cast <ACE_thread_key_t> (-1);
  #else  /* ! ACE_HAS_TSS_EMULATION */
    ACE_thread_key_t ACE_OS::NULL_key;
  #endif /* ! ACE_HAS_TSS_EMULATION */
  
  /*****************************************************************************/
  
  void
  ACE_OS::cleanup_tss (const u_int main_thread)
  {
  #if defined (ACE_HAS_TSS_EMULATION) || defined (ACE_WIN32)
    { // scope the cleanup instance
      // Call TSS destructors for current thread.
      TSS_Cleanup_Instance cleanup;
      if (cleanup.valid ())
        {
          cleanup->thread_exit ();
        }
    }
  #endif /* ACE_HAS_TSS_EMULATION || ACE_WIN32 */
  
    if (main_thread)
      {
  #if !defined (ACE_HAS_TSS_EMULATION)  &&  !defined (ACE_HAS_MINIMAL_ACE_OS)
        // Just close the ACE_Log_Msg for the current (which should be
        // main) thread.  We don't have TSS emulation; if there's native
        // TSS, it should call its destructors when the main thread
        // exits.
        ACE_Base_Thread_Adapter::close_log_msg ();
  #endif /* ! ACE_HAS_TSS_EMULATION  &&  ! ACE_HAS_MINIMAL_ACE_OS */
  
  #if defined (ACE_WIN32) || defined (ACE_HAS_TSS_EMULATION)
        // Finally, free up the ACE_TSS_Cleanup instance.  This method gets
        // called by the ACE_Object_Manager.
        TSS_Cleanup_Instance cleanup(TSS_Cleanup_Instance::DESTROY);
        if (cleanup.valid ())
        {
          ; // the pointer deletes the Cleanup when it goes out of scope
        }
  
  #endif /* WIN32 || ACE_HAS_TSS_EMULATION */
  
  #if defined (ACE_HAS_TSS_EMULATION)
        ACE_TSS_Emulation::tss_close ();
  #endif /* ACE_HAS_TSS_EMULATION */
      }
  }
  
  /*****************************************************************************/
  // CONDITIONS BEGIN
  /*****************************************************************************/
  
  #if defined (ACE_LACKS_COND_T)
  int
  ACE_OS::cond_broadcast (ACE_cond_t *cv)
  {
    ACE_OS_TRACE ("ACE_OS::cond_broadcast");
  # if defined (ACE_HAS_THREADS)
    // The <external_mutex> must be locked before this call is made.
  
    // This is needed to ensure that <waiters_> and <was_broadcast_> are
    // consistent relative to each other.
    if (ACE_OS::thread_mutex_lock (&cv->waiters_lock_) != 0)
      {
        return -1;
      }
  
    bool have_waiters = false;
  
    if (cv->waiters_ > 0)
      {
        // We are broadcasting, even if there is just one waiter...
        // Record the fact that we are broadcasting.  This helps the
        // cond_wait() method know how to optimize itself.  Be sure to
        // set this with the <waiters_lock_> held.
        cv->was_broadcast_ = 1;
        have_waiters = true;
      }
  
    if (ACE_OS::thread_mutex_unlock (&cv->waiters_lock_) != 0)
      {
        // This is really bad, we have the lock but can't release it anymore
        return -1;
      }
  
    int result = 0;
    if (have_waiters)
      {
        // Wake up all the waiters.
        if (ACE_OS::sema_post (&cv->sema_, cv->waiters_) == -1)
          result = -1;
        // Wait for all the awakened threads to acquire their part of
        // the counting semaphore.
  #   if defined (ACE_VXWORKS)
        else if (ACE_OS::sema_wait (&cv->waiters_done_) == -1)
  #   else
        else if (ACE_OS::event_wait (&cv->waiters_done_) == -1)
  #   endif /* ACE_VXWORKS */
          result = -1;
        // This is okay, even without the <waiters_lock_> held because
        // no other waiter threads can wake up to access it.
        cv->was_broadcast_ = 0;
      }
    return result;
  # else
    ACE_UNUSED_ARG (cv);
    ACE_NOTSUP_RETURN (-1);
  # endif /* ACE_HAS_THREADS */
  }
  
  int
  ACE_OS::cond_destroy (ACE_cond_t *cv)
  {
    ACE_OS_TRACE ("ACE_OS::cond_destroy");
  # if defined (ACE_HAS_THREADS)
  #   if defined (ACE_HAS_WTHREADS)
    ACE_OS::event_destroy (&cv->waiters_done_);
  #   elif defined (ACE_VXWORKS)
    ACE_OS::sema_destroy (&cv->waiters_done_);
  #   endif /* ACE_VXWORKS */
    int result = 0;
    if (ACE_OS::thread_mutex_destroy (&cv->waiters_lock_) != 0)
      result = -1;
  
    if (ACE_OS::sema_destroy (&cv->sema_) != 0)
      result = -1;
  
    return result;
  # else
    ACE_UNUSED_ARG (cv);
    ACE_NOTSUP_RETURN (-1);
  # endif /* ACE_HAS_THREADS */
  }
  
  int
  ACE_OS::cond_init (ACE_cond_t *cv,
                     ACE_condattr_t &attributes,
                     const char *name, void *arg)
  {
    return
      ACE_OS::cond_init (cv, static_cast<short> (attributes.type), name, arg);
  }
  
  # if defined (ACE_HAS_WCHAR)
  int
  ACE_OS::cond_init (ACE_cond_t *cv,
                     ACE_condattr_t &attributes,
                     const wchar_t *name, void *arg)
  {
    return
      ACE_OS::cond_init (cv, static_cast<short> (attributes.type), name, arg);
  }
  # endif /* ACE_HAS_WCHAR */
  
  int
  ACE_OS::cond_init (ACE_cond_t *cv, short type, const char *name, void *arg)
  {
    ACE_OS_TRACE ("ACE_OS::cond_init");
  # if defined (ACE_HAS_THREADS)
    cv->waiters_ = 0;
    cv->was_broadcast_ = 0;
  
    int result = 0;
    if (ACE_OS::sema_init (&cv->sema_, 0, type, name, arg) == -1)
      result = -1;
    else if (ACE_OS::thread_mutex_init (&cv->waiters_lock_) == -1)
      result = -1;
  #   if defined (ACE_VXWORKS)
    else if (ACE_OS::sema_init (&cv->waiters_done_, 0, type) == -1)
  #   else
    else if (ACE_OS::event_init (&cv->waiters_done_) == -1)
  #   endif /* ACE_VXWORKS */
      result = -1;
    return result;
  # else
    ACE_UNUSED_ARG (cv);
    ACE_UNUSED_ARG (type);
    ACE_UNUSED_ARG (name);
    ACE_UNUSED_ARG (arg);
    ACE_NOTSUP_RETURN (-1);
  # endif /* ACE_HAS_THREADS */
  }
  
  # if defined (ACE_HAS_WCHAR)
  int
  ACE_OS::cond_init (ACE_cond_t *cv, short type, const wchar_t *name, void *arg)
  {
    ACE_OS_TRACE ("ACE_OS::cond_init");
  #   if defined (ACE_HAS_THREADS)
    cv->waiters_ = 0;
    cv->was_broadcast_ = 0;
  
    int result = 0;
    if (ACE_OS::sema_init (&cv->sema_, 0, type, name, arg) == -1)
      result = -1;
    else if (ACE_OS::thread_mutex_init (&cv->waiters_lock_) == -1)
      result = -1;
  #     if defined (ACE_VXWORKS)
    else if (ACE_OS::sema_init (&cv->waiters_done_, 0, type) == -1)
  #     else
    else if (ACE_OS::event_init (&cv->waiters_done_) == -1)
  #     endif /* ACE_VXWORKS */
      result = -1;
    return result;
  #   else
    ACE_UNUSED_ARG (cv);
    ACE_UNUSED_ARG (type);
    ACE_UNUSED_ARG (name);
    ACE_UNUSED_ARG (arg);
    ACE_NOTSUP_RETURN (-1);
  #   endif /* ACE_HAS_THREADS */
  }
  # endif /* ACE_HAS_WCHAR */
  
  int
  ACE_OS::cond_signal (ACE_cond_t *cv)
  {
    ACE_OS_TRACE ("ACE_OS::cond_signal");
  # if defined (ACE_HAS_THREADS)
    // If there aren't any waiters, then this is a no-op.  Note that
    // this function *must* be called with the <external_mutex> held
    // since other wise there is a race condition that can lead to the
    // lost wakeup bug...  This is needed to ensure that the <waiters_>
    // value is not in an inconsistent internal state while being
    // updated by another thread.
    if (ACE_OS::thread_mutex_lock (&cv->waiters_lock_) != 0)
      return -1;
    bool const have_waiters = cv->waiters_ > 0;
    if (ACE_OS::thread_mutex_unlock (&cv->waiters_lock_) != 0)
      return -1;
  
    if (have_waiters)
      return ACE_OS::sema_post (&cv->sema_);
    else
      return 0; // No-op
  # else
    ACE_UNUSED_ARG (cv);
    ACE_NOTSUP_RETURN (-1);
  # endif /* ACE_HAS_THREADS */
  }
  
  int
  ACE_OS::cond_wait (ACE_cond_t *cv,
                     ACE_mutex_t *external_mutex)
  {
    ACE_OS_TRACE ("ACE_OS::cond_wait");
  # if defined (ACE_HAS_THREADS)
    // Prevent race conditions on the <waiters_> count.
    if (ACE_OS::thread_mutex_lock (&cv->waiters_lock_) != 0)
      return -1;
  
    ++cv->waiters_;
  
    if (ACE_OS::thread_mutex_unlock (&cv->waiters_lock_) != 0)
      return -1;
  
    int result = 0;
  
  #   if defined (ACE_HAS_SIGNAL_OBJECT_AND_WAIT)
    if (external_mutex->type_ == USYNC_PROCESS)
      {
        // This call will automatically release the mutex and wait on the semaphore.
        ACE_WIN32CALL (ACE_ADAPT_RETVAL (::SignalObjectAndWait (external_mutex->proc_mutex_,
                                                                cv->sema_, INFINITE, FALSE),
                                        result),
                      int, -1, result);
        if (result == -1)
          return result;
      }
    else
  #   endif /* ACE_HAS_SIGNAL_OBJECT_AND_WAIT */
      {
        // We keep the lock held just long enough to increment the count of
        // waiters by one.  Note that we can't keep it held across the call
        // to ACE_OS::sema_wait() since that will deadlock other calls to
        // ACE_OS::cond_signal().
        if (ACE_OS::mutex_unlock (external_mutex) != 0)
          return -1;
  
        // Wait to be awakened by a ACE_OS::cond_signal() or
        // ACE_OS::cond_broadcast().
        result = ACE_OS::sema_wait (&cv->sema_);
      }
  
    // Reacquire lock to avoid race conditions on the <waiters_> count.
    if (ACE_OS::thread_mutex_lock (&cv->waiters_lock_) != 0)
      return -1;
  
    // We're ready to return, so there's one less waiter.
    --cv->waiters_;
  
    bool const last_waiter = cv->was_broadcast_ && cv->waiters_ == 0;
  
    // Release the lock so that other collaborating threads can make
    // progress.
    if (ACE_OS::thread_mutex_unlock (&cv->waiters_lock_) != 0)
      return -1;
  
    if (result == -1)
      // Bad things happened, so let's just return below.
      /* NOOP */;
  #   if defined (ACE_HAS_SIGNAL_OBJECT_AND_WAIT)
    else if (external_mutex->type_ == USYNC_PROCESS)
      {
        if (last_waiter)
  
          // This call atomically signals the <waiters_done_> event and
          // waits until it can acquire the mutex.  This is important to
          // prevent unfairness.
          ACE_WIN32CALL (ACE_ADAPT_RETVAL (::SignalObjectAndWait (cv->waiters_done_,
                                                                  external_mutex->proc_mutex_,
                                                                  INFINITE, FALSE),
                                           result),
                         int, -1, result);
        else
          // We must always regain the <external_mutex>, even when
          // errors occur because that's the guarantee that we give to
          // our callers.
          if (ACE_OS::mutex_lock (external_mutex) != 0)
            return -1;
  
        return result;
        /* NOTREACHED */
      }
  #   endif /* ACE_HAS_SIGNAL_OBJECT_AND_WAIT */
    // If we're the last waiter thread during this particular broadcast
    // then let all the other threads proceed.
    else if (last_waiter)
  #   if defined (ACE_VXWORKS)
      ACE_OS::sema_post (&cv->waiters_done_);
  #   else
      ACE_OS::event_signal (&cv->waiters_done_);
  #   endif /* ACE_VXWORKS */
  
    // We must always regain the <external_mutex>, even when errors
    // occur because that's the guarantee that we give to our callers.
    ACE_OS::mutex_lock (external_mutex);
  
    return result;
  # else
    ACE_UNUSED_ARG (cv);
    ACE_UNUSED_ARG (external_mutex);
    ACE_NOTSUP_RETURN (-1);
  # endif /* ACE_HAS_THREADS */
  }
  
  int
  ACE_OS::cond_timedwait (ACE_cond_t *cv,
                          ACE_mutex_t *external_mutex,
                          ACE_Time_Value *timeout)
  {
    ACE_OS_TRACE ("ACE_OS::cond_timedwait");
  # if defined (ACE_HAS_THREADS)
    // Handle the easy case first.
    if (timeout == 0)
      return ACE_OS::cond_wait (cv, external_mutex);
  #   if defined (ACE_HAS_WTHREADS) || defined (ACE_VXWORKS)
  
    // Prevent race conditions on the <waiters_> count.
    if (ACE_OS::thread_mutex_lock (&cv->waiters_lock_) != 0)
      return -1;
  
    ++cv->waiters_;
  
    if (ACE_OS::thread_mutex_unlock (&cv->waiters_lock_) != 0)
      return -1;
  
    int result = 0;
    ACE_Errno_Guard error (errno, 0);
    int msec_timeout = 0;
  
    if (timeout != 0 && *timeout != ACE_Time_Value::zero)
      {
        // Note that we must convert between absolute time (which is
        // passed as a parameter) and relative time (which is what
        // WaitForSingleObjects() expects).
        ACE_Time_Value relative_time = timeout->to_relative_time ();
  
        // Watchout for situations where a context switch has caused the
        // current time to be > the timeout.
        if (relative_time > ACE_Time_Value::zero)
          msec_timeout = relative_time.msec ();
      }
  
  #     if defined (ACE_HAS_SIGNAL_OBJECT_AND_WAIT)
    if (external_mutex->type_ == USYNC_PROCESS)
      // This call will automatically release the mutex and wait on the
      // semaphore.
      result = ::SignalObjectAndWait (external_mutex->proc_mutex_,
                                      cv->sema_,
                                      msec_timeout,
                                      FALSE);
    else
  #     endif /* ACE_HAS_SIGNAL_OBJECT_AND_WAIT */
      {
        // We keep the lock held just long enough to increment the count
        // of waiters by one.  Note that we can't keep it held across
        // the call to WaitForSingleObject since that will deadlock
        // other calls to ACE_OS::cond_signal().
        if (ACE_OS::mutex_unlock (external_mutex) != 0)
          return -1;
  
        // Wait to be awakened by a ACE_OS::signal() or
        // ACE_OS::broadcast().
  #     if defined (ACE_WIN32)
  #       if !defined (ACE_USES_WINCE_SEMA_SIMULATION)
        result = ::WaitForSingleObject (cv->sema_, msec_timeout);
  #       else /* ACE_USES_WINCE_SEMA_SIMULATION */
        // Can't use Win32 API on our simulated semaphores.
        result = ACE_OS::sema_wait (&cv->sema_,
                                    timeout);
  #       endif /* ACE_USES_WINCE_SEMA_SIMULATION */
  #     elif defined (ACE_VXWORKS)
        // Inline the call to ACE_OS::sema_wait () because it takes an
        // ACE_Time_Value argument.  Avoid the cost of that conversion . . .
        int const ticks_per_sec = ::sysClkRateGet ();
        int const ticks = msec_timeout * ticks_per_sec / ACE_ONE_SECOND_IN_MSECS;
        result = ::semTake (cv->sema_.sema_, ticks);
  #     endif /* ACE_WIN32 || VXWORKS */
      }
  
    // Reacquire lock to avoid race conditions.
    if (ACE_OS::thread_mutex_lock (&cv->waiters_lock_) != 0)
      return -1;
  
    --cv->waiters_;
  
    bool const last_waiter = cv->was_broadcast_ && cv->waiters_ == 0;
  
    if (ACE_OS::thread_mutex_unlock (&cv->waiters_lock_) != 0)
      return -1;
  
  #     if defined (ACE_WIN32)
    if (result != (int)WAIT_OBJECT_0)
      {
        switch (result)
          {
          case WAIT_TIMEOUT:
            error = ETIME;
            break;
          default:
            error = ::GetLastError ();
            break;
          }
        result = -1;
      }
  #     elif defined (ACE_VXWORKS)
    if (result == ERROR)
      {
        switch (errno)
          {
          case S_objLib_OBJ_TIMEOUT:
            error = ETIME;
            break;
          case S_objLib_OBJ_UNAVAILABLE:
            if (msec_timeout == 0)
              error = ETIME;
            break;
          default:
            error = errno;
            break;
          }
        result = -1;
      }
  #     endif /* ACE_WIN32 || VXWORKS */
  #     if defined (ACE_HAS_SIGNAL_OBJECT_AND_WAIT)
    if (external_mutex->type_ == USYNC_PROCESS)
      {
        if (last_waiter)
          // This call atomically signals the <waiters_done_> event and
          // waits until it can acquire the mutex.  This is important to
          // prevent unfairness.
          ACE_WIN32CALL (ACE_ADAPT_RETVAL (::SignalObjectAndWait (cv->waiters_done_,
                                                                  external_mutex->proc_mutex_,
                                                                  INFINITE, FALSE),
                                           result),
                         int, -1, result);
        else
          {
            // We must always regain the <external_Mutex>, even when
            // errors occur because that's the guarantee that we give to
            // our callers.
            if (ACE_OS::mutex_lock (external_mutex) != 0)
              return -1;
          }
  
        return result;
        /* NOTREACHED */
      }
  #     endif /* ACE_HAS_SIGNAL_OBJECT_AND_WAIT */
    // Note that this *must* be an "if" statement rather than an "else
    // if" statement since the caller may have timed out and hence the
    // result would have been -1 above.
    if (last_waiter)
      {
        // Release the signaler/broadcaster if we're the last waiter.
  #     if defined (ACE_WIN32)
        if (ACE_OS::event_signal (&cv->waiters_done_) != 0)
  #     else
        if (ACE_OS::sema_post (&cv->waiters_done_) != 0)
  #     endif /* ACE_WIN32 */
          return -1;
      }
  
    // We must always regain the <external_mutex>, even when errors
    // occur because that's the guarantee that we give to our callers.
    if (ACE_OS::mutex_lock (external_mutex) != 0)
      return -1;
  
    return result;
  #   endif /* ACE_HAS_WTHREADS || ACE_HAS_VXWORKS */
  # else
    ACE_UNUSED_ARG (cv);
    ACE_UNUSED_ARG (external_mutex);
    ACE_UNUSED_ARG (timeout);
    ACE_NOTSUP_RETURN (-1);
  # endif /* ACE_HAS_THREADS */
  }
  #else
  int
  ACE_OS::cond_init (ACE_cond_t *cv, short type, const char *name, void *arg)
  {
    ACE_condattr_t attributes;
    if (ACE_OS::condattr_init (attributes, type) == 0
        && ACE_OS::cond_init (cv, attributes, name, arg) == 0)
      {
        (void) ACE_OS::condattr_destroy (attributes);
        return 0;
      }
    return -1;
  }
  #endif /* ACE_LACKS_COND_T */
  
  #if defined (ACE_WIN32) && defined (ACE_HAS_WTHREADS)
  int
  ACE_OS::cond_timedwait (ACE_cond_t *cv,
                          ACE_thread_mutex_t *external_mutex,
                          ACE_Time_Value *timeout)
  {
    ACE_OS_TRACE ("ACE_OS::cond_timedwait");
  #   if defined (ACE_HAS_THREADS)
    // Handle the easy case first.
    if (timeout == 0)
      return ACE_OS::cond_wait (cv, external_mutex);
  
  #   if defined (ACE_HAS_WTHREADS_CONDITION_VARIABLE)
    int msec_timeout = 0;
    int result = 0;
  
    ACE_Time_Value relative_time = timeout->to_relative_time ();
    // Watchout for situations where a context switch has caused the
    // current time to be > the timeout.
    if (relative_time > ACE_Time_Value::zero)
       msec_timeout = relative_time.msec ();
  
    ACE_OSCALL (ACE_ADAPT_RETVAL (::SleepConditionVariableCS (cv, external_mutex, msec_timeout),
                                  result),
                int, -1, result);
    return result;
  #else
    // Prevent race conditions on the <waiters_> count.
    if (ACE_OS::thread_mutex_lock (&cv->waiters_lock_) != 0)
      return -1;
  
    ++cv->waiters_;
  
    if (ACE_OS::thread_mutex_unlock (&cv->waiters_lock_) != 0)
      return -1;
  
    int result = 0;
    int error = 0;
    int msec_timeout = 0;
  
    if (timeout != 0 && *timeout != ACE_Time_Value::zero)
      {
        // Note that we must convert between absolute time (which is
        // passed as a parameter) and relative time (which is what
        // WaitForSingleObjects() expects).
        ACE_Time_Value relative_time = timeout->to_relative_time ();
  
        // Watchout for situations where a context switch has caused the
        // current time to be > the timeout.
        if (relative_time > ACE_Time_Value::zero)
          msec_timeout = relative_time.msec ();
      }
  
    // We keep the lock held just long enough to increment the count of
    // waiters by one.  Note that we can't keep it held across the call
    // to WaitForSingleObject since that will deadlock other calls to
    // ACE_OS::cond_signal().
    if (ACE_OS::thread_mutex_unlock (external_mutex) != 0)
      return -1;
  
    // Wait to be awakened by a ACE_OS::signal() or ACE_OS::broadcast().
  #     if defined (ACE_USES_WINCE_SEMA_SIMULATION)
    // Can't use Win32 API on simulated semaphores.
    result = ACE_OS::sema_wait (&cv->sema_,
                                timeout);
  
    if (result == -1 && errno == ETIME)
      result = WAIT_TIMEOUT;
  #     else
    result = ::WaitForSingleObject (cv->sema_, msec_timeout);
  #     endif /* ACE_USES_WINCE_SEMA_SIMULATION */
  
    // Reacquire lock to avoid race conditions.
    if (ACE_OS::thread_mutex_lock (&cv->waiters_lock_) != 0)
      return -1;
  
    --cv->waiters_;
  
    bool const last_waiter = cv->was_broadcast_ && cv->waiters_ == 0;
  
    if (ACE_OS::thread_mutex_unlock (&cv->waiters_lock_) != 0)
      return -1;
  
    if (result != (int)WAIT_OBJECT_0)
      {
        switch (result)
          {
          case WAIT_TIMEOUT:
            error = ETIME;
            break;
          default:
            error = ::GetLastError ();
            break;
          }
        result = -1;
      }
  
    if (last_waiter)
      {
        // Release the signaler/broadcaster if we're the last waiter.
        if (ACE_OS::event_signal (&cv->waiters_done_) != 0)
          return -1;
      }
  
    // We must always regain the <external_mutex>, even when errors
    // occur because that's the guarantee that we give to our callers.
    if (ACE_OS::thread_mutex_lock (external_mutex) != 0)
      result = -1;
  
    if (error != 0)
      {
        /* This assignment must only be done if error != 0,
         *   since writing 0 to errno violates the POSIX specification.
         */
        errno = error;
      }
    return result;
  #   endif
  #   else
    ACE_NOTSUP_RETURN (-1);
  #   endif /* ACE_HAS_THREADS */
  }
  
  int
  ACE_OS::cond_wait (ACE_cond_t *cv,
                     ACE_thread_mutex_t *external_mutex)
  {
    ACE_OS_TRACE ("ACE_OS::cond_wait");
  #   if defined (ACE_HAS_THREADS)
  #   if defined (ACE_HAS_WTHREADS_CONDITION_VARIABLE)
    int result;
    ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::SleepConditionVariableCS (cv, external_mutex, INFINITE), result),
                       int, -1);
  #else
    if (ACE_OS::thread_mutex_lock (&cv->waiters_lock_) != 0)
      return -1;
    ++cv->waiters_;
  
    if (ACE_OS::thread_mutex_unlock (&cv->waiters_lock_) != 0)
      return -1;
  
    int result = 0;
    int error = 0;
  
    // We keep the lock held just long enough to increment the count of
    // waiters by one.  Note that we can't keep it held across the call
    // to ACE_OS::sema_wait() since that will deadlock other calls to
    // ACE_OS::cond_signal().
    if (ACE_OS::thread_mutex_unlock (external_mutex) != 0)
      return -1;
  
    // Wait to be awakened by a ACE_OS::cond_signal() or
    // ACE_OS::cond_broadcast().
  #     if !defined (ACE_USES_WINCE_SEMA_SIMULATION)
    result = ::WaitForSingleObject (cv->sema_, INFINITE);
  #     else
    // Can't use Win32 API on simulated semaphores.
    result = ACE_OS::sema_wait (&cv->sema_);
  
    if (result != WAIT_OBJECT_0 && errno == ETIME)
      result = WAIT_TIMEOUT;
  
  #     endif /* ACE_USES_WINCE_SEMA_SIMULATION */
  
    // Reacquire lock to avoid race conditions.
    if (ACE_OS::thread_mutex_lock (&cv->waiters_lock_) != 0)
      return -1;
  
    cv->waiters_--;
  
    bool const last_waiter = cv->was_broadcast_ && cv->waiters_ == 0;
  
    if (ACE_OS::thread_mutex_unlock (&cv->waiters_lock_) != 0)
      return -1;
  
    if (result != (int)WAIT_OBJECT_0)
      {
        switch (result)
          {
          case WAIT_TIMEOUT:
            error = ETIME;
            break;
          default:
            error = ::GetLastError ();
            break;
          }
      }
    else if (last_waiter)
      {
        // Release the signaler/broadcaster if we're the last waiter.
        if (ACE_OS::event_signal (&cv->waiters_done_) != 0)
          return -1;
      }
  
    // We must always regain the <external_mutex>, even when errors
    // occur because that's the guarantee that we give to our callers.
    if (ACE_OS::thread_mutex_lock (external_mutex) != 0)
      result = -1;
  
    // Reset errno in case mutex_lock() also fails...
    if (error != 0)
    {
      /* This assignment must only be done if error != 0,
      *   since writing 0 to errno violates the POSIX specification.
      */
      errno = error;
    }
    return result;
  #endif
  #   else
    ACE_NOTSUP_RETURN (-1);
  #   endif /* ACE_HAS_THREADS */
  }
  # endif /* ACE_HAS_WTHREADS */
  
  /*****************************************************************************/
  // CONDITIONS END
  /*****************************************************************************/
  
  /*****************************************************************************/
  // MUTEXES BEGIN
  /*****************************************************************************/
  
  int
  ACE_OS::mutex_init (ACE_mutex_t *m,
                      int lock_scope,
                      const char *name,
                      ACE_mutexattr_t *attributes,
                      LPSECURITY_ATTRIBUTES sa,
                      int lock_type)
  {
    // ACE_OS_TRACE ("ACE_OS::mutex_init");
  #if defined (ACE_HAS_THREADS)
  # if defined (ACE_HAS_PTHREADS)
    ACE_UNUSED_ARG (name);
    ACE_UNUSED_ARG (sa);
  
  # if defined (ACE_PTHREAD_MUTEXATTR_T_INITIALIZE)
    /* Tests show that VxWorks 6.x pthread lib does not only
     * require zeroing of mutex/condition objects to function correctly
     * but also of the attribute objects.
     */
    pthread_mutexattr_t l_attributes = {0};
  # else
    pthread_mutexattr_t l_attributes;
  # endif
  
    if (attributes == 0)
      attributes = &l_attributes;
    int result = 0;
    int attr_init = 0;  // have we initialized the local attributes.
  
    // Only do these initializations if the <attributes> parameter
    // wasn't originally set.
    if (attributes == &l_attributes)
    {
        if (ACE_ADAPT_RETVAL (::pthread_mutexattr_init (attributes), result) == 0)
          {
            result = 0;
            attr_init = 1; // we have initialized these attributes
          }
        else
          {
            result = -1;        // ACE_ADAPT_RETVAL used it for intermediate status
          }
    }
  
    if (result == 0 && lock_scope != 0)
  {
  #     if defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_MUTEXATTR_PSHARED)
        (void) ACE_ADAPT_RETVAL (::pthread_mutexattr_setpshared (attributes,
                                                                 lock_scope),
                                 result);
  #     endif /* _POSIX_THREAD_PROCESS_SHARED && !ACE_LACKS_MUTEXATTR_PSHARED */
  }
  
    if (result == 0 && lock_type != 0)
  {
  #   if defined (ACE_HAS_RECURSIVE_MUTEXES)
        (void) ACE_ADAPT_RETVAL (::pthread_mutexattr_settype (attributes,
                                                              lock_type),
                                 result);
  #   endif /* ACE_HAS_RECURSIVE_MUTEXES */
  }
  
    if (result == 0)
  {
  #   if defined (ACE_PTHREAD_MUTEX_T_INITIALIZE)
        /* VxWorks 6.x API reference states:
         * If the memory for the mutex variable object has been allocated
         *   dynamically, it is a good policy to always zero out the
         *   block of memory so as to avoid spurious EBUSY return code
         *   when calling this routine.
         * Tests shows this to be necessary.
         */
        ACE_OS::memset (m, 0, sizeof (*m));
  #   endif
        if (ACE_ADAPT_RETVAL (::pthread_mutex_init (m, attributes), result) == 0)
          result = 0;
        else
          result = -1;        // ACE_ADAPT_RETVAL used it for intermediate status
  }
  
    // Only do the deletions if the <attributes> parameter wasn't
    // originally set.
    if (attributes == &l_attributes && attr_init)
      ::pthread_mutexattr_destroy (&l_attributes);
  
    return result;
  # elif defined (ACE_HAS_STHREADS)
    ACE_UNUSED_ARG (name);
    ACE_UNUSED_ARG (sa);
    ACE_UNUSED_ARG (lock_type);
    int result;
    ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::mutex_init (m,
                       lock_scope,
                       attributes),
    result),
    int, -1);
  # elif defined (ACE_HAS_WTHREADS)
    m->type_ = lock_scope;
  
    SECURITY_ATTRIBUTES sa_buffer;
    SECURITY_DESCRIPTOR sd_buffer;
    switch (lock_scope)
  {
    case USYNC_PROCESS:
  #   if defined (ACE_HAS_WINCE)
        // @@todo (brunsch) This idea should be moved into ACE_OS_Win32.
        m->proc_mutex_ =
    ::CreateMutexW (ACE_OS::default_win32_security_attributes_r
                            (sa, &sa_buffer, &sd_buffer),
                          FALSE,
                          ACE_Ascii_To_Wide (name).wchar_rep ());
  #   else /* ACE_HAS_WINCE */
        m->proc_mutex_ =
    ::CreateMutexA (ACE_OS::default_win32_security_attributes_r
                            (sa, &sa_buffer, &sd_buffer),
                          FALSE,
                          name);
  #   endif /* ACE_HAS_WINCE */
        if (m->proc_mutex_ == 0)
          ACE_FAIL_RETURN (-1);
        else
        {
          // Make sure to set errno to ERROR_ALREADY_EXISTS if necessary.
          ACE_OS::set_errno_to_last_error ();
          return 0;
        }
    case USYNC_THREAD:
      return ACE_OS::thread_mutex_init (&m->thr_mutex_,
                                         lock_type,
                                         name,
                                         attributes);
    default:
      errno = EINVAL;
      return -1;
  }
    /* NOTREACHED */
  
  # elif defined (ACE_VXWORKS)
    ACE_UNUSED_ARG (name);
    ACE_UNUSED_ARG (attributes);
    ACE_UNUSED_ARG (sa);
    ACE_UNUSED_ARG (lock_type);
  
    return (*m = ::semMCreate (lock_scope)) == 0 ? -1 : 0;
  # endif /* ACE_HAS_PTHREADS */
  #else
    ACE_UNUSED_ARG (m);
    ACE_UNUSED_ARG (lock_scope);
    ACE_UNUSED_ARG (name);
    ACE_UNUSED_ARG (attributes);
    ACE_UNUSED_ARG (sa);
    ACE_UNUSED_ARG (lock_type);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_HAS_THREADS */
  }
  
  int
  ACE_OS::mutex_destroy (ACE_mutex_t *m)
  {
    ACE_OS_TRACE ("ACE_OS::mutex_destroy");
  #if defined (ACE_HAS_THREADS)
  # if defined (ACE_HAS_PTHREADS)
    int result;
    ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::pthread_mutex_destroy (m),
                       result), int, -1);
  # elif defined (ACE_HAS_STHREADS)
    int result;
    ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::mutex_destroy (m), result), int, -1);
  # elif defined (ACE_HAS_WTHREADS)
    switch (m->type_)
  {
    case USYNC_PROCESS:
      ACE_WIN32CALL_RETURN (ACE_ADAPT_RETVAL (::CloseHandle (m->proc_mutex_),
                            ace_result_),
      int, -1);
    case USYNC_THREAD:
      return ACE_OS::thread_mutex_destroy (&m->thr_mutex_);
    default:
      errno = EINVAL;
      return -1;
  }
    /* NOTREACHED */
  # elif defined (ACE_VXWORKS)
    return ::semDelete (*m) == OK ? 0 : -1;
  # endif /* Threads variety case */
  #else
    ACE_UNUSED_ARG (m);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_HAS_THREADS */
  }
  
  #if defined (ACE_HAS_WCHAR)
  int
  ACE_OS::mutex_init (ACE_mutex_t *m,
                      int lock_scope,
                      const wchar_t *name,
                      ACE_mutexattr_t *attributes,
                      LPSECURITY_ATTRIBUTES sa,
                      int lock_type)
  {
  #if defined (ACE_HAS_THREADS) && defined (ACE_HAS_WTHREADS)
    m->type_ = lock_scope;
    SECURITY_ATTRIBUTES sa_buffer;
    SECURITY_DESCRIPTOR sd_buffer;
    switch (lock_scope)
    {
      case USYNC_PROCESS:
        m->proc_mutex_ =
        ::CreateMutexW (ACE_OS::default_win32_security_attributes_r
            (sa, &sa_buffer, &sd_buffer),
        FALSE,
        name);
        if (m->proc_mutex_ == 0)
          ACE_FAIL_RETURN (-1);
        else
          {
            // Make sure to set errno to ERROR_ALREADY_EXISTS if necessary.
            ACE_OS::set_errno_to_last_error ();
            return 0;
          }
      case USYNC_THREAD:
        return ACE_OS::thread_mutex_init (&m->thr_mutex_,
                                           lock_type,
                                           name,
                                           attributes);
    }
  
    errno = EINVAL;
    return -1;
  #else /* ACE_HAS_THREADS && ACE_HAS_WTHREADS */
    return ACE_OS::mutex_init (m,
                               lock_scope,
                               ACE_Wide_To_Ascii (name).char_rep (),
                               attributes,
                               sa,
                               lock_type);
  #endif /* ACE_HAS_THREADS && ACE_HAS_WTHREADS */
  }
  #endif /* ACE_HAS_WCHAR */
  
  int
  ACE_OS::mutex_lock (ACE_mutex_t *m)
  {
    // ACE_OS_TRACE ("ACE_OS::mutex_lock");
  #if defined (ACE_HAS_THREADS)
  # if defined (ACE_HAS_PTHREADS)
    // Note, don't use "::" here since the following call is often a macro.
    int result;
    ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (pthread_mutex_lock (m), result),
                       int, -1);
  # elif defined (ACE_HAS_STHREADS)
    int result;
    ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::mutex_lock (m), result), int, -1);
  # elif defined (ACE_HAS_WTHREADS)
    switch (m->type_)
  {
    case USYNC_PROCESS:
      switch (::WaitForSingleObject (m->proc_mutex_, INFINITE))
        {
          //
          // Timeout can't occur, so don't bother checking...
          //
        case WAIT_OBJECT_0:
        case WAIT_ABANDONED:
          // We will ignore abandonments in this method
          // Note that we still hold the lock
          return 0;
        default:
          // This is a hack, we need to find an appropriate mapping...
          ACE_OS::set_errno_to_last_error ();
          return -1;
        }
    case USYNC_THREAD:
      return ACE_OS::thread_mutex_lock (&m->thr_mutex_);
    default:
      errno = EINVAL;
      return -1;
  }
    /* NOTREACHED */
  # elif defined (ACE_VXWORKS)
    return ::semTake (*m, WAIT_FOREVER) == OK ? 0 : -1;
  # endif /* Threads variety case */
  #else
    ACE_UNUSED_ARG (m);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_HAS_THREADS */
  }
  
  int
  ACE_OS::mutex_lock (ACE_mutex_t *m,
                      int &abandoned)
  {
    ACE_OS_TRACE ("ACE_OS::mutex_lock");
  #if defined (ACE_HAS_THREADS) && defined (ACE_HAS_WTHREADS)
    abandoned = 0;
    switch (m->type_)
    {
      case USYNC_PROCESS:
        switch (::WaitForSingleObject (m->proc_mutex_, INFINITE))
        {
          //
            // Timeout can't occur, so don't bother checking...
          //
          case WAIT_OBJECT_0:
            return 0;
          case WAIT_ABANDONED:
            abandoned = 1;
            return 0;  // something goofed, but we hold the lock ...
          default:
            // This is a hack, we need to find an appropriate mapping...
            ACE_OS::set_errno_to_last_error ();
            return -1;
        }
      case USYNC_THREAD:
        return ACE_OS::thread_mutex_lock (&m->thr_mutex_);
      default:
        errno = EINVAL;
        return -1;
    }
    /* NOTREACHED */
  #else
    ACE_UNUSED_ARG (m);
    ACE_UNUSED_ARG (abandoned);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_HAS_THREADS and ACE_HAS_WTHREADS */
  }
  
  int
  ACE_OS::mutex_lock (ACE_mutex_t *m,
                      const ACE_Time_Value &timeout)
  {
  #if defined (ACE_HAS_THREADS) && defined (ACE_HAS_MUTEX_TIMEOUTS)
  
  #  if defined (ACE_HAS_PTHREADS)
    int result;
  
    // "timeout" should be an absolute time.
  
    timespec_t ts = timeout;  // Calls ACE_Time_Value::operator timespec_t().
  
    // Note that the mutex should not be a recursive one, i.e., it
    // should only be a standard mutex or an error checking mutex.
  
    ACE_OSCALL (ACE_ADAPT_RETVAL (::pthread_mutex_timedlock (m, &ts), result), int, -1, result);
  
    // We need to adjust this to make the errno values consistent.
    if (result == -1 && errno == ETIMEDOUT)
      errno = ETIME;
    return result;
  
  #  elif defined (ACE_HAS_WTHREADS)
    // Note that we must convert between absolute time (which is passed
    // as a parameter) and relative time (which is what the system call
    // expects).
    ACE_Time_Value relative_time = timeout.to_relative_time ();
  
    switch (m->type_)
    {
      case USYNC_PROCESS:
        switch (::WaitForSingleObject (m->proc_mutex_,
                  relative_time.msec ()))
        {
          case WAIT_OBJECT_0:
          case WAIT_ABANDONED:
            // We will ignore abandonments in this method
            // Note that we still hold the lock
            return 0;
          case WAIT_TIMEOUT:
            errno = ETIME;
            return -1;
          default:
            // This is a hack, we need to find an appropriate mapping...
            ACE_OS::set_errno_to_last_error ();
            return -1;
        }
      case USYNC_THREAD:
        ACE_NOTSUP_RETURN (-1);
      default:
        errno = EINVAL;
        return -1;
    }
    /* NOTREACHED */
  
  #  elif defined (ACE_VXWORKS)
  
    // Note that we must convert between absolute time (which is passed
    // as a parameter) and relative time (which is what the system call
    // expects).
    ACE_Time_Value relative_time = timeout.to_relative_time ();
  
    int ticks_per_sec = ::sysClkRateGet ();
  
    int ticks = relative_time.sec() * ticks_per_sec +
        relative_time.usec () * ticks_per_sec / ACE_ONE_SECOND_IN_USECS;
    if (::semTake (*m, ticks) == ERROR)
    {
      if (errno == S_objLib_OBJ_TIMEOUT)
          // Convert the VxWorks errno to one that's common for to ACE
          // platforms.
        errno = ETIME;
      else if (errno == S_objLib_OBJ_UNAVAILABLE)
        errno = EBUSY;
      return -1;
    }
    else
      return 0;
  #  endif /* ACE_HAS_PTHREADS */
  
  #else
    ACE_UNUSED_ARG (m);
    ACE_UNUSED_ARG (timeout);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_HAS_THREADS && ACE_HAS_MUTEX_TIMEOUTS */
  }
  
  int
  ACE_OS::mutex_trylock (ACE_mutex_t *m)
  {
    ACE_OS_TRACE ("ACE_OS::mutex_trylock");
  #if defined (ACE_HAS_THREADS)
  # if defined (ACE_HAS_PTHREADS)
    // Note, don't use "::" here since the following call is often a macro.
    int result;
    ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (pthread_mutex_trylock (m), result),
                       int, -1);
  # elif defined (ACE_HAS_STHREADS)
    int result;
    ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::mutex_trylock (m), result), int, -1);
  # elif defined (ACE_HAS_WTHREADS)
    switch (m->type_)
  {
    case USYNC_PROCESS:
    {
          // Try for 0 milliseconds - i.e. nonblocking.
      switch (::WaitForSingleObject (m->proc_mutex_, 0))
      {
        case WAIT_OBJECT_0:
          return 0;
        case WAIT_ABANDONED:
              // We will ignore abandonments in this method.  Note that
              // we still hold the lock.
          return 0;
        case WAIT_TIMEOUT:
          errno = EBUSY;
          return -1;
        default:
          ACE_OS::set_errno_to_last_error ();
          return -1;
      }
    }
    case USYNC_THREAD:
      return ACE_OS::thread_mutex_trylock (&m->thr_mutex_);
    default:
      errno = EINVAL;
      return -1;
  }
    /* NOTREACHED */
  # elif defined (ACE_VXWORKS)
    if (::semTake (*m, NO_WAIT) == ERROR)
      if (errno == S_objLib_OBJ_UNAVAILABLE)
  {
          // couldn't get the semaphore
    errno = EBUSY;
    return -1;
  }
      else
        // error
        return -1;
      else
      // got the semaphore
        return 0;
  # endif /* Threads variety case */
  #else
    ACE_UNUSED_ARG (m);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_HAS_THREADS */
  }
  
  int
  ACE_OS::mutex_trylock (ACE_mutex_t *m, int &abandoned)
  {
  #if defined (ACE_HAS_THREADS) && defined (ACE_HAS_WTHREADS)
    abandoned = 0;
    switch (m->type_)
    {
      case USYNC_PROCESS:
      {
          // Try for 0 milliseconds - i.e. nonblocking.
        switch (::WaitForSingleObject (m->proc_mutex_, 0))
        {
          case WAIT_OBJECT_0:
            return 0;
          case WAIT_ABANDONED:
            abandoned = 1;
            return 0;  // something goofed, but we hold the lock ...
          case WAIT_TIMEOUT:
            errno = EBUSY;
            return -1;
          default:
            ACE_OS::set_errno_to_last_error ();
            return -1;
        }
      }
      case USYNC_THREAD:
        return ACE_OS::thread_mutex_trylock (&m->thr_mutex_);
      default:
        errno = EINVAL;
        return -1;
    }
    /* NOTREACHED */
  #else
    ACE_UNUSED_ARG (m);
    ACE_UNUSED_ARG (abandoned);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_HAS_THREADS and ACE_HAS_WTHREADS */
  }
  
  int
  ACE_OS::mutex_unlock (ACE_mutex_t *m)
  {
    ACE_OS_TRACE ("ACE_OS::mutex_unlock");
  #if defined (ACE_HAS_THREADS)
  # if defined (ACE_HAS_PTHREADS)
    // Note, don't use "::" here since the following call is often a macro.
    int result;
    ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (pthread_mutex_unlock (m), result),
                       int, -1);
  # elif defined (ACE_HAS_STHREADS)
    int result;
    ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::mutex_unlock (m), result), int, -1);
  # elif defined (ACE_HAS_WTHREADS)
    switch (m->type_)
  {
    case USYNC_PROCESS:
      ACE_WIN32CALL_RETURN (ACE_ADAPT_RETVAL (::ReleaseMutex (m->proc_mutex_),
                            ace_result_),
      int, -1);
    case USYNC_THREAD:
      return ACE_OS::thread_mutex_unlock (&m->thr_mutex_);
    default:
      errno = EINVAL;
      return -1;
  }
    /* NOTREACHED */
  # elif defined (ACE_VXWORKS)
    return ::semGive (*m) == OK ? 0 : -1;
  # endif /* Threads variety case */
  #else
    ACE_UNUSED_ARG (m);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_HAS_THREADS */
  }
  
  void
  ACE_OS::mutex_lock_cleanup (void *mutex)
  {
    ACE_OS_TRACE ("ACE_OS::mutex_lock_cleanup");
  #if defined (ACE_HAS_THREADS)
  # if defined (ACE_HAS_PTHREADS)
    ACE_mutex_t *p_lock = (ACE_mutex_t *) mutex;
    ACE_OS::mutex_unlock (p_lock);
  # else
    ACE_UNUSED_ARG (mutex);
  # endif /* ACE_HAS_PTHREADS */
  #else
    ACE_UNUSED_ARG (mutex);
  #endif /* ACE_HAS_THREADS */
  }
  
  /*****************************************************************************/
  // MUTEXES END
  /*****************************************************************************/
  
  /*****************************************************************************/
  // EVENTS BEGIN
  /*****************************************************************************/
  
  int
  ACE_OS::event_destroy (ACE_event_t *event)
  {
  #if defined (ACE_WIN32)
    ACE_WIN32CALL_RETURN (ACE_ADAPT_RETVAL (::CloseHandle (*event), ace_result_), int, -1);
  #elif defined (ACE_HAS_THREADS)
    if (event->eventdata_)
      {
        // mutex_destroy()/cond_destroy() are called in a loop if the object
        // is BUSY.  This avoids conditions where we fail to destroy these
        // objects because at time of destroy they were just being used in
        // another thread possibly causing deadlocks later on if they keep
        // being used after we're gone.
  
        if (event->eventdata_->type_ == USYNC_PROCESS)
          {
            if (event->name_)
              {
                // Only destroy the event data if we're the ones who initialized
                // it.
  
                int r1, r2;
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \
          (!defined (ACE_LACKS_MUTEXATTR_PSHARED) || !defined (ACE_LACKS_CONDATTR_PSHARED))) || \
       (!defined (ACE_USES_FIFO_SEM) && \
          (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
                // First destroy the mutex so locking after this will return
                // errors.
                while ((r1 = ACE_OS::mutex_destroy (&event->eventdata_->lock_)) == -1
                       && errno == EBUSY)
                  {
                    ACE_OS::thr_yield ();
                  }
  # else
                r1 = ACE_OS::sema_destroy(&event->lock_);
  # endif
  
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_CONDATTR_PSHARED)) || \
      (!defined (ACE_USES_FIFO_SEM) && \
        (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
                // Now fix event to manual reset, raise signal and broadcast
                // until is's possible to destroy the condition.
                event->eventdata_->manual_reset_ = 1;
                while ((r2 = ACE_OS::cond_destroy (&event->eventdata_->condition_)) == -1
                       && errno == EBUSY)
                  {
                    event->eventdata_->is_signaled_ = 1;
                    if (ACE_OS::cond_broadcast (&event->eventdata_->condition_) != 0)
                      return -1;
                    ACE_OS::thr_yield ();
                  }
  # else
                r2 = ACE_OS::sema_destroy(&event->semaphore_);
  # endif
                ACE_OS::munmap (event->eventdata_,
                                sizeof (ACE_eventdata_t));
                ACE_OS::shm_unlink (ACE_TEXT_CHAR_TO_TCHAR(event->name_));
                ACE_OS::free (event->name_);
                return r1 != 0 || r2 != 0 ? -1 : 0;
              }
            else
              {
                ACE_OS::munmap (event->eventdata_,
                                sizeof (ACE_eventdata_t));
  # if (!defined (ACE_HAS_PTHREADS) || !defined (_POSIX_THREAD_PROCESS_SHARED) || \
          (defined (ACE_LACKS_MUTEXATTR_PSHARED) && defined (ACE_LACKS_CONDATTR_PSHARED))) && \
       (defined (ACE_USES_FIFO_SEM) || \
          (defined (ACE_HAS_POSIX_SEM) && defined (ACE_HAS_POSIX_SEM_TIMEOUT) && defined (ACE_LACKS_NAMED_POSIX_SEM)))
                ACE_OS::sema_destroy(&event->lock_);
  # endif
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_CONDATTR_PSHARED)) || \
      (!defined (ACE_USES_FIFO_SEM) && \
        (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
                return 0;
  # else
                return ACE_OS::sema_destroy(&event->semaphore_);
  # endif
              }
          }
        else
          {
            int r1, r2;
            // First destroy the mutex so locking after this will return errors.
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \
          (!defined (ACE_LACKS_MUTEXATTR_PSHARED) || !defined (ACE_LACKS_CONDATTR_PSHARED))) || \
       (!defined (ACE_USES_FIFO_SEM) && \
          (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
        // first destroy the mutex so locking after this will return errors
            while ((r1 = ACE_OS::mutex_destroy (&event->eventdata_->lock_)) == -1
                   && errno == EBUSY)
              {
                ACE_OS::thr_yield ();
              }
  # else
            r1 = ACE_OS::sema_destroy(&event->lock_);
  # endif
  
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_CONDATTR_PSHARED)) || \
      (!defined (ACE_USES_FIFO_SEM) && \
        (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
            // Now fix event to manual reset, raise signal and broadcast until
            // it's possible to destroy the condition.
            event->eventdata_->manual_reset_ = 1;
            while ((r2 = ACE_OS::cond_destroy (&event->eventdata_->condition_)) == -1
                   && errno == EBUSY)
              {
                event->eventdata_->is_signaled_ = 1;
                if (ACE_OS::cond_broadcast (&event->eventdata_->condition_) != 0)
                  return -1;
                ACE_OS::thr_yield ();
              }
  # else
            r2 = ACE_OS::sema_destroy(&event->semaphore_);
  # endif
            delete event->eventdata_;
            return r1 != 0 || r2 != 0 ? -1 : 0;
          }
      }
  
    return 0;
  #else
    ACE_UNUSED_ARG (event);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_WIN32 */
  }
  
  int
  ACE_OS::event_init (ACE_event_t *event,
                      int type,
                      ACE_condattr_t *attributes,
                      int manual_reset,
                      int initial_state,
                      const char *name,
                      void *arg,
                      LPSECURITY_ATTRIBUTES sa)
  {
  #if defined (ACE_WIN32)
    ACE_UNUSED_ARG (type);
    ACE_UNUSED_ARG (attributes);
    ACE_UNUSED_ARG (arg);
    SECURITY_ATTRIBUTES sa_buffer;
    SECURITY_DESCRIPTOR sd_buffer;
  # if defined (ACE_HAS_WINCE)
    // @@todo (brunsch) This idea should be moved into ACE_OS_Win32.
    *event = ::CreateEventW (ACE_OS::default_win32_security_attributes_r
                               (sa, &sa_buffer, &sd_buffer),
                             manual_reset,
                             initial_state,
                             ACE_Ascii_To_Wide (name).wchar_rep ());
  # else /* ACE_HAS_WINCE */
    *event = ::CreateEventA (ACE_OS::default_win32_security_attributes_r
                               (sa, &sa_buffer, &sd_buffer),
                             manual_reset,
                             initial_state,
                             name);
  # endif /* ACE_HAS_WINCE */
    if (*event == 0)
      ACE_FAIL_RETURN (-1);
    else
      {
        // Make sure to set errno to ERROR_ALREADY_EXISTS if necessary.
        ACE_OS::set_errno_to_last_error ();
        return 0;
      }
  #elif defined (ACE_HAS_THREADS)
    ACE_UNUSED_ARG (sa);
    event->eventdata_ = 0;
    ACE_eventdata_t* evtdata;
  
    if (type == USYNC_PROCESS)
      {
        const char *name_p = 0;
  #  if defined (ACE_SHM_OPEN_REQUIRES_ONE_SLASH)
        char adj_name[MAXPATHLEN];
        if (name[0] != '/')
          {
            adj_name[0] = '/';
            ACE_OS::strsncpy (&adj_name[1], name, MAXPATHLEN-1);
            name_p = adj_name;
          }
        else
          {
            name_p = name;
          }
  #  else
        name_p = name;
  #  endif /* ACE_SHM_OPEN_REQUIRES_ONE_SLASH */
        int owner = 0;
        // Let's see if the shared memory entity already exists.
        ACE_HANDLE fd = ACE_OS::shm_open (ACE_TEXT_CHAR_TO_TCHAR (name_p),
                                          O_RDWR | O_CREAT | O_EXCL,
                                          ACE_DEFAULT_FILE_PERMS);
        if (fd == ACE_INVALID_HANDLE)
          {
            if (errno == EEXIST)
              fd = ACE_OS::shm_open (ACE_TEXT_CHAR_TO_TCHAR (name_p),
                                     O_RDWR | O_CREAT,
                                     ACE_DEFAULT_FILE_PERMS);
            if (fd == ACE_INVALID_HANDLE)   // Still can't get it.
              return -1;
          }
        else
          {
            // We own this shared memory object!  Let's set its size.
            if (ACE_OS::ftruncate (fd, sizeof (ACE_eventdata_t)) == -1)
              {
                ACE_OS::close (fd);
                return -1;
              }
            owner = 1;
          }
  
        evtdata =
          (ACE_eventdata_t *) ACE_OS::mmap (0,
                                            sizeof (ACE_eventdata_t),
                                            PROT_RDWR,
                                            MAP_SHARED,
                                            fd,
                                            0);
        ACE_OS::close (fd);
        if (evtdata == MAP_FAILED)
          {
            if (owner)
              ACE_OS::shm_unlink (ACE_TEXT_CHAR_TO_TCHAR (name_p));
            return -1;
          }
  
        if (owner)
          {
            event->name_ = ACE_OS::strdup (name_p);
            if (event->name_ == 0)
              {
                ACE_OS::shm_unlink (ACE_TEXT_CHAR_TO_TCHAR (name_p));
                return -1;
              }
            event->eventdata_ = evtdata;
            event->eventdata_->type_ = type;
            event->eventdata_->manual_reset_ = manual_reset;
            event->eventdata_->is_signaled_ = initial_state;
            event->eventdata_->auto_event_signaled_ = false;
            event->eventdata_->waiting_threads_ = 0;
            event->eventdata_->signal_count_ = 0;
  
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_CONDATTR_PSHARED)) || \
      (!defined (ACE_USES_FIFO_SEM) && \
        (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
            int result = attributes == 0 ?
                            ACE_OS::cond_init (&event->eventdata_->condition_,
                                               type,
                                               name,
                                               arg) :
                            ACE_OS::cond_init (&event->eventdata_->condition_,
                                               *attributes,
                                               name,
                                               arg);
  # else
            char   sem_name[128];
            ACE_OS::strncpy (sem_name,
                             name,
                             sizeof (sem_name) - (1 + sizeof ("._ACE_EVTSEM_")));
            ACE_OS::strcat (sem_name, "._ACE_EVTSEM_");
            int result = ACE_OS::sema_init (&event->semaphore_,
                                            0,
                                            type,
                                            attributes,
                                            sem_name,
                                            arg);
  # endif
            if (result == 0)
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \
          (!defined (ACE_LACKS_MUTEXATTR_PSHARED) || !defined (ACE_LACKS_CONDATTR_PSHARED))) || \
       (!defined (ACE_USES_FIFO_SEM) && \
          (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
              result = ACE_OS::mutex_init (&event->eventdata_->lock_,
                                           type,
                                           name,
                                           (ACE_mutexattr_t *) arg);
  # else
            {
              char   lck_name[128];
              ACE_OS::strncpy
                (lck_name,
                 name,
                 sizeof (lck_name) - (1 + sizeof ("._ACE_EVTLCK_")));
              ACE_OS::strcat (lck_name, "._ACE_EVTLCK_");
              result = ACE_OS::sema_init (&event->lock_,
                                          0,
                                          type,
                                          attributes,
                                          lck_name,
                                          arg);
              if (result == 0)
                result = ACE_OS::sema_post (&event->lock_); /* Initially unlock */
            }
  # endif
            return result;
          }
        else
          {
            int result = 0;
  
            event->name_ = 0;
            event->eventdata_ = evtdata;
  # if (!defined (ACE_HAS_PTHREADS) || !defined (_POSIX_THREAD_PROCESS_SHARED) || defined (ACE_LACKS_CONDATTR_PSHARED)) && \
        (defined (ACE_USES_FIFO_SEM) || \
        (defined (ACE_HAS_POSIX_SEM) && defined (ACE_HAS_POSIX_SEM_TIMEOUT) && !defined (ACE_LACKS_NAMED_POSIX_SEM)))
            char   sem_name[128];
            ACE_OS::strncpy (sem_name,
                             name,
                             sizeof (sem_name) - (1 + sizeof ("._ACE_EVTSEM_")));
            ACE_OS::strcat (sem_name, "._ACE_EVTSEM_");
            result = ACE_OS::sema_init(&event->semaphore_,
                                       0,
                                       type,
                                       attributes,
                                       sem_name,
                                       arg);
  # endif
  
  # if (!defined (ACE_HAS_PTHREADS) || !defined (_POSIX_THREAD_PROCESS_SHARED) || \
          (defined (ACE_LACKS_MUTEXATTR_PSHARED) && defined (ACE_LACKS_CONDATTR_PSHARED))) && \
       (defined (ACE_USES_FIFO_SEM) || \
          (defined (ACE_HAS_POSIX_SEM) && defined (ACE_HAS_POSIX_SEM_TIMEOUT) && defined (ACE_LACKS_NAMED_POSIX_SEM)))
            if (result == 0)
              {
                char   lck_name[128];
                ACE_OS::strncpy
                  (lck_name,
                   name,
                   sizeof (lck_name) - (1 + sizeof ("._ACE_EVTLCK_")));
                ACE_OS::strcat (lck_name, "._ACE_EVTLCK_");
                result = ACE_OS::sema_init (&event->lock_,
                                            0,
                                            type,
                                            attributes,
                                            lck_name,
                                            arg);
              }
  # endif
            return result;
          }
      }
    else
      {
        ACE_NEW_RETURN (evtdata, ACE_eventdata_t, -1);
        event->name_ = 0;
        event->eventdata_ = evtdata;
        event->eventdata_->type_ = type;
        event->eventdata_->manual_reset_ = manual_reset;
        event->eventdata_->is_signaled_ = initial_state;
        event->eventdata_->auto_event_signaled_ = false;
        event->eventdata_->waiting_threads_ = 0;
        event->eventdata_->signal_count_ = 0;
  
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_CONDATTR_PSHARED)) || \
      (!defined (ACE_USES_FIFO_SEM) && \
        (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
        int result = attributes == 0 ?
                        ACE_OS::cond_init (&event->eventdata_->condition_,
                                           type,
                                           name,
                                           arg) :
                        ACE_OS::cond_init (&event->eventdata_->condition_,
                                           *attributes,
                                           name,
                                           arg);
  # else
        int result = ACE_OS::sema_init (&event->semaphore_,
                                        0,
                                        type,
                                        attributes,
                                        name,
                                        arg);
  # endif
        if (result == 0)
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \
          (!defined (ACE_LACKS_MUTEXATTR_PSHARED) || !defined (ACE_LACKS_CONDATTR_PSHARED))) || \
       (!defined (ACE_USES_FIFO_SEM) && \
          (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
          result = ACE_OS::mutex_init (&event->eventdata_->lock_,
                                       type,
                                       name,
                                       (ACE_mutexattr_t *) arg);
  # else
        result = ACE_OS::sema_init (&event->lock_,
                                    0,
                                    type,
                                    attributes,
                                    name,
                                    arg);
        if (result == 0)
          result = ACE_OS::sema_post(&event->lock_);    /* initially unlock */
  # endif
  
        return result;
      }
  #else
    ACE_UNUSED_ARG (event);
    ACE_UNUSED_ARG (manual_reset);
    ACE_UNUSED_ARG (initial_state);
    ACE_UNUSED_ARG (type);
    ACE_UNUSED_ARG (attributes);
    ACE_UNUSED_ARG (name);
    ACE_UNUSED_ARG (arg);
    ACE_UNUSED_ARG (sa);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_WIN32 */
  }
  
  int
  ACE_OS::event_pulse (ACE_event_t *event)
  {
  #if defined (ACE_WIN32)
    ACE_WIN32CALL_RETURN (ACE_ADAPT_RETVAL (::PulseEvent (*event), ace_result_), int, -1);
  #elif defined (ACE_HAS_THREADS)
    int result = 0;
    int error = 0;
  
    // grab the lock first
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \
          (!defined (ACE_LACKS_MUTEXATTR_PSHARED) || !defined (ACE_LACKS_CONDATTR_PSHARED))) || \
       (!defined (ACE_USES_FIFO_SEM) && \
          (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
    if (ACE_OS::mutex_lock (&event->eventdata_->lock_) == 0)
  # else
    if (ACE_OS::sema_wait (&event->lock_) == 0)
  # endif
    {
      if (event->eventdata_->waiting_threads_ > 0)
      {
        // Manual-reset event.
        if (event->eventdata_->manual_reset_ == 1)
        {
          // Wakeup all waiters.
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_CONDATTR_PSHARED)) || \
      (!defined (ACE_USES_FIFO_SEM) && \
        (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
          if (ACE_OS::cond_broadcast (&event->eventdata_->condition_) != 0)
          {
            result = -1;
            error = errno;
          }
          if (result == 0)
            event->eventdata_->signal_count_ = event->eventdata_->waiting_threads_;
  # else
          event->eventdata_->signal_count_ = event->eventdata_->waiting_threads_;
          for (unsigned long i=0; i<event->eventdata_->signal_count_ ;++i)
            if (ACE_OS::sema_post(&event->semaphore_) != 0)
            {
              event->eventdata_->signal_count_ = 0;
              result = -1;
              error = errno;
            }
  
          if (result == 0)
            while(event->eventdata_->signal_count_!=0 && event->eventdata_->waiting_threads_!=0)
              ACE_OS::thr_yield ();
  # endif
        }
        // Auto-reset event: wakeup one waiter.
        else
        {
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_CONDATTR_PSHARED)) || \
      (!defined (ACE_USES_FIFO_SEM) && \
        (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
          if (ACE_OS::cond_signal (&event->eventdata_->condition_) != 0)
  # else
          if (ACE_OS::sema_post(&event->semaphore_) != 0)
  # endif
          {
            result = -1;
            error = errno;
          }
  
          event->eventdata_->auto_event_signaled_ = true;
        }
      }
  
      // Reset event.
      event->eventdata_->is_signaled_ = 0;
  
      // Now we can let go of the lock.
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \
          (!defined (ACE_LACKS_MUTEXATTR_PSHARED) || !defined (ACE_LACKS_CONDATTR_PSHARED))) || \
       (!defined (ACE_USES_FIFO_SEM) && \
          (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
      if (ACE_OS::mutex_unlock (&event->eventdata_->lock_) != 0)
        return -1;
  # else
      if (ACE_OS::sema_post (&event->lock_) != 0)
        return -1;
  # endif
      if (result == -1)
        // Reset errno in case mutex_unlock() also fails...
        errno = error;
    }
    else
      result = -1;
    return result;
  #else
    ACE_UNUSED_ARG (event);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_WIN32 */
  }
  
  int
  ACE_OS::event_reset (ACE_event_t *event)
  {
  #if defined (ACE_WIN32)
    ACE_WIN32CALL_RETURN (ACE_ADAPT_RETVAL (::ResetEvent (*event), ace_result_), int, -1);
  #elif defined (ACE_HAS_THREADS)
    int result = 0;
  
    // Grab the lock first.
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \
          (!defined (ACE_LACKS_MUTEXATTR_PSHARED) || !defined (ACE_LACKS_CONDATTR_PSHARED))) || \
       (!defined (ACE_USES_FIFO_SEM) && \
          (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
    if (ACE_OS::mutex_lock (&event->eventdata_->lock_) == 0)
  # else
    if (ACE_OS::sema_wait (&event->lock_) == 0)
  # endif
    {
      // Reset event.
      event->eventdata_->is_signaled_ = 0;
      event->eventdata_->auto_event_signaled_ = false;
  
      // Now we can let go of the lock.
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \
          (!defined (ACE_LACKS_MUTEXATTR_PSHARED) || !defined (ACE_LACKS_CONDATTR_PSHARED))) || \
       (!defined (ACE_USES_FIFO_SEM) && \
          (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
      if (ACE_OS::mutex_unlock (&event->eventdata_->lock_) != 0)
        return -1;
  # else
      if (ACE_OS::sema_post (&event->lock_) != 0)
        return -1;
  # endif
    }
    else
      result = -1;
    return result;
  #else
    ACE_UNUSED_ARG (event);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_WIN32 */
  }
  
  int
  ACE_OS::event_signal (ACE_event_t *event)
  {
  #if defined (ACE_WIN32)
    ACE_WIN32CALL_RETURN (ACE_ADAPT_RETVAL (::SetEvent (*event), ace_result_), int, -1);
  #elif defined (ACE_HAS_THREADS)
    int result = 0;
    int error = 0;
  
    // grab the lock first
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \
          (!defined (ACE_LACKS_MUTEXATTR_PSHARED) || !defined (ACE_LACKS_CONDATTR_PSHARED))) || \
       (!defined (ACE_USES_FIFO_SEM) && \
          (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
    if (ACE_OS::mutex_lock (&event->eventdata_->lock_) == 0)
  # else
    if (ACE_OS::sema_wait (&event->lock_) == 0)
  # endif
    {
      // Manual-reset event.
      if (event->eventdata_->manual_reset_ == 1)
      {
        // wakeup all
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_CONDATTR_PSHARED)) || \
      (!defined (ACE_USES_FIFO_SEM) && \
        (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
        if (ACE_OS::cond_broadcast (&event->eventdata_->condition_) != 0)
        {
          result = -1;
          error = errno;
        }
  # else
        if (ACE_OS::sema_post(&event->semaphore_) != 0)
        {
          result = -1;
          error = errno;
        }
  # endif
  
        if (result == 0)
          // signal event
          event->eventdata_->is_signaled_ = 1;
      }
      // Auto-reset event
      else
      {
        if (event->eventdata_->waiting_threads_ == 0)
          // No waiters: signal event.
          event->eventdata_->is_signaled_ = 1;
        // Waiters: wakeup one waiter.
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_CONDATTR_PSHARED)) || \
      (!defined (ACE_USES_FIFO_SEM) && \
        (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
        else if (ACE_OS::cond_signal (&event->eventdata_->condition_) != 0)
  # else
        else if (ACE_OS::sema_post(&event->semaphore_) != 0)
  # endif
        {
          result = -1;
          error = errno;
        }
  
        event->eventdata_->auto_event_signaled_ = true;
      }
  
      // Now we can let go of the lock.
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \
          (!defined (ACE_LACKS_MUTEXATTR_PSHARED) || !defined (ACE_LACKS_CONDATTR_PSHARED))) || \
       (!defined (ACE_USES_FIFO_SEM) && \
          (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
      if (ACE_OS::mutex_unlock (&event->eventdata_->lock_) != 0)
        return -1;
  # else
      if (ACE_OS::sema_post (&event->lock_) != 0)
        return -1;
  # endif
  
      if (result == -1)
        // Reset errno in case mutex_unlock() also fails...
        errno = error;
    }
    else
      result = -1;
  
    return result;
  #else
    ACE_UNUSED_ARG (event);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_WIN32 */
  }
  
  int
  ACE_OS::event_timedwait (ACE_event_t *event,
                           ACE_Time_Value *timeout,
                           int use_absolute_time)
  {
    if (timeout == 0)
      // Wait indefinitely.
      return ACE_OS::event_wait (event);
  
  #if defined (ACE_WIN32)
    DWORD result;
  
    if (*timeout == ACE_Time_Value::zero)
      // Do a "poll".
      result = ::WaitForSingleObject (*event, 0);
    else
      {
        // Wait for upto <relative_time> number of milliseconds.  Note
        // that we must convert between absolute time (which is passed
        // as a parameter) and relative time (which is what
        // WaitForSingleObjects() expects).
        // <timeout> parameter is given in absolute or relative value
        // depending on parameter <use_absolute_time>.
        int msec_timeout = 0;
        if (use_absolute_time)
          {
            // Time is given in absolute time, we should use
            // gettimeofday() to calculate relative time
            ACE_Time_Value relative_time = timeout->to_relative_time ();
  
            // Watchout for situations where a context switch has caused
            // the current time to be > the timeout.  Thanks to Norbert
            // Rapp <NRapp@nexus-informatics.de> for pointing this.
            if (relative_time > ACE_Time_Value::zero)
              msec_timeout = relative_time.msec ();
          }
        else
          // time is given in relative time, just convert it into
          // milliseconds and use it
          msec_timeout = timeout->msec ();
        result = ::WaitForSingleObject (*event, msec_timeout);
      }
  
    switch (result)
      {
      case WAIT_OBJECT_0:
        return 0;
      case WAIT_TIMEOUT:
        errno = ETIME;
        return -1;
      default:
        // This is a hack, we need to find an appropriate mapping...
        ACE_OS::set_errno_to_last_error ();
        return -1;
      }
  #elif defined (ACE_HAS_THREADS)
    int result = 0;
    int error = 0;
  
    // grab the lock first
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \
          (!defined (ACE_LACKS_MUTEXATTR_PSHARED) || !defined (ACE_LACKS_CONDATTR_PSHARED))) || \
       (!defined (ACE_USES_FIFO_SEM) && \
          (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
    if (ACE_OS::mutex_lock (&event->eventdata_->lock_) == 0)
  # else
    if (ACE_OS::sema_wait (&event->lock_) == 0)
  # endif
      {
        if (event->eventdata_->is_signaled_ == 1)
          // event is currently signaled
          {
            if (event->eventdata_->manual_reset_ == 0)
              {
                // AUTO: reset state
                event->eventdata_->is_signaled_ = 0;
                event->eventdata_->auto_event_signaled_ = false;
              }
          }
        else
          // event is currently not signaled
          {
            event->eventdata_->waiting_threads_++;
  
            ACE_Time_Value absolute_timeout = *timeout;
  
            // cond_timewait() expects absolute time, check
            // <use_absolute_time> flag.
            if (use_absolute_time == 0)
              absolute_timeout = timeout->to_absolute_time ();
  
            while (event->eventdata_->is_signaled_ == 0 &&
                   event->eventdata_->auto_event_signaled_ == false)
              {
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_CONDATTR_PSHARED)) || \
      (!defined (ACE_USES_FIFO_SEM) && \
        (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
                if (ACE_OS::cond_timedwait (&event->eventdata_->condition_,
                                            &event->eventdata_->lock_,
                                            &absolute_timeout) != 0)
                  {
                    result = -1;
                    error = errno;
                    break;
                  }
  
                if (event->eventdata_->signal_count_ > 0)
                  {
                    event->eventdata_->signal_count_--;
                    break;
                  }
  # else
  #   if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_MUTEXATTR_PSHARED)) || \
        (!defined (ACE_USES_FIFO_SEM) && (!defined (ACE_HAS_POSIX_SEM) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
                if (ACE_OS::mutex_unlock (&event->eventdata_->lock_) != 0)
  #   else
                  if (ACE_OS::sema_post (&event->lock_) != 0)
  #   endif
                    {
                      event->eventdata_->waiting_threads_--;
                      return -1;
                    }
  
                if (ACE_OS::sema_wait(&event->semaphore_, absolute_timeout) !=0)
                  {
                    result = -1;
                    if (errno == ETIMEDOUT) // Semaphores time out with ETIMEDOUT (POSIX)
                      error = ETIME;
                    else
                      error = errno;
                  }
  
                bool signalled = false;
                if (result == 0 && event->eventdata_->signal_count_ > 0)
                  {
                    event->eventdata_->signal_count_--;
                    signalled = true;
                  }
  
  #   if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_MUTEXATTR_PSHARED)) || \
        (!defined (ACE_USES_FIFO_SEM) && (!defined (ACE_HAS_POSIX_SEM) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
                if (ACE_OS::mutex_lock (&event->eventdata_->lock_) != 0)
  #   else
                  if (ACE_OS::sema_wait (&event->lock_) != 0)
  #   endif
                    {
                      event->eventdata_->waiting_threads_--;  // yes, I know it's not save
                      return -1;
                    }
  
                if (result)
                  break;
  
                if (event->eventdata_->manual_reset_ == 1 && event->eventdata_->is_signaled_ == 1)
                  if (ACE_OS::sema_post(&event->semaphore_) != 0)
                    {
                      result = -1;
                      error = errno;
                      break;
                    }
  
                if (signalled)
                  break;
  # endif
              }
  
            // Reset the auto_event_signaled_ to false now that we have
            // woken up.
            if (event->eventdata_->auto_event_signaled_ == true)
              event->eventdata_->auto_event_signaled_ = false;
  
            event->eventdata_->waiting_threads_--;
          }
  
        // Now we can let go of the lock.
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \
          (!defined (ACE_LACKS_MUTEXATTR_PSHARED) || !defined (ACE_LACKS_CONDATTR_PSHARED))) || \
       (!defined (ACE_USES_FIFO_SEM) && \
          (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
        if (ACE_OS::mutex_unlock (&event->eventdata_->lock_) != 0)
          return -1;
  # else
        if (ACE_OS::sema_post (&event->lock_) != 0)
          return -1;
  # endif
  
        if (result == -1)
          // Reset errno in case mutex_unlock() also fails...
          errno = error;
      }
    else
      result = -1;
    return result;
  #else
    ACE_UNUSED_ARG (event);
    ACE_UNUSED_ARG (timeout);
    ACE_UNUSED_ARG (use_absolute_time);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_WIN32 */
  }
  
  int
  ACE_OS::event_wait (ACE_event_t *event)
  {
  #if defined (ACE_WIN32)
    switch (::WaitForSingleObject (*event, INFINITE))
      {
      case WAIT_OBJECT_0:
        return 0;
      default:
        {
          ACE_OS::set_errno_to_last_error ();
          return -1;
        }
      }
  #elif defined (ACE_HAS_THREADS)
    int result = 0;
    int error = 0;
  
    // grab the lock first
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \
          (!defined (ACE_LACKS_MUTEXATTR_PSHARED) || !defined (ACE_LACKS_CONDATTR_PSHARED))) || \
       (!defined (ACE_USES_FIFO_SEM) && \
          (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
    if (ACE_OS::mutex_lock (&event->eventdata_->lock_) == 0)
  # else
    if (ACE_OS::sema_wait (&event->lock_) == 0)
  # endif
      {
        if (event->eventdata_->is_signaled_ == 1)
          // Event is currently signaled.
          {
            if (event->eventdata_->manual_reset_ == 0)
              // AUTO: reset state
              event->eventdata_->is_signaled_ = 0;
          }
        else // event is currently not signaled
          {
            event->eventdata_->waiting_threads_++;
  
            while (event->eventdata_->is_signaled_ == 0 &&
                   event->eventdata_->auto_event_signaled_ == false)
              {
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_CONDATTR_PSHARED)) || \
      (!defined (ACE_USES_FIFO_SEM) && \
        (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
                if (ACE_OS::cond_wait (&event->eventdata_->condition_,
                                       &event->eventdata_->lock_) != 0)
                  {
                    result = -1;
                    error = errno;
                    // Something went wrong...
                    break;
                  }
                if (event->eventdata_->signal_count_ > 0)
                  {
                    event->eventdata_->signal_count_--;
                    break;
                  }
  # else
  #   if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_MUTEXATTR_PSHARED)) || \
        (!defined (ACE_USES_FIFO_SEM) && (!defined (ACE_HAS_POSIX_SEM) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
                if (ACE_OS::mutex_unlock (&event->eventdata_->lock_) != 0)
  #   else
                  if (ACE_OS::sema_post (&event->lock_) != 0)
  #   endif
                    {
                      event->eventdata_->waiting_threads_--;
                      return -1;
                    }
  
                if (ACE_OS::sema_wait (&event->semaphore_) !=0)
                  {
                    result = -1;
                    error = errno;
                  }
  
                bool signalled = false;
                if (result == 0 && event->eventdata_->signal_count_ > 0)
                  {
                    event->eventdata_->signal_count_--;
                    signalled = true;
                  }
  
  #   if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && !defined (ACE_LACKS_MUTEXATTR_PSHARED)) || \
        (!defined (ACE_USES_FIFO_SEM) && (!defined (ACE_HAS_POSIX_SEM) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
                if (ACE_OS::mutex_lock (&event->eventdata_->lock_) != 0)
  #   else
                  if (ACE_OS::sema_wait (&event->lock_) != 0)
  #   endif
                    {
                      event->eventdata_->waiting_threads_--;
                      return -1;
                    }
  
                if (result)
                  break;
  
                if (event->eventdata_->manual_reset_ == 1 && event->eventdata_->is_signaled_ == 1)
                  if (ACE_OS::sema_post(&event->semaphore_) != 0)
                    {
                      result = -1;
                      error = errno;
                      break;
                    }
  
                if (signalled)
                  break;
  # endif
              }
  
            // Reset it since we have woken up.
            if (event->eventdata_->auto_event_signaled_ == true)
              event->eventdata_->auto_event_signaled_ = false;
  
            event->eventdata_->waiting_threads_--;
          }
  
        // Now we can let go of the lock.
  # if (defined (ACE_HAS_PTHREADS) && defined (_POSIX_THREAD_PROCESS_SHARED) && \
          (!defined (ACE_LACKS_MUTEXATTR_PSHARED) || !defined (ACE_LACKS_CONDATTR_PSHARED))) || \
       (!defined (ACE_USES_FIFO_SEM) && \
          (!defined (ACE_HAS_POSIX_SEM) || !defined (ACE_HAS_POSIX_SEM_TIMEOUT) || defined (ACE_LACKS_NAMED_POSIX_SEM)))
        if (ACE_OS::mutex_unlock (&event->eventdata_->lock_) != 0)
          return -1;
  # else
        if (ACE_OS::sema_post (&event->lock_) != 0)
          return -1;
  # endif
  
        if (result == -1)
          // Reset errno in case mutex_unlock() also fails...
          errno = error;
      }
    else
      result = -1;
    return result;
  #else
    ACE_UNUSED_ARG (event);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_WIN32 */
  }
  
  /*****************************************************************************/
  // EVENTS END
  /*****************************************************************************/
  
  int
  ACE_OS::lwp_getparams (ACE_Sched_Params &sched_params)
  {
  #if defined (ACE_HAS_STHREADS) || defined (sun)
    // Get the class TS and RT class IDs.
    ACE_id_t rt_id;
    ACE_id_t ts_id;
    if (ACE_OS::scheduling_class ("RT", rt_id) == -1
        || ACE_OS::scheduling_class ("TS", ts_id) == -1)
      return -1;
  
    // Get this LWP's scheduling parameters.
    pcparms_t pcparms;
    // The following is just to avoid Purify warnings about unitialized
    // memory reads.
    ACE_OS::memset (&pcparms, 0, sizeof pcparms);
    pcparms.pc_cid = PC_CLNULL;
  
    if (ACE_OS::priority_control (P_LWPID,
                                  P_MYID,
                                  PC_GETPARMS,
                                  (char *) &pcparms) == -1)
      return -1;
    else if (pcparms.pc_cid == rt_id)
      {
        // RT class.
        rtparms_t rtparms;
        ACE_OS::memcpy (&rtparms, pcparms.pc_clparms, sizeof rtparms);
  
        sched_params.policy (ACE_SCHED_FIFO);
        sched_params.priority (rtparms.rt_pri);
        sched_params.scope (ACE_SCOPE_THREAD);
        ACE_Time_Value quantum (rtparms.rt_tqsecs,
                                rtparms.rt_tqnsecs == RT_TQINF
                                ? 0 : rtparms.rt_tqnsecs * 1000);
        sched_params.quantum (quantum);
        return 0;
      }
    else if (pcparms.pc_cid == ts_id)
      {
        /* TS class */
        tsparms_t tsparms;
        ACE_OS::memcpy (&tsparms, pcparms.pc_clparms, sizeof tsparms);
  
        sched_params.policy (ACE_SCHED_OTHER);
        sched_params.priority (tsparms.ts_upri);
        sched_params.scope (ACE_SCOPE_THREAD);
        return 0;
      }
    else
      return -1;
  
  #else  /* ! ACE_HAS_STHREADS && ! sun */
    ACE_UNUSED_ARG (sched_params);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ! ACE_HAS_STHREADS && ! sun */
  }
  
  int
  ACE_OS::lwp_setparams (const ACE_Sched_Params &sched_params)
  {
  #if defined (ACE_HAS_STHREADS) || defined (sun)
    ACE_Sched_Params lwp_params (sched_params);
    lwp_params.scope (ACE_SCOPE_LWP);
    return ACE_OS::sched_params (lwp_params);
  #else  /* ! ACE_HAS_STHREADS && ! sun */
    ACE_UNUSED_ARG (sched_params);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ! ACE_HAS_STHREADS && ! sun */
  }
  
  #if !defined (ACE_HAS_THREADS) || (defined (ACE_LACKS_RWLOCK_T) && \
                                     !defined (ACE_HAS_PTHREADS_UNIX98_EXT))
  int
  ACE_OS::rwlock_init (ACE_rwlock_t *rw,
                       int type,
                       const ACE_TCHAR *name,
                       void *arg)
  {
    // ACE_OS_TRACE ("ACE_OS::rwlock_init");
  # if defined (ACE_HAS_THREADS) && defined (ACE_LACKS_RWLOCK_T)
    // NT, POSIX, and VxWorks don't support this natively.
    ACE_UNUSED_ARG (name);
    int result = -1;
  
    // Since we cannot use the user specified name for all three
    // objects, we will create three completely new names.
    ACE_TCHAR name1[ACE_UNIQUE_NAME_LEN];
    ACE_TCHAR name2[ACE_UNIQUE_NAME_LEN];
    ACE_TCHAR name3[ACE_UNIQUE_NAME_LEN];
    ACE_TCHAR name4[ACE_UNIQUE_NAME_LEN];
  
    ACE_OS::unique_name ((const void *) &rw->lock_,
                         name1,
                         ACE_UNIQUE_NAME_LEN);
    ACE_OS::unique_name ((const void *) &rw->waiting_readers_,
                         name2,
                         ACE_UNIQUE_NAME_LEN);
    ACE_OS::unique_name ((const void *) &rw->waiting_writers_,
                         name3,
                         ACE_UNIQUE_NAME_LEN);
    ACE_OS::unique_name ((const void *) &rw->waiting_important_writer_,
                         name4,
                         ACE_UNIQUE_NAME_LEN);
  
    ACE_condattr_t attributes;
    if (ACE_OS::condattr_init (attributes, type) == 0)
      {
        if (ACE_OS::mutex_init (&rw->lock_, type, name1,
                                (ACE_mutexattr_t *) arg) == 0
            && ACE_OS::cond_init (&rw->waiting_readers_,
                                  attributes, name2, arg) == 0
            && ACE_OS::cond_init (&rw->waiting_writers_,
                                  attributes, name3, arg) == 0
            && ACE_OS::cond_init (&rw->waiting_important_writer_,
                                  attributes, name4, arg) == 0)
          {
            // Success!
            rw->ref_count_ = 0;
            rw->num_waiting_writers_ = 0;
            rw->num_waiting_readers_ = 0;
            rw->important_writer_ = false;
            result = 0;
          }
        ACE_OS::condattr_destroy (attributes);
      }
  
    if (result == -1)
      {
        // Save/restore errno.
        ACE_Errno_Guard error (errno);
  
        /* We're about to return -1 anyway, so
         * no need to check return values of these clean-up calls:
         */
        (void)ACE_OS::mutex_destroy (&rw->lock_);
        (void)ACE_OS::cond_destroy (&rw->waiting_readers_);
        (void)ACE_OS::cond_destroy (&rw->waiting_writers_);
        (void)ACE_OS::cond_destroy (&rw->waiting_important_writer_);
      }
    return result;
  # else
    ACE_UNUSED_ARG (rw);
    ACE_UNUSED_ARG (type);
    ACE_UNUSED_ARG (name);
    ACE_UNUSED_ARG (arg);
    ACE_NOTSUP_RETURN (-1);
  # endif /* ACE_HAS_THREADS */
  }
  #endif /* ! ACE_HAS_THREADS || ACE_LACKS_RWLOCK_T */
  
  int
  ACE_OS::sched_params (const ACE_Sched_Params &sched_params,
                        ACE_id_t id)
  {
    ACE_OS_TRACE ("ACE_OS::sched_params");
  #if defined (ACE_HAS_STHREADS)
    return ACE_OS::set_scheduling_params (sched_params, id);
  #elif defined (ACE_HAS_PTHREADS) && \
        (!defined (ACE_LACKS_SETSCHED) || \
        defined (ACE_HAS_PTHREAD_SCHEDPARAM))
    if (sched_params.quantum () != ACE_Time_Value::zero)
      {
        // quantums not supported
        errno = EINVAL;
        return -1;
      }
  
    // Thanks to Thilo Kielmann <kielmann@informatik.uni-siegen.de> for
    // providing this code for 1003.1c PThreads.  Please note that this
    // has only been tested for POSIX 1003.1c threads, and may cause
    // problems with other PThreads flavors!
  
    struct sched_param param;
    param.sched_priority = sched_params.priority ();
  
    if (sched_params.scope () == ACE_SCOPE_PROCESS)
      {
  # if defined (ACE_HAS_PTHREAD_SCHEDPARAM)
        ACE_UNUSED_ARG (id);
        ACE_NOTSUP_RETURN (-1);
  # else  /* !ACE_HAS_PTHREAD_SCHEDPARAM */
        int result = ::sched_setscheduler (id == ACE_SELF ? 0 : id,
                                           sched_params.policy (),
                                           &param) == -1 ? -1 : 0;
        return result;
  # endif /* !ACE_HAS_PTHREAD_SCHEDPARAM */
      }
    else if (sched_params.scope () == ACE_SCOPE_THREAD)
      {
        ACE_thread_t thr_id = ACE_OS::thr_self ();
  
        int result;
        ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::pthread_setschedparam (thr_id,
                                                                      sched_params.policy (),
                                                                      &param),
                                             result),
                           int, -1);
      }
  # if defined (sun)
    // We need to be able to set LWP priorities on Suns, even without
    // ACE_HAS_STHREADS, to obtain preemption.
    else if (sched_params.scope () == ACE_SCOPE_LWP)
      return ACE_OS::set_scheduling_params (sched_params, id);
  # endif /* sun */
    else // sched_params.scope () == ACE_SCOPE_LWP, which isn't POSIX
      {
        errno = EINVAL;
        return -1;
      }
  
  #elif defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)
  
    // PharLap ETS can act on the current thread - it can set the
    // quantum also, unlike Win32. All this only works on the RT
    // version.
  # if defined (ACE_HAS_PHARLAP_RT)
    if (id != ACE_SELF)
      ACE_NOTSUP_RETURN (-1);
  
  #   if !defined (ACE_PHARLAP_LABVIEW_RT)
    if (sched_params.quantum() != ACE_Time_Value::zero)
      EtsSetTimeSlice (sched_params.quantum().msec());
  #   endif
  
  # else
  
    if (sched_params.quantum () != ACE_Time_Value::zero)
      {
        // I don't know of a way to set the quantum on Win32.
        errno = EINVAL;
        return -1;
      }
  
  # endif /* ACE_HAS_PHARLAP_RT */
  
    if (sched_params.scope () == ACE_SCOPE_THREAD)
      {
  
        // Setting the REALTIME_PRIORITY_CLASS on Windows is almost always
        // a VERY BAD THING. This include guard will allow people
        // to easily disable this feature in ACE.
        // It won't work at all for Pharlap since there's no SetPriorityClass.
  #if !defined (ACE_HAS_PHARLAP) && \
      !defined (ACE_DISABLE_WIN32_INCREASE_PRIORITY)
        // Set the priority class of this process to the REALTIME process class
        // _if_ the policy is ACE_SCHED_FIFO.  Otherwise, set to NORMAL.
        if (!::SetPriorityClass (::GetCurrentProcess (),
                                 (sched_params.policy () == ACE_SCHED_FIFO ||
                                  sched_params.policy () == ACE_SCHED_RR)
                                 ? REALTIME_PRIORITY_CLASS
                                 : NORMAL_PRIORITY_CLASS))
          {
            ACE_OS::set_errno_to_last_error ();
            return -1;
          }
  #endif /* ACE_DISABLE_WIN32_INCREASE_PRIORITY */
  
        // Now that we have set the priority class of the process, set the
        // priority of the current thread to the desired value.
        return ACE_OS::thr_setprio (sched_params.priority ());
      }
    else if (sched_params.scope () == ACE_SCOPE_PROCESS)
      {
  
  # if defined (ACE_HAS_PHARLAP_RT)
        ACE_NOTSUP_RETURN (-1);
  # else
        HANDLE hProcess
          = ::OpenProcess (PROCESS_SET_INFORMATION,
                           FALSE,
                           id == ACE_SELF ? ::GetCurrentProcessId() : id);
        if (!hProcess)
          {
            ACE_OS::set_errno_to_last_error();
            return -1;
          }
        // There is no way for us to set the priority of the thread when we
        // are setting the priority of a different process.  So just ignore
        // the priority argument when ACE_SCOPE_PROCESS is specified.
        // Setting the priority class will automatically increase the base
        // priority of all the threads within a process while maintaining the
        // relative priorities of the threads within it.
        if (!::SetPriorityClass (hProcess,
                                 (sched_params.policy () == ACE_SCHED_FIFO ||
                                  sched_params.policy () == ACE_SCHED_RR)
                                 ? REALTIME_PRIORITY_CLASS
                                 : NORMAL_PRIORITY_CLASS))
          {
            ACE_OS::set_errno_to_last_error ();
            ::CloseHandle (hProcess);
            return -1;
          }
        ::CloseHandle (hProcess);
        return 0;
  #endif /* ACE_HAS_PHARLAP_RT */
  
      }
    else
      {
        errno = EINVAL;
        return -1;
      }
  #elif defined (ACE_VXWORKS)
    ACE_UNUSED_ARG (id);
  
    // There is only one class of priorities on VxWorks, and no time
    // quanta.  So, just set the current thread's priority.
  
    if (sched_params.policy () != ACE_SCHED_FIFO
        || sched_params.scope () != ACE_SCOPE_PROCESS
        || sched_params.quantum () != ACE_Time_Value::zero)
      {
        errno = EINVAL;
        return -1;
      }
  
    // Set the thread priority on the current thread.
    return ACE_OS::thr_setprio (sched_params.priority ());
  #else
    ACE_UNUSED_ARG (sched_params);
    ACE_UNUSED_ARG (id);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_HAS_STHREADS */
  }
  
  int
  ACE_OS::scheduling_class (const char *class_name, ACE_id_t &id)
  {
  #if defined (ACE_HAS_PRIOCNTL)
    // Get the priority class ID.
    pcinfo_t pcinfo;
    // The following is just to avoid Purify warnings about unitialized
    // memory reads.
    ACE_OS::memset (&pcinfo, 0, sizeof pcinfo);
  
    ACE_OS::strcpy (pcinfo.pc_clname, class_name);
    if (ACE_OS::priority_control (P_ALL /* ignored */,
                                  P_MYID /* ignored */,
                                  PC_GETCID,
                                  (char *) &pcinfo) == -1)
      {
        return -1;
      }
    else
      {
        id = pcinfo.pc_cid;
        return 0;
      }
  #else  /* ! ACE_HAS_PRIOCNTL */
    ACE_UNUSED_ARG (class_name);
    ACE_UNUSED_ARG (id);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ! ACE_HAS_PRIOCNTL */
  }
  
  int
  ACE_OS::set_scheduling_params (const ACE_Sched_Params &sched_params,
                                 ACE_id_t id)
  {
  #if defined (ACE_HAS_PRIOCNTL)
    // Set priority class, priority, and quantum of this LWP or process as
    // specified in sched_params.
  
    // Get the priority class ID.
    ACE_id_t class_id;
    if (ACE_OS::scheduling_class (sched_params.policy() == ACE_SCHED_OTHER  ?
                                    "TS"  :
                                    "RT", class_id) == -1)
      {
        return -1;
      }
  
    pcparms_t pcparms;
    // The following is just to avoid Purify warnings about unitialized
    // memory reads.
    ACE_OS::memset (&pcparms, 0, sizeof pcparms);
  
    pcparms.pc_cid = class_id;
  
    if (sched_params.policy () == ACE_SCHED_OTHER  &&
        sched_params.quantum () == ACE_Time_Value::zero)
        // SunOS doesn't support non-zero quantums in time-sharing class:  use
        // real-time class instead.
      {
        tsparms_t tsparms;
        // The following is just to avoid Purify warnings about unitialized
        // memory reads.
        ACE_OS::memset (&tsparms, 0, sizeof tsparms);
  
        // Don't change ts_uprilim (user priority limit)
        tsparms.ts_uprilim = TS_NOCHANGE;
        tsparms.ts_upri = sched_params.priority ();
  
        // Package up the TS class ID and parameters for the
        // priority_control () call.
        ACE_OS::memcpy (pcparms.pc_clparms, &tsparms, sizeof tsparms);
      }
    else if (sched_params.policy () == ACE_SCHED_FIFO  ||
             (sched_params.policy () == ACE_SCHED_RR &&
              sched_params.quantum () != ACE_Time_Value::zero))
             // must have non-zero quantum for RR, to make it meaningful
             // A zero quantum with FIFO has special significance:  it actually
             // means infinite time quantum, i.e., run-to-completion.
      {
        rtparms_t rtparms;
        // The following is just to avoid Purify warnings about unitialized
        // memory reads.
        ACE_OS::memset (&rtparms, 0, sizeof rtparms);
  
        rtparms.rt_pri = sched_params.priority ();
  
        if (sched_params.quantum () == ACE_Time_Value::zero)
          {
            // rtparms.rt_tqsecs is ignored with RT_TQINF
            rtparms.rt_tqnsecs = RT_TQINF;
          }
        else
          {
            rtparms.rt_tqsecs = (ulong) sched_params.quantum ().sec ();
            rtparms.rt_tqnsecs = sched_params.quantum ().usec () * 1000;
          }
  
        // Package up the RT class ID and parameters for the
        // priority_control () call.
        ACE_OS::memcpy (pcparms.pc_clparms, &rtparms, sizeof rtparms);
      }
    else
      {
        errno = EINVAL;
        return -1;
      }
  
    if (ACE_OS::priority_control ((idtype_t) (sched_params.scope () == ACE_SCOPE_THREAD
                                              ? ACE_SCOPE_PROCESS
                                              : sched_params.scope ()),
                                  id,
                                  PC_SETPARMS,
                                  (char *) &pcparms) < 0)
      {
        return ACE_OS::last_error ();
      }
  
    return 0;
  #else  /* ! ACE_HAS_PRIOCNTL */
    ACE_UNUSED_ARG (sched_params);
    ACE_UNUSED_ARG (id);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ! ACE_HAS_PRIOCNTL */
  }
  
  int
  ACE_OS::thr_create (ACE_THR_FUNC func,
                      void *args,
                      long flags,
                      ACE_thread_t *thr_id,
                      ACE_hthread_t *thr_handle,
                      long priority,
                      void *stack,
                      size_t stacksize,
                      ACE_Base_Thread_Adapter *thread_adapter,
                      const char** thr_name)
  {
    ACE_OS_TRACE ("ACE_OS::thr_create");
  
    if (ACE_BIT_DISABLED (flags, THR_DETACHED) &&
        ACE_BIT_DISABLED (flags, THR_JOINABLE))
      ACE_SET_BITS (flags, THR_JOINABLE);
  
  #if defined (ACE_NO_THREAD_ADAPTER)
  # define  ACE_THREAD_FUNCTION  func
  # define  ACE_THREAD_ARGUMENT  args
  #else /* ! defined (ACE_NO_THREAD_ADAPTER) */
  # define  ACE_THREAD_FUNCTION  thread_args->entry_point ()
  # define  ACE_THREAD_ARGUMENT  thread_args
  #endif /* ! defined (ACE_NO_THREAD_ADAPTER) */
  
    ACE_Base_Thread_Adapter *thread_args = 0;
    if (thread_adapter == 0)
  #if defined (ACE_HAS_WIN32_STRUCTURAL_EXCEPTIONS)
      ACE_NEW_RETURN (thread_args,
                      ACE_OS_Thread_Adapter (func, args,
                                             (ACE_THR_C_FUNC) ACE_THREAD_ADAPTER_NAME,
                                             ACE_OS_Object_Manager::seh_except_selector(),
                                             ACE_OS_Object_Manager::seh_except_handler(),
                                             flags),
                      -1);
  #else
    ACE_NEW_RETURN (thread_args,
                    ACE_OS_Thread_Adapter (func, args,
                                           (ACE_THR_C_FUNC) ACE_THREAD_ADAPTER_NAME,
                                           flags),
                    -1);
  
  #endif /* ACE_HAS_WIN32_STRUCTURAL_EXCEPTIONS */
    else
      thread_args = thread_adapter;
  
    auto_ptr <ACE_Base_Thread_Adapter> auto_thread_args;
  
    if (thread_adapter == 0)
      ACE_auto_ptr_reset (auto_thread_args,
                          thread_args);
  
  #if defined (ACE_HAS_THREADS)
  
    // *** Set Stack Size
  # if defined (ACE_NEEDS_HUGE_THREAD_STACKSIZE)
    if (stacksize < ACE_NEEDS_HUGE_THREAD_STACKSIZE)
      stacksize = ACE_NEEDS_HUGE_THREAD_STACKSIZE;
  # endif /* ACE_NEEDS_HUGE_THREAD_STACKSIZE */
  
    ACE_thread_t tmp_thr;
    if (thr_id == 0)
      thr_id = &tmp_thr;
  
    ACE_hthread_t tmp_handle;
    if (thr_handle == 0)
      thr_handle = &tmp_handle;
  
  # if defined (ACE_HAS_PTHREADS)
    int result;
  # if defined (ACE_PTHREAD_ATTR_T_INITIALIZE)
    /* Tests show that VxWorks 6.x pthread lib does not only
     * require zeroing of mutex/condition objects to function correctly
     * but also of the attribute objects.
     */
    pthread_attr_t attr = {0};
  #   else
    pthread_attr_t attr;
  #   endif
    if (ACE_ADAPT_RETVAL(::pthread_attr_init(&attr), result) != 0)
      return -1;
  
    if (stacksize != 0)
      {
        size_t size = stacksize;
  
  #   if defined (PTHREAD_STACK_MIN)
        if (size < static_cast <size_t> (PTHREAD_STACK_MIN))
          size = PTHREAD_STACK_MIN;
  #   endif /* PTHREAD_STACK_MIN */
  
  #   if !defined (ACE_LACKS_PTHREAD_ATTR_SETSTACKSIZE)
  #     if !defined (ACE_LACKS_PTHREAD_ATTR_SETSTACK)
        int result;
        if (stack != 0)
          result = ACE_ADAPT_RETVAL (pthread_attr_setstack (&attr, stack, size), result);
        else
          result = ACE_ADAPT_RETVAL (pthread_attr_setstacksize (&attr, size), result);
        if (result == -1)
  #     else
        if (ACE_ADAPT_RETVAL (pthread_attr_setstacksize (&attr, size), result) == -1)
  #     endif /* !ACE_LACKS_PTHREAD_ATTR_SETSTACK */
          {
            ::pthread_attr_destroy (&attr);
            return -1;
          }
  #   else
        ACE_UNUSED_ARG (size);
  #   endif /* !ACE_LACKS_PTHREAD_ATTR_SETSTACKSIZE */
      }
  
    // *** Set Stack Address
  #   if defined (ACE_LACKS_PTHREAD_ATTR_SETSTACK)
  #     if !defined (ACE_LACKS_PTHREAD_ATTR_SETSTACKADDR)
    if (stack != 0)
      {
        if (ACE_ADAPT_RETVAL(::pthread_attr_setstackaddr (&attr, stack), result) != 0)
          {
            ::pthread_attr_destroy (&attr);
            return -1;
          }
      }
  #     else
    ACE_UNUSED_ARG (stack);
  #     endif /* !ACE_LACKS_PTHREAD_ATTR_SETSTACKADDR */
  #   endif /* ACE_LACKS_PTHREAD_ATTR_SETSTACK */
  
    // *** Deal with various attributes
    if (flags != 0)
      {
        // *** Set Detach state
  #   if !defined (ACE_LACKS_SETDETACH)
        if (ACE_BIT_ENABLED (flags, THR_DETACHED)
            || ACE_BIT_ENABLED (flags, THR_JOINABLE))
          {
            int dstate = PTHREAD_CREATE_JOINABLE;
  
            if (ACE_BIT_ENABLED (flags, THR_DETACHED))
              dstate = PTHREAD_CREATE_DETACHED;
  
            if (ACE_ADAPT_RETVAL(::pthread_attr_setdetachstate (&attr, dstate),
                                 result) != 0)
              {
                ::pthread_attr_destroy (&attr);
                return -1;
              }
          }
  
        // Note: if ACE_LACKS_SETDETACH and THR_DETACHED is enabled, we
        // call ::pthread_detach () below.  If THR_DETACHED is not
        // enabled, we call ::pthread_detach () in the Thread_Manager,
        // after joining with the thread.
  #   endif /* ACE_LACKS_SETDETACH */
  
        // *** Set Policy
  #   if !defined (ACE_LACKS_SETSCHED) || defined (ACE_HAS_PTHREAD_SCHEDPARAM)
        // If we wish to set the priority explicitly, we have to enable
        // explicit scheduling, and a policy, too.
        if (priority != ACE_DEFAULT_THREAD_PRIORITY)
          {
            ACE_SET_BITS (flags, THR_EXPLICIT_SCHED);
            if (ACE_BIT_DISABLED (flags, THR_SCHED_FIFO)
                && ACE_BIT_DISABLED (flags, THR_SCHED_RR)
                && ACE_BIT_DISABLED (flags, THR_SCHED_DEFAULT))
              ACE_SET_BITS (flags, THR_SCHED_DEFAULT);
          }
  
        if (ACE_BIT_ENABLED (flags, THR_SCHED_FIFO)
            || ACE_BIT_ENABLED (flags, THR_SCHED_RR)
            || ACE_BIT_ENABLED (flags, THR_SCHED_DEFAULT))
          {
            int spolicy;
  
  #     if defined (ACE_HAS_ONLY_SCHED_OTHER)
            // SunOS, thru version 5.6, only supports SCHED_OTHER.
            spolicy = SCHED_OTHER;
  #     elif defined (ACE_HAS_ONLY_SCHED_FIFO)
            // NonStop OSS standard pthread supports only SCHED_FIFO.
            spolicy = SCHED_FIFO;
  #     else
            // Make sure to enable explicit scheduling, in case we didn't
            // enable it above (for non-default priority).
            ACE_SET_BITS (flags, THR_EXPLICIT_SCHED);
  
            if (ACE_BIT_ENABLED (flags, THR_SCHED_DEFAULT))
              spolicy = SCHED_OTHER;
            else if (ACE_BIT_ENABLED (flags, THR_SCHED_FIFO))
              spolicy = SCHED_FIFO;
  #       if defined (SCHED_IO)
            else if (ACE_BIT_ENABLED (flags, THR_SCHED_IO))
              spolicy = SCHED_IO;
  #       else
            else if (ACE_BIT_ENABLED (flags, THR_SCHED_IO))
              {
                errno = ENOSYS;
                return -1;
              }
  #       endif /* SCHED_IO */
            else
              spolicy = SCHED_RR;
  
  #     endif /* ACE_HAS_ONLY_SCHED_OTHER */
  
            (void) ACE_ADAPT_RETVAL(::pthread_attr_setschedpolicy (&attr, spolicy),
                             result);
            if (result != 0)
              {
                ::pthread_attr_destroy (&attr);
                return -1;
              }
          }
  
        // *** Set Priority (use reasonable default priorities)
  #     if defined(ACE_HAS_PTHREADS)
        // If we wish to explicitly set a scheduling policy, we also
        // have to specify a priority.  We choose a "middle" priority as
        // default.  Maybe this is also necessary on other POSIX'ish
        // implementations?
        if ((ACE_BIT_ENABLED (flags, THR_SCHED_FIFO)
             || ACE_BIT_ENABLED (flags, THR_SCHED_RR)
             || ACE_BIT_ENABLED (flags, THR_SCHED_DEFAULT))
            && priority == ACE_DEFAULT_THREAD_PRIORITY)
          {
            if (ACE_BIT_ENABLED (flags, THR_SCHED_FIFO))
              priority = ACE_THR_PRI_FIFO_DEF;
            else if (ACE_BIT_ENABLED (flags, THR_SCHED_RR))
              priority = ACE_THR_PRI_RR_DEF;
            else // THR_SCHED_DEFAULT
              priority = ACE_THR_PRI_OTHER_DEF;
          }
  #     endif /* ACE_HAS_PTHREADS */
        if (priority != ACE_DEFAULT_THREAD_PRIORITY)
          {
            struct sched_param sparam;
            ACE_OS::memset ((void *) &sparam, 0, sizeof sparam);
  
  #     if defined (PTHREAD_MAX_PRIORITY) && !defined(ACE_HAS_PTHREADS)
            /* For MIT pthreads... */
            sparam.prio = ACE_MIN (priority, PTHREAD_MAX_PRIORITY);
  #     elif defined(ACE_HAS_PTHREADS) && !defined (ACE_HAS_STHREADS)
            // The following code forces priority into range.
            if (ACE_BIT_ENABLED (flags, THR_SCHED_FIFO))
              sparam.sched_priority =
                ACE_MIN (ACE_THR_PRI_FIFO_MAX,
                         ACE_MAX (ACE_THR_PRI_FIFO_MIN, priority));
            else if (ACE_BIT_ENABLED(flags, THR_SCHED_RR))
              sparam.sched_priority =
                ACE_MIN (ACE_THR_PRI_RR_MAX,
                         ACE_MAX (ACE_THR_PRI_RR_MIN, priority));
            else // Default policy, whether set or not
              sparam.sched_priority =
                ACE_MIN (ACE_THR_PRI_OTHER_MAX,
                         ACE_MAX (ACE_THR_PRI_OTHER_MIN, priority));
  #     elif defined (PRIORITY_MAX)
            sparam.sched_priority = ACE_MIN (priority,
                                             (long) PRIORITY_MAX);
  #     else
            sparam.sched_priority = priority;
  #     endif /*  PTHREAD_MAX_PRIORITY */
  
            {
  #       if defined (sun)  &&  defined (ACE_HAS_ONLY_SCHED_OTHER)
              // SunOS, through 5.6, POSIX only allows priorities > 0 to
              // ::pthread_attr_setschedparam.  If a priority of 0 was
              // requested, set the thread priority after creating it, below.
              if (priority > 0)
  #       endif /* sun && ACE_HAS_ONLY_SCHED_OTHER */
                {
                  (void) ACE_ADAPT_RETVAL(::pthread_attr_setschedparam (&attr, &sparam),
                                          result);
                  if (result != 0)
                    {
                      ::pthread_attr_destroy (&attr);
                      return -1;
                    }
                }
            }
          }
  
  #       if !defined (ACE_LACKS_SETINHERITSCHED)
        // *** Set scheduling explicit or inherited
        if (ACE_BIT_ENABLED (flags, THR_INHERIT_SCHED)
            || ACE_BIT_ENABLED (flags, THR_EXPLICIT_SCHED))
          {
            int sched = PTHREAD_EXPLICIT_SCHED;
            if (ACE_BIT_ENABLED (flags, THR_INHERIT_SCHED))
              sched = PTHREAD_INHERIT_SCHED;
            if (ACE_ADAPT_RETVAL(::pthread_attr_setinheritsched (&attr, sched), result) != 0)
              {
                ::pthread_attr_destroy (&attr);
                return -1;
              }
          }
  #       endif /* ACE_LACKS_SETINHERITSCHED */
  #   else /* ACE_LACKS_SETSCHED */
        ACE_UNUSED_ARG (priority);
  #   endif /* ACE_LACKS_SETSCHED */
  
    // *** Set pthread name
  #   if defined (ACE_HAS_PTHREAD_ATTR_SETNAME)
    if (thr_name && *thr_name)
      {
        if (ACE_ADAPT_RETVAL(::pthread_attr_setname (&attr, const_cast<char*>(*thr_name)), result) != 0)
          {
            ::pthread_attr_destroy (&attr);
            return -1;
          }
      }
  #else
    ACE_UNUSED_ARG (thr_name);
  #   endif
  
        // *** Set Scope
  #   if !defined (ACE_LACKS_THREAD_PROCESS_SCOPING)
        if (ACE_BIT_ENABLED (flags, THR_SCOPE_SYSTEM)
            || ACE_BIT_ENABLED (flags, THR_SCOPE_PROCESS))
          {
  #     if defined (ACE_LACKS_PTHREAD_SCOPE_PROCESS)
            int scope = PTHREAD_SCOPE_SYSTEM;
  #     else /* ACE_LACKS_PTHREAD_SCOPE_PROCESS */
            int scope = PTHREAD_SCOPE_PROCESS;
  #     endif /* ACE_LACKS_PTHREAD_SCOPE_PROCESS */
            if (ACE_BIT_ENABLED (flags, THR_SCOPE_SYSTEM))
              scope = PTHREAD_SCOPE_SYSTEM;
  
            if (ACE_ADAPT_RETVAL(::pthread_attr_setscope (&attr, scope), result) != 0)
              {
                ::pthread_attr_destroy (&attr);
                return -1;
              }
          }
  #   endif /* !ACE_LACKS_THREAD_PROCESS_SCOPING */
  
  #   ifdef ACE_HAS_PTHREAD_ATTR_SETCREATESUSPEND_NP
        if (ACE_BIT_ENABLED (flags, THR_SUSPENDED))
          {
             if (ACE_ADAPT_RETVAL(::pthread_attr_setcreatesuspend_np(&attr), result) != 0)
              {
  
                ::pthread_attr_destroy (&attr);
                return -1;
              }
          }
  #   endif /* !ACE_HAS_PTHREAD_ATTR_SETCREATESUSPEND_NP */
  
  #   if ! defined(ACE_LACKS_THR_CONCURRENCY_FUNCS)
        if (ACE_BIT_ENABLED (flags, THR_NEW_LWP))
          {
            // Increment the number of LWPs by one to emulate the
            // SunOS semantics.
            int lwps = ACE_OS::thr_getconcurrency ();
            if (lwps == -1)
              {
                if (errno == ENOTSUP)
                  // Suppress the ENOTSUP because it's harmless.
                  errno = 0;
                else
                  // This should never happen on SunOS:
                  // ::thr_getconcurrency () should always succeed.
                  return -1;
              }
            else if (ACE_OS::thr_setconcurrency (lwps + 1) == -1)
              {
                if (errno == ENOTSUP)
                  {
                    // Unlikely: ::thr_getconcurrency () is supported
                    // but ::thr_setconcurrency () is not?
                  }
                else
                  return -1;
              }
          }
  #   endif /* ! ACE_LACKS_THR_CONCURRENCY_FUNCS */
      }
  
    ACE_OSCALL (ACE_ADAPT_RETVAL (::pthread_create (thr_id,
                                                    &attr,
                                                    thread_args->entry_point (),
                                                    thread_args),
                                  result),
                int, -1, result);
    ::pthread_attr_destroy (&attr);
  
    // This is a SunOS or POSIX implementation of pthreads, where we
    // assume that ACE_thread_t and ACE_hthread_t are the same.  If this
    // *isn't* correct on some platform, please let us know.
    if (result != -1)
      *thr_handle = *thr_id;
  
  #   if defined (sun)  &&  defined (ACE_HAS_ONLY_SCHED_OTHER)
    // SunOS prior to 5.7:
  
    // If the priority is 0, then we might have to set it now because we
    // couldn't set it with ::pthread_attr_setschedparam, as noted
    // above.  This doesn't provide strictly correct behavior, because
    // the thread was created (above) with the priority of its parent.
    // (That applies regardless of the inherit_sched attribute: if it
    // was PTHREAD_INHERIT_SCHED, then it certainly inherited its
    // parent's priority.  If it was PTHREAD_EXPLICIT_SCHED, then "attr"
    // was initialized by the SunOS ::pthread_attr_init () to contain
    // NULL for the priority, which indicated to SunOS ::pthread_create
    // () to inherit the parent priority.)
    if (priority == 0)
      {
        // Check the priority of this thread, which is the parent
        // of the newly created thread.  If it is 0, then the
        // newly created thread will have inherited the priority
        // of 0, so there's no need to explicitly set it.
        struct sched_param sparam;
        int policy = 0;
        ACE_OSCALL (ACE_ADAPT_RETVAL (::pthread_getschedparam (thr_self (),
                                                               &policy,
                                                               &sparam),
                                      result), int,
                    -1, result);
  
        // The only policy supported by by SunOS, thru version 5.6,
        // is SCHED_OTHER, so that's hard-coded here.
        policy = ACE_SCHED_OTHER;
  
        if (sparam.sched_priority != 0)
          {
            ACE_OS::memset ((void *) &sparam, 0, sizeof sparam);
            // The memset to 0 sets the priority to 0, so we don't need
            // to explicitly set sparam.sched_priority.
  
            ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::pthread_setschedparam (*thr_id,
                                                                          policy,
                                                                          &sparam),
                                                 result),
                               int, -1);
          }
      }
  
  #     if defined (ACE_NEEDS_LWP_PRIO_SET)
  #       if 0
    // It would be useful if we could make this work.  But, it requires
    // a mechanism for determining the ID of an LWP to which another
    // thread is bound.  Is there a way to do that?  Instead, just rely
    // on the code in ACE_Thread_Adapter::invoke () to set the LWP
    // priority.
  
    // If the thread is bound, then set the priority on its LWP.
    if (ACE_BIT_ENABLED (flags, THR_BOUND))
      {
        ACE_Sched_Params sched_params (ACE_BIT_ENABLED (flags, THR_SCHED_FIFO) ||
                                       ACE_BIT_ENABLED (flags, THR_SCHED_RR)  ?
                                       ACE_SCHED_FIFO  :
                                       ACE_SCHED_OTHER,
                                       priority);
        result = ACE_OS::lwp_setparams (sched_params,
                                        /* ? How do we find the ID of the LWP
                                           to which *thr_id is bound? */);
      }
  #       endif /* 0 */
  #     endif /* ACE_NEEDS_LWP_PRIO_SET */
  
  #   endif /* sun && ACE_HAS_ONLY_SCHED_OTHER */
    auto_thread_args.release ();
    return result;
  # elif defined (ACE_HAS_STHREADS)
    int result;
    int start_suspended = ACE_BIT_ENABLED (flags, THR_SUSPENDED);
  
    if (priority != ACE_DEFAULT_THREAD_PRIORITY)
      // If we need to set the priority, then we need to start the
      // thread in a suspended mode.
      ACE_SET_BITS (flags, THR_SUSPENDED);
  
    ACE_OSCALL (ACE_ADAPT_RETVAL (::thr_create (stack, stacksize,
                                                thread_args->entry_point (),
                                                thread_args,
                                                flags, thr_id), result),
                int, -1, result);
  
    if (result != -1)
      {
        // With SunOS threads, ACE_thread_t and ACE_hthread_t are the same.
        *thr_handle = *thr_id;
  
        if (priority != ACE_DEFAULT_THREAD_PRIORITY)
          {
            // Set the priority of the new thread and then let it
            // continue, but only if the user didn't start it suspended
            // in the first place!
            result = ACE_OS::thr_setprio (*thr_id, priority);
            if (result != 0)
              {
                errno = result;
                return -1;
              }
  
            if (start_suspended == 0)
              {
                result = ACE_OS::thr_continue (*thr_id);
                if (result != 0)
                  {
                    errno = result;
                    return -1;
                  }
              }
          }
      }
    auto_thread_args.release ();
    return result;
  # elif defined (ACE_HAS_WTHREADS)
    ACE_UNUSED_ARG (thr_name);
    ACE_UNUSED_ARG (stack);
  #   if defined (ACE_HAS_MFC) && (ACE_HAS_MFC != 0)
    if (ACE_BIT_ENABLED (flags, THR_USE_AFX))
      {
        CWinThread *cwin_thread =
          ::AfxBeginThread ((AFX_THREADPROC) thread_args->entry_point (),
                            thread_args,
                            priority,
                            0,
                            flags | THR_SUSPENDED);
        // Have to duplicate the handle because
        // CWinThread::~CWinThread() closes the original handle.
  #     if !defined (ACE_HAS_WINCE)
        (void) ::DuplicateHandle (::GetCurrentProcess (),
                                  cwin_thread->m_hThread,
                                  ::GetCurrentProcess (),
                                  thr_handle,
                                  0,
                                  TRUE,
                                  DUPLICATE_SAME_ACCESS);
  #     endif /* ! ACE_HAS_WINCE */
        *thr_id = cwin_thread->m_nThreadID;
  
        if (ACE_BIT_ENABLED (flags, THR_SUSPENDED) == 0)
          cwin_thread->ResumeThread ();
        // cwin_thread will be deleted in AfxThreadExit()
        // Warning: If AfxThreadExit() is called from within the
        // thread, ACE_TSS_Cleanup->thread_exit() never gets called !
      }
    else
  #   endif /* ACE_HAS_MFC */
      {
        int start_suspended = ACE_BIT_ENABLED (flags, THR_SUSPENDED);
  
        if (priority != ACE_DEFAULT_THREAD_PRIORITY)
          // If we need to set the priority, then we need to start the
          // thread in a suspended mode.
          ACE_SET_BITS (flags, THR_SUSPENDED);
  
        *thr_handle = (void *) ACE_BEGINTHREADEX (0,
                                                  static_cast <u_int> (stacksize),
                                                  thread_args->entry_point (),
                                                  thread_args,
                                                  flags,
                                                  thr_id);
  
        if (priority != ACE_DEFAULT_THREAD_PRIORITY && *thr_handle != 0)
          {
            // Set the priority of the new thread and then let it
            // continue, but only if the user didn't start it suspended
            // in the first place!
            if (ACE_OS::thr_setprio (*thr_handle, priority) != 0)
              {
                return -1;
              }
  
            if (start_suspended == 0)
              {
                ACE_OS::thr_continue (*thr_handle);
              }
          }
      }
  #   if 0
    *thr_handle = ::CreateThread
      (0,
       stacksize,
       LPTHREAD_START_ROUTINE (thread_args->entry_point ()),
       thread_args,
       flags,
       thr_id);
  #   endif /* 0 */
  
    // Close down the handle if no one wants to use it.
    if (thr_handle == &tmp_handle && tmp_handle != 0)
      ::CloseHandle (tmp_handle);
  
    if (*thr_handle != 0)
      {
        auto_thread_args.release ();
        return 0;
      }
    else
      ACE_FAIL_RETURN (-1);
    /* NOTREACHED */
  
  # elif defined (ACE_VXWORKS)
    // The hard-coded values below are what ::sp () would use.  (::sp ()
    // hardcodes priority to 100, flags to VX_FP_TASK, and stacksize to
    // 20,000.)  stacksize should be an even integer.  If a stack is not
    // specified, ::taskSpawn () is used so that we can set the
    // priority, flags, and stacksize.  If a stack is specified,
    // ::taskInit ()/::taskActivate() are used.
  
    // If called with thr_create() defaults, use same default values as ::sp ():
    if (priority == ACE_DEFAULT_THREAD_PRIORITY) priority = 100;
    // Assumes that there is a floating point coprocessor.  As noted
    // above, ::sp () hardcodes this, so we should be safe with it.
    if (flags == 0) flags = VX_FP_TASK;
    if (stacksize == 0) stacksize = 20000;
  
    ACE_thread_t tid;
  #   if 0 /* Don't support setting of stack, because it doesn't seem to work. */
    if (stack == 0)
      {
  #   else
        ACE_UNUSED_ARG (stack);
  #   endif /* 0 */
        // The call below to ::taskSpawn () causes VxWorks to assign a
        // unique task name of the form: "t" + an integer, because the
        // first argument is 0.
        tid = ::taskSpawn (thr_name && *thr_name ? const_cast <char*> (*thr_name) : 0,
                           priority,
                           (int) flags,
                           (int) stacksize,
                           thread_args->entry_point (),
                           (int) thread_args,
                           0, 0, 0, 0, 0, 0, 0, 0, 0);
  #   if 0 /* Don't support setting of stack, because it doesn't seem to work. */
      }
    else
      {
        // If a task name (thr_id) was not supplied, then the task will
        // not have a unique name.  That's VxWorks' behavior.
  
        // Carve out a TCB at the beginning of the stack space.  The TCB
        // occupies 400 bytes with VxWorks 5.3.1/I386.
        WIND_TCB *tcb = (WIND_TCB *) stack;
  
        // The TID is defined to be the address of the TCB.
        int status = ::taskInit (tcb,
                                 thr_name && *thr_name ? const_cast <char*>(*thr_name) : 0,
                                 priority,
                                 (int) flags,
                                 (char *) stack + sizeof (WIND_TCB),
                                 (int) (stacksize - sizeof (WIND_TCB)),
                                 thread_args->entry_point (),
                                 (int) thread_args,
                                 0, 0, 0, 0, 0, 0, 0, 0, 0);
  
        if (status == OK)
          {
            // The task was successfully initialized, now activate it.
            status = ::taskActivate ((ACE_hthread_t) tcb);
          }
  
        tid = status == OK  ? (ACE_thread_t) tcb  :  ERROR;
      }
  #   endif /* 0 */
  
    if (tid == ERROR)
      return -1;
    else
      {
        if (thr_id)
          *thr_id = tid;
  
        if (thr_handle)
          *thr_handle = tid;
  
        if (thr_name && !(*thr_name))
          *thr_name = ::taskName (tid);
  
        auto_thread_args.release ();
        return 0;
      }
  
  # endif /* ACE_HAS_STHREADS */
  #else
    ACE_UNUSED_ARG (func);
    ACE_UNUSED_ARG (args);
    ACE_UNUSED_ARG (flags);
    ACE_UNUSED_ARG (thr_id);
    ACE_UNUSED_ARG (thr_handle);
    ACE_UNUSED_ARG (priority);
    ACE_UNUSED_ARG (stack);
    ACE_UNUSED_ARG (stacksize);
    ACE_UNUSED_ARG (thr_name);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_HAS_THREADS */
  }
  
  void
  ACE_OS::thr_exit (ACE_THR_FUNC_RETURN status)
  {
    ACE_OS_TRACE ("ACE_OS::thr_exit");
  #if defined (ACE_HAS_THREADS)
  # if defined (ACE_HAS_PTHREADS)
      ::pthread_exit (status);
  # elif defined (ACE_HAS_STHREADS)
      ::thr_exit (status);
  # elif defined (ACE_HAS_WTHREADS)
      // Can't call it here because on NT, the thread is exited
      // directly by ACE_Thread_Adapter::invoke ().
      //   ACE_TSS_Cleanup::instance ()->thread_exit (status);
  
  #   if defined (ACE_HAS_MFC) && (ACE_HAS_MFC != 0)
      int using_afx = -1;
      // An ACE_Thread_Descriptor really is an ACE_OS_Thread_Descriptor.
      // But without #including ace/Thread_Manager.h, we don't know that.
      ACE_OS_Thread_Descriptor *td =
        ACE_Base_Thread_Adapter::thr_desc_log_msg ();
      if (td)
        using_afx = ACE_BIT_ENABLED (td->flags (), THR_USE_AFX);
  #   endif /* ACE_HAS_MFC && (ACE_HAS_MFC != 0) */
  
      // Call TSS destructors.
      ACE_OS::cleanup_tss (0 /* not main thread */);
  
      // Exit the thread.
      // Allow CWinThread-destructor to be invoked from AfxEndThread.
      // _endthreadex will be called from AfxEndThread so don't exit the
      // thread now if we are running an MFC thread.
  #   if defined (ACE_HAS_MFC) && (ACE_HAS_MFC != 0)
      if (using_afx != -1)
        {
          if (using_afx)
            ::AfxEndThread (status);
          else
            ACE_ENDTHREADEX (status);
        }
      else
        {
          // Not spawned by ACE_Thread_Manager, use the old buggy
          // version.  You should seriously consider using
          // ACE_Thread_Manager to spawn threads.  The following code is
          // know to cause some problem.
          CWinThread *pThread = ::AfxGetThread ();
          if (!pThread || pThread->m_nThreadID != ACE_OS::thr_self ())
            ACE_ENDTHREADEX (status);
          else
            ::AfxEndThread (status);
        }
  #   else
      ACE_ENDTHREADEX (status);
  #   endif /* ACE_HAS_MFC && ACE_HAS_MFS != 0*/
  
  # elif defined (ACE_HAS_VXTHREADS)
      ACE_thread_t tid = ACE_OS::thr_self ();
      *((int *) status) = ::taskDelete (tid);
  # endif /* ACE_HAS_PTHREADS */
  #else
    ACE_UNUSED_ARG (status);
  #endif /* ACE_HAS_THREADS */
  }
  
  #if defined (ACE_HAS_VXTHREADS)
  // Leave this in the global scope to allow
  // users to adjust the delay value.
  int ACE_THR_JOIN_DELAY = 5;
  
  int
  ACE_OS::thr_join (ACE_hthread_t thr_handle,
                    ACE_THR_FUNC_RETURN *status)
  {
    // We can't get the status of the thread
    if (status != 0)
      {
        *status = 0;
      }
  
    // This method can not support joining all threads
    if (ACE_OS::thr_cmp (thr_handle, ACE_OS::NULL_hthread))
      {
        ACE_NOTSUP_RETURN (-1);
      }
  
    int retval = ESRCH;
    ACE_thread_t current = ACE_OS::thr_self ();
  
    // Make sure we are not joining ourself
    if (ACE_OS::thr_cmp (thr_handle, current))
      {
        retval = EDEADLK;
      }
    else
      {
        // Whether the task exists or not
        // we will return a successful value
        retval = 0;
  
        // Verify that the task id still exists
        while (taskIdVerify (thr_handle) == OK)
          {
            // Wait a bit to see if the task is still active.
            ACE_OS::sleep (ACE_THR_JOIN_DELAY);
          }
      }
  
    // Adapt the return value into errno and return value.
    // The ACE_ADAPT_RETVAL macro doesn't exactly do what
    // we need to do here, so we do it manually.
    if (retval != 0)
      {
        errno = retval;
        retval = -1;
      }
  
    return retval;
  }
  
  int
  ACE_OS::thr_join (ACE_thread_t waiter_id,
                    ACE_thread_t *thr_id,
                    ACE_THR_FUNC_RETURN *status)
  {
    thr_id = 0;
    return ACE_OS::thr_join (waiter_id, status);
  }
  #endif /* ACE_HAS_VXTHREADS */
  
  int
  ACE_OS::thr_key_detach (ACE_thread_key_t key)
  {
  #if defined (ACE_HAS_WTHREADS) || defined (ACE_HAS_TSS_EMULATION)
    TSS_Cleanup_Instance cleanup;
    if (cleanup.valid ())
      {
        return cleanup->thread_detach_key (key);
      }
    else
      {
        return -1;
      }
  #else
    ACE_UNUSED_ARG (key);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_HAS_WTHREADS || ACE_HAS_TSS_EMULATION */
  }
  
  int
  ACE_OS::thr_get_affinity (ACE_hthread_t thr_id,
                            size_t cpu_set_size,
                            cpu_set_t * cpu_mask)
  {
  #if defined (ACE_HAS_PTHREAD_GETAFFINITY_NP)
    // Handle of the thread, which is NPTL thread-id, normally a big number
    if (::pthread_getaffinity_np (thr_id, cpu_set_size, cpu_mask) != 0)
      {
        return -1;
      }
    return 0;
  #elif defined (ACE_HAS_2_PARAM_SCHED_GETAFFINITY)
    // The process-id is expected as <thr_id>, which can be a thread-id of
    // linux-thread, thus making binding to cpu of that particular thread only.
    // If you are using this flag for NPTL-threads, however, please pass as a
    // thr_id process id obtained by ACE_OS::getpid ()
    ACE_UNUSED_ARG (cpu_set_size);
    if (::sched_getaffinity(thr_id, cpu_mask) == -1)
      {
        return -1;
      }
    return 0;
  #elif defined (ACE_HAS_SCHED_GETAFFINITY)
    // The process-id is expected as <thr_id>, which can be a thread-id of
    // linux-thread, thus making binding to cpu of that particular thread only.
    // If you are using this flag for NPTL-threads, however, please pass as a
    // thr_id process id obtained by ACE_OS::getpid ()
    if (::sched_getaffinity(thr_id, cpu_set_size, cpu_mask) == -1)
      {
        return -1;
      }
    return 0;
  #elif defined (ACE_HAS_TASKCPUAFFINITYSET)
    ACE_UNUSED_ARG (cpu_set_size);
    int result = 0;
    if (ACE_ADAPT_RETVAL (::taskCpuAffinitySet (thr_id, *cpu_mask), result) == -1)
      {
        return -1;
      }
    return 0;
  #else
    ACE_UNUSED_ARG (thr_id);
    ACE_UNUSED_ARG (cpu_set_size);
    ACE_UNUSED_ARG (cpu_mask);
    ACE_NOTSUP_RETURN (-1);
  #endif
  }
  
  int
  ACE_OS::thr_set_affinity (ACE_hthread_t thr_id,
                            size_t cpu_set_size,
                            const cpu_set_t * cpu_mask)
  {
  #if defined (ACE_HAS_PTHREAD_SETAFFINITY_NP)
    if (::pthread_setaffinity_np (thr_id, cpu_set_size, cpu_mask) != 0)
      {
        return -1;
      }
    return 0;
  #elif defined (ACE_HAS_2_PARAM_SCHED_SETAFFINITY)
    // The process-id is expected as <thr_id>, which can be a thread-id of
    // linux-thread, thus making binding to cpu of that particular thread only.
    // If you are using this flag for NPTL-threads, however, please pass as a
    // thr_id process id obtained by ACE_OS::getpid (), but whole process will bind your CPUs
    //
    ACE_UNUSED_ARG (cpu_set_size);
    if (::sched_setaffinity (thr_id, cpu_mask) == -1)
      {
        return -1;
      }
    return 0;
  #elif defined (ACE_HAS_SCHED_SETAFFINITY)
    // The process-id is expected as <thr_id>, which can be a thread-id of
    // linux-thread, thus making binding to cpu of that particular thread only.
    // If you are using this flag for NPTL-threads, however, please pass as a
    // thr_id process id obtained by ACE_OS::getpid (), but whole process will bind your CPUs
    //
    if (::sched_setaffinity (thr_id, cpu_set_size, cpu_mask) == -1)
      {
        return -1;
      }
    return 0;
  #elif defined (ACE_HAS_TASKCPUAFFINITYSET)
    int result = 0;
    if (ACE_ADAPT_RETVAL (::taskCpuAffinitySet (thr_id, *cpu_mask), result) == -1)
      {
        return -1;
      }
    return 0;
  #else
    ACE_UNUSED_ARG (thr_id);
    ACE_UNUSED_ARG (cpu_set_size);
    ACE_UNUSED_ARG (cpu_mask);
    ACE_NOTSUP_RETURN (-1);
  #endif
  }
  
  int
  ACE_OS::thr_key_used (ACE_thread_key_t key)
  {
  #if defined (ACE_WIN32) || defined (ACE_HAS_TSS_EMULATION)
    TSS_Cleanup_Instance cleanup;
    if (cleanup.valid ())
      {
        cleanup->thread_use_key (key);
        return 0;
      }
    return -1;
  #else
    ACE_UNUSED_ARG (key);
    ACE_NOTSUP_RETURN (-1);
  #endif /* ACE_WIN32 || ACE_HAS_TSS_EMULATION */
  }
  
  #if defined (ACE_HAS_THREAD_SPECIFIC_STORAGE)
  int
  ACE_OS::thr_keycreate_native (ACE_OS_thread_key_t *key,
  # if defined (ACE_HAS_THR_C_DEST)
                         ACE_THR_C_DEST dest
  # else
                         ACE_THR_DEST dest
  # endif /* ACE_HAS_THR_C_DEST */
                         )
  {
    // can't trace here. Trace uses TSS
    // ACE_OS_TRACE ("ACE_OS::thr_keycreate_native");
  # if defined (ACE_HAS_THREADS)
  #   if defined (ACE_HAS_PTHREADS)
      int result;
      ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::pthread_key_create (key, dest),
                                           result),
                         int, -1);
  #   elif defined (ACE_HAS_STHREADS)
      int result;
      ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::thr_keycreate (key, dest),
                                           result),
                         int, -1);
  #   elif defined (ACE_HAS_WTHREADS)
      ACE_UNUSED_ARG (dest);
      *key = ::TlsAlloc ();
  
      if (*key == ACE_SYSCALL_FAILED)
        ACE_FAIL_RETURN (-1);
      return 0;
  #   endif /* ACE_HAS_STHREADS */
  # else
    ACE_UNUSED_ARG (key);
    ACE_UNUSED_ARG (dest);
    ACE_NOTSUP_RETURN (-1);
  # endif /* ACE_HAS_THREADS */
  }
  #endif /* ACE_HAS_THREAD_SPECIFIC_STORAGE */
  
  int
  ACE_OS::thr_keycreate (ACE_thread_key_t *key,
  # if defined (ACE_HAS_THR_C_DEST)
                         ACE_THR_C_DEST dest)
  # else
                         ACE_THR_DEST dest)
  # endif /* ACE_HAS_THR_C_DEST */
  {
    // ACE_OS_TRACE ("ACE_OS::thr_keycreate");
  #if defined (ACE_HAS_THREADS)
  #   if defined (ACE_HAS_TSS_EMULATION)
      if (ACE_TSS_Emulation::next_key (*key) == 0)
        {
          ACE_TSS_Emulation::tss_destructor (*key, dest);
  
          // Extract out the thread-specific table instance and stash away
          // the key and destructor so that we can free it up later on...
          TSS_Cleanup_Instance cleanup (TSS_Cleanup_Instance::CREATE);
          if (cleanup.valid ())
            {
              return cleanup->insert (*key, dest);
            }
          else
            {
              return -1;
            }
        }
      else
        return -1;
  #   elif defined (ACE_HAS_WTHREADS)
      if (ACE_OS::thr_keycreate_native (key, dest) == 0)
        {
          // Extract out the thread-specific table instance and stash away
          // the key and destructor so that we can free it up later on...
          TSS_Cleanup_Instance cleanup (TSS_Cleanup_Instance::CREATE);
          if (cleanup.valid ())
            {
              return cleanup->insert (*key, dest);
            }
          else
            {
              return -1;
            }
        }
      else
        return -1;
        /* NOTREACHED */
  #   elif defined (ACE_HAS_THREAD_SPECIFIC_STORAGE)
      return  ACE_OS::thr_keycreate_native (key, dest);
  #   else
      ACE_UNUSED_ARG (key);
      ACE_UNUSED_ARG (dest);
      ACE_NOTSUP_RETURN (-1);
  #   endif /* ACE_HAS_TSS_EMULATION */
  # else /* ACE_HAS_THREADS */
    ACE_UNUSED_ARG (key);
    ACE_UNUSED_ARG (dest);
    ACE_NOTSUP_RETURN (-1);
  # endif /* ACE_HAS_THREADS */
  }
  
  #if defined (ACE_HAS_THREAD_SPECIFIC_STORAGE)
  int
  ACE_OS::thr_keyfree_native (ACE_OS_thread_key_t key)
  {
    ACE_OS_TRACE ("ACE_OS::thr_keyfree_native");
  # if defined (ACE_HAS_THREADS)
  #   if defined (ACE_HAS_BROKEN_THREAD_KEYFREE) || defined (ACE_HAS_THR_KEYDELETE)
      // For some systems, e.g. LynxOS, we need to ensure that
      // any registered thread destructor action for this slot
      // is now disabled. Otherwise in the event of a dynamic library
      // unload of libACE, by a program not linked with libACE,
      // ACE_TSS_cleanup will be invoked again at the thread exit
      // after libACE has been actually been unmapped from memory.
      (void) ACE_OS::thr_setspecific (key, 0);
  #   endif /* ACE_HAS_BROKEN_THREAD_KEYFREE */
  #   if defined (ACE_HAS_PTHREADS)
      return ::pthread_key_delete (key);
  #   elif defined (ACE_HAS_THR_KEYDELETE)
      return ::thr_keydelete (key);
  #   elif defined (ACE_HAS_STHREADS)
      ACE_UNUSED_ARG (key);
      ACE_NOTSUP_RETURN (-1);
  #   elif defined (ACE_HAS_WTHREADS)
      ACE_WIN32CALL_RETURN (ACE_ADAPT_RETVAL (::TlsFree (key), ace_result_), int, -1);
  #   else
      ACE_UNUSED_ARG (key);
      ACE_NOTSUP_RETURN (-1);
  #   endif /* ACE_HAS_PTHREADS */
  # else
    ACE_UNUSED_ARG (key);
    ACE_NOTSUP_RETURN (-1);
  # endif /* ACE_HAS_THREADS */
  }
  #endif /* ACE_HAS_THREAD_SPECIFIC_STORAGE */
  
  int
  ACE_OS::thr_keyfree (ACE_thread_key_t key)
  {
    ACE_OS_TRACE ("ACE_OS::thr_keyfree");
  # if defined (ACE_HAS_THREADS)
  #   if defined (ACE_HAS_TSS_EMULATION)
      // Release the key in the TSS_Emulation administration
      ACE_TSS_Emulation::release_key (key);
      TSS_Cleanup_Instance cleanup;
      if (cleanup.valid ())
        {
          return cleanup->free_key (key);
        }
      return -1;
  #   elif defined (ACE_HAS_WTHREADS)
      // Extract out the thread-specific table instance and free up
      // the key and destructor.
      TSS_Cleanup_Instance cleanup;
      if (cleanup.valid ())
        {
          return cleanup->free_key (key);
        }
      return -1;
  #   elif defined (ACE_HAS_THREAD_SPECIFIC_STORAGE)
      return ACE_OS::thr_keyfree_native (key);
  #   else
      ACE_UNUSED_ARG (key);
      ACE_NOTSUP_RETURN (-1);
  #   endif /* ACE_HAS_TSS_EMULATION */
  # else /* ACE_HAS_THREADS */
    ACE_UNUSED_ARG (key);
    ACE_NOTSUP_RETURN (-1);
    return 0;
  # endif /* ACE_HAS_THREADS */
  }
  
  int
  ACE_OS::thr_setprio (const ACE_Sched_Priority prio)
  {
    // Set the thread priority on the current thread.
    ACE_hthread_t my_thread_id;
    ACE_OS::thr_self (my_thread_id);
  
    int const status = ACE_OS::thr_setprio (my_thread_id, prio);
  
  #if defined (ACE_NEEDS_LWP_PRIO_SET)
    // If the thread is in the RT class, then set the priority on its
    // LWP.  (Instead of doing this if the thread is in the RT class, it
    // should be done for all bound threads.  But, there doesn't appear
    // to be an easy way to determine if the thread is bound.)
  
    if (status == 0)
      {
        // Find what scheduling class the thread's LWP is in.
        ACE_Sched_Params sched_params (ACE_SCHED_OTHER, 0);
        if (ACE_OS::lwp_getparams (sched_params) == -1)
          {
            return -1;
          }
        else if (sched_params.policy () == ACE_SCHED_FIFO  ||
                 sched_params.policy () == ACE_SCHED_RR)
          {
            // This thread's LWP is in the RT class, so we need to set
            // its priority.
            sched_params.priority (prio);
            return ACE_OS::lwp_setparams (sched_params);
          }
        // else this is not an RT thread.  Nothing more needs to be
        // done.
      }
  #endif /* ACE_NEEDS_LWP_PRIO_SET */
  
    return status;
  }
  
  # if defined (ACE_HAS_THREAD_SPECIFIC_STORAGE)
  int
  ACE_OS::thr_setspecific_native (ACE_OS_thread_key_t key, void *data)
  {
    // ACE_OS_TRACE ("ACE_OS::thr_setspecific_native");
  #   if defined (ACE_HAS_THREADS)
  #     if defined (ACE_HAS_PTHREADS)
        int result;
        ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (pthread_setspecific (key, data),
                                             result),
                           int, -1);
  #     elif defined (ACE_HAS_STHREADS)
        int result;
        ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::thr_setspecific (key, data), result), int, -1);
  #     elif defined (ACE_HAS_WTHREADS)
        ::TlsSetValue (key, data);
        return 0;
  #     else /* ACE_HAS_STHREADS */
        ACE_UNUSED_ARG (key);
        ACE_UNUSED_ARG (data);
        ACE_NOTSUP_RETURN (-1);
  #     endif /* ACE_HAS_STHREADS */
  #   else
      ACE_UNUSED_ARG (key);
      ACE_UNUSED_ARG (data);
      ACE_NOTSUP_RETURN (-1);
  #   endif /* ACE_HAS_THREADS */
  }
  # endif /* ACE_HAS_THREAD_SPECIFIC_STORAGE */
  
  int
  ACE_OS::thr_setspecific (ACE_thread_key_t key, void *data)
  {
    // ACE_OS_TRACE ("ACE_OS::thr_setspecific");
  #if defined (ACE_HAS_THREADS)
  #   if defined (ACE_HAS_TSS_EMULATION)
      if (ACE_TSS_Emulation::is_key (key) == 0)
        {
          errno = EINVAL;
          data = 0;
          return -1;
        }
      else
        {
          ACE_TSS_Emulation::ts_object (key) = data;
          TSS_Cleanup_Instance cleanup;
          if (cleanup.valid ())
            {
              cleanup->thread_use_key (key);
              // for TSS_Cleanup purposes treat stetting data to zero
              // like detaching.  This is a consequence of POSIX allowing
              // deletion of a "used" key.
              if (data == 0)
                {
                  cleanup->thread_detach_key (key);
                }
              return 0;
            }
          else
            {
              return -1;
            }
        }
  #   elif defined (ACE_HAS_WTHREADS)
      if (ACE_OS::thr_setspecific_native (key, data) == 0)
        {
          TSS_Cleanup_Instance cleanup;
          if (cleanup.valid ())
            {
              cleanup->thread_use_key (key);
              // for TSS_Cleanup purposes treat stetting data to zero
              // like detaching.  This is a consequence of POSIX allowing
              // deletion of a "used" key.
              if (data == 0)
                {
                  cleanup->thread_detach_key (key);
                }
              return 0;
            }
          return -1;
        }
      return -1;
  #   elif defined (ACE_HAS_THREAD_SPECIFIC_STORAGE)
        return ACE_OS::thr_setspecific_native (key, data);
  #   else /* ACE_HAS_TSS_EMULATION */
        ACE_UNUSED_ARG (key);
        ACE_UNUSED_ARG (data);
        ACE_NOTSUP_RETURN (-1);
  #   endif /* ACE_HAS_TSS_EMULATION */
  # else /* ACE_HAS_THREADS */
    ACE_UNUSED_ARG (key);
    ACE_UNUSED_ARG (data);
    ACE_NOTSUP_RETURN (-1);
  # endif /* ACE_HAS_THREADS */
  }
  
  void
  ACE_OS::unique_name (const void *object,
                       char *name,
                       size_t length)
  {
    // The process ID will provide uniqueness between processes on the
    // same machine. The "this" pointer of the <object> will provide
    // uniqueness between other "live" objects in the same process. The
    // uniqueness of this name is therefore only valid for the life of
    // <object>.
    char temp_name[ACE_UNIQUE_NAME_LEN];
    ACE_OS::sprintf (temp_name,
                     "%p%d",
                     object,
                     static_cast <int> (ACE_OS::getpid ()));
    ACE_OS::strsncpy (name,
                      temp_name,
                      length);
  }
  
  #if defined (ACE_USES_WCHAR)
  void
  ACE_OS::unique_name (const void *object,
                       wchar_t *name,
                       size_t length)
  {
    // The process ID will provide uniqueness between processes on the
    // same machine. The "this" pointer of the <object> will provide
    // uniqueness between other "live" objects in the same process. The
    // uniqueness of this name is therefore only valid for the life of
    // <object>.
    wchar_t temp_name[ACE_UNIQUE_NAME_LEN];
    ACE_OS::sprintf (temp_name,
                     ACE_TEXT ("%p%d"),
                     object,
                     static_cast <int> (ACE_OS::getpid ()));
    ACE_OS::strsncpy (name,
                      temp_name,
                      length);
  }
  #endif
  
  ACE_END_VERSIONED_NAMESPACE_DECL
  
  #if defined (ACE_VXWORKS) && !defined (__RTP__)
  # include /**/ <usrLib.h>   /* for ::sp() */
  # include /**/ <sysLib.h>   /* for ::sysClkRateGet() */
  # include "ace/Service_Config.h"
  
  // This global function can be used from the VxWorks shell to pass
  // arguments to a C main () function.
  //
  // usage: -> spa main, "arg1", "arg2"
  //
  // All arguments must be quoted, even numbers.
  int
  spa (FUNCPTR entry, ...)
  {
    static const unsigned int ACE_MAX_ARGS = 10;
    static char *argv[ACE_MAX_ARGS];
    va_list pvar;
    unsigned int argc;
  
    // Hardcode a program name because the real one isn't available
    // through the VxWorks shell.
    argv[0] = "ace_main";
  
    // Peel off arguments to spa () and put into argv.  va_arg () isn't
    // necessarily supposed to return 0 when done, though since the
    // VxWorks shell uses a fixed number (10) of arguments, it might 0
    // the unused ones.  This function could be used to increase that
    // limit, but then it couldn't depend on the trailing 0.  So, the
    // number of arguments would have to be passed.
    va_start (pvar, entry);
  
    for (argc = 1; argc <= ACE_MAX_ARGS; ++argc)
      {
        argv[argc] = va_arg (pvar, char *);
  
        if (argv[argc] == 0)
          break;
      }
  
    if (argc > ACE_MAX_ARGS  &&  argv[argc-1] != 0)
      {
        // try to read another arg, and warn user if the limit was exceeded
        if (va_arg (pvar, char *) != 0)
          ACE_OS::fprintf (stderr, "spa(): number of arguments limited to %d\n",
                           ACE_MAX_ARGS);
      }
    else
      {
        // fill unused argv slots with 0 to get rid of leftovers
        // from previous invocations
        for (unsigned int i = argc; i <= ACE_MAX_ARGS; ++i)
          argv[i] = 0;
      }
  
    // The hard-coded options are what ::sp () uses, except for the
    // larger stack size (instead of ::sp ()'s 20000).
    int const ret = ::taskSpawn (argv[0],    // task name
                                 100,        // task priority
                                 VX_FP_TASK, // task options
                                 ACE_NEEDS_HUGE_THREAD_STACKSIZE, // stack size
                                 entry,      // entry point
                                 argc,       // first argument to main ()
                                 (int) argv, // second argument to main ()
                                 0, 0, 0, 0, 0, 0, 0, 0);
    va_end (pvar);
  
    // ::taskSpawn () returns the taskID on success: return 0 instead if
    // successful
    return ret > 0 ? 0 : ret;
  }
  
  // A helper function for the extended spa functions
  static void
  add_to_argv (int& argc, char** argv, int max_args, char* string)
  {
    char indouble   = 0;
    size_t previous = 0;
    size_t length   = ACE_OS::strlen (string);
  
    if (length > 0)
      {
        // We use <= to make sure that we get the last argument
        for (size_t i = 0; i <= length; i++)
          {
            // Is it a double quote that hasn't been escaped?
            if (string[i] == '\"' && (i == 0 || string[i - 1] != '\\'))
              {
                indouble ^= 1;
                if (indouble)
                  {
                    // We have just entered a double quoted string, so
                    // save the starting position of the contents.
                    previous = i + 1;
                  }
                else
                  {
                    // We have just left a double quoted string, so
                    // zero out the ending double quote.
                    string[i] = '\0';
                  }
              }
            else if (string[i] == '\\')  // Escape the next character
              {
                // The next character is automatically skipped because
                // of the memmove().
                ACE_OS::memmove (string + i, string + i + 1, length);
                --length;
              }
            else if (!indouble &&
                     (ACE_OS::ace_isspace (string[i]) || string[i] == '\0'))
              {
                string[i] = '\0';
                if (argc < max_args)
                  {
                    argv[argc] = string + previous;
                    ++argc;
                  }
                else
                  {
                    ACE_OS::fprintf (stderr, "spae(): number of arguments "
                                             "limited to %d\n", max_args);
                  }
  
                // Skip over whitespace in between arguments
                for(++i; i < length && ACE_OS::ace_isspace (string[i]); ++i)
                  {
                  }
  
                // Save the starting point for the next time around
                previous = i;
  
                // Make sure we don't skip over a character due
                // to the above loop to skip over whitespace
                --i;
              }
          }
      }
  }
  
  // This global function can be used from the VxWorks shell to pass
  // arguments to a C main () function.
  //
  // usage: -> spae main, "arg1 arg2 \"arg3 with spaces\""
  //
  // All arguments must be within double quotes, even numbers.
  int
  spae (FUNCPTR entry, ...)
  {
    static int const WINDSH_ARGS = 10;
    static int const ACE_MAX_ARGS    = 128;
    static char* argv[ACE_MAX_ARGS]  = { "ace_main", 0 };
    va_list pvar;
    int argc = 1;
  
    // Peel off arguments to spa () and put into argv.  va_arg () isn't
    // necessarily supposed to return 0 when done, though since the
    // VxWorks shell uses a fixed number (10) of arguments, it might 0
    // the unused ones.
    va_start (pvar, entry);
  
    int i = 0;
    for (char* str = va_arg (pvar, char*);
         str != 0 && i < WINDSH_ARGS; str = va_arg (pvar, char*), ++i)
      {
        add_to_argv(argc, argv, ACE_MAX_ARGS, str);
      }
  
    // fill unused argv slots with 0 to get rid of leftovers
    // from previous invocations
    for (i = argc; i < ACE_MAX_ARGS; ++i)
      argv[i] = 0;
  
    // The hard-coded options are what ::sp () uses, except for the
    // larger stack size (instead of ::sp ()'s 20000).
    int const ret = ::taskSpawn (argv[0],    // task name
                                 100,        // task priority
                                 VX_FP_TASK, // task options
                                 ACE_NEEDS_HUGE_THREAD_STACKSIZE, // stack size
                                 entry,      // entry point
                                 argc,       // first argument to main ()
                                 (int) argv, // second argument to main ()
                                 0, 0, 0, 0, 0, 0, 0, 0);
    va_end (pvar);
  
    // ::taskSpawn () returns the taskID on success: return 0 instead if
    // successful
    return ret > 0 ? 0 : ret;
  }
  
  // This global function can be used from the VxWorks shell to pass
  // arguments to a C main () function.  The function will be run
  // within the shells task.
  //
  // usage: -> spaef main, "arg1 arg2 \"arg3 with spaces\""
  //
  // All arguments must be within double quotes, even numbers.
  // Unlike the spae function, this fuction executes the supplied
  // routine in the foreground, rather than spawning it in a separate
  // task.
  int
  spaef (FUNCPTR entry, ...)
  {
    static int const WINDSH_ARGS = 10;
    static int const ACE_MAX_ARGS    = 128;
    static char* argv[ACE_MAX_ARGS]  = { "ace_main", 0 };
    va_list pvar;
    int argc = 1;
  
    // Peel off arguments to spa () and put into argv.  va_arg () isn't
    // necessarily supposed to return 0 when done, though since the
    // VxWorks shell uses a fixed number (10) of arguments, it might 0
    // the unused ones.
    va_start (pvar, entry);
  
    int i = 0;
    for (char* str = va_arg (pvar, char*);
         str != 0 && i < WINDSH_ARGS; str = va_arg (pvar, char*), ++i)
      {
        add_to_argv(argc, argv, ACE_MAX_ARGS, str);
      }
  
    // fill unused argv slots with 0 to get rid of leftovers
    // from previous invocations
    for (i = argc; i < ACE_MAX_ARGS; ++i)
      argv[i] = 0;
  
    int ret = entry (argc, argv);
  
    va_end (pvar);
  
    // Return the return value of the invoked ace_main routine.
    return ret;
  }
  
  // This global function can be used from the VxWorks shell to pass
  // arguments to and run a main () function (i.e. ace_main).
  //
  // usage: -> vx_execae ace_main, "arg1 arg2 \"arg3 with spaces\"", [prio, [opt, [stacksz]]]
  //
  // All arguments must be within double quotes, even numbers.
  // This routine spawns the main () function in a separate task and waits till the
  // task has finished.
  static int _vx_call_rc = 0;
  
  static int
  _vx_call_entry(FUNCPTR entry, int argc, char* argv[])
  {
      ACE_Service_Config::current (ACE_Service_Config::global());
      _vx_call_rc = entry (argc, argv);
      return _vx_call_rc;
  }
  
  int
  vx_execae (FUNCPTR entry, char* arg, int prio, int opt, int stacksz, ...)
  {
    static int const ACE_MAX_ARGS    = 128;
    static char* argv[ACE_MAX_ARGS]  = { "ace_main", 0 };
    int argc = 1;
  
    // Peel off arguments to run_main () and put into argv.
    if (arg)
      {
        add_to_argv(argc, argv, ACE_MAX_ARGS, arg);
      }
  
    // fill unused argv slots with 0 to get rid of leftovers
    // from previous invocations
    for (int i = argc; i < ACE_MAX_ARGS; ++i)
      argv[i] = 0;
  
    // The hard-coded options are what ::sp () uses, except for the
    // larger stack size (instead of ::sp ()'s 20000).
    int const ret = ::taskSpawn (argv[0],    // task name
                                 prio==0 ? 100 : prio,        // task priority
                                 opt==0 ? VX_FP_TASK : opt, // task options
                                 stacksz==0 ? ACE_NEEDS_HUGE_THREAD_STACKSIZE : stacksz, // stack size
                                 (FUNCPTR)_vx_call_entry, // entrypoint caller
                                 (int)entry,              // entry point
                                 argc,                    // first argument to main ()
                                 (int) argv,              // second argument to main ()
                                 0, 0, 0, 0, 0, 0, 0);
  
    if (ret == ERROR)
      return 255;
  
    while( ret > 0 && ::taskIdVerify (ret) != ERROR )
      ::taskDelay (3 * ::sysClkRateGet ());
  
    // ::taskSpawn () returns the taskID on success: return _vx_call_rc instead if
    // successful
    return ret > 0 ? _vx_call_rc : 255;
  }
  
  #if defined(ACE_AS_STATIC_LIBS) && defined (ACE_VXWORKS_DEBUGGING_HELPER)
  /** Wind River workbench allows the user to spawn a kernel task as a
      "Debug Configuration".  Use this function as the entrypoint so that
      the arguments are translated into the form that ace_main() requires.
   */
  int ace_wb_exec (int arg0, int arg1, int arg2, int arg3, int arg4,
                   int arg5, int arg6, int arg7, int arg8, int arg9)
  {
    return spaef ((FUNCPTR) ace_main, arg0, arg1, arg2, arg3, arg4,
                  arg5, arg6, arg7, arg8, arg9);
  }
  #endif /* ACE_AS_STATIC_LIBS && ... */
  
  #endif /* ACE_VXWORKS && !__RTP__ */
  