#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 #include "ace/Auto_Ptr.h" #include "ace/Thread_Mutex.h" #include "ace/Condition_Thread_Mutex.h" #include "ace/Guard_T.h" #ifdef ACE_HAS_GETTID # include "ace/OS_NS_sys_resource.h" // syscall for gettid impl #endif 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 embedded 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, size_t thr_string_len) const { #if defined (ACE_WIN32) ACE_OS::snprintf (thr_string, thr_string_len, "%u", static_cast (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::snprintf (thr_string, thr_string_len, "%lu", # ifdef ACE_THREAD_T_IS_A_STRUCT *reinterpret_cast (& # else (unsigned long) ( # endif // ACE_THREAD_T_IS_A_STRUCT 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) && defined (ACE_HAS_VXTHREADS) # if (defined (_WRS_CONFIG_SMP) || defined (INCLUDE_AMP_CPU)) __thread void* ACE_TSS_Emulation::ace_tss_keys = 0; # else /* ! VxWorks SMP */ void* ACE_TSS_Emulation::ace_tss_keys = 0; # endif /* ! VxWorks SMP */ # elif 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_) { 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) { #ifdef ACE_HAS_ALLOC_HOOKS const size_t n = ACE_TSS_THREAD_KEYS_MAX * sizeof (void *); ACE_Allocator *const alloc = ACE_Allocator::instance (); ACE_ALLOCATOR_RETURN (ts_storage, static_cast (alloc->malloc (n)), 0); #else ACE_NEW_RETURN (ts_storage, void*[ACE_TSS_THREAD_KEYS_MAX], 0); #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 = 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_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_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_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_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=="); #ifdef ACE_THREAD_T_IS_A_STRUCT return 0 == ACE_OS::memcmp (&this->tid_, &info.tid_, sizeof tid_); #else return this->tid_ == info.tid_; #endif } // Check for inequality. 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; } } 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: ACE_ALLOC_HOOK_DECLARE; /// 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 objects. typedef ACE_TSS_Info ACE_TSS_TABLE[ACE_DEFAULT_THREAD_KEYS]; typedef ACE_TSS_Info *ACE_TSS_TABLE_ITERATOR; /// Table of '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_; }; ACE_ALLOC_HOOK_DEFINE (ACE_TSS_Cleanup); ACE_ALLOC_HOOK_DEFINE (ACE_TSS_Keys); /*****************************************************************************/ /** * @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 }; explicit 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 (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 (&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 (ts_keys); return 0; // Major problems, this should *never* happen! } } return reinterpret_cast (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 (-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 must be locked before this call is made. // This is needed to ensure that and 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 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) || defined (ACE_MQX) 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 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) || defined (ACE_MQX) 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 (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 (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) || defined (ACE_MQX) 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) || defined (ACE_MQX) 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 held // since other wise there is a race condition that can lead to the // lost wakeup bug... This is needed to ensure that the // 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 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 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 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 , 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) || defined (ACE_MQX) ACE_OS::sema_post (&cv->waiters_done_); # else ACE_OS::event_signal (&cv->waiters_done_); # endif /* ACE_VXWORKS */ // We must always regain the , 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) || defined (ACE_MQX) // Prevent race conditions on the 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); # else result = ACE_OS::sema_wait (&cv->sema_, timeout); # 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 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 , 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 , 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, result); return result; #else // Prevent race conditions on the 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 , 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); #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 , 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 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) && !defined (ACE_LACKS_PTHREAD_MUTEXATTR_SETTYPE) (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 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); # 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) # if defined (ACE_LACKS_PTHREAD_MUTEX_DESTROY) ACE_UNUSED_ARG (m); ACE_NOTSUP_RETURN (-1); # else int result; ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::pthread_mutex_destroy (m), result), int); # endif /* ACE_LACKS_PTHREAD_MUTEX_DESTROY */ # elif defined (ACE_HAS_STHREADS) int result; ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::mutex_destroy (m), result), int); # 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); # elif defined (ACE_HAS_STHREADS) int result; ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::mutex_lock (m), result), int); # 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, 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); # elif defined (ACE_HAS_STHREADS) int result; ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::mutex_trylock (m), result), int); # 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); # elif defined (ACE_HAS_STHREADS) int result; ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::mutex_unlock (m), result), int); # 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 /*****************************************************************************/ #ifndef ACE_WIN32 int ACE_event_t::lock () { # if !ACE_EVENT_USE_MUTEX_PSHARED if (this->eventdata_->type_ == USYNC_PROCESS) return ACE_OS::sema_wait (&this->lock_); # endif return ACE_OS::mutex_lock (&this->eventdata_->lock_); } int ACE_event_t::unlock () { # if !ACE_EVENT_USE_MUTEX_PSHARED if (this->eventdata_->type_ == USYNC_PROCESS) return ACE_OS::sema_post (&this->lock_); # endif return ACE_OS::mutex_unlock (&this->eventdata_->lock_); } int ACE_event_t::wake_one () { # if !ACE_EVENT_USE_COND_PSHARED if (this->eventdata_->type_ == USYNC_PROCESS) { if (ACE_OS::sema_post (&this->semaphore_) != 0) return -1; } else # endif if (ACE_OS::cond_signal (&this->eventdata_->condition_) != 0) return -1; return 0; } #endif /* ACE_WIN32 */ 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_) { errno = EINVAL; return -1; } // 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_) { int r1, r2; # if ACE_EVENT_USE_MUTEX_PSHARED // 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 ACE_EVENT_USE_COND_PSHARED // 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_)); # if defined (ACE_HAS_ALLOC_HOOKS) ACE_Allocator::instance ()->free (event->name_); # else ACE_OS::free (event->name_); # endif /* ACE_HAS_ALLOC_HOOKS */ return r1 != 0 || r2 != 0 ? -1 : 0; } else // !name_ (not owned by this process) { ACE_OS::munmap (event->eventdata_, sizeof (ACE_eventdata_t)); # if !ACE_EVENT_USE_MUTEX_PSHARED ACE_OS::sema_destroy (&event->lock_); # endif # if !ACE_EVENT_USE_COND_PSHARED return ACE_OS::sema_destroy (&event->semaphore_); # endif } } else // USYNC_THREAD: { int r1, r2; // 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 (); // 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 (); } delete event->eventdata_; return r1 != 0 || r2 != 0 ? -1 : 0; } return 0; #else ACE_UNUSED_ARG (event); ACE_NOTSUP_RETURN (-1); #endif /* ACE_WIN32 */ } ACE_ALLOC_HOOK_DEFINE (ACE_eventdata_t) #ifndef ACE_WIN32 namespace { int eventdata_init (ACE_eventdata_t *evtdata, int type, int manual_reset, int initial_state, ACE_condattr_t *attributes, const char *name, void *arg, int init_condition = 1, int init_mutex = 1) { evtdata->type_ = type; evtdata->manual_reset_ = manual_reset; evtdata->is_signaled_ = initial_state; evtdata->auto_event_signaled_ = false; evtdata->waiting_threads_ = 0; evtdata->signal_count_ = 0; if (init_condition) { const int result = attributes ? ACE_OS::cond_init (&evtdata->condition_, *attributes, name, arg) : ACE_OS::cond_init (&evtdata->condition_, type, name, arg); if (result != 0) return result; } return init_mutex ? ACE_OS::mutex_init (&evtdata->lock_, type, name, (ACE_mutexattr_t *) arg) : 0; } template void format_name (char (&str)[N], const char *name, const char (&suffix)[M]) { ACE_OS::strncpy (str, name, N - M); ACE_OS::strcat (str, suffix); } } #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->name_ = 0; event->eventdata_ = 0; if (type == USYNC_PROCESS) { const char *name_p = name; # 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; } # endif /* ACE_SHM_OPEN_REQUIRES_ONE_SLASH */ bool owner = false; // 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 = true; } void *const mapped = ACE_OS::mmap (0, sizeof (ACE_eventdata_t), PROT_RDWR, MAP_SHARED, fd); ACE_eventdata_t *evtdata = reinterpret_cast (mapped); ACE_OS::close (fd); if (evtdata == MAP_FAILED) { if (owner) ACE_OS::shm_unlink (ACE_TEXT_CHAR_TO_TCHAR (name_p)); return -1; } event->eventdata_ = evtdata; if (owner) { event->name_ = ACE_OS::strdup (name_p); if (event->name_ == 0 || eventdata_init (event->eventdata_, USYNC_PROCESS, manual_reset, initial_state, attributes, name, arg, ACE_EVENT_USE_COND_PSHARED, ACE_EVENT_USE_MUTEX_PSHARED) != 0) { ACE_OS::munmap (evtdata, sizeof (ACE_eventdata_t)); ACE_OS::shm_unlink (ACE_TEXT_CHAR_TO_TCHAR (name_p)); return -1; } } int result = 0; # if !ACE_EVENT_USE_COND_PSHARED char sem_name[128] = {}; format_name (sem_name, name, "._ACE_EVTSEM_"); result = ACE_OS::sema_init (&event->semaphore_, 0, type, attributes, sem_name, arg); # endif # if !ACE_EVENT_USE_MUTEX_PSHARED if (result == 0) { char lck_name[128] = {}; format_name (lck_name, name, "._ACE_EVTLCK_"); result = ACE_OS::sema_init (&event->lock_, owner, type, attributes, lck_name, arg); } # endif # if !ACE_EVENT_USE_COND_PSHARED || !ACE_EVENT_USE_MUTEX_PSHARED if (result != 0 && owner) ACE_OS::shm_unlink (ACE_TEXT_CHAR_TO_TCHAR (name_p)); # endif return result; } else { ACE_NEW_RETURN (event->eventdata_, ACE_eventdata_t, -1); return eventdata_init (event->eventdata_, USYNC_THREAD, manual_reset, initial_state, attributes, name, arg); } #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 error = 0; int result = event->lock (); if (result != 0) return result; if (event->eventdata_->waiting_threads_ > 0) { if (event->eventdata_->manual_reset_ == 1) { // Wakeup all waiters. # if !ACE_EVENT_USE_COND_PSHARED if (event->eventdata_->type_ == USYNC_PROCESS) { 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 (); } else #endif if (ACE_OS::cond_broadcast (&event->eventdata_->condition_) != 0) { result = -1; error = errno; } else event->eventdata_->signal_count_ = event->eventdata_->waiting_threads_; } else // Auto-reset event: wakeup one waiter. { if (event->wake_one () != 0) { result = -1; error = errno; } event->eventdata_->auto_event_signaled_ = true; } } event->eventdata_->is_signaled_ = 0; if (event->unlock () != 0) return -1; if (result == -1) errno = error; 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) if (event->lock () != 0) return -1; event->eventdata_->is_signaled_ = 0; event->eventdata_->auto_event_signaled_ = false; return event->unlock (); #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 error = 0; int result = event->lock (); if (result != 0) return result; if (event->eventdata_->manual_reset_ == 1) { // wakeup all # if !ACE_EVENT_USE_COND_PSHARED if (event->eventdata_->type_ == USYNC_PROCESS) { if (ACE_OS::sema_post (&event->semaphore_) != 0) { result = -1; error = errno; } } else #endif if (ACE_OS::cond_broadcast (&event->eventdata_->condition_) != 0) { result = -1; error = errno; } if (result == 0) event->eventdata_->is_signaled_ = 1; } else // Auto-reset event { if (event->eventdata_->waiting_threads_ == 0) event->eventdata_->is_signaled_ = 1; else if (event->wake_one () != 0) { result = -1; error = errno; } event->eventdata_->auto_event_signaled_ = true; } if (event->unlock () != 0) return -1; if (result == -1) errno = error; 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 defined (ACE_WIN32) DWORD result; if (timeout && *timeout == ACE_Time_Value::zero) // Do a "poll". result = ::WaitForSingleObject (*event, 0); else { // Wait for upto 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). // parameter is given in absolute or relative value // depending on parameter . int msec_timeout = 0; if (!timeout) { msec_timeout = INFINITE; } else 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 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 error = 0; int result = event->lock (); if (result != 0) return result; if (event->eventdata_->is_signaled_ == 1) { 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, converted_time; // cond_timedwait() expects absolute time, check flag if (timeout && use_absolute_time == 0) { converted_time = timeout->to_absolute_time (); absolute_timeout = &converted_time; } while (event->eventdata_->is_signaled_ == 0 && !event->eventdata_->auto_event_signaled_) # if !ACE_EVENT_USE_COND_PSHARED if (event->eventdata_->type_ == USYNC_PROCESS) { if (event->unlock () != 0) { --event->eventdata_->waiting_threads_; return -1; } if (ACE_OS::sema_wait (&event->semaphore_, absolute_timeout) != 0) { result = -1; error = (errno == ETIMEDOUT) // Semaphores use ETIMEDOUT (POSIX) ? ETIME : errno; } bool signalled = false; if (result == 0 && event->eventdata_->signal_count_ > 0) { --event->eventdata_->signal_count_; signalled = true; } if (event->lock () != 0) { --event->eventdata_->waiting_threads_; return -1; } if (result != 0) break; if (event->eventdata_->manual_reset_ == 1 && event->eventdata_->is_signaled_ == 1 && ACE_OS::sema_post (&event->semaphore_) != 0) { result = -1; error = errno; break; } if (signalled) break; } else #endif { 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; } } // Reset the auto_event_signaled_ to false now that we have woken up. if (event->eventdata_->auto_event_signaled_) event->eventdata_->auto_event_signaled_ = false; --event->eventdata_->waiting_threads_; } if (event->unlock () != 0) return -1; if (result == -1) errno = error; return result; #else ACE_UNUSED_ARG (event); ACE_UNUSED_ARG (timeout); ACE_UNUSED_ARG (use_absolute_time); 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 namespace { struct UniqueName { UniqueName (int type, const void *addr) { #ifdef ACE_WIN32 if (type == USYNC_THREAD) { this->named_ = false; return; } this->named_ = true; #else ACE_UNUSED_ARG (type); #endif ACE_OS::unique_name (addr, &this->buffer_[0], ACE_UNIQUE_NAME_LEN); } operator const ACE_TCHAR * () const { #ifdef ACE_WIN32 if (!this->named_) { return 0; } #endif return &this->buffer_[0]; } ACE_TCHAR buffer_[ACE_UNIQUE_NAME_LEN]; #ifdef ACE_WIN32 bool named_; #endif }; enum RWLockCleanup {RWLC_CondAttr, RWLC_Lock, RWLC_CondReaders, RWLC_CondWriters}; struct RWLockCleaner { RWLockCleaner (ACE_condattr_t &attr, ACE_rwlock_t *rw) : state_ (RWLC_CondAttr) , attr_ (attr) , rw_ (rw) {} ~RWLockCleaner () { ACE_Errno_Guard error (errno); switch (this->state_) { case RWLC_CondWriters: ACE_OS::cond_destroy (&this->rw_->waiting_writers_); // FALLTHROUGH case RWLC_CondReaders: ACE_OS::cond_destroy (&this->rw_->waiting_readers_); // FALLTHROUGH case RWLC_Lock: ACE_OS::mutex_destroy (&this->rw_->lock_); // FALLTHROUGH case RWLC_CondAttr: ACE_OS::condattr_destroy (this->attr_); } } RWLockCleanup state_; ACE_condattr_t &attr_; ACE_rwlock_t *rw_; }; } #endif #if !defined (ACE_HAS_THREADS) || defined (ACE_LACKS_RWLOCK_T) int ACE_OS::rwlock_init (ACE_rwlock_t *rw, int type, const ACE_TCHAR *, 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_condattr_t attributes; if (ACE_OS::condattr_init (attributes, type) != 0) return -1; RWLockCleaner cleanup (attributes, rw); if (ACE_OS::mutex_init (&rw->lock_, type, UniqueName (type, &rw->lock_), (ACE_mutexattr_t *) arg) != 0) return -1; cleanup.state_ = RWLC_Lock; if (ACE_OS::cond_init (&rw->waiting_readers_, attributes, UniqueName (type, &rw->waiting_readers_), arg) != 0) return -1; cleanup.state_ = RWLC_CondReaders; if (ACE_OS::cond_init (&rw->waiting_writers_, attributes, UniqueName (type, &rw->waiting_writers_), arg) != 0) return -1; cleanup.state_ = RWLC_CondWriters; if (ACE_OS::cond_init (&rw->waiting_important_writer_, attributes, UniqueName (type, &rw->waiting_important_writer_), arg) != 0) return -1; cleanup.state_ = RWLC_CondAttr; rw->ref_count_ = 0; rw->num_waiting_writers_ = 0; rw->num_waiting_readers_ = 0; rw->important_writer_ = false; return 0; # else ACE_UNUSED_ARG (rw); ACE_UNUSED_ARG (type); 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 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 (), ¶m) == -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 (), ¶m), result), int); } # 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_STRUCTURED_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_STRUCTURED_EXCEPTIONS */ else thread_args = thread_adapter; std::unique_ptr auto_thread_args; if (thread_adapter == 0) auto_thread_args.reset (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 (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(*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, 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, 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); } } # 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, 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 const 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 (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); } } } // 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 (*thr_name) : 0, priority, (int) flags, stacksize, thread_args->entry_point (), (ACE_VX_USR_ARG_T) 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 (*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 == ACE_VX_TASK_ID_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) && !defined (ACE_LACKS_PTHREAD_EXIT) ::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_UNUSED_ARG (status); ::taskDelete (ACE_OS::thr_self ()); # else ACE_UNUSED_ARG (status); # 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 , 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 , 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 , 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 , 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) 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_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); # elif defined (ACE_HAS_STHREADS) int result; ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::thr_keycreate (key, dest), result), int); # 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_native (key, 0); # endif /* ACE_HAS_BROKEN_THREAD_KEYFREE */ # if defined (ACE_HAS_PTHREADS) # if defined (ACE_LACKS_PTHREAD_KEY_DELETE) ACE_UNUSED_ARG (key); ACE_NOTSUP_RETURN (-1); # else return ::pthread_key_delete (key); # endif /* ACE_LACKS_PTHREAD_KEY_DELETE */ # 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); # elif defined (ACE_HAS_STHREADS) int result; ACE_OSCALL_RETURN (ACE_ADAPT_RETVAL (::thr_setspecific (key, data), result), int); # 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 will provide // uniqueness between other "live" objects in the same process. The // uniqueness of this name is therefore only valid for the life of // . ACE_OS::snprintf (name, length, "%p%d", object, static_cast (ACE_OS::getpid ())); } #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 will provide // uniqueness between other "live" objects in the same process. The // uniqueness of this name is therefore only valid for the life of // . ACE_OS::snprintf (name, length, ACE_TEXT ("%p%d"), object, static_cast (ACE_OS::getpid ())); } #endif ACE_END_VERSIONED_NAMESPACE_DECL #if defined (ACE_VXWORKS) && !defined (__RTP__) # include /**/ /* for ::sp() */ # include /**/ /* for ::sysClkRateGet() */ # include "ace/Service_Config.h" #if !defined (ACE_LACKS_VA_FUNCTIONS) // 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, ...) { // The called entrypoint can get the function name plus the normal 10 // optional arguments. static ACE_VX_USR_ARG_T const ACE_MAX_ARGS = 1 + 10; static char *argv[ACE_MAX_ARGS] = { 0 }; va_list pvar; ACE_VX_USR_ARG_T argc; // Hardcode a program name because the real one isn't available // through the VxWorks shell. argv[0] = const_cast ("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[ACE_MAX_ARGS - 1] != 0) { // Try to read another arg, and warn user if the limit was exceeded. // // Note that the VxWorks shell arguments change from int to long when // using a 64bit compiler. Cast the argument up so that the format // specifier remains correct for either build type. if (va_arg (pvar, char *) != 0) ACE_OS::fprintf (stderr, "spa(): number of arguments limited to %ld\n", (long)ACE_MAX_ARGS); } else { // fill unused argv slots with 0 to get rid of leftovers // from previous invocations for (ACE_VX_USR_ARG_T 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). ACE_VX_TASK_ID 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 () (ACE_VX_USR_ARG_T) 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 : -1; } #endif /* !ACE_LACKS_VA_FUNCTIONS */ // A helper function for the extended spa functions static void add_to_argv (ACE_VX_USR_ARG_T& 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; } } } } #if !defined (ACE_LACKS_VA_FUNCTIONS) // 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 ACE_VX_USR_ARG_T const ACE_MAX_ARGS = 128; static char* argv[ACE_MAX_ARGS] = { const_cast ("ace_main"), 0 }; va_list pvar; ACE_VX_USR_ARG_T 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). ACE_VX_TASK_ID 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 () (ACE_VX_USR_ARG_T) 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 : -1; } // 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 ACE_VX_USR_ARG_T const ACE_MAX_ARGS = 128; static char* argv[ACE_MAX_ARGS] = { const_cast ("ace_main"), 0 }; va_list pvar; ACE_VX_USR_ARG_T 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; } #endif /* !ACE_LACKS_VA_FUNCTIONS */ // 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, size_t stacksz, ...) { static ACE_VX_USR_ARG_T const ACE_MAX_ARGS = 128; static char* argv[ACE_MAX_ARGS] = { const_cast ("ace_main"), 0 }; ACE_VX_USR_ARG_T 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 (ACE_VX_USR_ARG_T 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). ACE_VX_TASK_ID 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 (ACE_VX_USR_ARG_T)entry, // entry point argc, // first argument to main () (ACE_VX_USR_ARG_T) argv, // second argument to main () 0, 0, 0, 0, 0, 0, 0); if (ret == ACE_VX_TASK_ID_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__ */