summaryrefslogtreecommitdiff
path: root/docs/tutorials/007/thread_pool.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'docs/tutorials/007/thread_pool.cpp')
-rw-r--r--docs/tutorials/007/thread_pool.cpp275
1 files changed, 0 insertions, 275 deletions
diff --git a/docs/tutorials/007/thread_pool.cpp b/docs/tutorials/007/thread_pool.cpp
deleted file mode 100644
index 49435a2a748..00000000000
--- a/docs/tutorials/007/thread_pool.cpp
+++ /dev/null
@@ -1,275 +0,0 @@
-
-// $Id$
-
-#include "thread_pool.h"
-
-/*
- We need this header so that we can invoke handle_input() on the objects we dequeue.
- */
-#include "ace/Event_Handler.h"
-
-
-/*
- All we do here is initialize our active thread counter.
- */
-Thread_Pool::Thread_Pool(void)
- : active_threads_(0)
-{
-}
-
-/*
- Our open() method is a thin disguise around the ACE_Task<> activate() method. By
- hiding activate() in this way, the users of Thread_Pool don't have to worry about
- the thread configuration flags.
- */
-int Thread_Pool::open( int _pool_size )
-{
- return this->activate(THR_NEW_LWP,_pool_size);
-}
-
-/*
- Closing the thread pool can be a tricky exercise. I've decided to take an easy approach
- and simply enqueue a secret message for each thread we have active.
- */
-int Thread_Pool::close(void)
-{
- /*
- Find out how many threads are currently active
- */
- int counter = active_threads_.value();
-
- /*
- For each one of the active threads, enqueue a "null" event handler. Below, we'll
- teach our svc() method that "null" means "shutdown".
- */
- while( counter-- )
- {
- this->enqueue( 0 );
- }
-
- /*
- As each svc() method exits, it will decrement the active thread counter. We just wait
- here for it to reach zero. Since we don't know how long it will take, we sleep for
- a quarter-second or so between tries.
- */
- while( active_threads_.value() )
- {
- ACE_OS::sleep( ACE_Time_Value(0.25) );
- }
-
- return(0);
-}
-
-/*
- When an object wants to do work in the pool, it should call the enqueue() method.
- We introduce the ACE_Message_Block here but, unfortunately, we seriously missuse it.
- */
-int Thread_Pool::enqueue( ACE_Event_Handler * _handler )
-{
- /*
- An ACE_Message_Block is a chunk of data. You put them into an ACE_Message_Queue.
- ACE_Task<> has an ACE_Message_Queue built in. In fact, the parameter to ACE_Task<>
- is passed directly to ACE_Message_Queue. If you look back at our header file you'll
- see that we used ACE_MT_SYNCH as the parameter indicating that we want MultiThread
- Synch safety. This allows us to safely put ACE_Message_Block objects into the
- message queue in one thread and take them out in another.
- */
-
- /*
- An ACE_Message_Block wants to have char* data. We don't have that. We could
- cast our ACE_Event_Handler* directly to a char* but I wanted to be more explicit.
- Since casting pointers around is a dangerous thing, I've gone out of my way here
- to be very clear about what we're doing.
-
- First: Cast the handler pointer to a void pointer. You can't do any useful work
- on a void pointer, so this is a clear message that we're making the
- pointer unusable.
-
- Next: Cast the void pointer to a char pointer that the ACE_Message_Block will accept.
- */
- void * v_data = (void*)_handler;
- char * c_data = (char*)v_data;
-
- /*
- Construct a new ACE_Message_Block. For efficiency, you might want to preallocate a
- stack of these and reuse them. For simplicity, I'll just create what I need as I need it.
- */
- ACE_Message_Block * mb = new ACE_Message_Block( c_data );
-
- /*
- Our putq() method is a wrapper around one of the enqueue methods of the ACE_Message_Queue
- that we own. Like all good methods, it returns -1 if it fails for some reason.
- */
- if( this->putq(mb) == -1 )
- {
- /*
- Another trait of the ACE_Message_Block objects is that they are reference counted.
- Since they're designed to be passed around between various objects in several threads
- we can't just delete them whenever we feel like it. The release() method is similar
- to the destroy() method we've used elsewhere. It watches the reference count and will
- delete the object when possible.
- */
- mb->release();
- return(-1);
- }
-
- return(0);
-}
-
-/*
- The "guard" concept is very powerful and used throughout multi-threaded applications.
- A guard normally does some operation on an object at construction and the "opposite"
- operation at destruction. For instance, when you guard a mutex (lock) object, the guard
- will acquire the lock on construction and release it on destruction. In this way, your
- method can simply let the guard go out of scope and know that the lock is released.
-
- Guards aren't only useful for locks however. In this application I've created two guard
- objects for quite a different purpose.
- */
-
-/*
- The Counter_Guard is constructed with a reference to the thread pool's active thread
- counter. The guard increments the counter when it is created and decrements it at
- destruction. By creating one of these in svc(), I know that the counter will be decremented
- no matter how or where svc() returns.
- */
-class Counter_Guard
-{
-public:
- Counter_Guard( Thread_Pool::counter_t & _counter )
- : counter_(_counter)
- {
- ++counter_;
- }
-
- ~Counter_Guard(void)
- {
- --counter_;
- }
-
-protected:
- Thread_Pool::counter_t & counter_;
-};
-
-/*
- My Message_Block_Guard is also a little non-traditional. It doesn't do anything in the
- constructor but it's destructor ensures that the message block's release() method is called.
- This is a cheap way to prevent a memory leak if I need an additional exit point in svc().
- */
-class Message_Block_Guard
-{
-public:
- Message_Block_Guard( ACE_Message_Block * & _mb )
- : mb_(_mb)
- {
- }
-
- ~Message_Block_Guard( void )
- {
- mb_->release();
- }
-
-protected:
- ACE_Message_Block * & mb_;
-};
-
-/*
- Now we come to the svc() method. As I said, this is being executed in each thread of the
- Thread_Pool. Here, we pull messages off of our built-in ACE_Message_Queue and cause them
- to do work.
- */
-int Thread_Pool::svc(void)
-{
- /*
- The getq() method takes a reference to a pointer. So... we need a pointer to give it
- a reference to.
- */
- ACE_Message_Block * mb;
-
- /*
- Create the guard for our active thread counter object. No matter where we choose to
- return() from svc(), we no know that the counter will be decremented.
- */
- Counter_Guard counter_guard(active_threads_);
-
- /*
- Get messages from the queue until we have a failure. There's no real good reason
- for failure so if it happens, we leave immediately.
- */
- while( this->getq(mb) != -1 )
- {
- /*
- A successful getq() will cause "mb" to point to a valid refernce-counted
- ACE_Message_Block. We use our guard object here so that we're sure to call
- the release() method of that message block and reduce it's reference count.
- Once the count reaches zero, it will be deleted.
- */
- Message_Block_Guard message_block_guard(mb);
-
- /*
- As noted before, the ACE_Message_Block stores it's data as a char*. We pull that
- out here and later turn it into an ACE_Event_Handler*
- */
- char * c_data = mb->base();
-
- /*
- We've chosen to use a "null" value as an indication to leave. If the data we got
- from the queue is not null then we have some work to do.
- */
- if( c_data )
- {
- /*
- Once again, we go to great lengths to emphasize the fact that we're casting pointers
- around in rather impolite ways. We could have cast the char* directly to an
- ACE_Event_Handler* but then folks might think that's an OK thing to do.
-
- (Note: The correct way to use an ACE_Message_Block is to write data into it.
- What I should have done was create a message block big enough to hold an
- event handler pointer and then written the pointer value into the block. When
- we got here, I would have to read that data back into a pointer. While politically
- correct, it is also a lot of work. If you're careful you can get away with casting
- pointers around.)
- */
- void * v_data = (void*)c_data;
-
- ACE_Event_Handler * handler = (ACE_Event_Handler*)v_data;
-
- /*
- Now that we finally have an event handler pointer, invoke it's handle_input() method.
- Since we don't know it's handle, we just give it a default. That's OK because we
- know that we're not using the handle in the method anyway.
- */
- if( handler->handle_input(ACE_INVALID_HANDLE) == -1 )
- {
- /*
- Tell the handler that it's time to go home. The "normal" method for shutting
- down a handler whose handler failed is to invoke handle_close(). This will
- take care of cleaning it up for us.
- Notice how we use the handler's get_handle() method to populate it's "handle"
- parameter. Convenient isn't it?
- */
- handler->handle_close(handler->get_handle(),0);
-
- /*
- Also notice that we don't exit the svc() method here! The first time I did
- this, I was exiting. After a few clients disconnect you have an empty
- thread pool. Hard to do any more work after that...
- */
- }
- }
- else
- {
- /*
- If we get here, we were given a message block with "null" data. That is our
- signal to leave, so we return(0) to leave gracefully.
- */
- return(0); // Ok, shutdown request
- }
-
- // message_block_guard goes out of scope here
- // and releases the message_block instance.
- }
-
- return(0);
-}
-