3 #if !defined (__ACE_INLINE__)
4 # include "ace/Token.inl"
5 #endif /* __ACE_INLINE__ */
7 #if defined (ACE_HAS_THREADS)
9 #include "ace/Thread.h"
10 #include "ace/Log_Category.h"
12 #if defined (ACE_TOKEN_DEBUGGING)
13 // FUZZ: disable check_for_streams_include
14 #include "ace/streams.h"
15 #endif /* ACE_TOKEN_DEBUGGING */
17 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
19 ACE_ALLOC_HOOK_DEFINE(ACE_Token
)
22 ACE_Token::dump () const
24 #if defined (ACE_HAS_DUMP)
25 ACE_TRACE ("ACE_Token::dump");
27 ACELIB_DEBUG ((LM_DEBUG
, ACE_BEGIN_DUMP
, this));
29 ACELIB_DEBUG ((LM_DEBUG
, ACE_TEXT ("\nthread = %d"), ACE_Thread::self ()));
30 // @@ Is there a portable way to do this?
31 // ACELIB_DEBUG ((LM_DEBUG, "\nowner_ = %d", (long) this->owner_));
32 ACELIB_DEBUG ((LM_DEBUG
, ACE_TEXT ("\nowner_ addr = %x"), &this->owner_
));
33 ACELIB_DEBUG ((LM_DEBUG
, ACE_TEXT ("\nwaiters_ = %d"), this->waiters_
));
34 ACELIB_DEBUG ((LM_DEBUG
, ACE_TEXT ("\nin_use_ = %d"), this->in_use_
));
35 ACELIB_DEBUG ((LM_DEBUG
, ACE_TEXT ("\nnesting level = %d"), this->nesting_level_
));
36 ACELIB_DEBUG ((LM_DEBUG
, ACE_END_DUMP
));
37 #endif /* ACE_HAS_DUMP */
40 ACE_Token::ACE_Token_Queue_Entry::ACE_Token_Queue_Entry (ACE_Thread_Mutex
&m
,
44 #if defined (ACE_TOKEN_USES_SEMAPHORE)
48 #endif /* ACE_TOKEN_USES_SEMAPHORE */
51 #if defined (ACE_TOKEN_USES_SEMAPHORE)
53 #endif /* ACE_TOKEN_USES_SEMAPHORE */
55 ACE_TRACE ("ACE_Token::ACE_Token_Queue_Entry::ACE_Token_Queue_Entry");
58 ACE_Token::ACE_Token_Queue_Entry::ACE_Token_Queue_Entry (ACE_Thread_Mutex
&m
,
60 ACE_Condition_Attributes
&attributes
)
63 #if defined (ACE_TOKEN_USES_SEMAPHORE)
67 #endif /* ACE_TOKEN_USES_SEMAPHORE */
70 #if defined (ACE_TOKEN_USES_SEMAPHORE)
72 ACE_UNUSED_ARG (attributes
);
73 #endif /* ACE_TOKEN_USES_SEMAPHORE */
75 ACE_TRACE ("ACE_Token::ACE_Token_Queue_Entry::ACE_Token_Queue_Entry");
78 ACE_Token::ACE_Token_Queue::ACE_Token_Queue ()
82 ACE_TRACE ("ACE_Token::ACE_Token_Queue::ACE_Token_Queue");
86 // Remove an entry from the list. Must be called with locks held.
89 ACE_Token::ACE_Token_Queue::remove_entry (ACE_Token::ACE_Token_Queue_Entry
*entry
)
91 ACE_TRACE ("ACE_Token::ACE_Token_Queue::remove_entry");
92 ACE_Token_Queue_Entry
*curr
= 0;
93 ACE_Token_Queue_Entry
*prev
= 0;
98 for (curr
= this->head_
;
99 curr
!= 0 && curr
!= entry
;
104 // Didn't find the entry...
107 // Delete at the head.
108 this->head_
= this->head_
->next_
;
110 // Delete in the middle.
111 prev
->next_
= curr
->next_
;
113 // We need to update the tail of the list if we've deleted the last
115 if (curr
->next_
== 0)
120 // Add an entry into the list. Must be called with locks held.
123 ACE_Token::ACE_Token_Queue::insert_entry (ACE_Token::ACE_Token_Queue_Entry
&entry
,
124 int requeue_position
)
126 if (this->head_
== 0)
128 // No other threads - just add me
129 this->head_
= &entry
;
130 this->tail_
= &entry
;
132 else if (requeue_position
== -1)
134 // Insert at the end of the queue.
135 this->tail_
->next_
= &entry
;
136 this->tail_
= &entry
;
138 else if (requeue_position
== 0)
140 // Insert at head of queue.
141 entry
.next_
= this->head_
;
142 this->head_
= &entry
;
145 // Insert in the middle of the queue somewhere.
147 // Determine where our thread should go in the queue of waiters.
149 ACE_Token::ACE_Token_Queue_Entry
*insert_after
= this->head_
;
150 while (requeue_position
-- && insert_after
->next_
!= 0)
151 insert_after
= insert_after
->next_
;
153 entry
.next_
= insert_after
->next_
;
155 if (entry
.next_
== 0)
156 this->tail_
= &entry
;
158 insert_after
->next_
= &entry
;
162 ACE_Token::ACE_Token (const ACE_TCHAR
*name
, void *any
)
163 : lock_ (name
, (ACE_mutexattr_t
*) any
),
164 owner_ (ACE_OS::NULL_thread
),
168 attributes_ (USYNC_THREAD
),
169 queueing_strategy_ (FIFO
)
171 // ACE_TRACE ("ACE_Token::ACE_Token");
174 ACE_Token::~ACE_Token ()
176 ACE_TRACE ("ACE_Token::~ACE_Token");
180 ACE_Token::shared_acquire (void (*sleep_hook_func
)(void *),
182 ACE_Time_Value
*timeout
,
183 ACE_Token_Op_Type op_type
)
185 ACE_TRACE ("ACE_Token::shared_acquire");
186 ACE_GUARD_RETURN (ACE_Thread_Mutex
, ace_mon
, this->lock_
, -1);
188 #if defined (ACE_TOKEN_DEBUGGING)
190 #endif /* ACE_TOKEN_DEBUGGING */
192 ACE_thread_t
const thr_id
= ACE_Thread::self ();
194 // Nobody holds the token.
198 this->in_use_
= op_type
;
199 this->owner_
= thr_id
;
203 // Someone already holds the token.
205 // Check if it is us.
206 if (ACE_OS::thr_equal (thr_id
, this->owner_
))
208 ++this->nesting_level_
;
212 // Do a quick check for "polling" behavior.
213 if (timeout
!= 0 && *timeout
== ACE_Time_Value::zero
)
220 // We've got to sleep until we get the token.
223 // Which queue we should end up in...
224 ACE_Token_Queue
*queue
= (op_type
== ACE_Token::READ_TOKEN
228 // Allocate queue entry on stack. This works since we don't exit
229 // this method's activation record until we've got the token.
230 ACE_Token::ACE_Token_Queue_Entry
my_entry (this->lock_
,
233 queue
->insert_entry (my_entry
, this->queueing_strategy_
);
236 // Execute appropriate <sleep_hook> callback. (@@ should these
237 // methods return a success/failure status, and if so, what should
242 (*sleep_hook_func
) (arg
);
247 // Execute virtual method.
252 bool timed_out
= false;
255 // Sleep until we've got the token (ignore signals).
258 int const result
= my_entry
.wait (timeout
, this->lock_
);
262 // Note, this should obey whatever thread-specific interrupt
263 // policy is currently in place...
267 #if defined (ACE_TOKEN_DEBUGGING)
268 cerr
<< '(' << ACE_Thread::self () << ')'
270 << (errno
== ETIME
? "timed out" : "error occurred")
272 #endif /* ACE_TOKEN_DEBUGGING */
274 // We come here if a timeout occurs or some serious
275 // ACE_Condition object error.
285 while (!ACE_OS::thr_equal (thr_id
, this->owner_
));
287 // Do this always and irrespective of the result of wait().
289 queue
->remove_entry (&my_entry
);
291 #if defined (ACE_TOKEN_DEBUGGING)
292 ACELIB_DEBUG ((LM_DEBUG
, "(%t) ACE_Token::shared_acquire (UNBLOCKED)\n"));
293 #endif /* ACE_TOKEN_DEBUGGING */
295 // If timeout occurred
298 // This thread was still selected to own the token.
299 if (my_entry
.runable_
)
301 // Wakeup next waiter since this thread timed out.
302 this->wakeup_next_waiter ();
314 // If this is a normal wakeup, this thread should be runnable.
315 ACE_ASSERT (my_entry
.runable_
);
320 // By default this is a no-op.
324 ACE_Token::sleep_hook ()
326 ACE_TRACE ("ACE_Token::sleep_hook");
330 ACE_Token::acquire (ACE_Time_Value
*timeout
)
332 ACE_TRACE ("ACE_Token::acquire");
333 return this->shared_acquire (0, 0, timeout
, ACE_Token::WRITE_TOKEN
);
336 // Acquire the token, sleeping until it is obtained or until <timeout>
340 ACE_Token::acquire (void (*sleep_hook_func
)(void *),
342 ACE_Time_Value
*timeout
)
344 ACE_TRACE ("ACE_Token::acquire");
345 return this->shared_acquire (sleep_hook_func
, arg
, timeout
, ACE_Token::WRITE_TOKEN
);
348 // Try to renew the token.
351 ACE_Token::renew (int requeue_position
,
352 ACE_Time_Value
*timeout
)
354 ACE_TRACE ("ACE_Token::renew");
355 ACE_GUARD_RETURN (ACE_Thread_Mutex
, ace_mon
, this->lock_
, -1);
357 #if defined (ACE_TOKEN_DEBUGGING)
359 #endif /* ACE_TOKEN_DEBUGGING */
360 // ACE_ASSERT (ACE_OS::thr_equal (ACE_Thread::self (), this->owner_));
362 // Check to see if there are any waiters worth giving up the lock
365 // If no writers and either we are a writer or there are no readers.
366 if (this->writers_
.head_
== 0 &&
367 (this->in_use_
== ACE_Token::WRITE_TOKEN
||
368 this->readers_
.head_
== 0))
372 // We've got to sleep until we get the token again.
374 // Determine which queue should this thread go to.
375 ACE_Token::ACE_Token_Queue
*this_threads_queue
=
376 this->in_use_
== ACE_Token::READ_TOKEN
?
377 &this->readers_
: &this->writers_
;
379 ACE_Token::ACE_Token_Queue_Entry
my_entry (this->lock_
,
382 this_threads_queue
->insert_entry (my_entry
,
383 // if requeue_position == 0 then we want to go next,
384 // otherwise use the queueing strategy, which might also
386 requeue_position
== 0 ? 0 : this->queueing_strategy_
);
389 // Remember nesting level...
390 int const save_nesting_level_
= this->nesting_level_
;
392 // Reset state for new owner.
393 this->nesting_level_
= 0;
396 this->wakeup_next_waiter ();
398 bool timed_out
= false;
401 // Sleep until we've got the token (ignore signals).
404 int const result
= my_entry
.wait (timeout
, this->lock_
);
408 // Note, this should obey whatever thread-specific interrupt
409 // policy is currently in place...
413 #if defined (ACE_TOKEN_DEBUGGING)
414 cerr
<< '(' << ACE_Thread::self () << ')'
416 << (errno
== ETIME
? "timed out" : "error occurred")
418 #endif /* ACE_TOKEN_DEBUGGING */
420 // We come here if a timeout occurs or some serious
421 // ACE_Condition object error.
431 while (!ACE_OS::thr_equal (my_entry
.thread_id_
, this->owner_
));
433 // Do this always and irrespective of the result of wait().
435 this_threads_queue
->remove_entry (&my_entry
);
437 #if defined (ACE_TOKEN_DEBUGGING)
438 ACELIB_DEBUG ((LM_DEBUG
, "(%t) ACE_Token::renew (UNBLOCKED)\n"));
439 #endif /* ACE_TOKEN_DEBUGGING */
441 // If timeout occurred
444 // This thread was still selected to own the token.
445 if (my_entry
.runable_
)
447 // Wakeup next waiter since this thread timed out.
448 this->wakeup_next_waiter ();
460 // If this is a normal wakeup, this thread should be runnable.
461 ACE_ASSERT (my_entry
.runable_
);
463 // Reinstate nesting level.
464 this->nesting_level_
= save_nesting_level_
;
469 // Release the current holder of the token (which had
470 // better be the caller's thread!).
473 ACE_Token::release ()
475 ACE_TRACE ("ACE_Token::release");
476 ACE_GUARD_RETURN (ACE_Thread_Mutex
, ace_mon
, this->lock_
, -1);
478 #if defined (ACE_TOKEN_DEBUGGING)
480 #endif /* ACE_TOKEN_DEBUGGING */
483 if (this->nesting_level_
> 0)
484 --this->nesting_level_
;
488 // Regular release...
492 this->wakeup_next_waiter ();
499 ACE_Token::wakeup_next_waiter ()
501 ACE_TRACE ("ACE_Token::wakeup_next_waiter");
503 // Reset state for new owner.
504 this->owner_
= ACE_OS::NULL_thread
;
508 if (this->writers_
.head_
== 0 &&
509 this->readers_
.head_
== 0)
511 // No more waiters...
515 // Wakeup next waiter.
516 ACE_Token_Queue
*queue
= 0;
518 // Writer threads get priority to run first.
519 if (this->writers_
.head_
!= 0)
521 this->in_use_
= ACE_Token::WRITE_TOKEN
;
522 queue
= &this->writers_
;
526 this->in_use_
= ACE_Token::READ_TOKEN
;
527 queue
= &this->readers_
;
530 // Wake up waiter and make it runable.
531 queue
->head_
->runable_
= 1;
532 queue
->head_
->signal ();
533 this->owner_
= queue
->head_
->thread_id_
;
536 ACE_END_VERSIONED_NAMESPACE_DECL
538 #endif /* ACE_HAS_THREADS */