diff options
Diffstat (limited to 'docs/tutorials/007/page08.html')
-rw-r--r-- | docs/tutorials/007/page08.html | 282 |
1 files changed, 0 insertions, 282 deletions
diff --git a/docs/tutorials/007/page08.html b/docs/tutorials/007/page08.html deleted file mode 100644 index 09db461fe37..00000000000 --- a/docs/tutorials/007/page08.html +++ /dev/null @@ -1,282 +0,0 @@ -<!-- $Id$ --> -<HTML> -<HEAD> - <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> - <META NAME="GENERATOR" CONTENT="Mozilla/4.04 [en] (X11; I; Linux 2.0.32 i486) [Netscape]"> - <META NAME="Author" CONTENT="James CE Johnson"> - <META NAME="Description" CONTENT="A first step towards using ACE productively"> - <TITLE>ACE Tutorial 007</TITLE> -</HEAD> -<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> - -<CENTER><B><FONT SIZE=+2>ACE Tutorial 007</FONT></B></CENTER> - -<CENTER><B><FONT SIZE=+2>Creating a thread-pool server</FONT></B></CENTER> -<HR> - -<P>Finally, <A HREF="thread_pool.cpp">thread_pool.cpp</A> -where we have the Thread_Pool object implementation. -<P> -Remember back in <A HREF="../006/page01.html">Tutorial 6</A> when I - was talking about <i>THR_NEW_LWP</i>? Look closely and you'll - see it here. It's bitwise OR'd with <i>THR_DETACHED</i> just to - keep things interesting. -<P> -<HR WIDTH="100%"> -<PRE> -<font color=red>// $Id$</font> - -<font color=blue>#include</font> "<font color=green>thread_pool.h</font>" - -<font color=red>/* We need this header so that we can invoke handle_input() on the - objects we dequeue. */</font> -<font color=blue>#include</font> "<A HREF="../../../ace/Event_Handler.h">ace/Event_Handler.h</A>" - -<font color=red>/* All we do here is initialize our active thread counter. */</font> -<font color=#008888>Thread_Pool::Thread_Pool</font> (void) - : active_threads_ (0) -{ -} - -<font color=red>/* 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. */</font> -int -<font color=#008888>Thread_Pool::open</font> (int pool_size) -{ - return this->activate (THR_NEW_LWP|THR_DETACHED, pool_size); -} - -<font color=red>/* 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. */</font> -int -<font color=#008888>Thread_Pool::close</font> (u_long flags) -{ - ACE_UNUSED_ARG(flags); - - <font color=red>/* Find out how many threads are currently active */</font> - int counter = active_threads_.value (); - - <font color=red>/* For each one of the active threads, enqueue a "<font color=green>null</font>" event - handler. Below, we'll teach our svc() method that "<font color=green>null</font>" means - "<font color=green>shutdown</font>". */</font> - while (counter--) - this->enqueue (0); - - <font color=red>/* 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 of a second - between tries. */</font> - while (active_threads_.value ()) - <font color=#008888>ACE_OS::sleep</font> (ACE_Time_Value (0, 250000)); - - return(0); -} - -<font color=red>/* 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 misuse it. */</font> -int -<font color=#008888>Thread_Pool::enqueue</font> (ACE_Event_Handler *handler) -{ - <font color=red>/* 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. */</font> - - <font color=red>/* 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. */</font> - void *v_data = (void *) handler; - char *c_data = (char *) v_data; - - ACE_Message_Block *mb; - - <font color=red>/* 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. */</font> - ACE_NEW_RETURN (mb, - ACE_Message_Block (c_data), - -1); - - <font color=red>/* 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. */</font> - if (this->putq (mb) == -1) - { - <font color=red>/* 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. */</font> - mb->release (); - return -1; - } - - return 0; -} - -<font color=red>/* The "<font color=green>guard</font>" concept is very powerful and used throughout - multi-threaded applications. A guard normally does some operation - on an object at construction and the "<font color=green>opposite</font>" 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. */</font> - -<font color=red>/* 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. */</font> -class Counter_Guard -{ -public: - Counter_Guard (<font color=#008888>Thread_Pool::counter_t</font> &counter) - : counter_ (counter) - { - ++counter_; - } - - ~Counter_Guard (void) - { - --counter_; - } - -protected: - <font color=#008888>Thread_Pool::counter_t</font> &counter_; -}; - -<font color=red>/* 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(). */</font> -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_; -}; - -<font color=red>/* 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. */</font> -int -<font color=#008888>Thread_Pool::svc</font> (void) -{ - <font color=red>/* The getq() method takes a reference to a pointer. So... we need - a pointer to give it a reference to. */</font> - ACE_Message_Block *mb; - - <font color=red>/* Create the guard for our active thread counter object. No matter - where we choose to return() from svc(), we now know that the - counter will be decremented. */</font> - Counter_Guard counter_guard (active_threads_); - - <font color=red>/* 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. */</font> - while (this->getq (mb) != -1) - { - <font color=red>/* A successful getq() will cause "<font color=green>mb</font>" 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. */</font> - Message_Block_Guard message_block_guard (mb); - - <font color=red>/* 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* */</font> - char *c_data = mb->base (); - - <font color=red>/* We've chosen to use a "<font color=green>null</font>" value as an indication to leave. - If the data we got from the queue is not null then we have - some work to do. */</font> - if (c_data) - { - <font color=red>/* 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.) */</font> - void *v_data = (void *) c_data; - - ACE_Event_Handler *handler = (ACE_Event_Handler *) v_data; - - <font color=red>/* 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. */</font> - if (handler->handle_input (ACE_INVALID_HANDLE) == -1) - { - <font color=red>/* Tell the handler that it's time to go home. The - "<font color=green>normal</font>" 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 - "<font color=green>handle</font>" parameter. Convenient isn't it? */</font> - handler->handle_close (handler->get_handle (), 0); - - <font color=red>/* 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... */</font> - } - } - else - <font color=red>/* If we get here, we were given a message block with "<font color=green>null</font>" - data. That is our signal to leave, so we return(0) to - leave gracefully. */</font> - return 0; <font color=red>// Ok, shutdown request</font> - - <font color=red>// message_block_guard goes out of scope here and releases the</font> - <font color=red>// message_block instance.</font> - } - - return 0; -} - -</PRE> -<P><HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page09.html">Continue This Tutorial</A>]</CENTER> - |