00001 //////////////////////////////////////////////////////////////////////////////////// 00002 /*! \file ReplyQueue.h 00003 * \brief Declares and defines template classes for synchronization of network comm. 00004 * \author $Author: rsharo $ 00005 * \version $Revision: 1.1 $ 00006 * \date $Date: 2004/04/07 13:45:04 $ 00007 */////////////////////////////////////////////////////////////////////////////////// 00008 #ifndef REPLY_QUEUE_H 00009 #define REPLY_QUEUE_H 00010 00011 #include "ace/Log_Msg.h" 00012 #include "ace/Synch_T.h" 00013 00014 #include <list> 00015 00016 // 00017 // Forward declaration of the PendingReply class. This forward declaration 00018 // is needed for the declaration of ReplyQueue. 00019 template <class T, ACE_SYNCH_DECL> 00020 class PendingReply; 00021 00022 // 00023 // This class encapsulates a synchronized queue through which solicited 00024 // data (replies) are passed between threads. 00025 // 00026 // Command threads follow this sequence of execution: 00027 // (1) Acquire the queue's mutex exactly one time. Mutex acquisition must 00028 // NOT be recursive. (VERY IMPORTANT). 00029 // (2) Schedule some sort of input from another thread 00030 // (3) Call GetReply(), which will block until error or the result is ready 00031 // (4) Release the queue's mutex 00032 // 00033 // Reply threads follow this sequence of execution: 00034 // (1) Generate data or read data from the network 00035 // (2) The thread may or may not acquire the queue's mutex. In this thread 00036 // acquisition may be recursive if the operating system supports it. 00037 // In any event, SetReply() will acquire the mutex as part of its 00038 // operation. 00039 // (3) Call SetReply() to pass the data to the command thread(s). 00040 // (4) Release the queue's mutex if the thread had previously acquired it. 00041 // 00042 // Multiple threads can wait in GetReply() and will receive replies 00043 // in FIFO order. This is possible because GetReply() temporarily 00044 // relinquishes the mutex as part of its operation. 00045 // 00046 // If the waiter times out before data is received, it is removed from 00047 // the queue. Note that this could be problematic if a reply arrives 00048 // after the deadline. Waiters should set a deadline only if they are 00049 // certain that missing a deadline means no reply will be coming. 00050 // 00051 // Any reply type for which the assignment operator is defined is allowed. 00052 // Reply types that don't support the assignment operator may be used 00053 // by specializing the Assign() method for your type. 00054 // 00055 // Examples of how to instantiate one of these queues: 00056 // 00057 // A synchronized queue with string reply types: 00058 // ReplyQueue<std::string, ACE_MT_SYNCH> foo; 00059 // 00060 // An unsynchronized queue with integer reply types: 00061 // ReplyQueue<int, ACE_NULL_SYNCH> foo; 00062 // 00063 // A queue with system-default synchronization and unspecified reply type: 00064 // ReplyQueue<void *, ACE_SYNCH> foo; 00065 template <class T, ACE_SYNCH_DECL> 00066 class ReplyQueue 00067 { 00068 public: 00069 // 00070 // Constructs a reply queue with the specified mutex. 00071 ReplyQueue(ACE_SYNCH_MUTEX_T &myMutex); 00072 00073 // 00074 // Destroys a reply queue. It calls Close() to ensure all waiters 00075 // are released. 00076 ~ReplyQueue(); 00077 00078 // 00079 // Closes the queue. The queue is cleared and all waiters are freed 00080 // with an error code. Any subsequent calls to GetReply() or SetReply() 00081 // will fail as well. 00082 // 00083 // If acquireMutex is false, then the method will not acquire its own 00084 // mutex. Do this only if the mutex is already externally acquired. 00085 // 00086 // Returns 0 on success or -1 if already closed. 00087 int Close(bool acquireMutex=true); 00088 00089 // 00090 // Closes the queue. The queue is cleared and all waiters are freed 00091 // with an error code. Any subsequent calls to GetReply() or SetReply() 00092 // will fail as well. 00093 // 00094 // If acquireMutex is false, then the method will not acquire its own 00095 // mutex. Do this only if the mutex is already externally acquired. 00096 // 00097 // Returns 0 on success or -1 if already closed. 00098 inline int Close_i(); 00099 00100 // 00101 // Indicates whether there are any threads currently waiting for a reply. 00102 // 00103 // If acquireMutex is false, then the method will not acquire its own 00104 // mutex. Do this only if the mutex is already externally acquired. 00105 // 00106 // Returns true if there are waiters, false otherwise. 00107 bool IsEmpty(bool acquireMutex=false); 00108 00109 // 00110 // Indicates whether there are any threads currently waiting for a reply. 00111 // 00112 // This method makes no attempt to acquire its own mutex. The mutex should 00113 // be externally acquired before calling this method. 00114 // 00115 // Returns true if there are waiters, false otherwise. 00116 inline bool IsEmpty_i(); 00117 00118 // 00119 // Blocks the calling thread until the deadline expires or 00120 // until a reply is sent received from the queue. 00121 // If a reply is enqueued before the deadline, its value 00122 // will be assigned to the "theReply" argument. 00123 // Note that the deadline argument is an absolute time -- not a relative 00124 // one. For example, if you want a 10 second timeout, you should compute 00125 // your deadline by calling ACE_OS::gettimeofday() and adding 10 seconds 00126 // to the result. 00127 // Passing a NULL pointer in the deadline argument means "wait forever". 00128 // 00129 // NOTE: You must acquire the queue's mutex exactly once when calling 00130 // this method. Acquiring the mutex more than once (i.e. recursively) 00131 // will result in a deadlock! 00132 // 00133 // Do not acquire the mutex externally before calling this method with 00134 // acquireMutex==true. 00135 // 00136 // If acquireMutex is false, then the method will not acquire its own 00137 // mutex. Do this only if the mutex is already externally acquired once. 00138 // 00139 // Returns 0 if the reply was successfully retrieved, -1 otherwise. 00140 int GetReply(T &theReply, const ACE_Time_Value *deadline, bool acquireMutex=false); 00141 00142 // 00143 // Blocks the calling thread until the deadline expires or 00144 // until a reply is sent received from the queue. 00145 // If a reply is enqueued before the deadline, its value 00146 // will be assigned to the "theReply" argument. 00147 // Note that the deadline argument is an absolute time -- not a relative 00148 // one. For example, if you want a 10 second timeout, you should compute 00149 // your deadline by calling ACE_OS::gettimeofday() and adding 10 seconds 00150 // to the result. 00151 // Passing a NULL pointer in the deadline argument means "wait forever". 00152 // 00153 // NOTE: You must acquire the queue's mutex exactly once before calling 00154 // this method. Acquiring the mutex more than once (i.e. recursively) 00155 // will result in a deadlock! If you don't acquire the mutex before 00156 // calling, the method will fail. 00157 // 00158 // This method makes no attempt to acquire its own mutex. The mutex should 00159 // be externally acquired once before calling this method. 00160 // 00161 // Returns 0 if the reply was successfully retrieved, -1 otherwise. 00162 inline int GetReply_i(T &theReply, const ACE_Time_Value *deadline); 00163 00164 // 00165 // Blocks the calling thread until a reply is sent received from the queue. 00166 // The reply is assigned to the "theReply" argument. 00167 // 00168 // NOTE: You must acquire the queue's mutex exactly once before calling 00169 // this method. Acquiring the mutex more than once (i.e. recursively) 00170 // will result in a deadlock! If you don't acquire the mutex before 00171 // calling, the method will fail. 00172 // 00173 // If acquireMutex is false, then the method will not acquire its own 00174 // mutex. Do this only if the mutex is already externally acquired. 00175 // 00176 // Returns 0 if the reply was successfully retrieved, -1 otherwise. 00177 int GetReply(T &theReply, bool acquireMutex=false); 00178 00179 00180 // 00181 // Blocks the calling thread until a reply is sent received from the queue. 00182 // The reply is assigned to the "theReply" argument. 00183 // 00184 // NOTE: You must acquire the queue's mutex exactly once before calling 00185 // this method. Acquiring the mutex more than once (i.e. recursively) 00186 // will result in a deadlock! If you don't acquire the mutex before 00187 // calling, the method will fail. 00188 // 00189 // This method makes no attempt to acquire its own mutex. The mutex should 00190 // be externally acquired before calling this method. 00191 // 00192 // Returns 0 if the reply was successfully retrieved, -1 otherwise. 00193 inline int GetReply_i(T &theReply); 00194 00195 00196 // 00197 // Sends a reply to the next waiter in GetReply(). 00198 // This method should only be called when a GetReply() call is pending. 00199 // It will free the next GetReply() waiter and give them the "theReply" 00200 // argument. 00201 // 00202 // If acquireMutex is false, then the method will not acquire its own 00203 // mutex. Do this only if the mutex is already externally acquired. 00204 // 00205 // Returns 0 on success and -1 on failure. 00206 int SetReply(T &theReply, bool acquireMutex=true); 00207 00208 // 00209 // Sends a reply to the next waiter in GetReply(). 00210 // This method should only be called when a GetReply() call is pending. 00211 // It will free the next GetReply() waiter and give them the "theReply" 00212 // argument. 00213 // 00214 // This method makes no attempt to acquire its own mutex. The mutex should 00215 // be externally acquired before calling this method. 00216 // 00217 // Returns 0 on success and -1 on failure. 00218 inline int SetReply_i(T &theReply); 00219 00220 // 00221 // Assigns the value of "from" to the object "to". 00222 // Returns a reference to "to". 00223 // This method exists solely to support partial template 00224 // specialization. 00225 inline T & Assign_i(T & from, T & to) { return to = from; } 00226 00227 // 00228 // Returns a reference to this queue's mutex. Use this method 00229 // if you wish to synchonize other objects with this queue. 00230 inline ACE_SYNCH_MUTEX_T & GetMutex() { return m_replyQueueMutex; } 00231 00232 protected: 00233 00234 // 00235 // The PendingReply type corresponding to this template 00236 // instantiation. 00237 typedef PendingReply<T, ACE_SYNCH_USE> PENDING_REPLY; 00238 // 00239 // The data type of the queue holding PendingReply's. 00240 typedef std::list<PENDING_REPLY *> QUEUE_TYPE; 00241 00242 // 00243 // The queue that holds PendingReply's. 00244 QUEUE_TYPE m_replyQueue; 00245 00246 // 00247 // The mutex type that synchronizes this queue. If the synch 00248 // type is ACE_NULL_SYNCH, then this mutex is a functionless 00249 // dummy object. 00250 ACE_SYNCH_MUTEX_T & m_replyQueueMutex; 00251 00252 // 00253 // A flag that indicates whether the queue is closed. 00254 bool m_closed; 00255 }; 00256 00257 00258 // 00259 // This class encapsulates a reply that is waiting in a ReplyQueue. 00260 template <class T, ACE_SYNCH_DECL> 00261 class PendingReply 00262 { 00263 public: 00264 // 00265 // The ReplyQueue type that holds this PendingReply type. 00266 typedef ReplyQueue<T, ACE_SYNCH_USE> REPLY_QUEUE; 00267 00268 // 00269 // The ReplyQueue, being a friend of this class, is allowed 00270 // to access this class's protected and private members. 00271 // We don't use the typedef in the friend declaration 00272 // because some (namely MSVC++) compilers can't handle it. 00273 friend class ReplyQueue<T, ACE_SYNCH_USE>; 00274 00275 // 00276 // Constructs a PendingReply. The "theReply" argument is a reference to 00277 // the object that will be set by PendingQueue::SetReply(). The "myMutex" 00278 // argument is a reference to the PendingQueue's mutex. 00279 PendingReply(T & theReply, ACE_SYNCH_MUTEX_T &myMutex) 00280 : m_condition(myMutex), m_isEnqueued(false), m_theReply(theReply) {} 00281 00282 // 00283 // Destroys a PendingReply. 00284 ~PendingReply() {} 00285 00286 protected: 00287 // 00288 // The condition variable used to signal PendingQueue::GetReply() 00289 // on success or failure. 00290 ACE_SYNCH_CONDITION_T m_condition; 00291 00292 // 00293 // Indicates whether this object is currently enqueued in the PendingQueue. 00294 // This is important when determining the validity of PendingQueue's 00295 // internal iterators. 00296 bool m_isEnqueued; 00297 00298 // 00299 // A reference to the object that will be set by a successful call to 00300 // PendingQueue::SetReply(). The object is provided by the caller of 00301 // PendingQueue::GetReply(). 00302 T & m_theReply; 00303 }; 00304 00305 00306 00307 00308 00309 template <class T, ACE_SYNCH_DECL> 00310 ReplyQueue<T, ACE_SYNCH_USE>::ReplyQueue(ACE_SYNCH_MUTEX_T &myMutex) 00311 : m_replyQueueMutex(myMutex), 00312 m_closed(false) 00313 { 00314 } 00315 00316 00317 template <class T, ACE_SYNCH_DECL> 00318 ReplyQueue<T, ACE_SYNCH_USE>::~ReplyQueue() 00319 { 00320 Close_i(); 00321 } 00322 00323 00324 00325 template <class T, ACE_SYNCH_DECL> 00326 int ReplyQueue<T, ACE_SYNCH_USE>::Close(bool acquireMutex) 00327 { 00328 if (acquireMutex) 00329 { 00330 ACE_Guard<ACE_SYNCH_MUTEX_T> g(m_replyQueueMutex); 00331 00332 return Close_i(); 00333 } 00334 00335 return Close_i(); 00336 } 00337 00338 00339 00340 template <class T, ACE_SYNCH_DECL> 00341 int ReplyQueue<T, ACE_SYNCH_USE>::Close_i() 00342 { 00343 // 00344 // Double-check idiom. Ensure that the queue wasn't closed 00345 // while we were waiting to acquire the mutex. 00346 if (m_closed) 00347 return -1; 00348 00349 // 00350 // Mark the queue as closed. 00351 m_closed = true; 00352 00353 // 00354 // Iterate through the queue and release all the 00355 // waiters. They'll see the queue is closed and return an error. 00356 typename QUEUE_TYPE::iterator i = m_replyQueue.begin(), 00357 end = m_replyQueue.end(); 00358 00359 // 00360 // A place to hold the results of system calls. 00361 int status = 0; 00362 00363 // 00364 // Mark all the PendingReplies as no longer enqueued and signal 00365 // all the waiters. The "status" variable collects any errors. 00366 for (; i != end; ++i) 00367 { 00368 (*i)->m_isEnqueued = false; 00369 status |= (*i)->m_condition.broadcast(); 00370 } 00371 00372 // 00373 // Clear out the queue. Its contents are now invalid. 00374 m_replyQueue.clear(); 00375 00376 // 00377 // Return 0 on success or -1 if signaling any waiters failed. 00378 return status; 00379 } 00380 00381 00382 00383 template <class T, ACE_SYNCH_DECL> 00384 bool ReplyQueue<T, ACE_SYNCH_USE>::IsEmpty(bool acquireMutex) 00385 { 00386 if (acquireMutex) 00387 { 00388 // 00389 // Don't bother acquiring the mutex if the queue is already closed. Just 00390 // report that it's empty (which it must be). 00391 if (m_closed) 00392 return true; 00393 00394 ACE_Guard<ACE_SYNCH_MUTEX_T> g(m_replyQueueMutex); 00395 00396 return IsEmpty_i(); 00397 } 00398 00399 return this->IsEmpty_i(); 00400 return false; 00401 } 00402 00403 00404 00405 template <class T, ACE_SYNCH_DECL> 00406 bool ReplyQueue<T, ACE_SYNCH_USE>::IsEmpty_i() 00407 { 00408 return m_replyQueue.empty(); 00409 } 00410 00411 00412 00413 template <class T, ACE_SYNCH_DECL> 00414 int ReplyQueue<T, ACE_SYNCH_USE>::GetReply(T &theReply, const ACE_Time_Value *deadline, bool acquireMutex) 00415 { 00416 if (acquireMutex) 00417 { 00418 // 00419 // Don't bother acquiring the mutex if the queue is already closed. Just 00420 // report failure. 00421 if (m_closed) 00422 return -1; 00423 00424 ACE_Guard<ACE_SYNCH_MUTEX_T> g(m_replyQueueMutex); 00425 00426 return GetReply_i(theReply, deadline); 00427 } 00428 00429 return GetReply_i(theReply, deadline); 00430 } 00431 00432 00433 00434 template <class T, ACE_SYNCH_DECL> 00435 int ReplyQueue<T, ACE_SYNCH_USE>::GetReply_i(T &theReply, const ACE_Time_Value *deadline) 00436 { 00437 // 00438 // Check to make sure the queue isn't closed already. 00439 if (m_closed) 00440 { 00441 ACE_ERROR_RETURN((LM_ERROR, 00442 "(%N:%l) Attempt to get a reply from a closed queue.\n"), 00443 -1); 00444 } 00445 00446 // 00447 // Construct a PendingReply so we can put it in the queue. 00448 // Do this before acquiring the mutex so we don't make other threads 00449 // wait through construction. 00450 PENDING_REPLY replyObj(theReply, m_replyQueueMutex); 00451 00452 // 00453 // Mark the PendingReply as residing on the queue, and then add it to 00454 // the queue. 00455 replyObj.m_isEnqueued = true; 00456 typename QUEUE_TYPE::iterator it = m_replyQueue.insert(m_replyQueue.end(), &replyObj); 00457 00458 // 00459 // Wait until signaled by a SetReply() call or until the deadline passes. 00460 // Note that m_replyQueueMutex is relinquished during the time we are 00461 // waiting. Its re-acquired on return from wait(). 00462 int status = replyObj.m_condition.wait(deadline); 00463 00464 // 00465 // If m_isEnqueued is still set, then remove the object from the queue 00466 // now. Since the queue is an STL list, the previously-recorded iterator 00467 // is still valid. 00468 if (replyObj.m_isEnqueued) 00469 { 00470 m_replyQueue.erase(it); 00471 } 00472 00473 // 00474 // If wait() failed, then this whole call failed. 00475 // Check with the ACE logger to see what the last error was. 00476 // It the failure was something other than a timeout, then log the 00477 // error. Timeout is a failure, but it isn't really an "error". 00478 if (status == -1 && ACE_Log_Msg::last_error_adapter() != ETIME) 00479 { 00480 ACE_ERROR((LM_ERROR, "(%N:%l) %p\n", "acquire")); 00481 } 00482 // 00483 // If the queue is closed at this point then we didn't get a value. Log a 00484 // warning to aid in debugging and return -1. 00485 else if (m_closed) 00486 { 00487 status = -1; 00488 ACE_ERROR((LM_WARNING, 00489 "(%N:%l) GetReply() failed because reply queue was closed before a response was set.\n")); 00490 } 00491 00492 return status; 00493 } 00494 00495 00496 00497 template <class T, ACE_SYNCH_DECL> 00498 int ReplyQueue<T, ACE_SYNCH_USE>::GetReply(T &theReply, bool acquireMutex) 00499 { 00500 if (acquireMutex) 00501 { 00502 // 00503 // Don't bother acquiring the mutex if the queue is already closed. Just 00504 // report failure. 00505 if (m_closed) 00506 return -1; 00507 00508 ACE_Guard<ACE_SYNCH_MUTEX_T> g(m_replyQueueMutex); 00509 00510 return GetReply_i(theReply); 00511 } 00512 00513 return GetReply_i(theReply); 00514 } 00515 00516 00517 00518 template <class T, ACE_SYNCH_DECL> 00519 int ReplyQueue<T, ACE_SYNCH_USE>::GetReply_i(T &theReply) 00520 { 00521 // 00522 // Check to make sure the queue isn't closed already. 00523 if (m_closed) 00524 { 00525 ACE_ERROR_RETURN((LM_ERROR, 00526 "(%N:%l) Attempt to get a reply from a closed queue.\n"), 00527 -1); 00528 } 00529 00530 // 00531 // Construct a PendingReply so we can put it in the queue. 00532 // Do this before acquiring the mutex so we don't make other threads 00533 // wait through construction. 00534 PENDING_REPLY replyObj(theReply, m_replyQueueMutex); 00535 00536 // 00537 // Mark the PendingReply as residing on the queue, and then add it to 00538 // the queue. 00539 replyObj.m_isEnqueued = true; 00540 typename QUEUE_TYPE::iterator it = m_replyQueue.insert(m_replyQueue.end(), &replyObj); 00541 00542 // 00543 // Wait until signaled by a SetReply() call or until an error occurs. 00544 // Note that m_replyQueueMutex is relinquished during the time we are 00545 // waiting. Its re-acquired on return from wait(). 00546 int status = replyObj.m_condition.wait(); 00547 00548 // 00549 // If m_isEnqueued is still set, then remove the object from the queue 00550 // now. Since the queue is an STL list, the previously-recorded iterator 00551 // is still valid. 00552 if (replyObj.m_isEnqueued) 00553 { 00554 m_replyQueue.erase(it); 00555 } 00556 00557 // 00558 // If wait() failed, then this whole call failed. Log an error message. 00559 if (status == -1) 00560 { 00561 ACE_ERROR((LM_ERROR, "(%N:%l) %p\n", "LOCK::acquire")); 00562 } 00563 // 00564 // If the queue is closed at this point then we didn't get a value. Log a 00565 // warning to aid in debugging and return -1. 00566 else if (m_closed) 00567 { 00568 status = -1; 00569 ACE_ERROR((LM_WARNING, 00570 "(%N:%l) GetReply() failed because reply queue was closed before a response was set.\n")); 00571 } 00572 00573 return status; 00574 } 00575 00576 00577 00578 template <class T, ACE_SYNCH_DECL> 00579 int ReplyQueue<T, ACE_SYNCH_USE>::SetReply(T &theReply, bool acquireMutex) 00580 { 00581 if (acquireMutex) 00582 { 00583 // 00584 // Don't bother acquiring the mutex if the queue is already closed. Just 00585 // report failure. 00586 if (m_closed) 00587 return -1; 00588 00589 ACE_Guard<ACE_SYNCH_MUTEX_T> g(m_replyQueueMutex); 00590 00591 return SetReply_i(theReply); 00592 } 00593 00594 return SetReply_i(theReply); 00595 } 00596 00597 00598 00599 template <class T, ACE_SYNCH_DECL> 00600 int ReplyQueue<T, ACE_SYNCH_USE>::SetReply_i(T &theReply) 00601 { 00602 // 00603 // A place to hold the status of system calls. 00604 int status = -1; 00605 00606 // 00607 // If the queue is already closed the log an error and return -1. 00608 if (m_closed) 00609 { 00610 ACE_ERROR_RETURN((LM_ERROR, 00611 "(%N:%l) Attempt to get a reply from a closed queue.\n"), 00612 -1); 00613 } 00614 00615 // 00616 // If the queue is empty, then someone called this method in error. 00617 // Log an error and return -1. 00618 if (m_replyQueue.empty()) 00619 { 00620 ACE_ERROR_RETURN((LM_ERROR, 00621 "(%N:%l) Attempt to set a reply when none are pending.\n"), -1); 00622 } 00623 else 00624 { 00625 // 00626 // Get the next PendingReply and mark it as no longer enqueued. 00627 PENDING_REPLY * replyObj = m_replyQueue.front(); 00628 replyObj->m_isEnqueued = false; 00629 m_replyQueue.pop_front(); 00630 00631 // 00632 // Assigns the value of theReply to replyObj->m_theReply 00633 Assign_i(theReply, replyObj->m_theReply); 00634 00635 // 00636 // Notify the waiter that their reply is ready. 00637 status = replyObj->m_condition.signal(); 00638 00639 // 00640 // If notification failed, then log a message. 00641 if (status == -1) 00642 { 00643 ACE_ERROR((LM_ERROR, 00644 "(%N:%l) %p\n", "ACE_Condition<>::signal")); 00645 } 00646 } 00647 00648 return status; 00649 } 00650 00651 #if 0 00652 // 00653 // The following class is used to test compilation. It is not intended to be used. 00654 namespace 00655 { 00656 class ReplyQueueTester 00657 { 00658 void TestCompilation() 00659 { 00660 ReplyQueue<int, ACE_MT_SYNCH> foo1; 00661 ReplyQueue<int, ACE_NULL_SYNCH> foo2; 00662 ReplyQueue<int, ACE_SYNCH> foo3; 00663 00664 00665 ACE_Thread_Mutex &m1 = foo1.GetMutex(); 00666 ACE_Null_Mutex &m2 = foo2.GetMutex(); 00667 ACE_SYNCH_MUTEX &m3 = foo3.GetMutex(); 00668 00669 int status, 00670 arg; 00671 00672 ACE_Time_Value tv = ACE_OS::gettimeofday() 00673 + ACE_Time_Value(10,0); 00674 00675 status = foo1.GetReply(arg, &tv); 00676 status = foo2.GetReply(arg, &tv); 00677 status = foo3.GetReply(arg, &tv); 00678 ; 00679 status = foo1.GetReply(arg); 00680 status = foo2.GetReply(arg); 00681 status = foo3.GetReply(arg); 00682 00683 status = foo1.SetReply(status); 00684 status = foo2.SetReply(status); 00685 status = foo3.SetReply(status); 00686 } 00687 }; 00688 } 00689 #endif // 0 00690 00691 #endif // REPLY_QUEUE_H 00692