/dep/ACE_wrappers/ace/Token.cpp
C++ | 545 lines | 353 code | 96 blank | 96 comment | 72 complexity | 15065961d9b424e0fcae8b357fc2a53a MD5 | raw file
- // $Id: Token.cpp 80826 2008-03-04 14:51:23Z wotte $
- #include "ace/Token.h"
- #if !defined (__ACE_INLINE__)
- # include "ace/Token.inl"
- #endif /* __ACE_INLINE__ */
- ACE_RCSID(ace, Token, "$Id: Token.cpp 80826 2008-03-04 14:51:23Z wotte $")
- #if defined (ACE_HAS_THREADS)
- #include "ace/Thread.h"
- #include "ace/Log_Msg.h"
- #if defined (ACE_TOKEN_DEBUGGING)
- // FUZZ: disable check_for_streams_include
- #include "ace/streams.h"
- #endif /* ACE_TOKEN_DEBUGGING */
- ACE_BEGIN_VERSIONED_NAMESPACE_DECL
- ACE_ALLOC_HOOK_DEFINE(ACE_Token)
- void
- ACE_Token::dump (void) const
- {
- #if defined (ACE_HAS_DUMP)
- ACE_TRACE ("ACE_Token::dump");
- ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this));
- ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nthread = %d"), ACE_Thread::self ()));
- // @@ Is there a portable way to do this?
- // ACE_DEBUG ((LM_DEBUG, "\nowner_ = %d", (long) this->owner_));
- ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nowner_ addr = %x"), &this->owner_));
- ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nwaiters_ = %d"), this->waiters_));
- ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nin_use_ = %d"), this->in_use_));
- ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nnesting level = %d"), this->nesting_level_));
- ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP));
- #endif /* ACE_HAS_DUMP */
- }
- ACE_Token::ACE_Token_Queue_Entry::ACE_Token_Queue_Entry (ACE_Thread_Mutex &m,
- ACE_thread_t t_id)
- : next_ (0),
- thread_id_ (t_id),
- #if defined (ACE_TOKEN_USES_SEMAPHORE)
- cv_ (0),
- #else
- cv_ (m),
- #endif /* ACE_TOKEN_USES_SEMAPHORE */
- runable_ (0)
- {
- #if defined (ACE_TOKEN_USES_SEMAPHORE)
- ACE_UNUSED_ARG (m);
- #endif /* ACE_TOKEN_USES_SEMAPHORE */
- ACE_TRACE ("ACE_Token::ACE_Token_Queue_Entry::ACE_Token_Queue_Entry");
- }
- ACE_Token::ACE_Token_Queue_Entry::ACE_Token_Queue_Entry (ACE_Thread_Mutex &m,
- ACE_thread_t t_id,
- ACE_Condition_Attributes &attributes)
- : next_ (0),
- thread_id_ (t_id),
- #if defined (ACE_TOKEN_USES_SEMAPHORE)
- cv_ (0),
- #else
- cv_ (m, attributes),
- #endif /* ACE_TOKEN_USES_SEMAPHORE */
- runable_ (0)
- {
- #if defined (ACE_TOKEN_USES_SEMAPHORE)
- ACE_UNUSED_ARG (m);
- ACE_UNUSED_ARG (attributes);
- #endif /* ACE_TOKEN_USES_SEMAPHORE */
- ACE_TRACE ("ACE_Token::ACE_Token_Queue_Entry::ACE_Token_Queue_Entry");
- }
- ACE_Token::ACE_Token_Queue::ACE_Token_Queue (void)
- : head_ (0),
- tail_ (0)
- {
- ACE_TRACE ("ACE_Token::ACE_Token_Queue::ACE_Token_Queue");
- }
- //
- // Remove an entry from the list. Must be called with locks held.
- //
- void
- ACE_Token::ACE_Token_Queue::remove_entry (ACE_Token::ACE_Token_Queue_Entry *entry)
- {
- ACE_TRACE ("ACE_Token::ACE_Token_Queue::remove_entry");
- ACE_Token_Queue_Entry *curr = 0;
- ACE_Token_Queue_Entry *prev = 0;
- if (this->head_ == 0)
- return;
- for (curr = this->head_;
- curr != 0 && curr != entry;
- curr = curr->next_)
- prev = curr;
- if (curr == 0)
- // Didn't find the entry...
- return;
- else if (prev == 0)
- // Delete at the head.
- this->head_ = this->head_->next_;
- else
- // Delete in the middle.
- prev->next_ = curr->next_;
- // We need to update the tail of the list if we've deleted the last
- // entry.
- if (curr->next_ == 0)
- this->tail_ = prev;
- }
- //
- // Add an entry into the list. Must be called with locks held.
- //
- void
- ACE_Token::ACE_Token_Queue::insert_entry (ACE_Token::ACE_Token_Queue_Entry &entry,
- int requeue_position)
- {
- if (this->head_ == 0)
- {
- // No other threads - just add me
- this->head_ = &entry;
- this->tail_ = &entry;
- }
- else if (requeue_position == -1)
- {
- // Insert at the end of the queue.
- this->tail_->next_ = &entry;
- this->tail_ = &entry;
- }
- else if (requeue_position == 0)
- {
- // Insert at head of queue.
- entry.next_ = this->head_;
- this->head_ = &entry;
- }
- else
- // Insert in the middle of the queue somewhere.
- {
- // Determine where our thread should go in the queue of waiters.
- ACE_Token::ACE_Token_Queue_Entry *insert_after = this->head_;
- while (requeue_position-- && insert_after->next_ != 0)
- insert_after = insert_after->next_;
- entry.next_ = insert_after->next_;
- if (entry.next_ == 0)
- this->tail_ = &entry;
- insert_after->next_ = &entry;
- }
- }
- ACE_Token::ACE_Token (const ACE_TCHAR *name, void *any)
- : lock_ (name, (ACE_mutexattr_t *) any),
- owner_ (ACE_OS::NULL_thread),
- in_use_ (0),
- waiters_ (0),
- nesting_level_ (0),
- attributes_ (USYNC_THREAD),
- queueing_strategy_ (FIFO)
- {
- // ACE_TRACE ("ACE_Token::ACE_Token");
- }
- ACE_Token::~ACE_Token (void)
- {
- ACE_TRACE ("ACE_Token::~ACE_Token");
- }
- int
- ACE_Token::shared_acquire (void (*sleep_hook_func)(void *),
- void *arg,
- ACE_Time_Value *timeout,
- ACE_Token_Op_Type op_type)
- {
- ACE_TRACE ("ACE_Token::shared_acquire");
- ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1);
- #if defined (ACE_TOKEN_DEBUGGING)
- this->dump ();
- #endif /* ACE_TOKEN_DEBUGGING */
- ACE_thread_t const thr_id = ACE_Thread::self ();
- // Nobody holds the token.
- if (!this->in_use_)
- {
- // Its mine!
- this->in_use_ = op_type;
- this->owner_ = thr_id;
- return 0;
- }
- //
- // Someone already holds the token.
- //
- // Check if it is us.
- if (ACE_OS::thr_equal (thr_id, this->owner_))
- {
- ++this->nesting_level_;
- return 0;
- }
- // Do a quick check for "polling" behavior.
- if (timeout != 0 && timeout->sec () == 0 && timeout->usec () == 0)
- {
- errno = ETIME;
- return -1;
- }
- //
- // We've got to sleep until we get the token.
- //
- // Which queue we should end up in...
- ACE_Token_Queue *queue = (op_type == ACE_Token::READ_TOKEN
- ? &this->readers_
- : &this->writers_);
- // Allocate queue entry on stack. This works since we don't exit
- // this method's activation record until we've got the token.
- ACE_Token::ACE_Token_Queue_Entry my_entry (this->lock_,
- thr_id,
- this->attributes_);
- queue->insert_entry (my_entry, this->queueing_strategy_);
- ++this->waiters_;
- // Execute appropriate <sleep_hook> callback. (@@ should these
- // methods return a success/failure status, and if so, what should
- // we do with it?)
- int ret = 0;
- if (sleep_hook_func)
- {
- (*sleep_hook_func) (arg);
- ++ret;
- }
- else
- {
- // Execute virtual method.
- this->sleep_hook ();
- ++ret;
- }
- bool timed_out = false;
- bool error = false;
- // Sleep until we've got the token (ignore signals).
- do
- {
- int const result = my_entry.wait (timeout, this->lock_);
- if (result == -1)
- {
- // Note, this should obey whatever thread-specific interrupt
- // policy is currently in place...
- if (errno == EINTR)
- continue;
- #if defined (ACE_TOKEN_DEBUGGING)
- cerr << '(' << ACE_Thread::self () << ')'
- << " acquire: "
- << (errno == ETIME ? "timed out" : "error occurred")
- << endl;
- #endif /* ACE_TOKEN_DEBUGGING */
- // We come here if a timeout occurs or some serious
- // ACE_Condition object error.
- if (errno == ETIME)
- timed_out = true;
- else
- error = true;
- // Stop the loop.
- break;
- }
- }
- while (!ACE_OS::thr_equal (thr_id, this->owner_));
- // Do this always and irrespective of the result of wait().
- --this->waiters_;
- queue->remove_entry (&my_entry);
- #if defined (ACE_TOKEN_DEBUGGING)
- ACE_DEBUG ((LM_DEBUG, "(%t) ACE_Token::shared_acquire (UNBLOCKED)\n"));
- #endif /* ACE_TOKEN_DEBUGGING */
- // If timeout occured
- if (timed_out)
- {
- // This thread was still selected to own the token.
- if (my_entry.runable_)
- {
- // Wakeup next waiter since this thread timed out.
- this->wakeup_next_waiter ();
- }
- // Return error.
- return -1;
- }
- else if (error)
- {
- // Return error.
- return -1;
- }
- // If this is a normal wakeup, this thread should be runnable.
- ACE_ASSERT (my_entry.runable_);
- return ret;
- }
- // By default this is a no-op.
- /* virtual */
- void
- ACE_Token::sleep_hook (void)
- {
- ACE_TRACE ("ACE_Token::sleep_hook");
- }
- int
- ACE_Token::acquire (ACE_Time_Value *timeout)
- {
- ACE_TRACE ("ACE_Token::acquire");
- return this->shared_acquire (0, 0, timeout, ACE_Token::WRITE_TOKEN);
- }
- // Acquire the token, sleeping until it is obtained or until <timeout>
- // expires.
- int
- ACE_Token::acquire (void (*sleep_hook_func)(void *),
- void *arg,
- ACE_Time_Value *timeout)
- {
- ACE_TRACE ("ACE_Token::acquire");
- return this->shared_acquire (sleep_hook_func, arg, timeout, ACE_Token::WRITE_TOKEN);
- }
- // Try to renew the token.
- int
- ACE_Token::renew (int requeue_position,
- ACE_Time_Value *timeout)
- {
- ACE_TRACE ("ACE_Token::renew");
- ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1);
- #if defined (ACE_TOKEN_DEBUGGING)
- this->dump ();
- #endif /* ACE_TOKEN_DEBUGGING */
- // ACE_ASSERT (ACE_OS::thr_equal (ACE_Thread::self (), this->owner_));
- // Check to see if there are any waiters worth giving up the lock
- // for.
- // If no writers and either we are a writer or there are no readers.
- if (this->writers_.head_ == 0 &&
- (this->in_use_ == ACE_Token::WRITE_TOKEN ||
- this->readers_.head_ == 0))
- // Immediate return.
- return 0;
- // We've got to sleep until we get the token again.
- // Determine which queue should this thread go to.
- ACE_Token::ACE_Token_Queue *this_threads_queue =
- this->in_use_ == ACE_Token::READ_TOKEN ?
- &this->readers_ : &this->writers_;
- ACE_Token::ACE_Token_Queue_Entry my_entry (this->lock_,
- this->owner_);
- this_threads_queue->insert_entry (my_entry,
- // if requeue_position == 0 then we want to go next,
- // otherwise use the queueing strategy, which might also
- // happen to be 0.
- requeue_position == 0 ? 0 : this->queueing_strategy_);
- ++this->waiters_;
- // Remember nesting level...
- int const save_nesting_level_ = this->nesting_level_;
- // Reset state for new owner.
- this->nesting_level_ = 0;
- // Wakeup waiter.
- this->wakeup_next_waiter ();
- bool timed_out = false;
- bool error = false;
- // Sleep until we've got the token (ignore signals).
- do
- {
- int const result = my_entry.wait (timeout, this->lock_);
- if (result == -1)
- {
- // Note, this should obey whatever thread-specific interrupt
- // policy is currently in place...
- if (errno == EINTR)
- continue;
- #if defined (ACE_TOKEN_DEBUGGING)
- cerr << '(' << ACE_Thread::self () << ')'
- << " renew: "
- << (errno == ETIME ? "timed out" : "error occurred")
- << endl;
- #endif /* ACE_TOKEN_DEBUGGING */
- // We come here if a timeout occurs or some serious
- // ACE_Condition object error.
- if (errno == ETIME)
- timed_out = true;
- else
- error = true;
- // Stop the loop.
- break;
- }
- }
- while (!ACE_OS::thr_equal (my_entry.thread_id_, this->owner_));
- // Do this always and irrespective of the result of wait().
- --this->waiters_;
- this_threads_queue->remove_entry (&my_entry);
- #if defined (ACE_TOKEN_DEBUGGING)
- ACE_DEBUG ((LM_DEBUG, "(%t) ACE_Token::renew (UNBLOCKED)\n"));
- #endif /* ACE_TOKEN_DEBUGGING */
- // If timeout occured
- if (timed_out)
- {
- // This thread was still selected to own the token.
- if (my_entry.runable_)
- {
- // Wakeup next waiter since this thread timed out.
- this->wakeup_next_waiter ();
- }
- // Return error.
- return -1;
- }
- else if (error)
- {
- // Return error.
- return -1;
- }
- // If this is a normal wakeup, this thread should be runnable.
- ACE_ASSERT (my_entry.runable_);
- // Reinstate nesting level.
- this->nesting_level_ = save_nesting_level_;
- return 0;
- }
- // Release the current holder of the token (which had
- // better be the caller's thread!).
- int
- ACE_Token::release (void)
- {
- ACE_TRACE ("ACE_Token::release");
- ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1);
- #if defined (ACE_TOKEN_DEBUGGING)
- this->dump ();
- #endif /* ACE_TOKEN_DEBUGGING */
- // Nested release...
- if (this->nesting_level_ > 0)
- --this->nesting_level_;
- else
- {
- //
- // Regular release...
- //
- // Wakeup waiter.
- this->wakeup_next_waiter ();
- }
- return 0;
- }
- void
- ACE_Token::wakeup_next_waiter (void)
- {
- ACE_TRACE ("ACE_Token::wakeup_next_waiter");
- // Reset state for new owner.
- this->owner_ = ACE_OS::NULL_thread;
- this->in_use_ = 0;
- // Any waiters...
- if (this->writers_.head_ == 0 &&
- this->readers_.head_ == 0)
- {
- // No more waiters...
- return;
- }
- // Wakeup next waiter.
- ACE_Token_Queue *queue = 0;
- // Writer threads get priority to run first.
- if (this->writers_.head_ != 0)
- {
- this->in_use_ = ACE_Token::WRITE_TOKEN;
- queue = &this->writers_;
- }
- else
- {
- this->in_use_ = ACE_Token::READ_TOKEN;
- queue = &this->readers_;
- }
- // Wake up waiter and make it runable.
- queue->head_->runable_ = 1;
- queue->head_->signal ();
- this->owner_ = queue->head_->thread_id_;
- }
- ACE_END_VERSIONED_NAMESPACE_DECL
- #endif /* ACE_HAS_THREADS */