// $Id$ #include "ace/Token_Manager.h" #if defined (ACE_HAS_TOKENS_LIBRARY) #include "ace/Object_Manager.h" #include "ace/os_include/os_typeinfo.h" #if !defined (__ACE_INLINE__) #include "ace/Token_Manager.inl" #endif /* __ACE_INLINE__ */ ACE_BEGIN_VERSIONED_NAMESPACE_DECL // singleton token manager ACE_Token_Manager *ACE_Token_Manager::token_manager_ = 0; ACE_Token_Manager::ACE_Token_Manager () { ACE_TRACE ("ACE_Token_Manager::ACE_Token_Manager"); } ACE_Token_Manager::~ACE_Token_Manager () { ACE_TRACE ("ACE_Token_Manager::~ACE_Token_Manager"); COLLECTION::ITERATOR iterator (collection_); for (COLLECTION::ENTRY *temp = 0; iterator.next (temp) != 0; iterator.advance ()) { // @ should I be doing an unbind here? delete temp->int_id_; // The ext_id_'s delete themselves when the array of // COLLECTION::ENTRYs goes away. } } ACE_Token_Manager * ACE_Token_Manager::instance (void) { ACE_TRACE ("ACE_Token_Manager::instance"); // This first check is to avoid acquiring the mutex in the common // case. Double-Check pattern rules. if (token_manager_ == 0) { #if defined (ACE_MT_SAFE) && (ACE_MT_SAFE != 0) ACE_TOKEN_CONST::MUTEX *lock = ACE_Managed_Object::get_preallocated_object (ACE_Object_Manager::ACE_TOKEN_MANAGER_CREATION_LOCK); ACE_GUARD_RETURN (ACE_TOKEN_CONST::MUTEX, ace_mon, *lock, 0); #endif /* ACE_MT_SAFE */ if (token_manager_ == 0) { ACE_NEW_RETURN (token_manager_, ACE_Token_Manager, 0); // Register for destruction with ACE_Object_Manager. ACE_Object_Manager::at_exit (token_manager_, 0, typeid (token_manager_).name ()); } } return token_manager_; } void ACE_Token_Manager::get_token (ACE_Token_Proxy *proxy, const ACE_TCHAR *token_name) { ACE_TRACE ("ACE_Token_Manager::get_token"); // Hmm. I think this makes sense. We perform our own locking here // (see safe_acquire.) We have to make sure that only one thread // uses the collection at a time. ACE_GUARD (ACE_TOKEN_CONST::MUTEX, ace_mon, this->lock_); TOKEN_NAME name (token_name); if (collection_.find (name, proxy->token_) == -1) // We did not find one in the collection. { // Make one. proxy->token_ = proxy->create_token (token_name); // Put it in the collection. if (collection_.bind (name, proxy->token_) == -1) { delete proxy->token_; proxy->token_ = 0; } } if (proxy->token_ != 0) proxy->token_->inc_reference (); // We may be returning proxy->token_ == 0 if new failed, caller must // check. } // 0. check_deadlock (TOKEN) // 1. if TOKEN->visited (), return 0. // 2. mark TOKEN visited. // 3. get ALL_OWNERS // 4. if CLIENT in ALL_OWNERS, return *DEADLOCK*. // 5. for each OWNER in ALL_OWNERS, // 6. if OWNER is not waiting for a NEW_TOKEN, continue. // 7. else, if check_deadlock (NEW_TOKEN) == 1, return *DEADLOCK* // 8. return 0. int ACE_Token_Manager::check_deadlock (ACE_Token_Proxy *proxy) { ACE_TRACE ("ACE_Token_Manager::check_deadlock"); // Start the recursive deadlock detection algorithm. int result = this->check_deadlock (proxy->token_, proxy); // Whether or not we detect deadlock, we have to unmark all tokens // for the next time. COLLECTION::ITERATOR iterator (collection_); for (COLLECTION::ENTRY *temp = 0; iterator.next (temp) != 0; iterator.advance ()) temp->int_id_->visit (0); return result; } int ACE_Token_Manager::check_deadlock (ACE_Tokens *token, ACE_Token_Proxy *proxy) { ACE_TRACE ("ACE_Token_Manager::check_deadlock"); if (token->visited ()) return 0; token->visit (1); ACE_Tokens::OWNER_STACK owners; int is_owner = token->owners (owners, proxy->client_id ()); switch (is_owner) { case -1: // Error. return -1; case 1: // The caller is an owner, so we have a deadlock situation. if (debug_) { ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Deadlock detected.\n"))); ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("%s owns %s and is waiting for %s.\n"), proxy->client_id (), token->name (), proxy->token_->name ())); } return 1; case 0: default: // Recurse on each owner. while (!owners.is_empty ()) { ACE_TPQ_Entry *e; owners.pop (e); // If the owner is waiting on another token, recurse. ACE_Tokens *twf = this->token_waiting_for (e->client_id ()); if ((twf != 0) && (this->check_deadlock (twf, proxy) == 1)) { if (debug_) { ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("%s owns %s and is waiting for %s.\n"), e->client_id (), token->name (), twf->name ())); } return 1; } // else, check the next owner. } // We've checked all the owners and found no deadlock. return 0; } } ACE_Tokens * ACE_Token_Manager::token_waiting_for (const ACE_TCHAR *client_id) { COLLECTION::ITERATOR iterator (collection_); for (COLLECTION::ENTRY *temp = 0; iterator.next (temp) != 0; iterator.advance ()) { if (temp->int_id_->is_waiting_for (client_id)) return temp->int_id_; } // nothing was found, return NULL. return 0; } // Notify the token manager that a token is has been released. If // as a result, there is no owner of the token, the token is // deleted. void ACE_Token_Manager::release_token (ACE_Tokens *&token) { ACE_TRACE ("ACE_Token_Manager::release_token"); // again, let's perform our own locking here. ACE_GUARD (ACE_TOKEN_CONST::MUTEX, ace_mon, this->lock_); if (token->dec_reference () == 0) { // No one has the token, so we can delete it and remove it from // our collection. First, let's get it from the collection. TOKEN_NAME token_name (token->name ()); ACE_Tokens *temp; if (collection_.unbind (token_name, temp) == -1) // we did not find one in the collection { errno = ENOENT; ACELIB_ERROR ((LM_ERROR, ACE_TEXT ("Token Manager could not release %s:%d\n"), token->name (), token->type ())); // @@ bad } else // we found it { // sanity pointer comparison. The token referenced by the // proxy better be the one we found in the list. ACE_ASSERT (token == temp); delete token; // or delete temp // we set their token to zero. if the calling proxy is // still going to be used, it had better check it's token // value before calling a method on it! token = 0; } } // else // someone is still interested in the token, so keep it around. } void ACE_Token_Manager::dump (void) const { #if defined (ACE_HAS_DUMP) ACE_TRACE ("ACE_Token_Manager::dump"); ACELIB_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("ACE_Token_Manager::dump:\n"))); ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("lock_\n"))); lock_.dump (); ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("collection_\n"))); collection_.dump (); ACELIB_DEBUG ((LM_DEBUG, ACE_END_DUMP)); #endif /* ACE_HAS_DUMP */ } ACE_END_VERSIONED_NAMESPACE_DECL #endif /* ACE_HAS_TOKENS_LIBRARY */