diff options
Diffstat (limited to 'docs/tutorials/007')
-rw-r--r-- | docs/tutorials/007/007.dsp | 124 | ||||
-rw-r--r-- | docs/tutorials/007/Makefile | 116 | ||||
-rw-r--r-- | docs/tutorials/007/client_acceptor.cpp | 56 | ||||
-rw-r--r-- | docs/tutorials/007/client_acceptor.h | 125 | ||||
-rw-r--r-- | docs/tutorials/007/client_handler.cpp | 233 | ||||
-rw-r--r-- | docs/tutorials/007/client_handler.h | 156 | ||||
-rw-r--r-- | docs/tutorials/007/combine.shar | 685 | ||||
-rw-r--r-- | docs/tutorials/007/page01.html | 84 | ||||
-rw-r--r-- | docs/tutorials/007/page02.html | 142 | ||||
-rw-r--r-- | docs/tutorials/007/page03.html | 155 | ||||
-rw-r--r-- | docs/tutorials/007/page04.html | 87 | ||||
-rw-r--r-- | docs/tutorials/007/page05.html | 188 | ||||
-rw-r--r-- | docs/tutorials/007/page06.html | 266 | ||||
-rw-r--r-- | docs/tutorials/007/page07.html | 121 | ||||
-rw-r--r-- | docs/tutorials/007/page08.html | 282 | ||||
-rw-r--r-- | docs/tutorials/007/page09.html | 79 | ||||
-rw-r--r-- | docs/tutorials/007/server.cpp | 113 | ||||
-rw-r--r-- | docs/tutorials/007/thread_pool.cpp | 252 | ||||
-rw-r--r-- | docs/tutorials/007/thread_pool.h | 89 |
19 files changed, 0 insertions, 3353 deletions
diff --git a/docs/tutorials/007/007.dsp b/docs/tutorials/007/007.dsp deleted file mode 100644 index d069843ec50..00000000000 --- a/docs/tutorials/007/007.dsp +++ /dev/null @@ -1,124 +0,0 @@ -# Microsoft Developer Studio Project File - Name="007" - Package Owner=<4>
-# Microsoft Developer Studio Generated Build File, Format Version 6.00
-# ** DO NOT EDIT **
-
-# TARGTYPE "Win32 (x86) Console Application" 0x0103
-
-CFG=007 - Win32 Debug
-!MESSAGE This is not a valid makefile. To build this project using NMAKE,
-!MESSAGE use the Export Makefile command and run
-!MESSAGE
-!MESSAGE NMAKE /f "007.mak".
-!MESSAGE
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
-!MESSAGE NMAKE /f "007.mak" CFG="007 - Win32 Debug"
-!MESSAGE
-!MESSAGE Possible choices for configuration are:
-!MESSAGE
-!MESSAGE "007 - Win32 Release" (based on "Win32 (x86) Console Application")
-!MESSAGE "007 - Win32 Debug" (based on "Win32 (x86) Console Application")
-!MESSAGE
-
-# Begin Project
-# PROP Scc_ProjName ""
-# PROP Scc_LocalPath ""
-CPP=cl.exe
-RSC=rc.exe
-
-!IF "$(CFG)" == "007 - Win32 Release"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 0
-# PROP BASE Output_Dir "Release"
-# PROP BASE Intermediate_Dir "Release"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 0
-# PROP Output_Dir "Release"
-# PROP Intermediate_Dir "Release"
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\.." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD BASE RSC /l 0x409 /d "NDEBUG"
-# ADD RSC /l 0x409 /d "NDEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
-# ADD LINK32 ace.lib /nologo /subsystem:console /machine:I386 /libpath:"..\..\..\ace"
-
-!ELSEIF "$(CFG)" == "007 - Win32 Debug"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 1
-# PROP BASE Output_Dir "Debug"
-# PROP BASE Intermediate_Dir "Debug"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 1
-# PROP Output_Dir "Debug"
-# PROP Intermediate_Dir "Debug"
-# PROP Ignore_Export_Lib 0
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /GX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD CPP /nologo /MDd /W3 /GX /Od /I "..\..\.." /D "WIN32" /D "_DEBUG" /YX /FD /c
-# ADD BASE RSC /l 0x409 /d "_DEBUG"
-# ADD RSC /l 0x409 /d "_DEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
-# ADD LINK32 aced.lib /nologo /subsystem:console /debug /machine:I386 /out:"server.exe" /pdbtype:sept /libpath:"..\..\..\ace"
-
-!ENDIF
-
-# Begin Target
-
-# Name "007 - Win32 Release"
-# Name "007 - Win32 Debug"
-# Begin Group "Source Files"
-
-# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
-# Begin Source File
-
-SOURCE=.\client_acceptor.cpp
-# End Source File
-# Begin Source File
-
-SOURCE=.\client_handler.cpp
-# End Source File
-# Begin Source File
-
-SOURCE=.\server.cpp
-# End Source File
-# Begin Source File
-
-SOURCE=.\thread_pool.cpp
-# End Source File
-# End Group
-# Begin Group "Header Files"
-
-# PROP Default_Filter "h;hpp;hxx;hm;inl"
-# Begin Source File
-
-SOURCE=.\client_acceptor.h
-# End Source File
-# Begin Source File
-
-SOURCE=.\client_handler.h
-# End Source File
-# Begin Source File
-
-SOURCE=.\thread_pool.h
-# End Source File
-# End Group
-# Begin Group "Resource Files"
-
-# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
-# End Group
-# End Target
-# End Project
diff --git a/docs/tutorials/007/Makefile b/docs/tutorials/007/Makefile deleted file mode 100644 index 7ed33571341..00000000000 --- a/docs/tutorials/007/Makefile +++ /dev/null @@ -1,116 +0,0 @@ -#---------------------------------------------------------------------------- -# $Id$ -#---------------------------------------------------------------------------- - -#---------------------------------------------------------------------------- -# Local macros -#---------------------------------------------------------------------------- - -# You can generally find a Makefile in the ACE examples, tests or the library -# itself that will satisfy our application needs. This one was taken from -# one of the examples. - - # Define the name of the binary we want to create. There has to be - # a CPP file $(BIN).cpp but it doesn't necessarily have to have your - # main() in it. Most of the time, though, it will. -BIN = server - - # Few applications will have a single source file. We use the FILES - # macro to build up a list of additional files to compile. Notice - # that we leave off the extension just as with BIN -FILES = -FILES += client_handler -FILES += client_acceptor -FILES += thread_pool - - # The BUILD macro is used by the ACE makefiles. Basically, it tells - # the system what to build. I don't really know what VBIN is other - # than it is constructed from the value of BIN. Just go with it... -BUILD = $(VBIN) - - # Here we use some GNU make extensions to build the SRC macro. Basically, - # we're just adding .cpp to the value of BIN and for each entry of the - # FILES macro. -SRC = $(addsuffix .cpp,$(BIN)) $(addsuffix .cpp,$(FILES)) - - # This is used by my Indent target below. It's not a part of standard - # ACE and you don't need it yourself. -HDR = *.h - -#---------------------------------------------------------------------------- -# Include macros and targets -#---------------------------------------------------------------------------- - - # This is where the real power lies! These included makefile components - # are similar to the C++ templates in ACE. That is, they do a tremendous - # amount of work for you and all you have to do is include them. - # As a matter of fact, in our project, I created a single file named - # "app.mk" that includes all of these. Our project makefiles then just - # need to include app.mk to get everything they need. - -include $(ACE_ROOT)/include/makeinclude/wrapper_macros.GNU -include $(ACE_ROOT)/include/makeinclude/macros.GNU -include $(ACE_ROOT)/include/makeinclude/rules.common.GNU -include $(ACE_ROOT)/include/makeinclude/rules.nonested.GNU -include $(ACE_ROOT)/include/makeinclude/rules.bin.GNU -include $(ACE_ROOT)/include/makeinclude/rules.local.GNU - -#---------------------------------------------------------------------------- -# Local targets -#---------------------------------------------------------------------------- - - # Sometimes I like to reformat my code to make it more readable. This is - # more useful for the comments than anything else. Unfortunately, the - # "indent" program doesn't quite grok C++ so I have to post-process it's - # output just a bit. -Indent : # - for i in $(SRC) $(HDR) ; do \ - indent -npsl -l80 -fca -fc1 -cli0 -cdb < $$i | \ - sed -e 's/: :/::/g' \ - -e 's/^.*\(public:\)/\1/' \ - -e 's/^.*\(protected:\)/\1/' \ - -e 's/^.*\(private:\)/\1/' \ - -e 's/:\(public\)/ : \1/' \ - -e 's/:\(protected\)/ : \1/' \ - -e 's/:\(private\)/ : \1/' \ - > $$i~ ;\ - mv $$i~ $$i ;\ - done - - # One of the targets in the ACE makefiles is "depend". It will invoke - # your compiler in a way that will generate a list of dependencies for - # you. This is a great thing! Unfortunately, it puts all of that mess - # directly into the Makefile. I prefer my Makefile to stay clean and - # uncluttered. The perl script referenced here pulls the dependency - # stuff back out of the Makefile and into a file ".depend" which we then - # include just like the makefile components above. -Depend : depend - perl ../fix.Makefile - -.depend : # - touch .depend - - -HTML : # - [ -f hdr ] || $(MAKE) UNSHAR - perl ../combine *.pre ; chmod +r *.html - -SHAR : # - [ ! -f combine.shar ] || exit 1 - shar -T hdr bodies *.pre *.pst > combine.shar && $(RM) hdr bodies *.pre *.pst - -UNSHAR : # - sh combine.shar - -CLEAN : realclean - $(RM) hdr bodies *.pre *.pst .depend - -#---------------------------------------------------------------------------- -# Dependencies -#---------------------------------------------------------------------------- - - # Don't put anything below here. Between the "depend" target and fix.Makefile - # it's guaranteed to be lost! - - # This is inserted by the fix.Makefile script -include .depend diff --git a/docs/tutorials/007/client_acceptor.cpp b/docs/tutorials/007/client_acceptor.cpp deleted file mode 100644 index 079ae8e6d6a..00000000000 --- a/docs/tutorials/007/client_acceptor.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// $Id$ - -#include "client_acceptor.h" - -/* Construct ourselves with the chosen concurrency strategy. Notice - that we also set our Thread_Pool reference to our private instance. */ -Client_Acceptor::Client_Acceptor (int concurrency) - : concurrency_ (concurrency), - the_thread_pool_ (private_thread_pool_) -{ -} - -/* Construct ourselves with a reference to somebody else' Thread_Pool. - Obvioulsy our concurrency strategy is "thread_pool_" at this point. */ -Client_Acceptor::Client_Acceptor (Thread_Pool &thread_pool) - : concurrency_ (thread_pool_), - the_thread_pool_ (thread_pool) -{ -} - -/* When we're destructed, we may need to cleanup after ourselves. If - we're running with a thread pool that we own, it is up to us to - close it down. */ -Client_Acceptor::~Client_Acceptor (void) -{ - if (this->concurrency() == thread_pool_ && thread_pool_is_private ()) - thread_pool ()->close (); -} - -/* Similar to the destructor (and close() below) it is necessary for - us to open the thread pool in some circumstances. - - Notice how we delegate most of the open() work to the open() method - of our baseclass. */ -int -Client_Acceptor::open (const ACE_INET_Addr &addr, - ACE_Reactor *reactor, - int pool_size) -{ - if (this->concurrency() == thread_pool_ && thread_pool_is_private ()) - thread_pool ()->open (pool_size); - - return inherited::open (addr, reactor); -} - -/* Here again we find that we have to manage the thread pool. Like - open() we also delegate the other work to our baseclass. */ -int -Client_Acceptor::close (void) -{ - if (this->concurrency() == thread_pool_ && thread_pool_is_private ()) - thread_pool ()->close (); - - return inherited::close (); -} - diff --git a/docs/tutorials/007/client_acceptor.h b/docs/tutorials/007/client_acceptor.h deleted file mode 100644 index fb591f548fb..00000000000 --- a/docs/tutorials/007/client_acceptor.h +++ /dev/null @@ -1,125 +0,0 @@ -// $Id$ - -#ifndef CLIENT_ACCEPTOR_H -#define CLIENT_ACCEPTOR_H - -/* The ACE_Acceptor<> template lives in the ace/Acceptor.h header - file. You'll find a very consitent naming convention between the - ACE objects and the headers where they can be found. In general, - the ACE object ACE_Foobar will be found in ace/Foobar.h. */ - -#include "ace/Acceptor.h" - -#if !defined (ACE_LACKS_PRAGMA_ONCE) -# pragma once -#endif /* ACE_LACKS_PRAGMA_ONCE */ - -/* Since we want to work with sockets, we'll need a SOCK_Acceptor to - allow the clients to connect to us. */ -#include "ace/SOCK_Acceptor.h" - -/* The Client_Handler object we develop will be used to handle clients - once they're connected. The ACE_Acceptor<> template's first - parameter requires such an object. In some cases, you can get by - with just a forward declaration on the class, in others you have to - have the whole thing. */ -#include "client_handler.h" - -/* Parameterize the ACE_Acceptor<> such that it will listen for socket - connection attempts and create Client_Handler objects when they - happen. In Tutorial 001, we wrote the basic acceptor logic on our - own before we realized that ACE_Acceptor<> was available. You'll - get spoiled using the ACE templates because they take away a lot of - the tedious details! */ -typedef ACE_Acceptor <Client_Handler, ACE_SOCK_ACCEPTOR> Client_Acceptor_Base; - -#include "thread_pool.h" - -/* This time we've added quite a bit more to our acceptor. In - addition to providing a choice of concurrency strategies, we also - maintain a Thread_Pool object in case that strategy is chosen. The - object still isn't very complex but it's come a long way from the - simple typedef we had in Tutorial 5. - - Why keep the thread pool as a member? If we go back to the inetd - concept you'll recall that we need several acceptors to make that - work. We may have a situation in which our different client types - requre different resources. That is, we may need a large thread - pool for some client types and a smaller one for others. We could - share a pool but then the client types may have undesirable impact - on one another. - - Just in case you do want to share a single thread pool, there is a - constructor below that will let you do that. */ -class Client_Acceptor : public Client_Acceptor_Base -{ -public: - typedef Client_Acceptor_Base inherited; - - /* Now that we have more than two strategies, we need more than a - boolean to tell us what we're using. A set of enums is a good - choice because it allows us to use named values. Another option - would be a set of static const integers. */ - enum concurrency_t - { - single_threaded_, - thread_per_connection_, - thread_pool_ - }; - - /* The default constructor allows the programmer to choose the - concurrency strategy. Since we want to focus on thread-pool, - that's what we'll use if nothing is specified. */ - Client_Acceptor (int concurrency = thread_pool_); - - /* Another option is to construct the object with an existing thread - pool. The concurrency strategy is pretty obvious at that point. */ - Client_Acceptor (Thread_Pool &thread_pool); - - /* Our destructor will take care of shutting down the thread-pool if - applicable. */ - ~Client_Acceptor (void); - - /* Open ourselves and register with the given reactor. The thread - pool size can be specified here if you want to use that - concurrency strategy. */ - int open (const ACE_INET_Addr &addr, - ACE_Reactor *reactor, - int pool_size = Thread_Pool::default_pool_size_); - - /* Close ourselves and our thread pool if applicable */ - int close (void); - - /* What is our concurrency strategy? */ - int concurrency (void) - { - return this->concurrency_; - } - - /* Give back a pointer to our thread pool. Our Client_Handler - objects will need this so that their handle_input() methods can - put themselves into the pool. Another alternative would be a - globally accessible thread pool. ACE_Singleton<> is a way to - achieve that. */ - Thread_Pool *thread_pool (void) - { - return &this->the_thread_pool_; - } - - /* Since we can be constructed with a Thread_Pool reference, there - are times when we need to know if the thread pool we're using is - ours or if we're just borrowing it from somebody else. */ - int thread_pool_is_private (void) - { - return &the_thread_pool_ == &private_thread_pool_; - } - -protected: - int concurrency_; - - Thread_Pool private_thread_pool_; - - Thread_Pool &the_thread_pool_; -}; - -#endif /* CLIENT_ACCEPTOR_H */ diff --git a/docs/tutorials/007/client_handler.cpp b/docs/tutorials/007/client_handler.cpp deleted file mode 100644 index 6399c1636d1..00000000000 --- a/docs/tutorials/007/client_handler.cpp +++ /dev/null @@ -1,233 +0,0 @@ -// $Id$ - -/* Since this is the third time we've seen most of this, I'm going to - strip out almost all of the comments that you've already seen. - That way, you can concentrate on the new items. */ - -#include "client_acceptor.h" -#include "client_handler.h" - -/* We're going to be registering and unregistering a couple of times. - To make sure that we use the same flags every time, I've created - these handy macros. */ -#define REGISTER_MASK ACE_Event_Handler::READ_MASK -#define REMOVE_MASK (ACE_Event_Handler::READ_MASK | ACE_Event_Handler::DONT_CALL) - -/* Our constructor still doesn't really do anything. We simply - initialize the acceptor pointer to "null" and get our current - thread id. The static self() method of ACE_Thread will return you - a thread id native to your platform. */ -Client_Handler::Client_Handler (void) - : client_acceptor_(0), - creator_ (ACE_Thread::self ()) -{ -} - -Client_Handler::~Client_Handler (void) -{ - this->peer().close(); -} - -/* Query our acceptor for the concurrency strategy. Notice that we - don't bother to check that our acceptor pointer is valid. That is - proably a bad idea... */ -int -Client_Handler::concurrency(void) -{ - return this->client_acceptor ()->concurrency (); -} - -/* And here we ask the acceptor about the thread pool. */ -Thread_Pool * -Client_Handler::thread_pool (void) -{ - return this->client_acceptor ()->thread_pool (); -} - -/* Back to our open() method. This is straight out of Tutorial 6. - There's nothing additional here for the thread-pool implementation. */ -int -Client_Handler::open (void *acceptor) -{ - client_acceptor ((Client_Acceptor *) acceptor); - - if (concurrency () == Client_Acceptor::thread_per_connection_) - return this->activate (THR_DETACHED); - - this->reactor (client_acceptor()->reactor ()); - - ACE_INET_Addr addr; - - if (this->peer ().get_remote_addr (addr) == -1) - return -1; - - if (this->reactor ()->register_handler (this, - REGISTER_MASK) == -1) - ACE_ERROR_RETURN ((LM_ERROR, - "(%P|%t) can't register with reactor\n"), - -1); - - ACE_DEBUG ((LM_DEBUG, - "(%P|%t) connected with %s\n", - addr.get_host_name ())); - return 0; -} - -/* The destroy() method will remove us from the reactor (with the - DONT_CALL flag set!) and then free our memory. This allows us to - be closed from outside of the reactor context without any danger. */ -void -Client_Handler::destroy (void) -{ - this->reactor ()->remove_handler (this, REMOVE_MASK); - delete this; -} - -/* As mentioned in the header, the typical way to close an object in a - threaded context is to invoke it's close() method. */ -int -Client_Handler::close (u_long flags) -{ - ACE_UNUSED_ARG(flags); - - /* - We use the destroy() method to clean up after ourselves. - That will take care of removing us from the reactor and then - freeing our memory. - */ - this->destroy (); - - /* Don't forward the close() to the baseclass! handle_close() above - has already taken care of delete'ing. Forwarding close() would - cause that to happen again and things would get really ugly at - that point! */ - return 0; -} - -/* We will be called when handle_input() returns -1. That's our queue - to delete ourselves to prevent memory leaks. */ -int -Client_Handler::handle_close (ACE_HANDLE handle, - ACE_Reactor_Mask mask) -{ - ACE_UNUSED_ARG (handle); - ACE_UNUSED_ARG (mask); - - delete this; - - return 0; -} - -/* In the open() method, we registered with the reactor and requested - to be notified when there is data to be read. When the reactor - sees that activity it will invoke this handle_input() method on us. - As I mentioned, the _handle parameter isn't useful to us but it - narrows the list of methods the reactor has to worry about and the - list of possible virtual functions we would have to override. - - You've read that much before... Now we have to do some extra stuff - in case we're using the thread-pool implementation. If we're - called by our creator thread then we must be in the reactor. In - that case, we arrange to be put into the thread pool. If we're not - in the creator thread then we must be in the thread pool and we can - do some work. */ -int -Client_Handler::handle_input (ACE_HANDLE handle) -{ - ACE_UNUSED_ARG (handle); - - /* Check our strategy. If we're using the thread pool and we're in - the creation thread then we know we were called by the reactor. */ - if (concurrency () == Client_Acceptor::thread_pool_) - { - if (ACE_OS::thr_equal (ACE_Thread::self(), - creator_)) - { - /* Remove ourselves from the reactor and ask to be put into - the thread pool's queue of work. (You should be able to - use suspend_handler() but I've had problems with that.) - - By removing ourselves from the reactor, we're guaranteed - that we won't be called back until the thread pool picks - us up out of the queue. If we didn't remove ourselves, - then the reactor would continue to invoke handle_input() - and we don't want that to happen. */ - this->reactor ()->remove_handler (this, REMOVE_MASK); - return this->thread_pool ()->enqueue (this); - } - } - - /* Any strategy other than thread-per-connection will eventually get - here. If we're in the single-threaded implementation or the - thread-pool, we still have to pass this way. */ - - char buf[BUFSIZ]; - - /* Invoke the process() method to do the work but save it's return - value instead of returning it immediately. */ - - int rval = this->process (buf, sizeof (buf)); - - /* Now, we look again to see if we're in the thread-pool - implementation. If so then we need to re-register ourselves with - the reactor so that we can get more work when it is available. - (If suspend_handler() worked then we would use resume_handler() - here.) */ - if (concurrency () == Client_Acceptor::thread_pool_) - { - if (rval != -1) - /* If we don't remember to re-register ourselves, then we - won't be able to respond to any future client requests. */ - this->reactor ()->register_handler (this, - REGISTER_MASK); - } - - /* Return the result of process() */ - return rval; -} - -/* Remember that when we leave our svc() method, the framework will - take care of calling our close() method so that we can cleanup - after ourselves. */ -int -Client_Handler::svc (void) -{ - char buf[BUFSIZ]; - - while (1) - if (this->process (buf, sizeof (buf)) == -1) - return -1; - - return 0; -} - -/* Once again, we see that the application-level logic has not been at - all affected by our choice of threading models. Of course, I'm not - sharing data between threads or anything. We'll leave locking - issues for a later tutorial. */ -int -Client_Handler::process (char *rdbuf, - int rdbuf_len) -{ - ssize_t bytes_read; - switch ( (bytes_read = this->peer ().recv (rdbuf, rdbuf_len)) ) - { - case -1: - ACE_ERROR_RETURN ((LM_ERROR, - "(%P|%t) %p bad read\n", - "client"), - -1); - case 0: - ACE_ERROR_RETURN ((LM_ERROR, - "(%P|%t) closing daemon (fd = %d)\n", - this->get_handle ()), - -1); - default: - rdbuf[bytes_read] = 0; - ACE_DEBUG ((LM_DEBUG, - "(%P|%t) from client: %s", - rdbuf)); - } - - return 0; -} diff --git a/docs/tutorials/007/client_handler.h b/docs/tutorials/007/client_handler.h deleted file mode 100644 index 9bb4af24358..00000000000 --- a/docs/tutorials/007/client_handler.h +++ /dev/null @@ -1,156 +0,0 @@ -// $Id$ - -#ifndef CLIENT_HANDLER_H -#define CLIENT_HANDLER_H - -/* Our client handler must exist somewhere in the ACE_Event_Handler - object hierarchy. This is a requirement of the ACE_Reactor because - it maintains ACE_Event_Handler pointers for each registered event - handler. You could derive our Client_Handler directly from - ACE_Event_Handler but you still have to have an ACE_SOCK_Stream for - the actually connection. With a direct derivative of - ACE_Event_Handler, you'll have to contain and maintain an - ACE_SOCK_Stream instance yourself. With ACE_Svc_Handler (which is - a derivative of ACE_Event_Handler) some of those details are - handled for you. */ - -#include "ace/Svc_Handler.h" - -#if !defined (ACE_LACKS_PRAGMA_ONCE) -# pragma once -#endif /* ACE_LACKS_PRAGMA_ONCE */ - -#include "ace/SOCK_Stream.h" - -class Client_Acceptor; -class Thread_Pool; - -/* Another feature of ACE_Svc_Handler is it's ability to present the - ACE_Task<> interface as well. That's what the ACE_NULL_SYNCH - parameter below is all about. That's beyond our scope here but - we'll come back to it in the next tutorial when we start looking at - concurrency options. */ -class Client_Handler : public ACE_Svc_Handler <ACE_SOCK_STREAM, ACE_NULL_SYNCH> -{ -public: - typedef ACE_Svc_Handler <ACE_SOCK_STREAM, ACE_NULL_SYNCH> inherited; - - // Constructor... - Client_Handler (void); - - /* The destroy() method is our preferred method of destruction. We - could have overloaded the delete operator but that is neither easy - nor intuitive (at least to me). Instead, we provide a new method - of destruction and we make our destructor protected so that only - ourselves, our derivatives and our friends can delete us. It's a - nice compromise. */ - void destroy (void); - - /* Most ACE objects have an open() method. That's how you make them - ready to do work. ACE_Event_Handler has a virtual open() method - which allows us to create this overrride. ACE_Acceptor<> will - invoke this method after creating a new Client_Handler when a - client connects. Notice that the parameter to open() is a void*. - It just so happens that the pointer points to the acceptor which - created us. You would like for the parameter to be an - ACE_Acceptor<>* but since ACE_Event_Handler is generic, that would - tie it too closely to the ACE_Acceptor<> set of objects. In our - definition of open() you'll see how we get around that. */ - int open (void *acceptor); - - /* When an ACE_Task<> object falls out of the svc() method, the - framework will call the close() method. That's where we want to - cleanup ourselves if we're running in either thread-per-connection - or thread-pool mode. */ - int close (u_long flags = 0); - - /* When there is activity on a registered handler, the - handle_input() method of the handler will be invoked. If that - method returns an error code (eg -- -1) then the reactor will - invoke handle_close() to allow the object to clean itself - up. Since an event handler can be registered for more than one - type of callback, the callback mask is provided to inform - handle_close() exactly which method failed. That way, you don't - have to maintain state information between your handle_* method - calls. The <handle> parameter is explained below... As a - side-effect, the reactor will also invoke remove_handler() for the - object on the mask that caused the -1 return. This means that we - don't have to do that ourselves! */ - int handle_close (ACE_HANDLE handle, - ACE_Reactor_Mask mask); - - /* When we register with the reactor, we're going to tell it that we - want to be notified of READ events. When the reactor sees that - there is read activity for us, our handle_input() will be - invoked. The _handleg provided is the handle (file descriptor in - Unix) of the actual connection causing the activity. Since we're - derived from ACE_Svc_Handler<> and it maintains it's own peer - (ACE_SOCK_Stream) object, this is redundant for us. However, if - we had been derived directly from ACE_Event_Handler, we may have - chosen not to contain the peer. In that case, the <handle> would - be important to us for reading the client's data. */ - int handle_input (ACE_HANDLE handle); - -protected: - - /* If the Client_Acceptor which created us has chosen a - thread-per-connection strategy then our open() method will - activate us into a dedicate thread. The svc() method will then - execute in that thread performing some of the functions we used to - leave up to the reactor. */ - int svc (void); - - /* This has nothing at all to do with ACE. I've added this here as - a worker function which I will call from handle_input(). That - allows me to introduce concurrencly in later tutorials with a no - changes to the worker function. You can think of process() as - application-level code and everything elase as - application-framework code. */ - int process (char *rdbuf, int rdbuf_len); - - /* We don't really do anything in our destructor but we've declared - it to be protected to prevent casual deletion of this object. As - I said above, I really would prefer that everyone goes through the - destroy() method to get rid of us. */ - ~Client_Handler (void); - - /* When we get to the definition of Client_Handler we'll see that - there are several places where we go back to the Client_Acceptor - for information. It is generally a good idea to do that through - an accesor rather than using the member variable directly. */ - Client_Acceptor *client_acceptor (void) - { - return this->client_acceptor_; - } - - /* And since you shouldn't access a member variable directly, - neither should you set (mutate) it. Although it might seem silly - to do it this way, you'll thank yourself for it later. */ - void client_acceptor (Client_Acceptor *_client_acceptor) - { - this->client_acceptor_ = _client_acceptor; - } - - /* The concurrency() accessor tells us the current concurrency - strategy. It actually queries the Client_Acceptor for it but by - having the accessor in place, we could change our implementation - without affecting everything that needs to know. */ - int concurrency (void); - - /* Likewise for access to the Thread_Pool that we belong to. */ - Thread_Pool * thread_pool (void); - - Client_Acceptor *client_acceptor_; - - /* For some reason I didn't create accessor/mutator methods for - this. So much for consistency.... - - This variable is used to remember the thread in which we were - created: the "creator" thread in other words. handle_input() - needs to know if it is operating in the main reactor thread (which - is the one that created us) or if it is operating in one of the - thread pool threads. More on this when we get to handle_input(). */ - ACE_thread_t creator_; -}; - -#endif /* CLIENT_HANDLER_H */ diff --git a/docs/tutorials/007/combine.shar b/docs/tutorials/007/combine.shar deleted file mode 100644 index ba4dfe41b2b..00000000000 --- a/docs/tutorials/007/combine.shar +++ /dev/null @@ -1,685 +0,0 @@ -#!/bin/sh -# This is a shell archive (produced by GNU sharutils 4.2). -# To extract the files from this archive, save it to some FILE, remove -# everything before the `!/bin/sh' line above, then type `sh FILE'. -# -# Made on 2000-03-19 15:00 EST by <jcej@chiroptera.tragus.org>. -# Source directory was `/home/jcej/projects/ACE_wrappers/docs/tutorials/007'. -# -# Existing files will *not* be overwritten unless `-c' is specified. -# -# This shar contains: -# length mode name -# ------ ---------- ------------------------------------------ -# 576 -rw-rw-r-- hdr -# 123 -rw-rw-r-- bodies -# 3383 -rw-rw-r-- page01.pre -# 87 -rw-rw-r-- page02.pre -# 120 -rw-rw-r-- page03.pre -# 171 -rw-rw-r-- page04.pre -# 105 -rw-rw-r-- page05.pre -# 160 -rw-rw-r-- page06.pre -# 340 -rw-rw-r-- page07.pre -# 377 -rw-rw-r-- page08.pre -# 2070 -rw-rw-r-- page09.pre -# 173 -rw-rw-r-- page02.pst -# 116 -rw-rw-r-- page03.pst -# 106 -rw-rw-r-- page04.pst -# 234 -rw-rw-r-- page05.pst -# 177 -rw-rw-r-- page06.pst -# 97 -rw-rw-r-- page07.pst -# -save_IFS="${IFS}" -IFS="${IFS}:" -gettext_dir=FAILED -locale_dir=FAILED -first_param="$1" -for dir in $PATH -do - if test "$gettext_dir" = FAILED && test -f $dir/gettext \ - && ($dir/gettext --version >/dev/null 2>&1) - then - set `$dir/gettext --version 2>&1` - if test "$3" = GNU - then - gettext_dir=$dir - fi - fi - if test "$locale_dir" = FAILED && test -f $dir/shar \ - && ($dir/shar --print-text-domain-dir >/dev/null 2>&1) - then - locale_dir=`$dir/shar --print-text-domain-dir` - fi -done -IFS="$save_IFS" -if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED -then - echo=echo -else - TEXTDOMAINDIR=$locale_dir - export TEXTDOMAINDIR - TEXTDOMAIN=sharutils - export TEXTDOMAIN - echo="$gettext_dir/gettext -s" -fi -touch -am 1231235999 $$.touch >/dev/null 2>&1 -if test ! -f 1231235999 && test -f $$.touch; then - shar_touch=touch -else - shar_touch=: - echo - $echo 'WARNING: not restoring timestamps. Consider getting and' - $echo "installing GNU \`touch', distributed in GNU File Utilities..." - echo -fi -rm -f 1231235999 $$.touch -# -if mkdir _sh32447; then - $echo 'x -' 'creating lock directory' -else - $echo 'failed to create lock directory' - exit 1 -fi -# ============= hdr ============== -if test -f 'hdr' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'hdr' '(file already exists)' -else - $echo 'x -' extracting 'hdr' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'hdr' && -<HTML> -<HEAD> -X <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> -X <META NAME="GENERATOR" CONTENT="Mozilla/4.04 [en] (X11; I; Linux 2.0.32 i486) [Netscape]"> -X <META NAME="Author" CONTENT="James CE Johnson"> -X <META NAME="Description" CONTENT="A first step towards using ACE productively"> -X <TITLE>ACE Tutorial 007</TITLE> -</HEAD> -<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> -X -<CENTER><B><FONT SIZE=+2>ACE Tutorial 007</FONT></B></CENTER> -X -<CENTER><B><FONT SIZE=+2>Creating a thread-pool server</FONT></B></CENTER> -<HR> -SHAR_EOF - $shar_touch -am 03191459100 'hdr' && - chmod 0664 'hdr' || - $echo 'restore of' 'hdr' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'hdr:' 'MD5 check failed' -151b1b4bda96cc1e3ef55356e819ca42 hdr -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'hdr'`" - test 576 -eq "$shar_count" || - $echo 'hdr:' 'original size' '576,' 'current size' "$shar_count!" - fi -fi -# ============= bodies ============== -if test -f 'bodies' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'bodies' '(file already exists)' -else - $echo 'x -' extracting 'bodies' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'bodies' && -PAGE=2 -server.cpp -client_acceptor.h -client_acceptor.cpp -client_handler.h -client_handler.cpp -thread_pool.h -thread_pool.cpp -X -SHAR_EOF - $shar_touch -am 0121152599 'bodies' && - chmod 0664 'bodies' || - $echo 'restore of' 'bodies' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'bodies:' 'MD5 check failed' -7675a97fa145886f534c43a8e1a0e6d1 bodies -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'bodies'`" - test 123 -eq "$shar_count" || - $echo 'bodies:' 'original size' '123,' 'current size' "$shar_count!" - fi -fi -# ============= page01.pre ============== -if test -f 'page01.pre' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page01.pre' '(file already exists)' -else - $echo 'x -' extracting 'page01.pre' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page01.pre' && -X -X -X -<P>In this tutorial, we're going to extend Tutorial 6 to add a third concurrency -strategy: thread-pool. Like Tutorial 6 did to Tutorial 5, we're -going to keep the existing strategies that we've already created and add -this one in as a "bonus". As you'll see, our basic objects will change -but not by a whole lot. To accomplish this, we'll introduce one new -major object that helps to abstract the thread pool concept. -<P> -Some folks have noted that this tutorial is a bit confusing if you -don't first know about ACE_Task. My advice is to give it all a good -read a couple of times. If you're still having problems, take a look -at the ACE_Task tests in $ACE_ROOT/tests or examples in $ACE_ROOT/examples. -<P> -Kirthika's Abstract: -<UL> -In this multithreaded server, the Client_Acceptor has the additional -strategy of managing a thread pool. This helps when two clients don't -want to share the same resources or when different clients -need to run in different priority threads. We could then pool all the -same priority clients into one thread-pool. The thread_pool class is a -new addition used to implement this strategy. It inherits from ACE_Task -with ACE_MT_SYNCH parameter which takes care of syncronization issues -amongst multiple threads. -<P> -ACE_Task follows the Active Object pattern and executes the methods on -the task object in a new thread of execution, i.e it decouples the -execution of a method from its invocation. An ACE_Task has an underlying -thread (or pool of threads) and a Message_Queue which is the only means -of communication among tasks. A Message_Queue consists of -Message_Blocks. -<P> -The Client_Acceptor is registered with the reactor waiting for -connection requests. -On some activity, the reactor calls the handle_input() method on the -Acceptor. The Client_Handler of the Acceptor (for the thread-pool -strategy) unregisters itself from the reactor and -enqueues itself in the Message_Queue of the thread-pool waiting for -svc() to call handle_input() on it. It would then process the data in -its new thread of execution. The ACE_MT_SYNCH option facilitates access -of the Mesage_Blocks across different Message_Queues (here from the main -thread to the one in the thread pool). -<P> -The thread_pool class derives from the ACE_Task class. Its svc() method -dequeues the threads in the Message_Queue and calls handle_input() on -them. The idle threads can take work from the queue while the other -threads are working. It also uses ACE_Atomic_Op as a counter for active -threads in the pool. Also, the ACE_Guard class has been used to provide -thread-safe counter-incrementation and destruction of the Message_Blocks -of the thread-pool. This class guards the critical section region by -acquiring the mutex lock on creation and releasing it as soon as it goes -out of scope. -<P> -Note: a sleep period before all the threads in the pool exit is -necessary for complete destruction of the thread pool. -<P> -This tutorial gives us a flavour of implementing a server with a -thread-pool strategy and how it can be managed using the ACE_Task class, -which provides an OO approach to thread-creation and implementation. -</UL> -<font size=-1>* The additions to this tutorial make use of -ACE_Message_Queue which is discussed in depth in -<A HREF="../010/page01.html">Tutorial 10</A>. Feel free to read ahead -if you get lost in the message queue stuff. -</font> -SHAR_EOF - $shar_touch -am 03191459100 'page01.pre' && - chmod 0664 'page01.pre' || - $echo 'restore of' 'page01.pre' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page01.pre:' 'MD5 check failed' -0bd3a7ce4fbd16691109b32892c3a864 page01.pre -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page01.pre'`" - test 3383 -eq "$shar_count" || - $echo 'page01.pre:' 'original size' '3383,' 'current size' "$shar_count!" - fi -fi -# ============= page02.pre ============== -if test -f 'page02.pre' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page02.pre' '(file already exists)' -else - $echo 'x -' extracting 'page02.pre' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page02.pre' && -<P>As usualy, we start with <A HREF="server.cpp">server.cpp</A> -<BR> -<HR WIDTH="100%"> -SHAR_EOF - $shar_touch -am 03191459100 'page02.pre' && - chmod 0664 'page02.pre' || - $echo 'restore of' 'page02.pre' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page02.pre:' 'MD5 check failed' -37639524942e8882c94523e5189b22ff page02.pre -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page02.pre'`" - test 87 -eq "$shar_count" || - $echo 'page02.pre:' 'original size' '87,' 'current size' "$shar_count!" - fi -fi -# ============= page03.pre ============== -if test -f 'page03.pre' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page03.pre' '(file already exists)' -else - $echo 'x -' extracting 'page03.pre' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page03.pre' && -X -<P>Let's see what things we've had to add to <A HREF="client_acceptor.h">client_acceptor.h</A>. -X -<P> -<HR WIDTH="100%"> -SHAR_EOF - $shar_touch -am 03191459100 'page03.pre' && - chmod 0664 'page03.pre' || - $echo 'restore of' 'page03.pre' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page03.pre:' 'MD5 check failed' -64592ded5ea700b4147face8ad77018f page03.pre -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page03.pre'`" - test 120 -eq "$shar_count" || - $echo 'page03.pre:' 'original size' '120,' 'current size' "$shar_count!" - fi -fi -# ============= page04.pre ============== -if test -f 'page04.pre' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page04.pre' '(file already exists)' -else - $echo 'x -' extracting 'page04.pre' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page04.pre' && -X -<P>Something new this time is <A HREF="client_acceptor.cpp">client_acceptor.cpp</A>. -I finally had enough code to move it out of the header. -X -<P> -<HR WIDTH="100%"> -SHAR_EOF - $shar_touch -am 03191459100 'page04.pre' && - chmod 0664 'page04.pre' || - $echo 'restore of' 'page04.pre' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page04.pre:' 'MD5 check failed' -d5640eb97c0a746761c946c4e93db2e8 page04.pre -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page04.pre'`" - test 171 -eq "$shar_count" || - $echo 'page04.pre:' 'original size' '171,' 'current size' "$shar_count!" - fi -fi -# ============= page05.pre ============== -if test -f 'page05.pre' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page05.pre' '(file already exists)' -else - $echo 'x -' extracting 'page05.pre' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page05.pre' && -X -<P>As you might expect, <A HREF="client_handler.h">client_handler.h</A> -is next. -X -<P> -<HR WIDTH="100%"> -SHAR_EOF - $shar_touch -am 03191459100 'page05.pre' && - chmod 0664 'page05.pre' || - $echo 'restore of' 'page05.pre' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page05.pre:' 'MD5 check failed' -e882d389de5d95571737cfc58552153a page05.pre -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page05.pre'`" - test 105 -eq "$shar_count" || - $echo 'page05.pre:' 'original size' '105,' 'current size' "$shar_count!" - fi -fi -# ============= page06.pre ============== -if test -f 'page06.pre' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page06.pre' '(file already exists)' -else - $echo 'x -' extracting 'page06.pre' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page06.pre' && -X -<P><A HREF="client_handler.cpp">client_handler.cpp</A> -shows some of the changes due to the thread-pool. Just a few -though. -X -<P> -<HR WIDTH="100%"> -SHAR_EOF - $shar_touch -am 03191459100 'page06.pre' && - chmod 0664 'page06.pre' || - $echo 'restore of' 'page06.pre' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page06.pre:' 'MD5 check failed' -d884389625246dfcd8049f0fc648997d page06.pre -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page06.pre'`" - test 160 -eq "$shar_count" || - $echo 'page06.pre:' 'original size' '160,' 'current size' "$shar_count!" - fi -fi -# ============= page07.pre ============== -if test -f 'page07.pre' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page07.pre' '(file already exists)' -else - $echo 'x -' extracting 'page07.pre' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page07.pre' && -<P>Two new files this time. The first is <A HREF="thread_pool.h">thread_pool.h</A> -where we declare our Thread_Pool object. This is responsible for -abstracting away the thread pool implementation details and allowing us -to make so few changes to the rest of the code. -X -<P> -<HR WIDTH="100%"><FONT FACE="Arial,Helvetica"></FONT> -X -SHAR_EOF - $shar_touch -am 03191459100 'page07.pre' && - chmod 0664 'page07.pre' || - $echo 'restore of' 'page07.pre' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page07.pre:' 'MD5 check failed' -e5bcf4bee3e756dda50ccb69c18ac3a1 page07.pre -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page07.pre'`" - test 340 -eq "$shar_count" || - $echo 'page07.pre:' 'original size' '340,' 'current size' "$shar_count!" - fi -fi -# ============= page08.pre ============== -if test -f 'page08.pre' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page08.pre' '(file already exists)' -else - $echo 'x -' extracting 'page08.pre' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page08.pre' && -X -<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 -X was talking about <i>THR_NEW_LWP</i>? Look closely and you'll -X see it here. It's bitwise OR'd with <i>THR_DETACHED</i> just to -X keep things interesting. -<P> -<HR WIDTH="100%"> -SHAR_EOF - $shar_touch -am 03191459100 'page08.pre' && - chmod 0664 'page08.pre' || - $echo 'restore of' 'page08.pre' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page08.pre:' 'MD5 check failed' -09c4d5adcd767080e64649a01bd0957c page08.pre -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page08.pre'`" - test 377 -eq "$shar_count" || - $echo 'page08.pre:' 'original size' '377,' 'current size' "$shar_count!" - fi -fi -# ============= page09.pre ============== -if test -f 'page09.pre' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page09.pre' '(file already exists)' -else - $echo 'x -' extracting 'page09.pre' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page09.pre' && -X -<P>That's it for Tutorial 7. As with Tutorial 6, we really didn't -have to change much to introduce a new threading strategy. Most of -the work was in creating the Thread_Pool object itself. Everything -else was just minor housekeeping. -X -<P>There is a fourth common thread strategy: thread-per-request. -It's not one of my favorites, so I wasn't planning to go into it. -If you want to contribute a tutorial on that topic though, I'll be glad -to include it here. -X -<P>For reference, here's the file list again: -<UL> -<LI> -<A HREF="Makefile">Makefile</A></LI> -X -<LI> -<A HREF="client_acceptor.h">client_acceptor.h</A></LI> -X -<LI> -<A HREF="client_acceptor.cpp">client_acceptor.cpp</A></LI> -X -<LI> -<A HREF="client_handler.cpp">client_handler.cpp</A></LI> -X -<LI> -<A HREF="client_handler.h">client_handler.h</A></LI> -X -<LI> -<A HREF="server.cpp">server.cpp</A></LI> -X -<LI> -<A HREF="thread_pool.h">thread_pool.h</A></LI> -X -<LI> -<A HREF="thread_pool.cpp">thread_pool.cpp</A></LI> -X -</UL> -<P> -<HR WIDTH="100%"> -<P> -<center><h2>Danger, Warning!</h2></center> -Now that I've gone through all of this to create a thread pool server, -I have to point out that this isn't exactly the best or safest way to -do so. The biggest danger we face with this approach is the -possibility of an event handler existing in the thread pool's message -queue <i>after</i> it has been deleted. When the thread's svc() -method attempts to invoke <i>handle_input()</i> you will get a nasty -core dump. -<p> -The safest way to handle the situation is to use reference-counted -pointers everywhere a Client_Handler pointer would be used. That's -beyond the scope of the tutorial but I encourage you to give it a -try. If you want to contribute that back as an enhanced Tutorial, -I'll be glad to include it. -<p> -Another approach that should work quite well is to use the -ACE_TP_Reactor instead of just ACE_Reactor. This takes a little more -setup but results in a cleaner implementation. Again, I've not had -time to develop a Tutorial on the TP_Reactor but would welcome any -contributions. -SHAR_EOF - $shar_touch -am 03191459100 'page09.pre' && - chmod 0664 'page09.pre' || - $echo 'restore of' 'page09.pre' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page09.pre:' 'MD5 check failed' -8649089f4b28456c033dede0e32276a8 page09.pre -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page09.pre'`" - test 2070 -eq "$shar_count" || - $echo 'page09.pre:' 'original size' '2070,' 'current size' "$shar_count!" - fi -fi -# ============= page02.pst ============== -if test -f 'page02.pst' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page02.pst' '(file already exists)' -else - $echo 'x -' extracting 'page02.pst' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page02.pst' && -<HR WIDTH="100%"> -X -<P>Hmmm... No change there. Maybe I should leave out comments -on the stuff I don't change. Let's take a look at client_acceptor.h. -X -<P> -SHAR_EOF - $shar_touch -am 03191459100 'page02.pst' && - chmod 0664 'page02.pst' || - $echo 'restore of' 'page02.pst' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page02.pst:' 'MD5 check failed' -b6226123f4f50eeb16db2f7675aaa171 page02.pst -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page02.pst'`" - test 173 -eq "$shar_count" || - $echo 'page02.pst:' 'original size' '173,' 'current size' "$shar_count!" - fi -fi -# ============= page03.pst ============== -if test -f 'page03.pst' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page03.pst' '(file already exists)' -else - $echo 'x -' extracting 'page03.pst' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page03.pst' && -<HR WIDTH="100%"> -X -<P>Well, except for the new Thread_Pool member variable, most of the changes -are informational. -X -SHAR_EOF - $shar_touch -am 03191459100 'page03.pst' && - chmod 0664 'page03.pst' || - $echo 'restore of' 'page03.pst' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page03.pst:' 'MD5 check failed' -88a4cc7d635a4a6b7645011be580808f page03.pst -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page03.pst'`" - test 116 -eq "$shar_count" || - $echo 'page03.pst:' 'original size' '116,' 'current size' "$shar_count!" - fi -fi -# ============= page04.pst ============== -if test -f 'page04.pst' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page04.pst' '(file already exists)' -else - $echo 'x -' extracting 'page04.pst' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page04.pst' && -<HR WIDTH="100%"> -X -<P>Nothing really surprising here. Most of it just manages the Thread_Pool. -X -<P> -SHAR_EOF - $shar_touch -am 03191459100 'page04.pst' && - chmod 0664 'page04.pst' || - $echo 'restore of' 'page04.pst' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page04.pst:' 'MD5 check failed' -57acbd600df965b4dc96ef0ad7ea9390 page04.pst -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page04.pst'`" - test 106 -eq "$shar_count" || - $echo 'page04.pst:' 'original size' '106,' 'current size' "$shar_count!" - fi -fi -# ============= page05.pst ============== -if test -f 'page05.pst' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page05.pst' '(file already exists)' -else - $echo 'x -' extracting 'page05.pst' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page05.pst' && -<HR WIDTH="100%"> -X -<P>Still, we're just not seeing a lot of changes due to introduction of -the thread pool. That's a good thing! You don't want to go turning -your application upside down just because you changed thread models. -X -SHAR_EOF - $shar_touch -am 03191459100 'page05.pst' && - chmod 0664 'page05.pst' || - $echo 'restore of' 'page05.pst' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page05.pst:' 'MD5 check failed' -98cba63a4dffe925484ca86368c863bb page05.pst -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page05.pst'`" - test 234 -eq "$shar_count" || - $echo 'page05.pst:' 'original size' '234,' 'current size' "$shar_count!" - fi -fi -# ============= page06.pst ============== -if test -f 'page06.pst' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page06.pst' '(file already exists)' -else - $echo 'x -' extracting 'page06.pst' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page06.pst' && -<HR WIDTH="100%"> -X -<P>Ok, now we've gone and changed handle_input() so that it knows when -to do work and when to enqueue itself. Beyond that, we're still about -the same. -X -SHAR_EOF - $shar_touch -am 03191459100 'page06.pst' && - chmod 0664 'page06.pst' || - $echo 'restore of' 'page06.pst' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page06.pst:' 'MD5 check failed' -bfbc05b1679c397403e7106ef12065d9 page06.pst -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page06.pst'`" - test 177 -eq "$shar_count" || - $echo 'page06.pst:' 'original size' '177,' 'current size' "$shar_count!" - fi -fi -# ============= page07.pst ============== -if test -f 'page07.pst' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page07.pst' '(file already exists)' -else - $echo 'x -' extracting 'page07.pst' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page07.pst' && -<HR WIDTH="100%"> -X -<P>Well, that doesn't look too complex. What about the implementation? -X -SHAR_EOF - $shar_touch -am 03191459100 'page07.pst' && - chmod 0664 'page07.pst' || - $echo 'restore of' 'page07.pst' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page07.pst:' 'MD5 check failed' -c1a7fbfe20f12e5a8bdeccc7c8e1af1c page07.pst -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page07.pst'`" - test 97 -eq "$shar_count" || - $echo 'page07.pst:' 'original size' '97,' 'current size' "$shar_count!" - fi -fi -rm -fr _sh32447 -exit 0 diff --git a/docs/tutorials/007/page01.html b/docs/tutorials/007/page01.html deleted file mode 100644 index c8aacc25752..00000000000 --- a/docs/tutorials/007/page01.html +++ /dev/null @@ -1,84 +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>In this tutorial, we're going to extend Tutorial 6 to add a third concurrency -strategy: thread-pool. Like Tutorial 6 did to Tutorial 5, we're -going to keep the existing strategies that we've already created and add -this one in as a "bonus". As you'll see, our basic objects will change -but not by a whole lot. To accomplish this, we'll introduce one new -major object that helps to abstract the thread pool concept. -<P> -Some folks have noted that this tutorial is a bit confusing if you -don't first know about ACE_Task. My advice is to give it all a good -read a couple of times. If you're still having problems, take a look -at the ACE_Task tests in $ACE_ROOT/tests or examples in $ACE_ROOT/examples. -<P> -Kirthika's Abstract: -<UL> -In this multithreaded server, the Client_Acceptor has the additional -strategy of managing a thread pool. This helps when two clients don't -want to share the same resources or when different clients -need to run in different priority threads. We could then pool all the -same priority clients into one thread-pool. The thread_pool class is a -new addition used to implement this strategy. It inherits from ACE_Task -with ACE_MT_SYNCH parameter which takes care of syncronization issues -amongst multiple threads. -<P> -ACE_Task follows the Active Object pattern and executes the methods on -the task object in a new thread of execution, i.e it decouples the -execution of a method from its invocation. An ACE_Task has an underlying -thread (or pool of threads) and a Message_Queue which is the only means -of communication among tasks. A Message_Queue consists of -Message_Blocks. -<P> -The Client_Acceptor is registered with the reactor waiting for -connection requests. -On some activity, the reactor calls the handle_input() method on the -Acceptor. The Client_Handler of the Acceptor (for the thread-pool -strategy) unregisters itself from the reactor and -enqueues itself in the Message_Queue of the thread-pool waiting for -svc() to call handle_input() on it. It would then process the data in -its new thread of execution. The ACE_MT_SYNCH option facilitates access -of the Mesage_Blocks across different Message_Queues (here from the main -thread to the one in the thread pool). -<P> -The thread_pool class derives from the ACE_Task class. Its svc() method -dequeues the threads in the Message_Queue and calls handle_input() on -them. The idle threads can take work from the queue while the other -threads are working. It also uses ACE_Atomic_Op as a counter for active -threads in the pool. Also, the ACE_Guard class has been used to provide -thread-safe counter-incrementation and destruction of the Message_Blocks -of the thread-pool. This class guards the critical section region by -acquiring the mutex lock on creation and releasing it as soon as it goes -out of scope. -<P> -Note: a sleep period before all the threads in the pool exit is -necessary for complete destruction of the thread pool. -<P> -This tutorial gives us a flavour of implementing a server with a -thread-pool strategy and how it can be managed using the ACE_Task class, -which provides an OO approach to thread-creation and implementation. -</UL> -<font size=-1>* The additions to this tutorial make use of -ACE_Message_Queue which is discussed in depth in -<A HREF="../010/page01.html">Tutorial 10</A>. Feel free to read ahead -if you get lost in the message queue stuff. -</font> -<P><HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page02.html">Continue This Tutorial</A>]</CENTER> - diff --git a/docs/tutorials/007/page02.html b/docs/tutorials/007/page02.html deleted file mode 100644 index e9dc3693cce..00000000000 --- a/docs/tutorials/007/page02.html +++ /dev/null @@ -1,142 +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>As usualy, we start with <A HREF="server.cpp">server.cpp</A> -<BR> -<HR WIDTH="100%"> -<PRE> -<font color=red>// $Id$</font> - -<font color=red>/* We try to keep main() very simple. One of the ways we do that is - to push much of the complicated stuff into worker objects. In this - case, we only need to include the acceptor header in our main - source file. We let it worry about the "<font color=green>real work</font>". */</font> - -<font color=blue>#include</font> "<font color=green>client_acceptor.h</font>" - -<font color=red>/* As before, we create a simple signal handler that will set our - finished flag. There are, of course, more elegant ways to handle - program shutdown requests but that isn't really our focus right - now, so we'll just do the easiest thing. */</font> - -static sig_atomic_t finished = 0; -extern "<font color=green>C</font>" void handler (int) -{ - finished = 1; -} - -<font color=red>/* A server has to listen for clients at a known TCP/IP port. The - default ACE port is 10002 (at least on my system) and that's good - enough for what we want to do here. Obviously, a more robust - application would take a command line parameter or read from a - configuration file or do some other clever thing. Just like the - signal handler above, though, that's what we want to focus on, so - we're taking the easy way out. */</font> - -static const u_short PORT = ACE_DEFAULT_SERVER_PORT; - -<font color=red>/* Finally, we get to main. Some C++ compilers will complain loudly - if your function signature doesn't match the prototype. Even - though we're not going to use the parameters, we still have to - specify them. */</font> - -int -main (int argc, char *argv[]) -{ - ACE_UNUSED_ARG(argc); - ACE_UNUSED_ARG(argv); - - <font color=red>/* In our earlier servers, we used a global pointer to get to the - reactor. I've never really liked that idea, so I've moved it into - main() this time. When we get to the Client_Handler object you'll - see how we manage to get a pointer back to this reactor. */</font> - ACE_Reactor reactor; - - <font color=red>/* The acceptor will take care of letting clients connect to us. It - will also arrange for a Client_Handler to be created for each new - client. Since we're only going to listen at one TCP/IP port, we - only need one acceptor. If we wanted, though, we could create - several of these and listen at several ports. (That's what we - would do if we wanted to rewrite inetd for instance.) */</font> - Client_Acceptor peer_acceptor; - - <font color=red>/* Create an ACE_INET_Addr that represents our endpoint of a - connection. We then open our acceptor object with that Addr. - Doing so tells the acceptor where to listen for connections. - Servers generally listen at "<font color=green>well known</font>" addresses. If not, there - must be some mechanism by which the client is informed of the - server's address. - - Note how ACE_ERROR_RETURN is used if we fail to open the acceptor. - This technique is used over and over again in our tutorials. */</font> - if (peer_acceptor.open (ACE_INET_Addr (PORT), &reactor) == -1) - ACE_ERROR_RETURN ((LM_ERROR, - "<font color=green>%p\n</font>", - "<font color=green>open</font>"), - -1); - - <font color=red>/* Install our signal handler. You can actually register signal - handlers with the reactor. You might do that when the signal - handler is responsible for performing "<font color=green>real</font>" work. Our simple - flag-setter doesn't justify deriving from ACE_Event_Handler and - providing a callback function though. */</font> - ACE_Sig_Action sa ((ACE_SignalHandler) handler, SIGINT); - - <font color=red>/* Like ACE_ERROR_RETURN, the ACE_DEBUG macro gets used quite a bit. - It's a handy way to generate uniform debug output from your - program. */</font> - ACE_DEBUG ((LM_DEBUG, - "<font color=green>(%P|%t) starting up server daemon\n</font>")); - - <font color=red>/* This will loop "<font color=green>forever</font>" invoking the handle_events() method of - our reactor. handle_events() watches for activity on any - registered handlers and invokes their appropriate callbacks when - necessary. Callback-driven programming is a big thing in ACE, you - should get used to it. If the signal handler catches something, - the finished flag will be set and we'll exit. Conveniently - enough, handle_events() is also interrupted by signals and will - exit back to the while() loop. (If you want your event loop to - not be interrupted by signals, checkout the <i>restart</i> flag on - the open() method of ACE_Reactor if you're interested.) */</font> - while (!finished) - reactor.handle_events (); - - ACE_DEBUG ((LM_DEBUG, - "<font color=green>(%P|%t) shutting down server daemon\n</font>")); - - return 0; -} - -<font color=blue>#if defined</font> (<font color=purple>ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION</font>) -template class ACE_Acceptor <Client_Handler, ACE_SOCK_ACCEPTOR>; -template class ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>; -template class ACE_Guard<ACE_Mutex>; -template class ACE_Atomic_Op<ACE_Mutex, int>; -<font color=blue>#elif defined</font> (<font color=purple>ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA</font>) -<font color=blue>#pragma</font> <font color=purple>instantiate</font> ACE_Acceptor <Client_Handler, ACE_SOCK_ACCEPTOR> -<font color=blue>#pragma</font> <font color=purple>instantiate</font> ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> -<font color=blue>#pragma</font> <font color=purple>instantiate</font> ACE_Guard<ACE_Mutex> -<font color=blue>#pragma</font> <font color=purple>instantiate</font> ACE_Atomic_Op<ACE_Mutex, int> -<font color=blue>#endif</font> <font color=red>/* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */</font> -</PRE> -<HR WIDTH="100%"> - -<P>Hmmm... No change there. Maybe I should leave out comments -on the stuff I don't change. Let's take a look at client_acceptor.h. - -<P> -<P><HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page03.html">Continue This Tutorial</A>]</CENTER> - diff --git a/docs/tutorials/007/page03.html b/docs/tutorials/007/page03.html deleted file mode 100644 index 25ddbad3bd1..00000000000 --- a/docs/tutorials/007/page03.html +++ /dev/null @@ -1,155 +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>Let's see what things we've had to add to <A HREF="client_acceptor.h">client_acceptor.h</A>. - -<P> -<HR WIDTH="100%"> -<PRE> -<font color=red>// $Id$</font> - -<font color=blue>#ifndef</font> <font color=purple>CLIENT_ACCEPTOR_H</font> -<font color=blue>#define</font> <font color=purple>CLIENT_ACCEPTOR_H</font> - -<font color=red>/* The ACE_Acceptor<> template lives in the ace/Acceptor.h header - file. You'll find a very consitent naming convention between the - ACE objects and the headers where they can be found. In general, - the ACE object ACE_Foobar will be found in ace/Foobar.h. */</font> - -<font color=blue>#include</font> "<A HREF="../../../ace/Acceptor.h">ace/Acceptor.h</A>" - -<font color=blue>#if !defined</font> (<font color=purple>ACE_LACKS_PRAGMA_ONCE</font>) -<font color=blue># pragma</font> <font color=purple>once</font> -<font color=blue>#endif</font> <font color=red>/* ACE_LACKS_PRAGMA_ONCE */</font> - -<font color=red>/* Since we want to work with sockets, we'll need a SOCK_Acceptor to - allow the clients to connect to us. */</font> -<font color=blue>#include</font> "<A HREF="../../../ace/SOCK_Acceptor.h">ace/SOCK_Acceptor.h</A>" - -<font color=red>/* The Client_Handler object we develop will be used to handle clients - once they're connected. The ACE_Acceptor<> template's first - parameter requires such an object. In some cases, you can get by - with just a forward declaration on the class, in others you have to - have the whole thing. */</font> -<font color=blue>#include</font> "<font color=green>client_handler.h</font>" - -<font color=red>/* Parameterize the ACE_Acceptor<> such that it will listen for socket - connection attempts and create Client_Handler objects when they - happen. In Tutorial 001, we wrote the basic acceptor logic on our - own before we realized that ACE_Acceptor<> was available. You'll - get spoiled using the ACE templates because they take away a lot of - the tedious details! */</font> -typedef ACE_Acceptor <Client_Handler, ACE_SOCK_ACCEPTOR> Client_Acceptor_Base; - -<font color=blue>#include</font> "<font color=green>thread_pool.h</font>" - -<font color=red>/* This time we've added quite a bit more to our acceptor. In - addition to providing a choice of concurrency strategies, we also - maintain a Thread_Pool object in case that strategy is chosen. The - object still isn't very complex but it's come a long way from the - simple typedef we had in Tutorial 5. - - Why keep the thread pool as a member? If we go back to the inetd - concept you'll recall that we need several acceptors to make that - work. We may have a situation in which our different client types - requre different resources. That is, we may need a large thread - pool for some client types and a smaller one for others. We could - share a pool but then the client types may have undesirable impact - on one another. - - Just in case you do want to share a single thread pool, there is a - constructor below that will let you do that. */</font> -class Client_Acceptor : public Client_Acceptor_Base -{ -public: - typedef Client_Acceptor_Base inherited; - - <font color=red>/* Now that we have more than two strategies, we need more than a - boolean to tell us what we're using. A set of enums is a good - choice because it allows us to use named values. Another option - would be a set of static const integers. */</font> - enum concurrency_t - { - single_threaded_, - thread_per_connection_, - thread_pool_ - }; - - <font color=red>/* The default constructor allows the programmer to choose the - concurrency strategy. Since we want to focus on thread-pool, - that's what we'll use if nothing is specified. */</font> - Client_Acceptor (int concurrency = thread_pool_); - - <font color=red>/* Another option is to construct the object with an existing thread - pool. The concurrency strategy is pretty obvious at that point. */</font> - Client_Acceptor (Thread_Pool &thread_pool); - - <font color=red>/* Our destructor will take care of shutting down the thread-pool if - applicable. */</font> - ~Client_Acceptor (void); - - <font color=red>/* Open ourselves and register with the given reactor. The thread - pool size can be specified here if you want to use that - concurrency strategy. */</font> - int open (const ACE_INET_Addr &addr, - ACE_Reactor *reactor, - int pool_size = <font color=#008888>Thread_Pool::default_pool_size_</font>); - - <font color=red>/* Close ourselves and our thread pool if applicable */</font> - int close (void); - - <font color=red>/* What is our concurrency strategy? */</font> - int concurrency (void) - { - return this->concurrency_; - } - - <font color=red>/* Give back a pointer to our thread pool. Our Client_Handler - objects will need this so that their handle_input() methods can - put themselves into the pool. Another alternative would be a - globally accessible thread pool. ACE_Singleton<> is a way to - achieve that. */</font> - Thread_Pool *thread_pool (void) - { - return &this->the_thread_pool_; - } - - <font color=red>/* Since we can be constructed with a Thread_Pool reference, there - are times when we need to know if the thread pool we're using is - ours or if we're just borrowing it from somebody else. */</font> - int thread_pool_is_private (void) - { - return &the_thread_pool_ == &private_thread_pool_; - } - -protected: - int concurrency_; - - Thread_Pool private_thread_pool_; - - Thread_Pool &the_thread_pool_; -}; - -<font color=blue>#endif</font> <font color=red>/* CLIENT_ACCEPTOR_H */</font> -</PRE> -<HR WIDTH="100%"> - -<P>Well, except for the new Thread_Pool member variable, most of the changes -are informational. - -<P><HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page04.html">Continue This Tutorial</A>]</CENTER> - diff --git a/docs/tutorials/007/page04.html b/docs/tutorials/007/page04.html deleted file mode 100644 index 8f5a04a13d9..00000000000 --- a/docs/tutorials/007/page04.html +++ /dev/null @@ -1,87 +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>Something new this time is <A HREF="client_acceptor.cpp">client_acceptor.cpp</A>. -I finally had enough code to move it out of the header. - -<P> -<HR WIDTH="100%"> -<PRE> -<font color=red>// $Id$</font> - -<font color=blue>#include</font> "<font color=green>client_acceptor.h</font>" - -<font color=red>/* Construct ourselves with the chosen concurrency strategy. Notice - that we also set our Thread_Pool reference to our private instance. */</font> -<font color=#008888>Client_Acceptor::Client_Acceptor</font> (int concurrency) - : concurrency_ (concurrency), - the_thread_pool_ (private_thread_pool_) -{ -} - -<font color=red>/* Construct ourselves with a reference to somebody else' Thread_Pool. - Obvioulsy our concurrency strategy is "<font color=green>thread_pool_</font>" at this point. */</font> -<font color=#008888>Client_Acceptor::Client_Acceptor</font> (Thread_Pool &thread_pool) - : concurrency_ (thread_pool_), - the_thread_pool_ (thread_pool) -{ -} - -<font color=red>/* When we're destructed, we may need to cleanup after ourselves. If - we're running with a thread pool that we own, it is up to us to - close it down. */</font> -<font color=#008888>Client_Acceptor::~Client_Acceptor</font> (void) -{ - if (this->concurrency() == thread_pool_ && thread_pool_is_private ()) - thread_pool ()->close (); -} - -<font color=red>/* Similar to the destructor (and close() below) it is necessary for - us to open the thread pool in some circumstances. - - Notice how we delegate most of the open() work to the open() method - of our baseclass. */</font> -int -<font color=#008888>Client_Acceptor::open</font> (const ACE_INET_Addr &addr, - ACE_Reactor *reactor, - int pool_size) -{ - if (this->concurrency() == thread_pool_ && thread_pool_is_private ()) - thread_pool ()->open (pool_size); - - return <font color=#008888>inherited::open</font> (addr, reactor); -} - -<font color=red>/* Here again we find that we have to manage the thread pool. Like - open() we also delegate the other work to our baseclass. */</font> -int -<font color=#008888>Client_Acceptor::close</font> (void) -{ - if (this->concurrency() == thread_pool_ && thread_pool_is_private ()) - thread_pool ()->close (); - - return <font color=#008888>inherited::close</font> (); -} - -</PRE> -<HR WIDTH="100%"> - -<P>Nothing really surprising here. Most of it just manages the Thread_Pool. - -<P> -<P><HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page05.html">Continue This Tutorial</A>]</CENTER> - diff --git a/docs/tutorials/007/page05.html b/docs/tutorials/007/page05.html deleted file mode 100644 index 89307d103d5..00000000000 --- a/docs/tutorials/007/page05.html +++ /dev/null @@ -1,188 +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>As you might expect, <A HREF="client_handler.h">client_handler.h</A> -is next. - -<P> -<HR WIDTH="100%"> -<PRE> -<font color=red>// $Id$</font> - -<font color=blue>#ifndef</font> <font color=purple>CLIENT_HANDLER_H</font> -<font color=blue>#define</font> <font color=purple>CLIENT_HANDLER_H</font> - -<font color=red>/* Our client handler must exist somewhere in the ACE_Event_Handler - object hierarchy. This is a requirement of the ACE_Reactor because - it maintains ACE_Event_Handler pointers for each registered event - handler. You could derive our Client_Handler directly from - ACE_Event_Handler but you still have to have an ACE_SOCK_Stream for - the actually connection. With a direct derivative of - ACE_Event_Handler, you'll have to contain and maintain an - ACE_SOCK_Stream instance yourself. With ACE_Svc_Handler (which is - a derivative of ACE_Event_Handler) some of those details are - handled for you. */</font> - -<font color=blue>#include</font> "<A HREF="../../../ace/Svc_Handler.h">ace/Svc_Handler.h</A>" - -<font color=blue>#if !defined</font> (<font color=purple>ACE_LACKS_PRAGMA_ONCE</font>) -<font color=blue># pragma</font> <font color=purple>once</font> -<font color=blue>#endif</font> <font color=red>/* ACE_LACKS_PRAGMA_ONCE */</font> - -<font color=blue>#include</font> "<A HREF="../../../ace/SOCK_Stream.h">ace/SOCK_Stream.h</A>" - -class Client_Acceptor; -class Thread_Pool; - -<font color=red>/* Another feature of ACE_Svc_Handler is it's ability to present the - ACE_Task<> interface as well. That's what the ACE_NULL_SYNCH - parameter below is all about. That's beyond our scope here but - we'll come back to it in the next tutorial when we start looking at - concurrency options. */</font> -class Client_Handler : public ACE_Svc_Handler <ACE_SOCK_STREAM, ACE_NULL_SYNCH> -{ -public: - typedef ACE_Svc_Handler <ACE_SOCK_STREAM, ACE_NULL_SYNCH> inherited; - - <font color=red>// Constructor...</font> - Client_Handler (void); - - <font color=red>/* The destroy() method is our preferred method of destruction. We - could have overloaded the delete operator but that is neither easy - nor intuitive (at least to me). Instead, we provide a new method - of destruction and we make our destructor protected so that only - ourselves, our derivatives and our friends can delete us. It's a - nice compromise. */</font> - void destroy (void); - - <font color=red>/* Most ACE objects have an open() method. That's how you make them - ready to do work. ACE_Event_Handler has a virtual open() method - which allows us to create this overrride. ACE_Acceptor<> will - invoke this method after creating a new Client_Handler when a - client connects. Notice that the parameter to open() is a void*. - It just so happens that the pointer points to the acceptor which - created us. You would like for the parameter to be an - ACE_Acceptor<>* but since ACE_Event_Handler is generic, that would - tie it too closely to the ACE_Acceptor<> set of objects. In our - definition of open() you'll see how we get around that. */</font> - int open (void *acceptor); - - <font color=red>/* When an ACE_Task<> object falls out of the svc() method, the - framework will call the close() method. That's where we want to - cleanup ourselves if we're running in either thread-per-connection - or thread-pool mode. */</font> - int close (u_long flags = 0); - - <font color=red>/* When there is activity on a registered handler, the - handle_input() method of the handler will be invoked. If that - method returns an error code (eg -- -1) then the reactor will - invoke handle_close() to allow the object to clean itself - up. Since an event handler can be registered for more than one - type of callback, the callback mask is provided to inform - handle_close() exactly which method failed. That way, you don't - have to maintain state information between your handle_* method - calls. The <handle> parameter is explained below... As a - side-effect, the reactor will also invoke remove_handler() for the - object on the mask that caused the -1 return. This means that we - don't have to do that ourselves! */</font> - int handle_close (ACE_HANDLE handle, - ACE_Reactor_Mask mask); - - <font color=red>/* When we register with the reactor, we're going to tell it that we - want to be notified of READ events. When the reactor sees that - there is read activity for us, our handle_input() will be - invoked. The _handleg provided is the handle (file descriptor in - Unix) of the actual connection causing the activity. Since we're - derived from ACE_Svc_Handler<> and it maintains it's own peer - (ACE_SOCK_Stream) object, this is redundant for us. However, if - we had been derived directly from ACE_Event_Handler, we may have - chosen not to contain the peer. In that case, the <handle> would - be important to us for reading the client's data. */</font> - int handle_input (ACE_HANDLE handle); - -protected: - - <font color=red>/* If the Client_Acceptor which created us has chosen a - thread-per-connection strategy then our open() method will - activate us into a dedicate thread. The svc() method will then - execute in that thread performing some of the functions we used to - leave up to the reactor. */</font> - int svc (void); - - <font color=red>/* This has nothing at all to do with ACE. I've added this here as - a worker function which I will call from handle_input(). That - allows me to introduce concurrencly in later tutorials with a no - changes to the worker function. You can think of process() as - application-level code and everything elase as - application-framework code. */</font> - int process (char *rdbuf, int rdbuf_len); - - <font color=red>/* We don't really do anything in our destructor but we've declared - it to be protected to prevent casual deletion of this object. As - I said above, I really would prefer that everyone goes through the - destroy() method to get rid of us. */</font> - ~Client_Handler (void); - - <font color=red>/* When we get to the definition of Client_Handler we'll see that - there are several places where we go back to the Client_Acceptor - for information. It is generally a good idea to do that through - an accesor rather than using the member variable directly. */</font> - Client_Acceptor *client_acceptor (void) - { - return this->client_acceptor_; - } - - <font color=red>/* And since you shouldn't access a member variable directly, - neither should you set (mutate) it. Although it might seem silly - to do it this way, you'll thank yourself for it later. */</font> - void client_acceptor (Client_Acceptor *_client_acceptor) - { - this->client_acceptor_ = _client_acceptor; - } - - <font color=red>/* The concurrency() accessor tells us the current concurrency - strategy. It actually queries the Client_Acceptor for it but by - having the accessor in place, we could change our implementation - without affecting everything that needs to know. */</font> - int concurrency (void); - - <font color=red>/* Likewise for access to the Thread_Pool that we belong to. */</font> - Thread_Pool * thread_pool (void); - - Client_Acceptor *client_acceptor_; - - <font color=red>/* For some reason I didn't create accessor/mutator methods for - this. So much for consistency.... - - This variable is used to remember the thread in which we were - created: the "<font color=green>creator</font>" thread in other words. handle_input() - needs to know if it is operating in the main reactor thread (which - is the one that created us) or if it is operating in one of the - thread pool threads. More on this when we get to handle_input(). */</font> - ACE_thread_t creator_; -}; - -<font color=blue>#endif</font> <font color=red>/* CLIENT_HANDLER_H */</font> -</PRE> -<HR WIDTH="100%"> - -<P>Still, we're just not seeing a lot of changes due to introduction of -the thread pool. That's a good thing! You don't want to go turning -your application upside down just because you changed thread models. - -<P><HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page06.html">Continue This Tutorial</A>]</CENTER> - diff --git a/docs/tutorials/007/page06.html b/docs/tutorials/007/page06.html deleted file mode 100644 index 2da15db1278..00000000000 --- a/docs/tutorials/007/page06.html +++ /dev/null @@ -1,266 +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><A HREF="client_handler.cpp">client_handler.cpp</A> -shows some of the changes due to the thread-pool. Just a few -though. - -<P> -<HR WIDTH="100%"> -<PRE> -<font color=red>// $Id$</font> - -<font color=red>/* Since this is the third time we've seen most of this, I'm going to - strip out almost all of the comments that you've already seen. - That way, you can concentrate on the new items. */</font> - -<font color=blue>#include</font> "<font color=green>client_acceptor.h</font>" -<font color=blue>#include</font> "<font color=green>client_handler.h</font>" - -<font color=red>/* We're going to be registering and unregistering a couple of times. - To make sure that we use the same flags every time, I've created - these handy macros. */</font> -<font color=blue>#define</font> <font color=purple>REGISTER_MASK</font> <font color=#008888>ACE_Event_Handler::READ_MASK</font> -<font color=blue>#define</font> <font color=purple>REMOVE_MASK</font> (<font color=#008888>ACE_Event_Handler::READ_MASK</font> | ACE_Event_Handler::DONT_CALL) - -<font color=red>/* Our constructor still doesn't really do anything. We simply - initialize the acceptor pointer to "<font color=green>null</font>" and get our current - thread id. The static self() method of ACE_Thread will return you - a thread id native to your platform. */</font> -<font color=#008888>Client_Handler::Client_Handler</font> (void) - : client_acceptor_(0), - creator_ (<font color=#008888>ACE_Thread::self</font> ()) -{ -} - -<font color=#008888>Client_Handler::~Client_Handler</font> (void) -{ - this->peer().close(); -} - -<font color=red>/* Query our acceptor for the concurrency strategy. Notice that we - don't bother to check that our acceptor pointer is valid. That is - proably a bad idea... */</font> -int -<font color=#008888>Client_Handler::concurrency</font>(void) -{ - return this->client_acceptor ()->concurrency (); -} - -<font color=red>/* And here we ask the acceptor about the thread pool. */</font> -Thread_Pool * -<font color=#008888>Client_Handler::thread_pool</font> (void) -{ - return this->client_acceptor ()->thread_pool (); -} - -<font color=red>/* Back to our open() method. This is straight out of Tutorial 6. - There's nothing additional here for the thread-pool implementation. */</font> -int -<font color=#008888>Client_Handler::open</font> (void *acceptor) -{ - client_acceptor ((Client_Acceptor *) acceptor); - - if (concurrency () == <font color=#008888>Client_Acceptor::thread_per_connection_</font>) - return this->activate (THR_DETACHED); - - this->reactor (client_acceptor()->reactor ()); - - ACE_INET_Addr addr; - - if (this->peer ().get_remote_addr (addr) == -1) - return -1; - - if (this->reactor ()->register_handler (this, - REGISTER_MASK) == -1) - ACE_ERROR_RETURN ((LM_ERROR, - "<font color=green>(%P|%t) can't register with reactor\n</font>"), - -1); - - ACE_DEBUG ((LM_DEBUG, - "<font color=green>(%P|%t) connected with %s\n</font>", - addr.get_host_name ())); - return 0; -} - -<font color=red>/* The destroy() method will remove us from the reactor (with the - DONT_CALL flag set!) and then free our memory. This allows us to - be closed from outside of the reactor context without any danger. */</font> -void -<font color=#008888>Client_Handler::destroy</font> (void) -{ - this->reactor ()->remove_handler (this, REMOVE_MASK); - delete this; -} - -<font color=red>/* As mentioned in the header, the typical way to close an object in a - threaded context is to invoke it's close() method. */</font> -int -<font color=#008888>Client_Handler::close</font> (u_long flags) -{ - ACE_UNUSED_ARG(flags); - - <font color=red>/* - We use the destroy() method to clean up after ourselves. - That will take care of removing us from the reactor and then - freeing our memory. - */</font> - this->destroy (); - - <font color=red>/* Don't forward the close() to the baseclass! handle_close() above - has already taken care of delete'ing. Forwarding close() would - cause that to happen again and things would get really ugly at - that point! */</font> - return 0; -} - -<font color=red>/* We will be called when handle_input() returns -1. That's our queue - to delete ourselves to prevent memory leaks. */</font> -int -<font color=#008888>Client_Handler::handle_close</font> (ACE_HANDLE handle, - ACE_Reactor_Mask mask) -{ - ACE_UNUSED_ARG (handle); - ACE_UNUSED_ARG (mask); - - delete this; - - return 0; -} - -<font color=red>/* In the open() method, we registered with the reactor and requested - to be notified when there is data to be read. When the reactor - sees that activity it will invoke this handle_input() method on us. - As I mentioned, the _handle parameter isn't useful to us but it - narrows the list of methods the reactor has to worry about and the - list of possible virtual functions we would have to override. - - You've read that much before... Now we have to do some extra stuff - in case we're using the thread-pool implementation. If we're - called by our creator thread then we must be in the reactor. In - that case, we arrange to be put into the thread pool. If we're not - in the creator thread then we must be in the thread pool and we can - do some work. */</font> -int -<font color=#008888>Client_Handler::handle_input</font> (ACE_HANDLE handle) -{ - ACE_UNUSED_ARG (handle); - - <font color=red>/* Check our strategy. If we're using the thread pool and we're in - the creation thread then we know we were called by the reactor. */</font> - if (concurrency () == <font color=#008888>Client_Acceptor::thread_pool_</font>) - { - if (<font color=#008888>ACE_OS::thr_equal</font> (ACE_Thread::self(), - creator_)) - { - <font color=red>/* Remove ourselves from the reactor and ask to be put into - the thread pool's queue of work. (You should be able to - use suspend_handler() but I've had problems with that.) - - By removing ourselves from the reactor, we're guaranteed - that we won't be called back until the thread pool picks - us up out of the queue. If we didn't remove ourselves, - then the reactor would continue to invoke handle_input() - and we don't want that to happen. */</font> - this->reactor ()->remove_handler (this, REMOVE_MASK); - return this->thread_pool ()->enqueue (this); - } - } - - <font color=red>/* Any strategy other than thread-per-connection will eventually get - here. If we're in the single-threaded implementation or the - thread-pool, we still have to pass this way. */</font> - - char buf[BUFSIZ]; - - <font color=red>/* Invoke the process() method to do the work but save it's return - value instead of returning it immediately. */</font> - - int rval = this->process (buf, sizeof (buf)); - - <font color=red>/* Now, we look again to see if we're in the thread-pool - implementation. If so then we need to re-register ourselves with - the reactor so that we can get more work when it is available. - (If suspend_handler() worked then we would use resume_handler() - here.) */</font> - if (concurrency () == <font color=#008888>Client_Acceptor::thread_pool_</font>) - { - if (rval != -1) - <font color=red>/* If we don't remember to re-register ourselves, then we - won't be able to respond to any future client requests. */</font> - this->reactor ()->register_handler (this, - REGISTER_MASK); - } - - <font color=red>/* Return the result of process() */</font> - return rval; -} - -<font color=red>/* Remember that when we leave our svc() method, the framework will - take care of calling our close() method so that we can cleanup - after ourselves. */</font> -int -<font color=#008888>Client_Handler::svc</font> (void) -{ - char buf[BUFSIZ]; - - while (1) - if (this->process (buf, sizeof (buf)) == -1) - return -1; - - return 0; -} - -<font color=red>/* Once again, we see that the application-level logic has not been at - all affected by our choice of threading models. Of course, I'm not - sharing data between threads or anything. We'll leave locking - issues for a later tutorial. */</font> -int -<font color=#008888>Client_Handler::process</font> (char *rdbuf, - int rdbuf_len) -{ - ssize_t bytes_read; - switch ( (bytes_read = this->peer ().recv (rdbuf, rdbuf_len)) ) - { - case -1: - ACE_ERROR_RETURN ((LM_ERROR, - "<font color=green>(%P|%t) %p bad read\n</font>", - "<font color=green>client</font>"), - -1); - case 0: - ACE_ERROR_RETURN ((LM_ERROR, - "<font color=green>(%P|%t) closing daemon (fd = %d)\n</font>", - this->get_handle ()), - -1); - default: - rdbuf[bytes_read] = 0; - ACE_DEBUG ((LM_DEBUG, - "<font color=green>(%P|%t) from client: %s</font>", - rdbuf)); - } - - return 0; -} -</PRE> -<HR WIDTH="100%"> - -<P>Ok, now we've gone and changed handle_input() so that it knows when -to do work and when to enqueue itself. Beyond that, we're still about -the same. - -<P><HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page07.html">Continue This Tutorial</A>]</CENTER> - diff --git a/docs/tutorials/007/page07.html b/docs/tutorials/007/page07.html deleted file mode 100644 index 52f6ecfac70..00000000000 --- a/docs/tutorials/007/page07.html +++ /dev/null @@ -1,121 +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>Two new files this time. The first is <A HREF="thread_pool.h">thread_pool.h</A> -where we declare our Thread_Pool object. This is responsible for -abstracting away the thread pool implementation details and allowing us -to make so few changes to the rest of the code. - -<P> -<HR WIDTH="100%"><FONT FACE="Arial,Helvetica"></FONT> - -<PRE> -<font color=red>// $Id$</font> - -<font color=blue>#ifndef</font> <font color=purple>THREAD_POOL_H</font> -<font color=blue>#define</font> <font color=purple>THREAD_POOL_H</font> - -<font color=red>/* In order to implement a thread pool, we have to have an object that - can create a thread. The ACE_Task<> is the basis for doing just - such a thing. */</font> -<font color=blue>#include</font> "<A HREF="../../../ace/Task.h">ace/Task.h</A>" - -<font color=blue>#if !defined</font> (<font color=purple>ACE_LACKS_PRAGMA_ONCE</font>) -<font color=blue># pragma</font> <font color=purple>once</font> -<font color=blue>#endif</font> <font color=red>/* ACE_LACKS_PRAGMA_ONCE */</font> - -<font color=red>/* We need a forward reference for ACE_Event_Handler so that our - enqueue() method can accept a pointer to one. */</font> -class ACE_Event_Handler; - -<font color=red>/* Although we modified the rest of our program to make use of the - thread pool implementation, if you look closely you'll see that the - changes were rather minor. The "<font color=green>ACE way</font>" is generally to create a - helper object that abstracts away the details not relevant to your - application. That's what I'm trying to do here by creating the - Thread_Pool object. */</font> -class Thread_Pool : public ACE_Task<ACE_MT_SYNCH> -{ -public: - typedef ACE_Task<ACE_MT_SYNCH> inherited; - - <font color=red>/* Provide an enumeration for the default pool size. By doing this, - other objects can use the value when they want a default. */</font> - enum size_t - { - default_pool_size_ = 5 - }; - - <font color=red>// Basic constructor</font> - Thread_Pool (void); - - <font color=red>/* Opening the thread pool causes one or more threads to be - activated. When activated, they all execute the svc() method - declared below. */</font> - int open (int pool_size = default_pool_size_); - - <font color=red>/* Some compilers will complain that our open() above attempts to - override a virtual function in the baseclass. We have no - intention of overriding that method but in order to keep the - compiler quiet we have to add this method as a pass-thru to the - baseclass method. */</font> - virtual int open (void *void_data) - { - return <font color=#008888>inherited::open</font> (void_data); - } - - <font color=red>/* - */</font> - virtual int close (u_long flags = 0); - - <font color=red>/* To use the thread pool, you have to put some unit of work into - it. Since we're dealing with event handlers (or at least their - derivatives), I've chosen to provide an enqueue() method that - takes a pointer to an ACE_Event_Handler. The handler's - handle_input() method will be called, so your object has to know - when it is being called by the thread pool. */</font> - int enqueue (ACE_Event_Handler *handler); - - <font color=red>/* Another handy ACE template is ACE_Atomic_Op<>. When - parameterized, this allows is to have a thread-safe counting - object. The typical arithmetic operators are all internally - thread-safe so that you can share it across threads without - worrying about any contention issues. */</font> - typedef ACE_Atomic_Op<ACE_Mutex, int> counter_t; - -protected: - - <font color=red>/* Our svc() method will dequeue the enqueued event handler objects - and invoke the handle_input() method on each. Since we're likely - running in more than one thread, idle threads can take work from - the queue while other threads are busy executing handle_input() on - some object. */</font> - int svc (void); - - <font color=red>/* We use the atomic op to keep a count of the number of threads in - which our svc() method is running. This is particularly important - when we want to close() it down! */</font> - counter_t active_threads_; -}; - -<font color=blue>#endif</font> <font color=red>/* THREAD_POOL_H */</font> -</PRE> -<HR WIDTH="100%"> - -<P>Well, that doesn't look too complex. What about the implementation? - -<P><HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page08.html">Continue This Tutorial</A>]</CENTER> - 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> - diff --git a/docs/tutorials/007/page09.html b/docs/tutorials/007/page09.html deleted file mode 100644 index a80bf7036ad..00000000000 --- a/docs/tutorials/007/page09.html +++ /dev/null @@ -1,79 +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>That's it for Tutorial 7. As with Tutorial 6, we really didn't -have to change much to introduce a new threading strategy. Most of -the work was in creating the Thread_Pool object itself. Everything -else was just minor housekeeping. - -<P>There is a fourth common thread strategy: thread-per-request. -It's not one of my favorites, so I wasn't planning to go into it. -If you want to contribute a tutorial on that topic though, I'll be glad -to include it here. - -<P>For reference, here's the file list again: -<UL> -<LI> -<A HREF="Makefile">Makefile</A></LI> - -<LI> -<A HREF="client_acceptor.h">client_acceptor.h</A></LI> - -<LI> -<A HREF="client_acceptor.cpp">client_acceptor.cpp</A></LI> - -<LI> -<A HREF="client_handler.cpp">client_handler.cpp</A></LI> - -<LI> -<A HREF="client_handler.h">client_handler.h</A></LI> - -<LI> -<A HREF="server.cpp">server.cpp</A></LI> - -<LI> -<A HREF="thread_pool.h">thread_pool.h</A></LI> - -<LI> -<A HREF="thread_pool.cpp">thread_pool.cpp</A></LI> - -</UL> -<P> -<HR WIDTH="100%"> -<P> -<center><h2>Danger, Warning!</h2></center> -Now that I've gone through all of this to create a thread pool server, -I have to point out that this isn't exactly the best or safest way to -do so. The biggest danger we face with this approach is the -possibility of an event handler existing in the thread pool's message -queue <i>after</i> it has been deleted. When the thread's svc() -method attempts to invoke <i>handle_input()</i> you will get a nasty -core dump. -<p> -The safest way to handle the situation is to use reference-counted -pointers everywhere a Client_Handler pointer would be used. That's -beyond the scope of the tutorial but I encourage you to give it a -try. If you want to contribute that back as an enhanced Tutorial, -I'll be glad to include it. -<p> -Another approach that should work quite well is to use the -ACE_TP_Reactor instead of just ACE_Reactor. This takes a little more -setup but results in a cleaner implementation. Again, I've not had -time to develop a Tutorial on the TP_Reactor but would welcome any -contributions. -<P><HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] </CENTER> - diff --git a/docs/tutorials/007/server.cpp b/docs/tutorials/007/server.cpp deleted file mode 100644 index 4e61be6f23a..00000000000 --- a/docs/tutorials/007/server.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// $Id$ - -/* We try to keep main() very simple. One of the ways we do that is - to push much of the complicated stuff into worker objects. In this - case, we only need to include the acceptor header in our main - source file. We let it worry about the "real work". */ - -#include "client_acceptor.h" - -/* As before, we create a simple signal handler that will set our - finished flag. There are, of course, more elegant ways to handle - program shutdown requests but that isn't really our focus right - now, so we'll just do the easiest thing. */ - -static sig_atomic_t finished = 0; -extern "C" void handler (int) -{ - finished = 1; -} - -/* A server has to listen for clients at a known TCP/IP port. The - default ACE port is 10002 (at least on my system) and that's good - enough for what we want to do here. Obviously, a more robust - application would take a command line parameter or read from a - configuration file or do some other clever thing. Just like the - signal handler above, though, that's what we want to focus on, so - we're taking the easy way out. */ - -static const u_short PORT = ACE_DEFAULT_SERVER_PORT; - -/* Finally, we get to main. Some C++ compilers will complain loudly - if your function signature doesn't match the prototype. Even - though we're not going to use the parameters, we still have to - specify them. */ - -int -main (int argc, char *argv[]) -{ - ACE_UNUSED_ARG(argc); - ACE_UNUSED_ARG(argv); - - /* In our earlier servers, we used a global pointer to get to the - reactor. I've never really liked that idea, so I've moved it into - main() this time. When we get to the Client_Handler object you'll - see how we manage to get a pointer back to this reactor. */ - ACE_Reactor reactor; - - /* The acceptor will take care of letting clients connect to us. It - will also arrange for a Client_Handler to be created for each new - client. Since we're only going to listen at one TCP/IP port, we - only need one acceptor. If we wanted, though, we could create - several of these and listen at several ports. (That's what we - would do if we wanted to rewrite inetd for instance.) */ - Client_Acceptor peer_acceptor; - - /* Create an ACE_INET_Addr that represents our endpoint of a - connection. We then open our acceptor object with that Addr. - Doing so tells the acceptor where to listen for connections. - Servers generally listen at "well known" addresses. If not, there - must be some mechanism by which the client is informed of the - server's address. - - Note how ACE_ERROR_RETURN is used if we fail to open the acceptor. - This technique is used over and over again in our tutorials. */ - if (peer_acceptor.open (ACE_INET_Addr (PORT), &reactor) == -1) - ACE_ERROR_RETURN ((LM_ERROR, - "%p\n", - "open"), - -1); - - /* Install our signal handler. You can actually register signal - handlers with the reactor. You might do that when the signal - handler is responsible for performing "real" work. Our simple - flag-setter doesn't justify deriving from ACE_Event_Handler and - providing a callback function though. */ - ACE_Sig_Action sa ((ACE_SignalHandler) handler, SIGINT); - - /* Like ACE_ERROR_RETURN, the ACE_DEBUG macro gets used quite a bit. - It's a handy way to generate uniform debug output from your - program. */ - ACE_DEBUG ((LM_DEBUG, - "(%P|%t) starting up server daemon\n")); - - /* This will loop "forever" invoking the handle_events() method of - our reactor. handle_events() watches for activity on any - registered handlers and invokes their appropriate callbacks when - necessary. Callback-driven programming is a big thing in ACE, you - should get used to it. If the signal handler catches something, - the finished flag will be set and we'll exit. Conveniently - enough, handle_events() is also interrupted by signals and will - exit back to the while() loop. (If you want your event loop to - not be interrupted by signals, checkout the <i>restart</i> flag on - the open() method of ACE_Reactor if you're interested.) */ - while (!finished) - reactor.handle_events (); - - ACE_DEBUG ((LM_DEBUG, - "(%P|%t) shutting down server daemon\n")); - - return 0; -} - -#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) -template class ACE_Acceptor <Client_Handler, ACE_SOCK_ACCEPTOR>; -template class ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>; -template class ACE_Guard<ACE_Mutex>; -template class ACE_Atomic_Op<ACE_Mutex, int>; -#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) -#pragma instantiate ACE_Acceptor <Client_Handler, ACE_SOCK_ACCEPTOR> -#pragma instantiate ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> -#pragma instantiate ACE_Guard<ACE_Mutex> -#pragma instantiate ACE_Atomic_Op<ACE_Mutex, int> -#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ diff --git a/docs/tutorials/007/thread_pool.cpp b/docs/tutorials/007/thread_pool.cpp deleted file mode 100644 index 08302a6811f..00000000000 --- a/docs/tutorials/007/thread_pool.cpp +++ /dev/null @@ -1,252 +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|THR_DETACHED, 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 (u_long flags) -{ - ACE_UNUSED_ARG(flags); - - /* 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 of a second - between tries. */ - while (active_threads_.value ()) - ACE_OS::sleep (ACE_Time_Value (0, 250000)); - - 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 misuse 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; - - ACE_Message_Block *mb; - - /* 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_NEW_RETURN (mb, - ACE_Message_Block (c_data), - -1); - - /* 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 now 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; -} - diff --git a/docs/tutorials/007/thread_pool.h b/docs/tutorials/007/thread_pool.h deleted file mode 100644 index 9686b5a29da..00000000000 --- a/docs/tutorials/007/thread_pool.h +++ /dev/null @@ -1,89 +0,0 @@ -// $Id$ - -#ifndef THREAD_POOL_H -#define THREAD_POOL_H - -/* In order to implement a thread pool, we have to have an object that - can create a thread. The ACE_Task<> is the basis for doing just - such a thing. */ -#include "ace/Task.h" - -#if !defined (ACE_LACKS_PRAGMA_ONCE) -# pragma once -#endif /* ACE_LACKS_PRAGMA_ONCE */ - -/* We need a forward reference for ACE_Event_Handler so that our - enqueue() method can accept a pointer to one. */ -class ACE_Event_Handler; - -/* Although we modified the rest of our program to make use of the - thread pool implementation, if you look closely you'll see that the - changes were rather minor. The "ACE way" is generally to create a - helper object that abstracts away the details not relevant to your - application. That's what I'm trying to do here by creating the - Thread_Pool object. */ -class Thread_Pool : public ACE_Task<ACE_MT_SYNCH> -{ -public: - typedef ACE_Task<ACE_MT_SYNCH> inherited; - - /* Provide an enumeration for the default pool size. By doing this, - other objects can use the value when they want a default. */ - enum size_t - { - default_pool_size_ = 5 - }; - - // Basic constructor - Thread_Pool (void); - - /* Opening the thread pool causes one or more threads to be - activated. When activated, they all execute the svc() method - declared below. */ - int open (int pool_size = default_pool_size_); - - /* Some compilers will complain that our open() above attempts to - override a virtual function in the baseclass. We have no - intention of overriding that method but in order to keep the - compiler quiet we have to add this method as a pass-thru to the - baseclass method. */ - virtual int open (void *void_data) - { - return inherited::open (void_data); - } - - /* - */ - virtual int close (u_long flags = 0); - - /* To use the thread pool, you have to put some unit of work into - it. Since we're dealing with event handlers (or at least their - derivatives), I've chosen to provide an enqueue() method that - takes a pointer to an ACE_Event_Handler. The handler's - handle_input() method will be called, so your object has to know - when it is being called by the thread pool. */ - int enqueue (ACE_Event_Handler *handler); - - /* Another handy ACE template is ACE_Atomic_Op<>. When - parameterized, this allows is to have a thread-safe counting - object. The typical arithmetic operators are all internally - thread-safe so that you can share it across threads without - worrying about any contention issues. */ - typedef ACE_Atomic_Op<ACE_Mutex, int> counter_t; - -protected: - - /* Our svc() method will dequeue the enqueued event handler objects - and invoke the handle_input() method on each. Since we're likely - running in more than one thread, idle threads can take work from - the queue while other threads are busy executing handle_input() on - some object. */ - int svc (void); - - /* We use the atomic op to keep a count of the number of threads in - which our svc() method is running. This is particularly important - when we want to close() it down! */ - counter_t active_threads_; -}; - -#endif /* THREAD_POOL_H */ |