//============================================================================= /** * @file Bound_Ptr_Test.cpp * * This example tests the and * and illustrates how they may be dispersed * between multiple threads using an implementation of the Active * Object pattern, which is available in the POSA2 book * . * * @author Christopher Kohlhoff */ //============================================================================= #include "test_config.h" #include "ace/Null_Mutex.h" #include "ace/Method_Request.h" #include "Bound_Ptr_Test.h" #include "ace/Task.h" // The following Parent and Child classes illustrate how you might use the // ACE_Strong_Bound_Ptr and ACE_Weak_Bound_Ptr together in cyclic // relationships. struct Child_Base { virtual ~Child_Base (); // Perform some operation. virtual void do_something () = 0; }; // This class should only be created on the heap. Normally it would be an // abstract class, and the implementation would be elsewhere. struct Parent { Parent (); ~Parent (); // Weak pointer to this object used to hand out new references. Must be // weak since it can't own itself! ACE_Weak_Bound_Ptr weak_self_; // The parent owns the child. When the parent is destroyed the child will // be automatically deleted. ACE_Strong_Bound_Ptr child_; // Called by the child to perform some operation. void do_something (); static size_t instance_count_; }; // This class should only be created on the heap. Normally it would be an // abstract class, and the implementation would be elsewhere. struct Child : public Child_Base { Child (ACE_Weak_Bound_Ptr parent); ~Child () override; // Back pointer to the parent. The child does not own the parent so has no // effect on its lifetime. ACE_Weak_Bound_Ptr parent_; // Perform some operation. Delegates the work to the parent. void do_something () override; static size_t instance_count_; }; Child_Base::~Child_Base () { } size_t Parent::instance_count_ = 0; Parent::Parent () : weak_self_(this), child_(new Child(weak_self_)) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Creating Parent object\n"))); ++Parent::instance_count_; } Parent::~Parent () { --Parent::instance_count_; ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Deleting Parent object\n"))); } void Parent::do_something () { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Parent doing something\n"))); } size_t Child::instance_count_ = 0; Child::Child (ACE_Weak_Bound_Ptr parent) : parent_(parent) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Creating Child object\n"))); ++Child::instance_count_; } Child::~Child () { --Child::instance_count_; ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Deleting Child object\n"))); } void Child::do_something () { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Child doing something\n"))); // Using operator-> on a weak pointer will automatically create a strong // pointer as a temporary. This ensures that the object exists for the // lifetime of the call (although it does not check for null). parent_->do_something (); // In cases where we may need to call operations on the weak pointer // many times, we can reduce the overhead by explicitly converting to a // strong pointer first. ACE_Strong_Bound_Ptr strong_parent (parent_); // You can check for null to see if the parent object still exists (in this // case it is not strictly necessary since the child will only exist if the // parent still exists). if (strong_parent == 0) return; for (int i = 0; i < 5; ++i) strong_parent->do_something (); } size_t Printer::instance_count_ = 0; Printer::Printer (const char *message) : message_ (message) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Creating Printer object\n"))); ++Printer::instance_count_; } Printer::~Printer () { --Printer::instance_count_; ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Deleting Printer object\n"))); } void Printer::print () { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) %C\n"), this->message_)); } #if defined (ACE_HAS_THREADS) /** * @class Method_Request_print * * @brief Reification of the method. */ class Method_Request_print : public ACE_Method_Request { public: explicit Method_Request_print (Printer_var &printer); ~Method_Request_print () override; /// This is the entry point into the Active Object method. int call () override; private: Printer_var printer_; }; Method_Request_print::Method_Request_print (Printer_var &printer) : printer_ (printer) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Method_Request_print created\n"))); } Method_Request_print::~Method_Request_print () { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Method_Request_print will be deleted.\n"))); } int Method_Request_print::call () { // Dispatch the Servant's operation and store the result into the // Future. Printer_var temp = printer_; temp->print (); return 0; } /** * @class Method_Request_end * * @brief Reification of the method. */ class Method_Request_end : public ACE_Method_Request { public: Method_Request_end (Scheduler *new_Prime_Scheduler); ~Method_Request_end () override; int call () override; private: Scheduler *scheduler_; }; Method_Request_end::Method_Request_end (Scheduler *scheduler) : scheduler_ (scheduler) { } Method_Request_end::~Method_Request_end () { } int Method_Request_end::call () { // Shut down the scheduler by deactivating the activation queue's // underlying message queue - should pop all worker threads off their // wait and they'll exit. this->scheduler_->msg_queue ()->deactivate (); return -1; } // Constructor // Associates the activation queue with this task's message queue, // allowing easy access to the message queue for shutting it down // when it's time to stop this object's service threads. Scheduler::Scheduler () : activation_queue_ (msg_queue ()) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Scheduler created\n"))); } // Destructor Scheduler::~Scheduler () { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Scheduler will be destroyed\n"))); } // open int Scheduler::open (void *) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Scheduler open\n"))); // Become an Active Object. int num_threads = 3; return this->activate (THR_BOUND | THR_JOINABLE, num_threads); } // close int Scheduler::close (u_long) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) rundown\n"))); return 0; } // Service.. int Scheduler::svc () { for (;;) { // Dequeue the next method request (we use an strong pointer in // case an exception is thrown in the ). ACE_Strong_Bound_Ptr mo (this->activation_queue_.dequeue ()); if (mo == 0) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) activation queue shut down\n"))); break; } ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) calling method request\n"))); // Call it. if (mo->call () == -1) break; // Destructor automatically deletes it. } return 0; } void Scheduler::end () { this->activation_queue_.enqueue (new Method_Request_end (this)); } // Here's where the work takes place. void Scheduler::print (Printer_var &printer) { this->activation_queue_.enqueue (new Method_Request_print (printer)); } // Total number of loops. static int n_loops = 10; #endif /* ACE_HAS_THREADS */ int run_main (int, ACE_TCHAR *[]) { ACE_START_TEST (ACE_TEXT ("Bound_Ptr_Test")); // ========================================================================= // The following test uses the ACE_Strong_Bound_Ptr in a single // thread of control, hence we use the ACE_Null_Mutex ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) performing synchronous test...\n"))); Parent *parent1 = 0; ACE_NEW_RETURN (parent1, Parent, -1); ACE_Weak_Bound_Ptr p8; { // Must get the pointer from the parent object's weak_self_ member. ACE_Strong_Bound_Ptr p(parent1->weak_self_); ACE_Strong_Bound_Ptr p1(p); ACE_Strong_Bound_Ptr p2(p); ACE_Weak_Bound_Ptr p3(p); ACE_Strong_Bound_Ptr p4(p); ACE_Strong_Bound_Ptr p5 = p2; ACE_Strong_Bound_Ptr p6 = p3; ACE_Weak_Bound_Ptr p7(p1); p8 = p2; p->child_->do_something (); } ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Parent instance count is %d, expecting 0\n"), Parent::instance_count_)); if (Parent::instance_count_ != 0) { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%t) parent instance count not 0...\n")), -1); } ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Child instance count is %d, expecting 0\n"), Child::instance_count_)); if (Child::instance_count_ != 0) { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%t) child instance count not 0...\n")), -1); } // Weak pointer should now be set to null. if(!p8.null ()) { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%t) p8 not nill...\n")), -1); } Printer *printer1 = 0; ACE_NEW_RETURN (printer1, Printer ("I am printer 1"), -1); ACE_Weak_Bound_Ptr r9; { ACE_Strong_Bound_Ptr r(printer1); ACE_Strong_Bound_Ptr r1(r); ACE_Strong_Bound_Ptr r2(r); ACE_Strong_Bound_Ptr r3(r); ACE_Strong_Bound_Ptr r4(r); ACE_Strong_Bound_Ptr r5 = r2; ACE_Strong_Bound_Ptr r6 = r1; ACE_Weak_Bound_Ptr r7(r1); ACE_Weak_Bound_Ptr r8 = r2; r9 = r3; r9->print (); } ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Printer instance count is %d, expecting 0\n"), Printer::instance_count_)); if (Printer::instance_count_ != 0) { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%t) Printer instance count not 0...\n")), -1); } // Weak pointer should now be set to null. if (!r9.null ()) { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%t) r9 not nill...\n")), -1); } #if defined (ACE_HAS_THREADS) // ========================================================================= // The following test uses the ACE_Strong_Bound_Ptr in multiple // threads of control. ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) performing asynchronous test...\n"))); Scheduler *scheduler_ptr = 0; // Create active objects.. ACE_NEW_RETURN (scheduler_ptr, Scheduler (), -1); ACE_Strong_Bound_Ptr scheduler(scheduler_ptr); if (scheduler->open () == -1) { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%t) Scheduler open failed...\n")), -1); } { Printer *printer2 = 0; ACE_NEW_RETURN (printer2, Printer ("I am printer 2"), -1); Printer_var r (printer2); for (int i = 0; i < n_loops; i++) // Spawn off the methods, which run in a separate thread as // active object invocations. scheduler->print (r); } // Close things down. scheduler->end (); scheduler->wait (); ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Printer instance count is %d, expecting 0\n"), Printer::instance_count_)); if (Printer::instance_count_ != 0) return -1; #endif /* ACE_HAS_THREADS */ ACE_END_TEST; return 0; }