diff options
Diffstat (limited to 'ACE/ace/Thread_Manager.cpp')
-rw-r--r-- | ACE/ace/Thread_Manager.cpp | 2327 |
1 files changed, 2327 insertions, 0 deletions
diff --git a/ACE/ace/Thread_Manager.cpp b/ACE/ace/Thread_Manager.cpp new file mode 100644 index 00000000000..4daf221f8d9 --- /dev/null +++ b/ACE/ace/Thread_Manager.cpp @@ -0,0 +1,2327 @@ +// $Id$ + +#include "ace/TSS_T.h" +#include "ace/Thread_Manager.h" +#include "ace/Dynamic.h" +#include "ace/Object_Manager.h" +#include "ace/Singleton.h" +#include "ace/Auto_Ptr.h" +#include "ace/Guard_T.h" + +#include "ace/Time_Value.h" +#include "ace/OS_NS_sys_time.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Thread_Manager.inl" +#endif /* __ACE_INLINE__ */ + + +ACE_RCSID (ace, + Thread_Manager, + "$Id$") + + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + +ACE_At_Thread_Exit::~ACE_At_Thread_Exit (void) +{ + this->do_apply (); +} + +ACE_At_Thread_Exit_Func::~ACE_At_Thread_Exit_Func (void) +{ + this->do_apply (); +} + +void +ACE_At_Thread_Exit_Func::apply (void) +{ + this->func_ (this->object_, this->param_); +} + +#endif /* ! ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + + +ACE_ALLOC_HOOK_DEFINE(ACE_Thread_Control) +ACE_ALLOC_HOOK_DEFINE(ACE_Thread_Manager) + +#if ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) +// Process-wide Thread Manager. +ACE_Thread_Manager *ACE_Thread_Manager::thr_mgr_ = 0; + +// Controls whether the Thread_Manager is deleted when we shut down +// (we can only delete it safely if we created it!) +int ACE_Thread_Manager::delete_thr_mgr_ = 0; +#endif /* ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) */ + +ACE_TSS_TYPE (ACE_Thread_Exit) *ACE_Thread_Manager::thr_exit_ = 0; + +int +ACE_Thread_Manager::set_thr_exit (ACE_TSS_TYPE (ACE_Thread_Exit) *ptr) +{ + if (ACE_Thread_Manager::thr_exit_ == 0) + ACE_Thread_Manager::thr_exit_ = ptr; + else + return -1; + return 0; +} + +void +ACE_Thread_Manager::dump (void) +{ +#if defined (ACE_HAS_DUMP) + ACE_TRACE ("ACE_Thread_Manager::dump"); + // Cast away const-ness of this in order to use its non-const lock_. + ACE_MT (ACE_GUARD (ACE_Thread_Mutex, ace_mon, + ((ACE_Thread_Manager *) this)->lock_)); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ngrp_id_ = %d"), this->grp_id_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ncurrent_count_ = %d"), this->thr_list_.size ())); + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + iter.next ()->dump (); + + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +#endif /* ACE_HAS_DUMP */ +} + +ACE_Thread_Descriptor::~ACE_Thread_Descriptor (void) +{ + delete this->sync_; +} + +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) +void ACE_Thread_Descriptor::at_pop (int apply) + +{ + ACE_TRACE ("ACE_Thread_Descriptor::at_pop"); + // Get first at from at_exit_list + ACE_At_Thread_Exit* at = this->at_exit_list_; + // Remove at from at_exit list + this->at_exit_list_ = at->next_; + // Apply if required + if (apply) + { + at->apply (); + // Do the apply method + at->was_applied (1); + // Mark at has been applied to avoid double apply from + // at destructor + } + // If at is not owner delete at. + if (!at->is_owner ()) + delete at; +} + +void +ACE_Thread_Descriptor::at_push (ACE_At_Thread_Exit* cleanup, int is_owner) + +{ + ACE_TRACE ("ACE_Thread_Descriptor::at_push"); + cleanup->is_owner (is_owner); + cleanup->td_ = this; + cleanup->next_ = at_exit_list_; + at_exit_list_ = cleanup; +} + +int +ACE_Thread_Descriptor::at_exit (ACE_At_Thread_Exit& cleanup) + +{ + ACE_TRACE ("ACE_Thread_Descriptor::at_exit"); + at_push (&cleanup, 1); + return 0; +} + +int +ACE_Thread_Descriptor::at_exit (ACE_At_Thread_Exit* cleanup) + +{ + ACE_TRACE ("ACE_Thread_Descriptor::at_exit"); + if (cleanup==0) + return -1; + else + { + this->at_push (cleanup); + return 0; + } +} + +void +ACE_Thread_Descriptor::do_at_exit () + +{ + ACE_TRACE ("ACE_Thread_Descriptor::do_at_exit"); + while (at_exit_list_!=0) + this->at_pop (); +} + +void +ACE_Thread_Descriptor::terminate () + +{ + ACE_TRACE ("ACE_Thread_Descriptor::terminate"); + + if (!terminated_) + { + ACE_Log_Msg* log_msg = this->log_msg_; + terminated_ = true; + // Run at_exit hooks + this->do_at_exit (); + // We must remove Thread_Descriptor from Thread_Manager list + if (this->tm_ != 0) + { + int close_handle = 0; + +#if !defined (ACE_VXWORKS) + // Threads created with THR_DAEMON shouldn't exist here, but + // just to be safe, let's put it here. + + if (ACE_BIT_DISABLED (this->thr_state_, ACE_Thread_Manager::ACE_THR_JOINING)) + { + if (ACE_BIT_DISABLED (this->flags_, THR_DETACHED | THR_DAEMON) + || ACE_BIT_ENABLED (this->flags_, THR_JOINABLE)) + { + // Mark thread as terminated. + ACE_SET_BITS (this->thr_state_, ACE_Thread_Manager::ACE_THR_TERMINATED); + tm_->register_as_terminated (this); + // Must copy the information here because td will be + // "freed" below. + } +#if defined (ACE_WIN32) + else + { + close_handle = 1; + } +#endif /* ACE_WIN32 */ + } +#endif /* ! ACE_VXWORKS */ + + // Remove thread descriptor from the table. + if (this->tm_ != 0) + tm_->remove_thr (this, close_handle); + } + + // Check if we need delete ACE_Log_Msg instance + // If ACE_TSS_cleanup was not executed first log_msg == 0 + if (log_msg == 0) + { + // Only inform to ACE_TSS_cleanup that it must delete the log instance + // setting ACE_LOG_MSG thr_desc to 0. + ACE_LOG_MSG->thr_desc (0); + } + else + { + // Thread_Descriptor is the owner of the Log_Msg instance!! + // deleted. + this->log_msg_ = 0; + delete log_msg; + } + } +} + +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + +int +ACE_Thread_Descriptor::at_exit (void *object, + ACE_CLEANUP_FUNC cleanup_hook, + void *param) +{ + ACE_TRACE ("ACE_Thread_Descriptor::at_exit"); +#if defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + this->cleanup_info_.cleanup_hook_ = cleanup_hook; + this->cleanup_info_.object_ = object; + this->cleanup_info_.param_ = param; +#else + // To keep compatibility, when cleanup_hook is null really is a at_pop + // without apply. + if (cleanup_hook == 0) + { + if (this->at_exit_list_!= 0) + this->at_pop(0); + } + else + { + ACE_At_Thread_Exit* cleanup = 0; + ACE_NEW_RETURN (cleanup, + ACE_At_Thread_Exit_Func (object, + cleanup_hook, + param), + -1); + this->at_push (cleanup); + } +#endif /* ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + return 0; +} + +void +ACE_Thread_Descriptor::dump (void) const +{ +#if defined (ACE_HAS_DUMP) + ACE_TRACE ("ACE_Thread_Descriptor::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nthr_id_ = %d"), this->thr_id_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nthr_handle_ = %d"), this->thr_handle_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ngrp_id_ = %d"), this->grp_id_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nthr_state_ = %d"), this->thr_state_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ncleanup_info_.cleanup_hook_ = %x"), this->cleanup_info_.cleanup_hook_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nflags_ = %x\n"), this->flags_)); + + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +#endif /* ACE_HAS_DUMP */ +} + +ACE_Thread_Descriptor::ACE_Thread_Descriptor (void) +#if !defined (ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + : log_msg_ (0), + at_exit_list_ (0), + terminated_ (false) +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ +{ + ACE_TRACE ("ACE_Thread_Descriptor::ACE_Thread_Descriptor"); + ACE_NEW (this->sync_, + ACE_DEFAULT_THREAD_MANAGER_LOCK); +} + +void +ACE_Thread_Descriptor::acquire_release (void) +{ + // Just try to acquire the lock then release it. +#if defined (ACE_THREAD_MANAGER_USES_SAFE_SPAWN) + if (ACE_BIT_DISABLED (this->thr_state_, ACE_Thread_Manager::ACE_THR_SPAWNED)) +#endif /* ACE_THREAD_MANAGER_USES_SAFE_SPAWN */ + { + this->sync_->acquire (); + // Acquire the lock before removing <td> from the thread table. If + // this thread is in the table already, it should simply acquire the + // lock easily. + + // Once we get the lock, we must have registered. + ACE_ASSERT (ACE_BIT_ENABLED (this->thr_state_, ACE_Thread_Manager::ACE_THR_SPAWNED)); + + this->sync_->release (); + // Release the lock before putting it back to freelist. + } +} + +void +ACE_Thread_Descriptor::acquire (void) +{ + // Just try to acquire the lock then release it. +#if defined (ACE_THREAD_MANAGER_USES_SAFE_SPAWN) + if (ACE_BIT_DISABLED (this->thr_state_, ACE_Thread_Manager::ACE_THR_SPAWNED)) +#endif /* ACE_THREAD_MANAGER_USES_SAFE_SPAWN */ + { + this->sync_->acquire (); + } +} + +void +ACE_Thread_Descriptor::release (void) +{ + // Just try to acquire the lock then release it. +#if defined (ACE_THREAD_MANAGER_USES_SAFE_SPAWN) + if (ACE_BIT_DISABLED (this->thr_state_, ACE_Thread_Manager::ACE_THR_SPAWNED)) +#endif /* ACE_THREAD_MANAGER_USES_SAFE_SPAWN */ + { + this->sync_->release (); + // Release the lock before putting it back to freelist. + } +} + +// The following macro simplifies subsequence code. +#define ACE_FIND(OP,INDEX) \ + ACE_Thread_Descriptor *INDEX = OP; \ + +ACE_Thread_Descriptor * +ACE_Thread_Manager::thread_descriptor (ACE_thread_t thr_id) +{ + ACE_TRACE ("ACE_Thread_Manager::thread_descriptor"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, 0)); + + ACE_FIND (this->find_thread (thr_id), ptr); + return ptr; +} + +ACE_Thread_Descriptor * +ACE_Thread_Manager::hthread_descriptor (ACE_hthread_t thr_handle) +{ + ACE_TRACE ("ACE_Thread_Manager::hthread_descriptor"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, 0)); + + ACE_FIND (this->find_hthread (thr_handle), ptr); + return ptr; +} + +// Return the thread descriptor (indexed by ACE_hthread_t). + +int +ACE_Thread_Manager::thr_self (ACE_hthread_t &self) +{ + ACE_TRACE ("ACE_Thread_Manager::thr_self"); + + ACE_Thread_Descriptor *desc = + this->thread_desc_self (); + + if (desc == 0) + return -1; + else + desc->self (self); + + return 0; +} + +// Initialize the synchronization variables. + +ACE_Thread_Manager::ACE_Thread_Manager (size_t prealloc, + size_t lwm, + size_t inc, + size_t hwm) + : grp_id_ (1), + automatic_wait_ (1) +#if defined (ACE_HAS_THREADS) + , zero_cond_ (lock_) +#endif /* ACE_HAS_THREADS */ + , thread_desc_freelist_ (ACE_FREE_LIST_WITH_POOL, + prealloc, lwm, hwm, inc) +{ + ACE_TRACE ("ACE_Thread_Manager::ACE_Thread_Manager"); +} + +#if ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) +ACE_Thread_Manager * +ACE_Thread_Manager::instance (void) +{ + ACE_TRACE ("ACE_Thread_Manager::instance"); + + if (ACE_Thread_Manager::thr_mgr_ == 0) + { + // Perform Double-Checked Locking Optimization. + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, + *ACE_Static_Object_Lock::instance (), 0)); + + if (ACE_Thread_Manager::thr_mgr_ == 0) + { + ACE_NEW_RETURN (ACE_Thread_Manager::thr_mgr_, + ACE_Thread_Manager, + 0); + ACE_Thread_Manager::delete_thr_mgr_ = 1; + } + } + + return ACE_Thread_Manager::thr_mgr_; +} + +ACE_Thread_Manager * +ACE_Thread_Manager::instance (ACE_Thread_Manager *tm) +{ + ACE_TRACE ("ACE_Thread_Manager::instance"); + ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, + *ACE_Static_Object_Lock::instance (), 0)); + + ACE_Thread_Manager *t = ACE_Thread_Manager::thr_mgr_; + // We can't safely delete it since we don't know who created it! + ACE_Thread_Manager::delete_thr_mgr_ = 0; + + ACE_Thread_Manager::thr_mgr_ = tm; + return t; +} + +void +ACE_Thread_Manager::close_singleton (void) +{ + ACE_TRACE ("ACE_Thread_Manager::close_singleton"); + + ACE_MT (ACE_GUARD (ACE_Recursive_Thread_Mutex, ace_mon, + *ACE_Static_Object_Lock::instance ())); + + if (ACE_Thread_Manager::delete_thr_mgr_) + { + // First, we clean up the thread descriptor list. + ACE_Thread_Manager::thr_mgr_->close (); + delete ACE_Thread_Manager::thr_mgr_; + ACE_Thread_Manager::thr_mgr_ = 0; + ACE_Thread_Manager::delete_thr_mgr_ = 0; + } + + ACE_Thread_Exit::cleanup (ACE_Thread_Manager::thr_exit_); +} +#endif /* ! defined (ACE_THREAD_MANAGER_LACKS_STATICS) */ + +// Close up and release all resources. + +int +ACE_Thread_Manager::close () +{ + ACE_TRACE ("ACE_Thread_Manager::close"); + + // Clean up the thread descriptor list. + if (this->automatic_wait_) + this->wait (0, 1); + else + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + this->remove_thr_all (); + } + + return 0; +} + +ACE_Thread_Manager::~ACE_Thread_Manager (void) +{ + ACE_TRACE ("ACE_Thread_Manager::~ACE_Thread_Manager"); + this->close (); +} + + +// Run the entry point for thread spawned under the control of the +// <ACE_Thread_Manager>. This must be an extern "C" to make certain +// compilers happy... +// +// The interaction with <ACE_Thread_Exit> and +// <ace_thread_manager_adapter> works like this, with +// ACE_HAS_THREAD_SPECIFIC_STORAGE or ACE_HAS_TSS_EMULATION: +// +// o Every thread in the <ACE_Thread_Manager> is run with +// <ace_thread_manager_adapter>. +// +// o <ace_thread_manager_adapter> retrieves the singleton +// <ACE_Thread_Exit> instance from <ACE_Thread_Exit::instance>. +// The singleton gets created in thread-specific storage +// in the first call to that function. The key point is that the +// instance is in thread-specific storage. +// +// o A thread can exit by various means, such as <ACE_Thread::exit>, C++ +// or Win32 exception, "falling off the end" of the thread entry +// point function, etc. +// +// o If you follow this so far, now it gets really fun . . . +// When the thread-specific storage (for the thread that +// is being destroyed) is cleaned up, the OS threads package (or +// the ACE emulation of thread-specific storage) will destroy any +// objects that are in thread-specific storage. It has a list of +// them, and just walks down the list and destroys each one. +// +// o That's where the ACE_Thread_Exit destructor gets called. + +#if defined(ACE_USE_THREAD_MANAGER_ADAPTER) +extern "C" void * +ace_thread_manager_adapter (void *args) +{ +#if defined (ACE_HAS_TSS_EMULATION) + // As early as we can in the execution of the new thread, allocate + // its local TS storage. Allocate it on the stack, to save dynamic + // allocation/dealloction. + void *ts_storage[ACE_TSS_Emulation::ACE_TSS_THREAD_KEYS_MAX]; + ACE_TSS_Emulation::tss_open (ts_storage); +#endif /* ACE_HAS_TSS_EMULATION */ + + ACE_Thread_Adapter *thread_args = (ACE_Thread_Adapter *) args; + + // NOTE: this preprocessor directive should match the one in above + // ACE_Thread_Exit::instance (). With the Xavier Pthreads package, + // the exit_hook in TSS causes a seg fault. So, this works around + // that by creating exit_hook on the stack. +#if defined (ACE_HAS_THREAD_SPECIFIC_STORAGE) || defined (ACE_HAS_TSS_EMULATION) + // Obtain our thread-specific exit hook and make sure that it knows + // how to clean us up! Note that we never use this pointer directly + // (it's stored in thread-specific storage), so it's ok to + // dereference it here and only store it as a reference. + ACE_Thread_Exit &exit_hook = *ACE_Thread_Exit::instance (); +#else + // Without TSS, create an <ACE_Thread_Exit> instance. When this + // function returns, its destructor will be called because the + // object goes out of scope. The drawback with this appraoch is + // that the destructor _won't_ get called if <thr_exit> is called. + // So, threads shouldn't exit that way. Instead, they should return + // from <svc>. + ACE_Thread_Exit exit_hook; +#endif /* ACE_HAS_THREAD_SPECIFIC_STORAGE || ACE_HAS_TSS_EMULATION */ + + // Keep track of the <Thread_Manager> that's associated with this + // <exit_hook>. + exit_hook.thr_mgr (thread_args->thr_mgr ()); + + // Invoke the user-supplied function with the args. + void *status = thread_args->invoke (); + + return status; +} +#endif + +// Call the appropriate OS routine to spawn a thread. Should *not* be +// called with the lock_ held... + +int +ACE_Thread_Manager::spawn_i (ACE_THR_FUNC func, + void *args, + long flags, + ACE_thread_t *t_id, + ACE_hthread_t *t_handle, + long priority, + int grp_id, + void *stack, + size_t stack_size, + ACE_Task_Base *task) +{ + // First, threads created by Thread Manager should not be daemon threads. + // Using assertion is probably a bit too strong. However, it helps + // finding this kind of error as early as possible. Perhaps we can replace + // assertion by returning error. + ACE_ASSERT (ACE_BIT_DISABLED (flags, THR_DAEMON)); + + // Create a new thread running <func>. *Must* be called with the + // <lock_> held... + // Get a "new" Thread Descriptor from the freelist. + auto_ptr<ACE_Thread_Descriptor> new_thr_desc (this->thread_desc_freelist_.remove ()); + + // Reset thread descriptor status + new_thr_desc->reset (this); + + ACE_Thread_Adapter *thread_args = 0; +# if defined (ACE_HAS_WIN32_STRUCTURAL_EXCEPTIONS) + ACE_NEW_RETURN (thread_args, + ACE_Thread_Adapter (func, + args, + (ACE_THR_C_FUNC) ACE_THREAD_ADAPTER_NAME, + this, + new_thr_desc.get (), + ACE_OS_Object_Manager::seh_except_selector(), + ACE_OS_Object_Manager::seh_except_handler()), + -1); +# else + ACE_NEW_RETURN (thread_args, + ACE_Thread_Adapter (func, + args, + (ACE_THR_C_FUNC) ACE_THREAD_ADAPTER_NAME, + this, + new_thr_desc.get ()), + -1); +# endif /* ACE_HAS_WIN32_STRUCTURAL_EXCEPTIONS */ + + ACE_TRACE ("ACE_Thread_Manager::spawn_i"); + ACE_hthread_t thr_handle; + +#if defined (ACE_VXWORKS) && !defined (ACE_HAS_PTHREADS) + // On VxWorks, ACE_thread_t is char *. If t_id is 0, allocate space + // for ACE_OS::thr_create () to store the task name. If t_id is not + // 0, and it doesn't point to a 0 char *, then the non-zero char * + // will be used for the task name in ACE_OS::thr_create (). If t_id + // is not 0, but does point to a 0 char *, the t_id will be set to + // point to the task name in the TCB in ACE_OS::thr_create (). + if (t_id == 0) + { + ACE_NEW_RETURN (t_id, + char*, + -1); + ACE_NEW_RETURN (*t_id, + char[16], + -1); + // Mark the thread ID to show that the ACE_Thread_Manager + // allocated it. + (*t_id)[0] = ACE_THR_ID_ALLOCATED; + (*t_id)[1] = '\0'; + } +#else /* ! ACE_VXWORKS */ + ACE_thread_t thr_id; + if (t_id == 0) + t_id = &thr_id; +#endif /* ! ACE_VXWORKS */ + + new_thr_desc->sync_->acquire (); + // Acquire the <sync_> lock to block the spawned thread from + // removing this Thread Descriptor before it gets put into our + // thread table. + + int const result = ACE_Thread::spawn (func, + args, + flags, + t_id, + &thr_handle, + priority, + stack, + stack_size, + thread_args); + + if (result != 0) + { + // _Don't_ clobber errno here! result is either 0 or -1, and + // ACE_OS::thr_create () already set errno! D. Levine 28 Mar 1997 + // errno = result; + ACE_Errno_Guard guard (errno); // Lock release may smash errno + new_thr_desc->sync_->release (); + return -1; + } + +#if defined (ACE_HAS_WTHREADS) + // Have to duplicate handle if client asks for it. + // @@ How are thread handles implemented on AIX? Do they + // also need to be duplicated? + if (t_handle != 0) +# if defined (ACE_HAS_WINCE) + *t_handle = thr_handle; +# else /* ! ACE_HAS_WINCE */ + (void) ::DuplicateHandle (::GetCurrentProcess (), + thr_handle, + ::GetCurrentProcess (), + t_handle, + 0, + TRUE, + DUPLICATE_SAME_ACCESS); +# endif /* ! ACE_HAS_WINCE */ +#else /* ! ACE_HAS_WTHREADS */ + if (t_handle != 0) + *t_handle = thr_handle; +#endif /* ! ACE_HAS_WTHREADS */ + + // append_thr also put the <new_thr_desc> into Thread_Manager's + // double-linked list. Only after this point, can we manipulate + // double-linked list from a spawned thread's context. + return this->append_thr (*t_id, + thr_handle, + ACE_THR_SPAWNED, + grp_id, + task, + flags, + new_thr_desc.release ()); +} + +int +ACE_Thread_Manager::spawn (ACE_THR_FUNC func, + void *args, + long flags, + ACE_thread_t *t_id, + ACE_hthread_t *t_handle, + long priority, + int grp_id, + void *stack, + size_t stack_size) +{ + ACE_TRACE ("ACE_Thread_Manager::spawn"); + + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + if (grp_id == -1) + grp_id = this->grp_id_++; // Increment the group id. + + if (priority != ACE_DEFAULT_THREAD_PRIORITY) + ACE_CLR_BITS (flags, THR_INHERIT_SCHED); + + if (this->spawn_i (func, + args, + flags, + t_id, + t_handle, + priority, + grp_id, + stack, + stack_size, + 0) == -1) + return -1; + + return grp_id; +} + +// Create N new threads running FUNC. + +int +ACE_Thread_Manager::spawn_n (size_t n, + ACE_THR_FUNC func, + void *args, + long flags, + long priority, + int grp_id, + ACE_Task_Base *task, + ACE_hthread_t thread_handles[], + void *stack[], + size_t stack_size[]) +{ + ACE_TRACE ("ACE_Thread_Manager::spawn_n"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + if (grp_id == -1) + grp_id = this->grp_id_++; // Increment the group id. + + for (size_t i = 0; i < n; i++) + { + // @@ What should happen if this fails?! e.g., should we try to + // cancel the other threads that we've already spawned or what? + if (this->spawn_i (func, + args, + flags, + 0, + thread_handles == 0 ? 0 : &thread_handles[i], + priority, + grp_id, + stack == 0 ? 0 : stack[i], + stack_size == 0 ? 0 : stack_size[i], + task) == -1) + return -1; + } + + return grp_id; +} + +// Create N new threads running FUNC. + +int +ACE_Thread_Manager::spawn_n (ACE_thread_t thread_ids[], + size_t n, + ACE_THR_FUNC func, + void *args, + long flags, + long priority, + int grp_id, + void *stack[], + size_t stack_size[], + ACE_hthread_t thread_handles[], + ACE_Task_Base *task) +{ + ACE_TRACE ("ACE_Thread_Manager::spawn_n"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + if (grp_id == -1) + grp_id = this->grp_id_++; // Increment the group id. + + for (size_t i = 0; i < n; i++) + { + // @@ What should happen if this fails?! e.g., should we try to + // cancel the other threads that we've already spawned or what? + if (this->spawn_i (func, + args, + flags, + thread_ids == 0 ? 0 : &thread_ids[i], + thread_handles == 0 ? 0 : &thread_handles[i], + priority, + grp_id, + stack == 0 ? 0 : stack[i], + stack_size == 0 ? 0 : stack_size[i], + task) == -1) + return -1; + } + + return grp_id; +} + +// Append a thread into the pool (does not check for duplicates). +// Must be called with locks held. + +int +ACE_Thread_Manager::append_thr (ACE_thread_t t_id, + ACE_hthread_t t_handle, + ACE_UINT32 thr_state, + int grp_id, + ACE_Task_Base *task, + long flags, + ACE_Thread_Descriptor *td) +{ + ACE_TRACE ("ACE_Thread_Manager::append_thr"); + ACE_Thread_Descriptor *thr_desc = 0; + + if (td == 0) + { + ACE_NEW_RETURN (thr_desc, + ACE_Thread_Descriptor, + -1); +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + thr_desc->tm_ = this; + // Setup the Thread_Manager. +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + } + else + thr_desc = td; + + thr_desc->thr_id_ = t_id; + thr_desc->thr_handle_ = t_handle; + thr_desc->grp_id_ = grp_id; + thr_desc->task_ = task; + thr_desc->flags_ = flags; + + this->thr_list_.insert_head (thr_desc); + ACE_SET_BITS (thr_desc->thr_state_, thr_state); + thr_desc->sync_->release (); + + return 0; +} + +// Return the thread descriptor (indexed by ACE_hthread_t). + +ACE_Thread_Descriptor * +ACE_Thread_Manager::find_hthread (ACE_hthread_t h_id) +{ + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (ACE_OS::thr_cmp (iter.next ()->thr_handle_, h_id)) + return iter.next (); + + return 0; +} + +// Locate the index in the table associated with <t_id>. Must be +// called with the lock held. + +ACE_Thread_Descriptor * +ACE_Thread_Manager::find_thread (ACE_thread_t t_id) +{ + ACE_TRACE ("ACE_Thread_Manager::find_thread"); + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (ACE_OS::thr_equal (iter.next ()->thr_id_, t_id)) + return iter.next (); + return 0; +} + +// Insert a thread into the pool (checks for duplicates and doesn't +// allow them to be inserted twice). + +int +ACE_Thread_Manager::insert_thr (ACE_thread_t t_id, + ACE_hthread_t t_handle, + int grp_id, + long flags) +{ + ACE_TRACE ("ACE_Thread_Manager::insert_thr"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + // Check for duplicates and bail out if we're already registered... +#if defined (ACE_VXWORKS) && !defined (ACE_HAS_PTHREADS) + if (this->find_hthread (t_handle) != 0 ) + return -1; +#else /* ! ACE_VXWORKS */ + if (this->find_thread (t_id) != 0 ) + return -1; +#endif /* ! ACE_VXWORKS */ + + if (grp_id == -1) + grp_id = this->grp_id_++; + + if (this->append_thr (t_id, + t_handle, + ACE_THR_SPAWNED, + grp_id, + 0, + flags) == -1) + return -1; + + return grp_id; +} + +// Run the registered hooks when the thread exits. + +void +ACE_Thread_Manager::run_thread_exit_hooks (int i) +{ +#if 0 // currently unused! + ACE_TRACE ("ACE_Thread_Manager::run_thread_exit_hooks"); + + // @@ Currently, we have just one hook. This should clearly be + // generalized to support an arbitrary number of hooks. + + ACE_Thread_Descriptor *td = this->thread_desc_self (); + if (td != 0 && td->cleanup_info.cleanup_hook_ != 0) + { + (*td->cleanup_info_.cleanup_hook_) + (td->cleanup_info_.object_, + td->cleanup_info_.param_); + + td->cleanup_info_.cleanup_hook_ = 0; + } + ACE_UNUSED_ARG (i); +#else + ACE_UNUSED_ARG (i); +#endif /* 0 */ +} + +// Remove a thread from the pool. Must be called with locks held. + +void +ACE_Thread_Manager::remove_thr (ACE_Thread_Descriptor *td, + int close_handler) +{ + ACE_TRACE ("ACE_Thread_Manager::remove_thr"); + +#if defined (ACE_VXWORKS) && !defined (ACE_HAS_PTHREADS) + ACE_thread_t tid = td->self (); +#endif /* ACE_VXWORKS */ + +#if !defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + td->tm_ = 0; +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + this->thr_list_.remove (td); + +#if defined (ACE_VXWORKS) && !defined (ACE_HAS_PTHREADS) + // Delete the thread ID, if the ACE_Thread_Manager allocated it. + if (tid && tid[0] == ACE_THR_ID_ALLOCATED) + { + delete [] tid; + } +#endif /* ACE_VXWORKS */ + +#if defined (ACE_WIN32) + if (close_handler != 0) + ::CloseHandle (td->thr_handle_); +#else + ACE_UNUSED_ARG (close_handler); +#endif /* ACE_WIN32 */ + + this->thread_desc_freelist_.add (td); + +#if defined (ACE_HAS_THREADS) + // Tell all waiters when there are no more threads left in the pool. + if (this->thr_list_.size () == 0) + this->zero_cond_.broadcast (); +#endif /* ACE_HAS_THREADS */ +} + +// Repeatedly call remove_thr on all table entries until there +// is no thread left. Must be called with lock held. +void +ACE_Thread_Manager::remove_thr_all (void) +{ + ACE_Thread_Descriptor *td = 0; + + while ((td = this->thr_list_.delete_head ()) != 0) + { + this->remove_thr (td, 1); + } +} + +// ------------------------------------------------------------------ +// Factor out some common behavior to simplify the following methods. +#define ACE_THR_OP(OP,STATE) \ + int result = OP (td->thr_handle_); \ + if (result == -1) { \ + if (errno != ENOTSUP) \ + this->thr_to_be_removed_.enqueue_tail (td); \ + return -1; \ + } \ + else { \ + ACE_SET_BITS (td->thr_state_, STATE); \ + return 0; \ + } + +int +ACE_Thread_Manager::join_thr (ACE_Thread_Descriptor *td, int) +{ + ACE_TRACE ("ACE_Thread_Manager::join_thr"); + int result = ACE_Thread::join (td->thr_handle_); + if (result != 0) + { + // Since the thread are being joined, we should + // let it remove itself from the list. + + // this->remove_thr (td); + errno = result; + return -1; + } + + return 0; +} + +int +ACE_Thread_Manager::suspend_thr (ACE_Thread_Descriptor *td, int) +{ + ACE_TRACE ("ACE_Thread_Manager::suspend_thr"); + + int const result = ACE_Thread::suspend (td->thr_handle_); + if (result == -1) { + if (errno != ENOTSUP) + this->thr_to_be_removed_.enqueue_tail (td); + return -1; + } + else { + ACE_SET_BITS (td->thr_state_, ACE_THR_SUSPENDED); + return 0; + } +} + +int +ACE_Thread_Manager::resume_thr (ACE_Thread_Descriptor *td, int) +{ + ACE_TRACE ("ACE_Thread_Manager::resume_thr"); + + int const result = ACE_Thread::resume (td->thr_handle_); + if (result == -1) { + if (errno != ENOTSUP) + this->thr_to_be_removed_.enqueue_tail (td); + return -1; + } + else { + ACE_CLR_BITS (td->thr_state_, ACE_THR_SUSPENDED); + return 0; + } +} + +int +ACE_Thread_Manager::cancel_thr (ACE_Thread_Descriptor *td, int async_cancel) +{ + ACE_TRACE ("ACE_Thread_Manager::cancel_thr"); + // Must set the state first and then try to cancel the thread. + ACE_SET_BITS (td->thr_state_, ACE_THR_CANCELLED); + + if (async_cancel != 0) + // Note that this call only does something relevant if the OS + // platform supports asynchronous thread cancellation. Otherwise, + // it's a no-op. + return ACE_Thread::cancel (td->thr_id_); + + return 0; +} + +int +ACE_Thread_Manager::kill_thr (ACE_Thread_Descriptor *td, int signum) +{ + ACE_TRACE ("ACE_Thread_Manager::kill_thr"); + + ACE_thread_t tid = td->thr_id_; +#if defined (ACE_VXWORKS) && !defined (ACE_HAS_PTHREADS) + // Skip over the ID-allocated marker, if present. + tid += tid[0] == ACE_THR_ID_ALLOCATED ? 1 : 0; +#endif /* ACE_VXWORKS */ + + int const result = ACE_Thread::kill (tid, signum); + + if (result != 0) + { + // Only remove a thread from us when there is a "real" error. + if (errno != ENOTSUP) + this->thr_to_be_removed_.enqueue_tail (td); + + return -1; + } + + return 0; +} + +// ------------------------------------------------------------------ +// Factor out some common behavior to simplify the following methods. +#define ACE_EXECUTE_OP(OP, ARG) \ + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); \ + ACE_ASSERT (this->thr_to_be_removed_.is_empty ()); \ + ACE_FIND (this->find_thread (t_id), ptr); \ + if (ptr == 0) \ + { \ + errno = ENOENT; \ + return -1; \ + } \ + int result = OP (ptr, ARG); \ + ACE_Errno_Guard error (errno); \ + while (! this->thr_to_be_removed_.is_empty ()) { \ + ACE_Thread_Descriptor * td = 0; \ + this->thr_to_be_removed_.dequeue_head (td); \ + this->remove_thr (td, 1); \ + } \ + return result + +// Suspend a single thread. + +int +ACE_Thread_Manager::suspend (ACE_thread_t t_id) +{ + ACE_TRACE ("ACE_Thread_Manager::suspend"); + ACE_EXECUTE_OP (this->suspend_thr, 0); +} + +// Resume a single thread. + +int +ACE_Thread_Manager::resume (ACE_thread_t t_id) +{ + ACE_TRACE ("ACE_Thread_Manager::resume"); + ACE_EXECUTE_OP (this->resume_thr, 0); +} + +// Cancel a single thread. + +int +ACE_Thread_Manager::cancel (ACE_thread_t t_id, int async_cancel) +{ + ACE_TRACE ("ACE_Thread_Manager::cancel"); + ACE_EXECUTE_OP (this->cancel_thr, async_cancel); +} + +// Send a signal to a single thread. + +int +ACE_Thread_Manager::kill (ACE_thread_t t_id, int signum) +{ + ACE_TRACE ("ACE_Thread_Manager::kill"); + ACE_EXECUTE_OP (this->kill_thr, signum); +} + +int +ACE_Thread_Manager::check_state (ACE_UINT32 state, + ACE_thread_t id, + int enable) +{ + ACE_TRACE ("ACE_Thread_Manager::check_state"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + ACE_UINT32 thr_state; + + int self_check = ACE_OS::thr_equal (id, ACE_OS::thr_self ()); + + // If we're checking the state of our thread, try to get the cached + // value out of TSS to avoid lookup. + if (self_check) + { + ACE_Thread_Descriptor *desc = ACE_LOG_MSG->thr_desc (); + if (desc == 0) + return 0; // Always return false. + thr_state = desc->thr_state_; + } + else + { + // Not calling from self, have to look it up from the list. + ACE_FIND (this->find_thread (id), ptr); + if (ptr == 0) + return 0; + thr_state = ptr->thr_state_; + } + if (enable) + return ACE_BIT_ENABLED (thr_state, state); + + return ACE_BIT_DISABLED (thr_state, state); +} + +// Test if a single thread has terminated. + +int +ACE_Thread_Manager::testterminate (ACE_thread_t t_id) +{ + ACE_TRACE ("ACE_Thread_Manager::testterminate"); + return this->check_state (ACE_THR_TERMINATED, t_id); +} + +// Test if a single thread is suspended. + +int +ACE_Thread_Manager::testsuspend (ACE_thread_t t_id) +{ + ACE_TRACE ("ACE_Thread_Manager::testsuspend"); + return this->check_state (ACE_THR_SUSPENDED, t_id); +} + +// Test if a single thread is active (i.e., resumed). + +int +ACE_Thread_Manager::testresume (ACE_thread_t t_id) +{ + ACE_TRACE ("ACE_Thread_Manager::testresume"); + return this->check_state (ACE_THR_SUSPENDED, t_id, 0); +} + +// Test if a single thread is cancelled. + +int +ACE_Thread_Manager::testcancel (ACE_thread_t t_id) +{ + ACE_TRACE ("ACE_Thread_Manager::testcancel"); + return this->check_state (ACE_THR_CANCELLED, t_id); +} + +// Thread information query functions. + +int +ACE_Thread_Manager::hthread_within (ACE_hthread_t handle) +{ + ACE_TRACE ("ACE_Thread_Manager::hthread_within"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_monx, this->lock_, -1)); + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (ACE_OS::thr_cmp(iter.next ()->thr_handle_, handle)) + return 1; + + return 0; +} + +int +ACE_Thread_Manager::thread_within (ACE_thread_t tid) +{ + ACE_TRACE ("ACE_Thread_Manager::thread_within"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_monx, this->lock_, -1)); + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (ACE_OS::thr_equal (iter.next ()->thr_id_, tid)) + return 1; + + return 0; +} + +// Get group ids for a particular thread id. + +int +ACE_Thread_Manager::get_grp (ACE_thread_t t_id, int &grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::get_grp"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + ACE_FIND (this->find_thread (t_id), ptr); + + if (ptr) + grp_id = ptr->grp_id_; + else + return -1; + return 0; +} + +// Set group ids for a particular thread id. + +int +ACE_Thread_Manager::set_grp (ACE_thread_t t_id, int grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::set_grp"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + ACE_FIND (this->find_thread (t_id), ptr); + if (ptr) + ptr->grp_id_ = grp_id; + else + return -1; + return 0; +} + +// Suspend a group of threads. + +int +ACE_Thread_Manager::apply_grp (int grp_id, + ACE_THR_MEMBER_FUNC func, + int arg) +{ + ACE_TRACE ("ACE_Thread_Manager::apply_grp"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_monx, this->lock_, -1)); + ACE_ASSERT (this->thr_to_be_removed_.is_empty ()); + + int result = 0; + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (iter.next ()->grp_id_ == grp_id) + if ((this->*func) (iter.next (), arg) == -1) + result = -1; + + // Must remove threads after we have traversed the thr_list_ to + // prevent clobber thr_list_'s integrity. + + if (! this->thr_to_be_removed_.is_empty ()) + { + // Save/restore errno. + ACE_Errno_Guard error (errno); + + for (ACE_Thread_Descriptor *td; + this->thr_to_be_removed_.dequeue_head (td) != -1; + ) + this->remove_thr (td, 1); + } + + return result; +} + +int +ACE_Thread_Manager::suspend_grp (int grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::suspend_grp"); + return this->apply_grp (grp_id, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::suspend_thr)); +} + +// Resume a group of threads. + +int +ACE_Thread_Manager::resume_grp (int grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::resume_grp"); + return this->apply_grp (grp_id, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::resume_thr)); +} + +// Kill a group of threads. + +int +ACE_Thread_Manager::kill_grp (int grp_id, int signum) +{ + ACE_TRACE ("ACE_Thread_Manager::kill_grp"); + return this->apply_grp (grp_id, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::kill_thr), signum); +} + +// Cancel a group of threads. + +int +ACE_Thread_Manager::cancel_grp (int grp_id, int async_cancel) +{ + ACE_TRACE ("ACE_Thread_Manager::cancel_grp"); + return this->apply_grp (grp_id, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::cancel_thr), + async_cancel); +} + +int +ACE_Thread_Manager::apply_all (ACE_THR_MEMBER_FUNC func, int arg) +{ + ACE_TRACE ("ACE_Thread_Manager::apply_all"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + ACE_ASSERT (this->thr_to_be_removed_.is_empty ()); + + int result = 0; + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if ((this->*func)(iter.next (), arg) == -1) + result = -1; + + // Must remove threads after we have traversed the thr_list_ to + // prevent clobber thr_list_'s integrity. + + if (! this->thr_to_be_removed_.is_empty ()) + { + // Save/restore errno. + ACE_Errno_Guard error (errno); + + for (ACE_Thread_Descriptor *td; + this->thr_to_be_removed_.dequeue_head (td) != -1; + ) + this->remove_thr (td, 1); + } + + return result; +} + +// Resume all threads that are suspended. + +int +ACE_Thread_Manager::resume_all (void) +{ + ACE_TRACE ("ACE_Thread_Manager::resume_all"); + return this->apply_all (ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::resume_thr)); +} + +int +ACE_Thread_Manager::suspend_all (void) +{ + ACE_TRACE ("ACE_Thread_Manager::suspend_all"); + return this->apply_all (ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::suspend_thr)); +} + +int +ACE_Thread_Manager::kill_all (int sig) +{ + ACE_TRACE ("ACE_Thread_Manager::kill_all"); + return this->apply_all (&ACE_Thread_Manager::kill_thr, sig); +} + +int +ACE_Thread_Manager::cancel_all (int async_cancel) +{ + ACE_TRACE ("ACE_Thread_Manager::cancel_all"); + return this->apply_all (ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::cancel_thr), + async_cancel); +} + +int +ACE_Thread_Manager::join (ACE_thread_t tid, ACE_THR_FUNC_RETURN *status) +{ + ACE_TRACE ("ACE_Thread_Manager::join"); + + ACE_Thread_Descriptor_Base tdb; + int found = 0; + + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + +#if !defined (ACE_VXWORKS) + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor_Base> biter (this->terminated_thr_list_); + !biter.done (); + biter.advance ()) + if (ACE_OS::thr_equal (biter.next ()->thr_id_, tid)) + { + ACE_Thread_Descriptor_Base *tdb = biter.advance_and_remove (0); +# if defined (_AIX) + // The AIX xlC compiler does not match the proper function here - it + // confuses ACE_Thread::join(ACE_thread_t, ACE_thread_t *, void **=0) and + // ACE_Thread::join(ACE_hthread_t, void **=0). At least at 3.1.4.7 and .8. + // The 2nd arg is ignored for pthreads anyway. + + // And, g++ on AIX needs the three-arg thr_join, also, to pick up the + // proper version from the AIX libraries. + if (ACE_Thread::join (tdb->thr_handle_, + &tdb->thr_handle_, + status) == -1) +# else /* ! _AIX */ + if (ACE_Thread::join (tdb->thr_handle_, status) == -1) +# endif /* ! _AIX */ + return -1; + +# if defined (ACE_HAS_PTHREADS_DRAFT4) && defined (ACE_LACKS_SETDETACH) + // Must explicitly detach threads. Threads without THR_DETACHED + // were detached in ACE_OS::thr_create (). + ::pthread_detach (&tdb->thr_handle_); +# endif /* ACE_HAS_PTHREADS_DRAFT4 && ACE_LACKS_SETDETACH */ + + delete tdb; + return 0; + // return immediately if we've found the thread we want to join. + } +#endif /* !ACE_VXWORKS */ + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + // If threads are created as THR_DETACHED or THR_DAEMON, we + // can't help much. + if (ACE_OS::thr_equal (iter.next ()->thr_id_,tid) && + (ACE_BIT_DISABLED (iter.next ()->flags_, THR_DETACHED | THR_DAEMON) + || ACE_BIT_ENABLED (iter.next ()->flags_, THR_JOINABLE))) + { + tdb = *iter.next (); + ACE_SET_BITS (iter.next ()->thr_state_, ACE_THR_JOINING); + found = 1; + break; + } + + if (found == 0) + return -1; + // Didn't find the thread we want or the thread is not joinable. + } + +# if defined (_AIX) + // The AIX xlC compiler does not match the proper function here - it + // confuses ACE_Thread::join(ACE_thread_t, ACE_thread_t *, void **=0) and + // ACE_Thread::join(ACE_hthread_t, void **=0). At least at 3.1.4.7 and .8. + // The 2nd arg is ignored for pthreads anyway. + + // And, g++ on AIX needs the three-arg thr_join, also, to pick up the + // proper version from the AIX libraries. + if (ACE_Thread::join (tdb.thr_handle_, &tdb.thr_handle_, status) == -1) +# else /* ! _AIX */ + if (ACE_Thread::join (tdb.thr_handle_, status) == -1) +# endif /* ! _AIX */ + return -1; + +# if defined (ACE_HAS_PTHREADS_DRAFT4) && defined (ACE_LACKS_SETDETACH) + // Must explicitly detach threads. Threads without THR_DETACHED + // were detached in ACE_OS::thr_create (). + + ::pthread_detach (&tdb.thr_handle_); +# endif /* ACE_HAS_PTHREADS_DRAFT4 && ACE_LACKS_SETDETACH */ + return 0; +} + +// Wait for group of threads + +int +ACE_Thread_Manager::wait_grp (int grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::wait_grp"); + + int copy_count = 0; + ACE_Thread_Descriptor_Base *copy_table = 0; + + // We have to make sure that while we wait for these threads to + // exit, we do not have the lock. Therefore we make a copy of all + // interesting entries and let go of the lock. + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + +#if !defined (ACE_VXWORKS) + ACE_NEW_RETURN (copy_table, + ACE_Thread_Descriptor_Base [this->thr_list_.size () + + this->terminated_thr_list_.size ()], + -1); +#else + ACE_NEW_RETURN (copy_table, + ACE_Thread_Descriptor_Base [this->thr_list_.size ()], + -1); +#endif /* ACE_VXWORKS */ + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + // If threads are created as THR_DETACHED or THR_DAEMON, we + // can't help much. + if (iter.next ()->grp_id_ == grp_id && + (ACE_BIT_DISABLED (iter.next ()->flags_, THR_DETACHED | THR_DAEMON) + || ACE_BIT_ENABLED (iter.next ()->flags_, THR_JOINABLE))) + { + ACE_SET_BITS (iter.next ()->thr_state_, ACE_THR_JOINING); + copy_table[copy_count++] = *iter.next (); + } + +#if !defined (ACE_VXWORKS) + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor_Base> biter (this->terminated_thr_list_); + !biter.done (); + biter.advance ()) + // If threads are created as THR_DETACHED or THR_DAEMON, we + // can't help much. + if (biter.next ()->grp_id_ == grp_id) + { + ACE_Thread_Descriptor_Base *tdb = biter.advance_and_remove (0); + copy_table[copy_count++] = *tdb; + delete tdb; + } +#endif /* !ACE_VXWORKS */ + } + + // Now actually join() with all the threads in this group. + int result = 0; + + for (int i = 0; + i < copy_count && result != -1; + i++) + { + if (ACE_Thread::join (copy_table[i].thr_handle_) == -1) + result = -1; + +# if defined (ACE_HAS_PTHREADS_DRAFT4) && defined (ACE_LACKS_SETDETACH) + // Must explicitly detach threads. Threads without THR_DETACHED + // were detached in ACE_OS::thr_create (). + ::pthread_detach (©_table[i].thr_handle_); +# endif /* ACE_HAS_PTHREADS_DRAFT4 && ACE_LACKS_SETDETACH */ + } + + delete [] copy_table; + + return result; +} + +// Must be called when thread goes out of scope to clean up its table +// slot. + +ACE_THR_FUNC_RETURN +ACE_Thread_Manager::exit (ACE_THR_FUNC_RETURN status, int do_thr_exit) +{ + ACE_TRACE ("ACE_Thread_Manager::exit"); +#if defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + int close_handle = 0; +#endif /* ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + +#if defined (ACE_WIN32) + // Remove detached thread handle. + + if (do_thr_exit) + { +#if 0 + // @@ This callback is now taken care of by TSS_Cleanup. Do we + // need it anymore? + + // On Win32, if we really wants to exit from a thread, we must + // first clean up the thread specific storage. By doing so, + // ACE_Thread_Manager::exit will be called again with + // do_thr_exit = 0 and cleaning up the ACE_Cleanup_Info (but not + // exiting the thread.) After the following call returns, we + // are safe to exit this thread. + delete ACE_Thread_Exit::instance (); +#endif /* 0 */ + ACE_Thread::exit (status); + } +#endif /* ACE_WIN32 */ + +#if defined(ACE_USE_ONE_SHOT_AT_THREAD_EXIT) + ACE_Cleanup_Info cleanup_info; + + // Just hold onto the guard while finding this thread's id and + // copying the exit hook. + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, 0)); + + // Find the thread id, but don't use the cache. It might have been + // deleted already. +#if defined (ACE_VXWORKS) && !defined (ACE_HAS_PTHREADS) + ACE_hthread_t id; + ACE_OS::thr_self (id); + ACE_Thread_Descriptor *td = this->find_hthread (id); +#else /* ! ACE_VXWORKS */ + ACE_thread_t id = ACE_OS::thr_self (); + ACE_Thread_Descriptor *td = this->find_thread (id); +#endif /* ! ACE_VXWORKS */ + + // Locate thread id. + if (td != 0) + { + // @@ Currently, we have just one hook. This should clearly + // be generalized to support an arbitrary number of hooks. + + if (td->cleanup_info_.cleanup_hook_ != 0) + { + // Copy the hook so that we can call it after releasing + // the guard. + cleanup_info = td->cleanup_info_; + td->cleanup_info_.cleanup_hook_ = 0; + } + +#if !defined (ACE_VXWORKS) + // Threads created with THR_DAEMON shouldn't exist here, but + // just to be safe, let's put it here. + + if (ACE_BIT_DISABLED (td->thr_state_, ACE_THR_JOINING)) + if (ACE_BIT_DISABLED (td->flags_, THR_DETACHED | THR_DAEMON) + || ACE_BIT_ENABLED (td->flags_, THR_JOINABLE)) + { + // Mark thread as terminated. + ACE_SET_BITS (td->thr_state_, ACE_THR_TERMINATED); + this->register_as_terminated (td); + // Must copy the information here because td will be "freed" below. + } +#if defined (ACE_WIN32) + else + { + close_handle = 1; + } +#endif /* ACE_WIN32 */ +#endif /* ! ACE_VXWORKS */ + + // Remove thread descriptor from the table. + this->remove_thr (td, close_handle); + } + // Release the guard. + } + + // Call the cleanup hook. + if (cleanup_info.cleanup_hook_ != 0) + (*cleanup_info.cleanup_hook_) (cleanup_info.object_, + cleanup_info.param_); +#else /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + // Just hold onto the guard while finding this thread's id and + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, 0)); + + // Find the thread id, but don't use the cache. It might have been + // deleted already. +#if defined (ACE_VXWORKS) && !defined (ACE_HAS_PTHREADS) + ACE_hthread_t id; + ACE_OS::thr_self (id); + ACE_Thread_Descriptor* td = this->find_hthread (id); +#else /* ! ACE_VXWORKS */ + ACE_thread_t id = ACE_OS::thr_self (); + ACE_Thread_Descriptor* td = this->find_thread (id); +#endif /* ! ACE_VXWORKS */ + if (td != 0) + { + // @@ We call Thread_Descriptor terminate this realize the cleanup + // process itself. + td->terminate(); + } + } + + +#endif /* !ACE_USE_ONE_SHOT_AT_THREAD_EXIT */ + + if (do_thr_exit) + { + ACE_Thread::exit (status); + // On reasonable systems <ACE_Thread::exit> should not return. + // However, due to horrible semantics with Win32 thread-specific + // storage this call can return (don't ask...). + } + + return 0; +} + +// Wait for all the threads to exit. + +int +ACE_Thread_Manager::wait (const ACE_Time_Value *timeout, + bool abandon_detached_threads, + bool use_absolute_time) +{ + ACE_TRACE ("ACE_Thread_Manager::wait"); + + ACE_Time_Value local_timeout; + // Check to see if we're using absolute time or not. + if (use_absolute_time == false && timeout != 0) + { + local_timeout = *timeout; + local_timeout += ACE_OS::gettimeofday (); + timeout = &local_timeout; + } + +#if !defined (ACE_VXWORKS) + ACE_Double_Linked_List<ACE_Thread_Descriptor_Base> term_thr_list_copy; +#endif /* ACE_VXWORKS */ + +#if defined (ACE_HAS_THREADS) + { + // Just hold onto the guard while waiting. + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + if (ACE_Object_Manager::shutting_down () != 1) + { + // Program is not shutting down. Perform a normal wait on threads. + if (abandon_detached_threads != 0) + { + ACE_ASSERT (this->thr_to_be_removed_.is_empty ()); + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> + iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (ACE_BIT_ENABLED (iter.next ()->flags_, + THR_DETACHED | THR_DAEMON) + && ACE_BIT_DISABLED (iter.next ()->flags_, THR_JOINABLE)) + { + this->thr_to_be_removed_.enqueue_tail (iter.next ()); + ACE_SET_BITS (iter.next ()->thr_state_, ACE_THR_JOINING); + } + + if (! this->thr_to_be_removed_.is_empty ()) + { + ACE_Thread_Descriptor *td; + while (this->thr_to_be_removed_.dequeue_head (td) != -1) + this->remove_thr (td, 1); + } + } + + while (this->thr_list_.size () > 0) + if (this->zero_cond_.wait (timeout) == -1) + return -1; + } + else + // Program is shutting down, no chance to wait on threads. + // Therefore, we'll just remove threads from the list. + this->remove_thr_all (); + +#if !defined (ACE_VXWORKS) + ACE_Thread_Descriptor_Base* item = 0; + while ((item = this->terminated_thr_list_.delete_head ()) != 0) + { + term_thr_list_copy.insert_tail (item); + } +#endif /* ACE_VXWORKS */ + // Release the guard, giving other threads a chance to run. + } + +#if !defined (ACE_VXWORKS) + // @@ VxWorks doesn't support thr_join (yet.) We are working + //on our implementation. Chorus'es thr_join seems broken. + ACE_Thread_Descriptor_Base *item = 0; + + while ((item = term_thr_list_copy.delete_head ()) != 0) + { + if (ACE_BIT_DISABLED (item->flags_, THR_DETACHED | THR_DAEMON) + || ACE_BIT_ENABLED (item->flags_, THR_JOINABLE)) + // Detached handles shouldn't reached here. + ACE_Thread::join (item->thr_handle_); + +# if defined (ACE_HAS_PTHREADS_DRAFT4) && defined (ACE_LACKS_SETDETACH) + // Must explicitly detach threads. Threads without + // THR_DETACHED were detached in ACE_OS::thr_create (). + ::pthread_detach (&item->thr_handle_); +# endif /* ACE_HAS_PTHREADS_DRAFT4 && ACE_LACKS_SETDETACH */ + delete item; + } + +#endif /* ! ACE_VXWORKS */ +#else + ACE_UNUSED_ARG (timeout); + ACE_UNUSED_ARG (abandon_detached_threads); +#endif /* ACE_HAS_THREADS */ + + return 0; +} + +int +ACE_Thread_Manager::apply_task (ACE_Task_Base *task, + ACE_THR_MEMBER_FUNC func, + int arg) +{ + ACE_TRACE ("ACE_Thread_Manager::apply_task"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + ACE_ASSERT (this->thr_to_be_removed_.is_empty ()); + + int result = 0; + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (iter.next ()->task_ == task + && (this->*func) (iter.next (), arg) == -1) + result = -1; + + // Must remove threads after we have traversed the thr_list_ to + // prevent clobber thr_list_'s integrity. + + if (! this->thr_to_be_removed_.is_empty ()) + { + // Save/restore errno. + ACE_Errno_Guard error (errno); + + for (ACE_Thread_Descriptor *td; + this->thr_to_be_removed_.dequeue_head (td) != -1; + ) + this->remove_thr (td, 1); + } + + return result; +} + +// Wait for all threads to exit a task. + +int +ACE_Thread_Manager::wait_task (ACE_Task_Base *task) +{ + int copy_count = 0; + ACE_Thread_Descriptor_Base *copy_table = 0; + + // We have to make sure that while we wait for these threads to + // exit, we do not have the lock. Therefore we make a copy of all + // interesting entries and let go of the lock. + { + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + +#if !defined (ACE_VXWORKS) + ACE_NEW_RETURN (copy_table, + ACE_Thread_Descriptor_Base [this->thr_list_.size () + + this->terminated_thr_list_.size ()], + -1); +#else + ACE_NEW_RETURN (copy_table, + ACE_Thread_Descriptor_Base [this->thr_list_.size ()], + -1); +#endif /* ACE_VXWORKS */ + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + // If threads are created as THR_DETACHED or THR_DAEMON, we + // can't wait on them here. + if (iter.next ()->task_ == task && + (ACE_BIT_DISABLED (iter.next ()->flags_, + THR_DETACHED | THR_DAEMON) + || ACE_BIT_ENABLED (iter.next ()->flags_, + THR_JOINABLE))) + { + ACE_SET_BITS (iter.next ()->thr_state_, + ACE_THR_JOINING); + copy_table[copy_count++] = *iter.next (); + } + +#if !defined (ACE_VXWORKS) + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor_Base> titer (this->terminated_thr_list_); + !titer.done (); + titer.advance ()) + // If threads are created as THR_DETACHED or THR_DAEMON, we can't help much here. + if (titer.next ()->task_ == task) + { + ACE_Thread_Descriptor_Base *tdb = + titer.advance_and_remove (0); + copy_table[copy_count++] = *tdb; + delete tdb; + } +#endif /* ACE_VXWORKS */ + } + + // Now to do the actual work + int result = 0; + + for (int i = 0; + i < copy_count && result != -1; + i++) + { + if (ACE_Thread::join (copy_table[i].thr_handle_) == -1) + result = -1; + +# if defined (ACE_HAS_PTHREADS_DRAFT4) && defined (ACE_LACKS_SETDETACH) + // Must explicitly detach threads. Threads without THR_DETACHED + // were detached in ACE_OS::thr_create (). + ::pthread_detach (©_table[i].thr_handle_); +# endif /* ACE_HAS_PTHREADS_DRAFT4 && ACE_LACKS_SETDETACH */ + } + + delete [] copy_table; + + return result; +} + +// Suspend a task + +int +ACE_Thread_Manager::suspend_task (ACE_Task_Base *task) +{ + ACE_TRACE ("ACE_Thread_Manager::suspend_task"); + return this->apply_task (task, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::suspend_thr)); +} + +// Resume a task. +int +ACE_Thread_Manager::resume_task (ACE_Task_Base *task) +{ + ACE_TRACE ("ACE_Thread_Manager::resume_task"); + return this->apply_task (task, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::resume_thr)); +} + +// Kill a task. + +int +ACE_Thread_Manager::kill_task (ACE_Task_Base *task, int /* signum */) +{ + ACE_TRACE ("ACE_Thread_Manager::kill_task"); + return this->apply_task (task, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::kill_thr)); +} + +// Cancel a task. +int +ACE_Thread_Manager::cancel_task (ACE_Task_Base *task, + int async_cancel) +{ + ACE_TRACE ("ACE_Thread_Manager::cancel_task"); + return this->apply_task (task, + ACE_THR_MEMBER_FUNC (&ACE_Thread_Manager::cancel_thr), + async_cancel); +} + +// Locate the index in the table associated with <task> from the +// beginning of the table up to an index. Must be called with the +// lock held. + +ACE_Thread_Descriptor * +ACE_Thread_Manager::find_task (ACE_Task_Base *task, size_t slot) +{ + ACE_TRACE ("ACE_Thread_Manager::find_task"); + + size_t i = 0; + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (i >= slot) + break; + + if (task == iter.next ()->task_) + return iter.next (); + + i++; + } + + return 0; +} + +// Returns the number of ACE_Task in a group. + +int +ACE_Thread_Manager::num_tasks_in_group (int grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::num_tasks_in_group"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + int tasks_count = 0; + size_t i = 0; + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (iter.next ()->grp_id_ == grp_id + && this->find_task (iter.next ()->task_, i) == 0 + && iter.next ()->task_ != 0) + tasks_count++; + + i++; + } + return tasks_count; +} + +// Returns the number of threads in an ACE_Task. + +int +ACE_Thread_Manager::num_threads_in_task (ACE_Task_Base *task) +{ + ACE_TRACE ("ACE_Thread_Manager::num_threads_in_task"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + int threads_count = 0; + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (iter.next ()->task_ == task) + threads_count++; + + return threads_count; +} + +// Returns in task_list a list of ACE_Tasks registered with ACE_Thread_Manager. + +ssize_t +ACE_Thread_Manager::task_all_list (ACE_Task_Base *task_list[], + size_t n) +{ + ACE_TRACE ("ACE_Thread_Manager::task_all_list"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + size_t task_list_count = 0; + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (task_list_count >= n) + break; + + ACE_Task_Base *task_p = iter.next ()->task_; + if (0 != task_p) + { + // This thread has a task pointer; see if it's already in the + // list. Don't add duplicates. + size_t i = 0; + for (; i < task_list_count; ++i) + if (task_list[i] == task_p) + break; + if (i == task_list_count) // No match - add this one + task_list[task_list_count++] = task_p; + } + } + + return task_list_count; +} + +// Returns in thread_list a list of all thread ids + +ssize_t +ACE_Thread_Manager::thread_all_list (ACE_thread_t thread_list[], + size_t n) +{ + ACE_TRACE ("ACE_Thread_Manager::thread_all_list"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + size_t thread_count = 0; + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (thread_count >= n) + break; + + thread_list[thread_count] = iter.next ()->thr_id_; + thread_count ++; + } + + return thread_count; +} + + +int +ACE_Thread_Manager::thr_state (ACE_thread_t id, + ACE_UINT32& state) +{ + ACE_TRACE ("ACE_Thread_Manager::thr_state"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + int self_check = ACE_OS::thr_equal (id, ACE_OS::thr_self ()); + + // If we're checking the state of our thread, try to get the cached + // value out of TSS to avoid lookup. + if (self_check) + { + ACE_Thread_Descriptor *desc = ACE_LOG_MSG->thr_desc (); + if (desc == 0) + return 0; // Always return false. + state = desc->thr_state_; + } + else + { + // Not calling from self, have to look it up from the list. + ACE_FIND (this->find_thread (id), ptr); + if (ptr == 0) + return 0; + state = ptr->thr_state_; + } + + return 1; +} + +// Returns in task_list a list of ACE_Tasks in a group. + +ssize_t +ACE_Thread_Manager::task_list (int grp_id, + ACE_Task_Base *task_list[], + size_t n) +{ + ACE_TRACE ("ACE_Thread_Manager::task_list"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + ACE_Task_Base **task_list_iterator = task_list; + size_t task_list_count = 0; + size_t i = 0; + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (task_list_count >= n) + break; + + if (iter.next ()->grp_id_ == grp_id + && this->find_task (iter.next ()->task_, i) == 0) + { + task_list_iterator[task_list_count] = iter.next ()->task_; + task_list_count++; + } + + i++; + } + + return task_list_count; +} + +// Returns in thread_list a list of thread ids in an ACE_Task. + +ssize_t +ACE_Thread_Manager::thread_list (ACE_Task_Base *task, + ACE_thread_t thread_list[], + size_t n) +{ + ACE_TRACE ("ACE_Thread_Manager::thread_list"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + size_t thread_count = 0; + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (thread_count >= n) + break; + + if (iter.next ()->task_ == task) + { + thread_list[thread_count] = iter.next ()->thr_id_; + thread_count++; + } + } + + return thread_count; +} + +// Returns in thread_list a list of thread handles in an ACE_Task. + +ssize_t +ACE_Thread_Manager::hthread_list (ACE_Task_Base *task, + ACE_hthread_t hthread_list[], + size_t n) +{ + ACE_TRACE ("ACE_Thread_Manager::hthread_list"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + size_t hthread_count = 0; + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (hthread_count >= n) + break; + + if (iter.next ()->task_ == task) + { + hthread_list[hthread_count] = iter.next ()->thr_handle_; + hthread_count++; + } + } + + return hthread_count; +} + +ssize_t +ACE_Thread_Manager::thread_grp_list (int grp_id, + ACE_thread_t thread_list[], + size_t n) +{ + ACE_TRACE ("ACE_Thread_Manager::thread_grp_list"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + size_t thread_count = 0; + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (thread_count >= n) + break; + + if (iter.next ()->grp_id_ == grp_id) + { + thread_list[thread_count] = iter.next ()->thr_id_; + thread_count++; + } + } + + return thread_count; +} + +// Returns in thread_list a list of thread handles in an ACE_Task. + +ssize_t +ACE_Thread_Manager::hthread_grp_list (int grp_id, + ACE_hthread_t hthread_list[], + size_t n) +{ + ACE_TRACE ("ACE_Thread_Manager::hthread_grp_list"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + size_t hthread_count = 0; + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + { + if (hthread_count >= n) + break; + + if (iter.next ()->grp_id_ == grp_id) + { + hthread_list[hthread_count] = iter.next ()->thr_handle_; + hthread_count++; + } + } + + return hthread_count; +} + +int +ACE_Thread_Manager::set_grp (ACE_Task_Base *task, int grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::set_grp"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + for (ACE_Double_Linked_List_Iterator<ACE_Thread_Descriptor> iter (this->thr_list_); + !iter.done (); + iter.advance ()) + if (iter.next ()->task_ == task) + iter.next ()->grp_id_ = grp_id; + + return 0; +} + +int +ACE_Thread_Manager::get_grp (ACE_Task_Base *task, int &grp_id) +{ + ACE_TRACE ("ACE_Thread_Manager::get_grp"); + ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1)); + + ACE_FIND (this->find_task (task), ptr); + grp_id = ptr->grp_id_; + return 0; +} + +ACE_END_VERSIONED_NAMESPACE_DECL |