diff options
Diffstat (limited to 'docs/tutorials/007')
-rw-r--r-- | docs/tutorials/007/007.dsp | 124 | ||||
-rw-r--r-- | docs/tutorials/007/Makefile | 101 | ||||
-rw-r--r-- | docs/tutorials/007/client_acceptor.cpp | 67 | ||||
-rw-r--r-- | docs/tutorials/007/client_acceptor.h | 141 | ||||
-rw-r--r-- | docs/tutorials/007/client_handler.cpp | 239 | ||||
-rw-r--r-- | docs/tutorials/007/client_handler.h | 172 | ||||
-rw-r--r-- | docs/tutorials/007/page01.html | 33 | ||||
-rw-r--r-- | docs/tutorials/007/page02.html | 197 | ||||
-rw-r--r-- | docs/tutorials/007/page03.html | 263 | ||||
-rw-r--r-- | docs/tutorials/007/page04.html | 133 | ||||
-rw-r--r-- | docs/tutorials/007/page05.html | 208 | ||||
-rw-r--r-- | docs/tutorials/007/page06.html | 272 | ||||
-rw-r--r-- | docs/tutorials/007/page07.html | 197 | ||||
-rw-r--r-- | docs/tutorials/007/page08.html | 537 | ||||
-rw-r--r-- | docs/tutorials/007/page09.html | 85 | ||||
-rw-r--r-- | docs/tutorials/007/server.cpp | 119 | ||||
-rw-r--r-- | docs/tutorials/007/thread_pool.cpp | 277 | ||||
-rw-r--r-- | docs/tutorials/007/thread_pool.h | 102 |
18 files changed, 0 insertions, 3267 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 8e430900570..00000000000 --- a/docs/tutorials/007/Makefile +++ /dev/null @@ -1,101 +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 - -#---------------------------------------------------------------------------- -# 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 6cc90612558..00000000000 --- a/docs/tutorials/007/client_acceptor.cpp +++ /dev/null @@ -1,67 +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 82c761879ea..00000000000 --- a/docs/tutorials/007/client_acceptor.h +++ /dev/null @@ -1,141 +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 647739e34e2..00000000000 --- a/docs/tutorials/007/client_handler.cpp +++ /dev/null @@ -1,239 +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(); - } - - 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) -{ - /* - 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.) - */ - 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[128]; - ACE_OS::memset (buf, 0, sizeof (buf)); - - /* - 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 ) - { - 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[128]; - ACE_OS::memset (buf, 0, sizeof (buf)); - - 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) -{ - switch (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: - 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 6807231c3b0..00000000000 --- a/docs/tutorials/007/client_handler.h +++ /dev/null @@ -1,172 +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 _handleg - 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/page01.html b/docs/tutorials/007/page01.html deleted file mode 100644 index 24b94f686c7..00000000000 --- a/docs/tutorials/007/page01.html +++ /dev/null @@ -1,33 +0,0 @@ -<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> - - -<P> -<HR WIDTH="100%"> - -<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> -<HR WIDTH="100%"> -<CENTER>[<A HREF="..">Tutorial -Index</A>] [<A HREF="page02.html">Continue -This Tutorial</A>]</CENTER> - -</BODY> -</HTML> diff --git a/docs/tutorials/007/page02.html b/docs/tutorials/007/page02.html deleted file mode 100644 index c6e7bedec43..00000000000 --- a/docs/tutorials/007/page02.html +++ /dev/null @@ -1,197 +0,0 @@ -<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> - - -<P> -<HR WIDTH="100%"> - -<P>As usualy, we start with <A HREF="server.cpp">server.cpp</A> -<BR> -<HR WIDTH="100%"> - -<P><FONT FACE="Arial,Helvetica">// $Id: server.cpp,v 1.1 1998/08/30 16:04:12 -jcej Exp $</FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> We try to keep main() very -simple. One of the ways we do that is to push</FONT> -<BR><FONT FACE="Arial,Helvetica"> much of the complicated stuff -into worker objects. In this case, we only</FONT> -<BR><FONT FACE="Arial,Helvetica"> need to include the acceptor -header in our main source file. We let it</FONT> -<BR><FONT FACE="Arial,Helvetica"> worry about the "real work".</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> - -<P><FONT FACE="Arial,Helvetica">#include "client_acceptor.h"</FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> As before, we create a simple -signal handler that will set our finished</FONT> -<BR><FONT FACE="Arial,Helvetica"> flag. There are, of -course, more elegant ways to handle program shutdown</FONT> -<BR><FONT FACE="Arial,Helvetica"> requests but that isn't really -our focus right now, so we'll just do the</FONT> -<BR><FONT FACE="Arial,Helvetica"> easiest thing.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> - -<P><FONT FACE="Arial,Helvetica">static sig_atomic_t finished = 0;</FONT> -<BR><FONT FACE="Arial,Helvetica">extern "C" void handler (int)</FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica"> finished = 1;</FONT> -<BR><FONT FACE="Arial,Helvetica">}</FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> A server has to listen for -clients at a known TCP/IP port. The default ACE</FONT> -<BR><FONT FACE="Arial,Helvetica"> port is 10002 (at least on -my system) and that's good enough for what we</FONT> -<BR><FONT FACE="Arial,Helvetica"> want to do here. Obviously, -a more robust application would take a command</FONT> -<BR><FONT FACE="Arial,Helvetica"> line parameter or read from -a configuration file or do some other clever</FONT> -<BR><FONT FACE="Arial,Helvetica"> thing. Just like the -signal handler above, though, that's what we want to</FONT> -<BR><FONT FACE="Arial,Helvetica"> focus on, so we're taking -the easy way out.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> - -<P><FONT FACE="Arial,Helvetica">static const u_short PORT = ACE_DEFAULT_SERVER_PORT;</FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> Finally, we get to main. -Some C++ compilers will complain loudly if your</FONT> -<BR><FONT FACE="Arial,Helvetica"> function signature doesn't -match the prototype. Even though we're not</FONT> -<BR><FONT FACE="Arial,Helvetica"> going to use the parameters, -we still have to specify them.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> - -<P><FONT FACE="Arial,Helvetica">int main (int argc, char *argv[])</FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> In our earlier servers, we -used a global pointer to get to the reactor. I've</FONT> -<BR><FONT FACE="Arial,Helvetica"> never really liked that idea, -so I've moved it into main() this time. When</FONT> -<BR><FONT FACE="Arial,Helvetica"> we get to the Client_Handler -object you'll see how we manage to get a</FONT> -<BR><FONT FACE="Arial,Helvetica"> pointer back to this reactor.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica"> ACE_Reactor reactor;</FONT> - -<P><FONT FACE="Arial,Helvetica"> /*</FONT> -<BR><FONT FACE="Arial,Helvetica"> The acceptor -will take care of letting clients connect to us. It will</FONT> -<BR><FONT FACE="Arial,Helvetica"> also arrange -for a Client_Handler to be created for each new client.</FONT> -<BR><FONT FACE="Arial,Helvetica"> Since we're only -going to listen at one TCP/IP port, we only need one</FONT> -<BR><FONT FACE="Arial,Helvetica"> acceptor. -If we wanted, though, we could create several of these and</FONT> -<BR><FONT FACE="Arial,Helvetica"> listen at several -ports. (That's what we would do if we wanted to rewrite</FONT> -<BR><FONT FACE="Arial,Helvetica"> inetd for -instance.)</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica"> Client_Acceptor peer_acceptor;</FONT> - -<P><FONT FACE="Arial,Helvetica"> /*</FONT> -<BR><FONT FACE="Arial,Helvetica"> Create an ACE_INET_Addr -that represents our endpoint of a connection. We</FONT> -<BR><FONT FACE="Arial,Helvetica"> then open our -acceptor object with that Addr. Doing so tells the acceptor</FONT> -<BR><FONT FACE="Arial,Helvetica"> where to listen -for connections. Servers generally listen at "well known"</FONT> -<BR><FONT FACE="Arial,Helvetica"> addresses. -If not, there must be some mechanism by which the client is</FONT> -<BR><FONT FACE="Arial,Helvetica"> informed of the -server's address.</FONT> - -<P><FONT FACE="Arial,Helvetica"> Note how ACE_ERROR_RETURN -is used if we fail to open the acceptor. This</FONT> -<BR><FONT FACE="Arial,Helvetica"> technique is -used over and over again in our tutorials.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica"> if (peer_acceptor.open (ACE_INET_Addr -(PORT), &reactor) == -1)</FONT> -<BR><FONT FACE="Arial,Helvetica"> ACE_ERROR_RETURN ((LM_ERROR, -"%p\n", "open"), -1);</FONT> - -<P><FONT FACE="Arial,Helvetica"> /*</FONT> -<BR><FONT FACE="Arial,Helvetica"> Install our signal -handler. You can actually register signal handlers</FONT> -<BR><FONT FACE="Arial,Helvetica"> with the reactor. -You might do that when the signal handler is</FONT> -<BR><FONT FACE="Arial,Helvetica"> responsible for -performing "real" work. Our simple flag-setter doesn't</FONT> -<BR><FONT FACE="Arial,Helvetica"> justify deriving -from ACE_Event_Handler and providing a callback function</FONT> -<BR><FONT FACE="Arial,Helvetica"> though.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica"> ACE_Sig_Action sa ((ACE_SignalHandler) -handler, SIGINT);</FONT> - -<P><FONT FACE="Arial,Helvetica"> /*</FONT> -<BR><FONT FACE="Arial,Helvetica"> Like ACE_ERROR_RETURN, -the ACE_DEBUG macro gets used quite a bit. It's a</FONT> -<BR><FONT FACE="Arial,Helvetica"> handy way to -generate uniform debug output from your program.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica"> ACE_DEBUG ((LM_DEBUG, "(%P|%t) -starting up server daemon\n"));</FONT> - -<P><FONT FACE="Arial,Helvetica"> /*</FONT> -<BR><FONT FACE="Arial,Helvetica"> This will loop -"forever" invoking the handle_events() method of our</FONT> -<BR><FONT FACE="Arial,Helvetica"> reactor. handle_events() -watches for activity on any registered handlers</FONT> -<BR><FONT FACE="Arial,Helvetica"> and invokes their -appropriate callbacks when necessary. Callback-driven</FONT> -<BR><FONT FACE="Arial,Helvetica"> programming is -a big thing in ACE, you should get used to it. If the</FONT> -<BR><FONT FACE="Arial,Helvetica"> signal handler -catches something, the finished flag will be set and we'll</FONT> -<BR><FONT FACE="Arial,Helvetica"> exit. Conveniently -enough, handle_events() is also interrupted by signals</FONT> -<BR><FONT FACE="Arial,Helvetica"> and will exit -back to the while() loop. (If you want your event loop to</FONT> -<BR><FONT FACE="Arial,Helvetica"> not be interrupted -by signals, checkout the <i>restart</i> flag on the</FONT> -<BR><FONT FACE="Arial,Helvetica"> open() method -of ACE_Reactor if you're interested.)</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica"> while (!finished)</FONT> -<BR><FONT FACE="Arial,Helvetica"> -reactor.handle_events ();</FONT> - -<P><FONT FACE="Arial,Helvetica"> ACE_DEBUG ((LM_DEBUG, "(%P|%t) shutting -down server daemon\n"));</FONT> -<BR><FONT FACE="Arial,Helvetica"> </FONT> -<BR><FONT FACE="Arial,Helvetica"> return 0;</FONT> -<BR><FONT FACE="Arial,Helvetica">}</FONT> - -<P> -<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 lookt at client_acceptor.h. - -<P> -<HR WIDTH="100%"> -<CENTER>[<A HREF="..">Tutorial -Index</A>] [<A HREF="page03.html">Continue -This Tutorial</A>]</CENTER> - -</BODY> -</HTML> diff --git a/docs/tutorials/007/page03.html b/docs/tutorials/007/page03.html deleted file mode 100644 index af048108811..00000000000 --- a/docs/tutorials/007/page03.html +++ /dev/null @@ -1,263 +0,0 @@ -<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> - - -<P> -<HR WIDTH="100%"> - -<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%"> -<BR><FONT FACE="Arial,Helvetica">// $Id: client_acceptor.h,v 1.1 1998/08/30 -16:04:11 jcej Exp $</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">#ifndef CLIENT_ACCEPTOR_H</FONT> -<BR><FONT FACE="Arial,Helvetica">#define CLIENT_ACCEPTOR_H</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> The ACE_Acceptor<> template -lives in the ace/Acceptor.h header file. You'll</FONT> -<BR><FONT FACE="Arial,Helvetica"> find a very consitent naming -convention between the ACE objects and the</FONT> -<BR><FONT FACE="Arial,Helvetica"> headers where they can be -found. In general, the ACE object ACE_Foobar will</FONT> -<BR><FONT FACE="Arial,Helvetica"> be found in ace/Foobar.h.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">#include "ace/Acceptor.h"</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> Since we want to work with -sockets, we'll need a SOCK_Acceptor to allow the</FONT> -<BR><FONT FACE="Arial,Helvetica"> clients to connect to us.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">#include "ace/SOCK_Acceptor.h"</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> The Client_Handler object -we develop will be used to handle clients once</FONT> -<BR><FONT FACE="Arial,Helvetica"> they're connected. -The ACE_Acceptor<> template's first parameter requires</FONT> -<BR><FONT FACE="Arial,Helvetica"> such an object. In -some cases, you can get by with just a forward</FONT> -<BR><FONT FACE="Arial,Helvetica"> declaration on the class, -in others you have to have the whole thing.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">#include "client_handler.h"</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> Parameterize the ACE_Acceptor<> -such that it will listen for socket</FONT> -<BR><FONT FACE="Arial,Helvetica"> connection attempts and create -Client_Handler objects when they happen. In</FONT> -<BR><FONT FACE="Arial,Helvetica"> Tutorial 001, we wrote the -basic acceptor logic on our own before we</FONT> -<BR><FONT FACE="Arial,Helvetica"> realized that ACE_Acceptor<> -was available. You'll get spoiled using the</FONT> -<BR><FONT FACE="Arial,Helvetica"> ACE templates because they -take away a lot of the tedious details!</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">typedef ACE_Acceptor < Client_Handler, -ACE_SOCK_ACCEPTOR > Client_Acceptor_Base;</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">#include "thread_pool.h"</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> This time we've added quite -a bit more to our acceptor. In addition to</FONT> -<BR><FONT FACE="Arial,Helvetica"> providing a choice of concurrency -strategies, we also maintain a Thread_Pool</FONT> -<BR><FONT FACE="Arial,Helvetica"> object in case that strategy -is chosen. The object still isn't very complex</FONT> -<BR><FONT FACE="Arial,Helvetica"> but it's come a long way -from the simple typedef we had in Tutorial 5.</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> Why keep the thread pool as -a member? If we go back to the inetd concept</FONT> -<BR><FONT FACE="Arial,Helvetica"> you'll recall that we need -several acceptors to make that work. We may have</FONT> -<BR><FONT FACE="Arial,Helvetica"> a situation in which our -different client types requre different resources.</FONT> -<BR><FONT FACE="Arial,Helvetica"> That is, we may need a large -thread pool for some client types and a smaller</FONT> -<BR><FONT FACE="Arial,Helvetica"> one for others. We -could share a pool but then the client types may have</FONT> -<BR><FONT FACE="Arial,Helvetica"> undesirable impact on one -another.</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> Just in case you do want to -share a single thread pool, there is a constructor</FONT> -<BR><FONT FACE="Arial,Helvetica"> below that will let you do -that.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">class Client_Acceptor : public Client_Acceptor_Base</FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica">public:</FONT> -<BR><FONT FACE="Arial,Helvetica"> -typedef Client_Acceptor_Base inherited;</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Now that we have more than two strategies, we need more than a boolean</FONT> -<BR><FONT FACE="Arial,Helvetica"> -to tell us what we're using. A set of enums is a good choice because</FONT> -<BR><FONT FACE="Arial,Helvetica"> -it allows us to use named values. Another option would be a set of</FONT> -<BR><FONT FACE="Arial,Helvetica"> -static const integers.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -enum concurrency_t</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -single_threaded_,</FONT> -<BR><FONT FACE="Arial,Helvetica"> -thread_per_connection_,</FONT> -<BR><FONT FACE="Arial,Helvetica"> -thread_pool_</FONT> -<BR><FONT FACE="Arial,Helvetica"> -};</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -The default constructor allows the programmer to choose the concurrency</FONT> -<BR><FONT FACE="Arial,Helvetica"> -strategy. Since we want to focus on thread-pool, that's what we'll -use</FONT> -<BR><FONT FACE="Arial,Helvetica"> -if nothing is specified.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Client_Acceptor( int _concurrency = thread_pool_ );</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Another option is to construct the object with an existing thread pool.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -The concurrency strategy is pretty obvious at that point.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Client_Acceptor( Thread_Pool & _thread_pool );</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Our destructor will take care of shutting down the thread-pool</FONT> -<BR><FONT FACE="Arial,Helvetica"> -if applicable.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -~Client_Acceptor( void );</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Open ourselves and register with the given reactor. The thread pool -size</FONT> -<BR><FONT FACE="Arial,Helvetica"> -can be specified here if you want to use that concurrency strategy.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -int open( const ACE_INET_Addr & _addr, ACE_Reactor * _reactor,</FONT> -<BR><FONT FACE="Arial,Helvetica"> -int _pool_size = Thread_Pool::default_pool_size_ );</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Close ourselves and our thread pool if applicable</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -int close(void);</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -What is our concurrency strategy?</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -int concurrency(void)</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{ return this->concurrency_; }</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Give back a pointer to our thread pool. Our Client_Handler objects</FONT> -<BR><FONT FACE="Arial,Helvetica"> -will need this so that their handle_input() methods can put themselves</FONT> -<BR><FONT FACE="Arial,Helvetica"> -into the pool. Another alternative would be a globally accessible</FONT> -<BR><FONT FACE="Arial,Helvetica"> -thread pool. ACE_Singleton<> is a way to achieve that.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Thread_Pool * thread_pool(void)</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{ return & this->the_thread_pool_; }</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Since we can be constructed with a Thread_Pool reference, there are times</FONT> -<BR><FONT FACE="Arial,Helvetica"> -when we need to know if the thread pool we're using is ours or if we're</FONT> -<BR><FONT FACE="Arial,Helvetica"> -just borrowing it from somebody else.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -int thread_pool_is_private(void)</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{ return &the_thread_pool_ == &private_thread_pool_; }</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">protected:</FONT> -<BR><FONT FACE="Arial,Helvetica"> -int concurrency_;</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -Thread_Pool private_thread_pool_;</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -Thread_Pool & the_thread_pool_;</FONT> -<BR><FONT FACE="Arial,Helvetica">};</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">#endif // CLIENT_ACCEPTOR_H</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P> -<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> - -</BODY> -</HTML> diff --git a/docs/tutorials/007/page04.html b/docs/tutorials/007/page04.html deleted file mode 100644 index 34424605180..00000000000 --- a/docs/tutorials/007/page04.html +++ /dev/null @@ -1,133 +0,0 @@ -<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> - - -<P> -<HR WIDTH="100%"> - -<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%"> -<BR><FONT FACE="Arial,Helvetica">// $Id: client_acceptor.cpp,v 1.1 1998/08/30 -16:04:11 jcej Exp $</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">#include "client_acceptor.h"</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> Construct ourselves with -the chosen concurrency strategy. Notice that we also</FONT> -<BR><FONT FACE="Arial,Helvetica"> set our Thread_Pool reference -to our private instance.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">Client_Acceptor::Client_Acceptor( int -_concurrency )</FONT> -<BR><FONT FACE="Arial,Helvetica"> : concurrency_(_concurrency)</FONT> -<BR><FONT FACE="Arial,Helvetica"> ,the_thread_pool_(private_thread_pool_)</FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica">}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> Construct ourselves with -a reference to somebody else' Thread_Pool. Obvioulsy</FONT> -<BR><FONT FACE="Arial,Helvetica"> our concurrency strategy -is "thread_pool_" at this point.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">Client_Acceptor::Client_Acceptor( Thread_Pool -& _thread_pool )</FONT> -<BR><FONT FACE="Arial,Helvetica"> : concurrency_(thread_pool_)</FONT> -<BR><FONT FACE="Arial,Helvetica"> ,the_thread_pool_(_thread_pool)</FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica">}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> When we're destructed, we -may need to cleanup after ourselves. If we're running</FONT> -<BR><FONT FACE="Arial,Helvetica"> with a thread pool that we -own, it is up to us to close it down.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">Client_Acceptor::~Client_Acceptor( void -)</FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -if( this->concurrency() == thread_pool_ && thread_pool_is_private() -)</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -thread_pool()->close();</FONT> -<BR><FONT FACE="Arial,Helvetica"> -}</FONT> -<BR><FONT FACE="Arial,Helvetica">}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> Similar to the destructor -(and close() below) it is necessary for us to open the</FONT> -<BR><FONT FACE="Arial,Helvetica"> thread pool in some circumstances.</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> Notice how we delegate most -of the open() work to the open() method of our baseclass.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">int Client_Acceptor::open( const ACE_INET_Addr -& _addr, ACE_Reactor * _reactor, int _pool_size )</FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -if( this->concurrency() == thread_pool_ && thread_pool_is_private() -)</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -thread_pool()->open(_pool_size);</FONT> -<BR><FONT FACE="Arial,Helvetica"> -}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -return inherited::open(_addr,_reactor);</FONT> -<BR><FONT FACE="Arial,Helvetica">}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> Here again we find that we -have to manage the thread pool. Like open() we also delegate</FONT> -<BR><FONT FACE="Arial,Helvetica"> the other work to our baseclass.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">int Client_Acceptor::close(void)</FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -if( this->concurrency() == thread_pool_ && thread_pool_is_private() -)</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -thread_pool()->close();</FONT> -<BR><FONT FACE="Arial,Helvetica"> -}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -return inherited::close();</FONT> -<BR><FONT FACE="Arial,Helvetica">}</FONT> -<BR><FONT FACE="Arial,Helvetica"></FONT> <FONT FACE="Arial,Helvetica"></FONT> - -<P> -<HR WIDTH="100%"> - -<P>Nothing really surprising here. Most of it just manages the Thread_Pool. - -<P> -<HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page05.html">Continue -This Tutorial</A>]</CENTER> - -</BODY> -</HTML> diff --git a/docs/tutorials/007/page05.html b/docs/tutorials/007/page05.html deleted file mode 100644 index c797b188cec..00000000000 --- a/docs/tutorials/007/page05.html +++ /dev/null @@ -1,208 +0,0 @@ -<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> - - -<P> -<HR WIDTH="100%"> - -<P>As you might expect, <A HREF="client_handler.h">client_handler.h</A> -is next. - -<P> -<HR WIDTH="100%"> -<pre><FONT FACE="Arial,Helvetica"> -#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 _handleg - 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 -</pre> -<HR WIDTH="100%"> - -<P>Still, we're just not seeing a lot of changes due to intruduction 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="..">Tutorial -Index</A>] [<A HREF="page06.html">Continue -This Tutorial</A>]</CENTER> - -</BODY> -</HTML> diff --git a/docs/tutorials/007/page06.html b/docs/tutorials/007/page06.html deleted file mode 100644 index 3c2fdd30357..00000000000 --- a/docs/tutorials/007/page06.html +++ /dev/null @@ -1,272 +0,0 @@ -<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> - - -<P> -<HR WIDTH="100%"> - -<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 FACE="Arial,Helvetica"> -/* - 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(); - } - - 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. We use the - destroy() method to clean up after ourselves. -*/ -int Client_Handler::close(u_long flags) -{ - 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.) - */ - 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[128]; - ACE_OS::memset (buf, 0, sizeof (buf)); - - /* - 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 ) - { - 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[128]; - ACE_OS::memset (buf, 0, sizeof (buf)); - - 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) -{ - switch (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: - ACE_DEBUG ((LM_DEBUG, "(%P|%t) from client: %s", _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="..">Tutorial -Index</A>] [<A HREF="page07.html">Continue -This Tutorial</A>]</CENTER> - -</BODY> -</HTML> diff --git a/docs/tutorials/007/page07.html b/docs/tutorials/007/page07.html deleted file mode 100644 index c4fd555cf68..00000000000 --- a/docs/tutorials/007/page07.html +++ /dev/null @@ -1,197 +0,0 @@ -<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> - - -<P> -<HR WIDTH="100%"> - -<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> - -<P><FONT FACE="Arial,Helvetica">// $Id: thread_pool.h,v 1.1 1998/08/30 -16:04:12 jcej Exp $</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">#ifndef THREAD_POOL_H</FONT> -<BR><FONT FACE="Arial,Helvetica">#define THREAD_POOL_H</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> In order to implement a thread -pool, we have to have an object that can create</FONT> -<BR><FONT FACE="Arial,Helvetica"> a thread. The ACE_Task<> -is the basis for doing just such a thing.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">#include "ace/Task.h"</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> We need a forward reference -for ACE_Event_Handler so that our enqueue() method</FONT> -<BR><FONT FACE="Arial,Helvetica"> can accept a pointer to one.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">class ACE_Event_Handler;</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> Although we modified the -rest of our program to make use of the thread pool</FONT> -<BR><FONT FACE="Arial,Helvetica"> implementation, if you look -closely you'll see that the changes were rather</FONT> -<BR><FONT FACE="Arial,Helvetica"> minor. The "ACE way" -is generally to create a helper object that abstracts</FONT> -<BR><FONT FACE="Arial,Helvetica"> away the details not relevant -to your application. That's what I'm trying</FONT> -<BR><FONT FACE="Arial,Helvetica"> to do here by creating the -Thread_Pool object.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">class Thread_Pool : public ACE_Task<ACE_MT_SYNCH></FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica">public:</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Provide an enumeration for the default pool size. By doing this, -other objects</FONT> -<BR><FONT FACE="Arial,Helvetica"> -can use the value when they want a default.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -enum size_t</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -default_pool_size_ = 5</FONT> -<BR><FONT FACE="Arial,Helvetica"> -};</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -// Basic constructor</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Thread_Pool(void);</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Opening the thread pool causes one or more threads to be activated. -When activated,</FONT> -<BR><FONT FACE="Arial,Helvetica"> -they all execute the svc() method declared below.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -int open( int _pool_size = default_pool_size_ );</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -When you're done wit the thread pool, you have to have some way to shut -it down.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -This is what close() is for.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -int close(void);</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -To use the thread pool, you have to put some unit of work into it. -Since we're</FONT> -<BR><FONT FACE="Arial,Helvetica"> -dealing with event handlers (or at least their derivatives), I've chosen -to provide</FONT> -<BR><FONT FACE="Arial,Helvetica"> -an enqueue() method that takes a pointer to an ACE_Event_Handler. -The handler's</FONT> -<BR><FONT FACE="Arial,Helvetica"> -handle_input() method will be called, so your object has to know when it -is being</FONT> -<BR><FONT FACE="Arial,Helvetica"> -called by the thread pool.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -int enqueue( ACE_Event_Handler * _handler );</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">protected:</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Our svc() method will dequeue the enqueued event handler objects and invoke -the</FONT> -<BR><FONT FACE="Arial,Helvetica"> -handle_input() method on each. Since we're likely running in more -than one thread,</FONT> -<BR><FONT FACE="Arial,Helvetica"> -idle threads can take work from the queue while other threads are busy -executing</FONT> -<BR><FONT FACE="Arial,Helvetica"> -handle_input() on some object.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -int svc(void);</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Another handy ACE template is ACE_Atomic_Op<>. When parameterized, -this allows</FONT> -<BR><FONT FACE="Arial,Helvetica"> -is to have a thread-safe counting object. The typical arithmetic -operators are</FONT> -<BR><FONT FACE="Arial,Helvetica"> -all internally thread-safe so that you can share it across threads without -worrying</FONT> -<BR><FONT FACE="Arial,Helvetica"> -about any contention issues.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -typedef ACE_Atomic_Op<ACE_Mutex,int> counter_t;</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -We use the atomic op to keep a count of the number of threads in which -our svc()</FONT> -<BR><FONT FACE="Arial,Helvetica"> -method is running. This is particularly important when we want to -close() it down!</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -counter_t active_threads_;</FONT> -<BR><FONT FACE="Arial,Helvetica">};</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">#endif // THREAD_POOL_H</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P> -<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> - -</BODY> -</HTML> diff --git a/docs/tutorials/007/page08.html b/docs/tutorials/007/page08.html deleted file mode 100644 index 094804e743d..00000000000 --- a/docs/tutorials/007/page08.html +++ /dev/null @@ -1,537 +0,0 @@ -<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> - - -<P> -<HR WIDTH="100%"> - -<P>Finally, <A HREF="thread_pool.cpp">thread_pool.cpp</A> -where we have the Thread_Pool object implementation. - -<P> -<HR WIDTH="100%"> - -<P><FONT FACE="Arial,Helvetica">// $Id: thread_pool.cpp,v 1.1 1998/08/30 -23:47:15 schmidt Exp $</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">#include "thread_pool.h"</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> We need this header so that -we can invoke handle_input() on the objects we dequeue.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">#include "ace/Event_Handler.h"</FONT> -<BR><FONT FACE="Arial,Helvetica"></FONT> <FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> All we do here is initialize -our active thread counter.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">Thread_Pool::Thread_Pool(void)</FONT> -<BR><FONT FACE="Arial,Helvetica"> : active_threads_(0)</FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica">}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> Our open() method is a thin -disguise around the ACE_Task<> activate() method. By</FONT> -<BR><FONT FACE="Arial,Helvetica"> hiding activate() in this -way, the users of Thread_Pool don't have to worry about</FONT> -<BR><FONT FACE="Arial,Helvetica"> the thread configuration -flags.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">int Thread_Pool::open( int _pool_size -)</FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica"> return this->activate(THR_NEW_LWP|THR_DETACHED,_pool_size);</FONT> -<BR><FONT FACE="Arial,Helvetica">}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> Closing the thread pool can -be a tricky exercise. I've decided to take an easy approach</FONT> -<BR><FONT FACE="Arial,Helvetica"> and simply enqueue a secret -message for each thread we have active.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">int Thread_Pool::close(void)</FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Find out how many threads are currently active</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -int counter = active_threads_.value();</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -For each one of the active threads, enqueue a "null" event handler. -Below, we'll</FONT> -<BR><FONT FACE="Arial,Helvetica"> -teach our svc() method that "null" means "shutdown".</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -while( counter-- )</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -this->enqueue( 0 );</FONT> -<BR><FONT FACE="Arial,Helvetica"> -}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -As each svc() method exits, it will decrement the active thread counter. -We just wait</FONT> -<BR><FONT FACE="Arial,Helvetica"> -here for it to reach zero. Since we don't know how long it will take, -we sleep for</FONT> -<BR><FONT FACE="Arial,Helvetica"> -a quarter-second or so between tries.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -while( active_threads_.value() )</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -ACE_OS::sleep( ACE_Time_Value(0.25) );</FONT> -<BR><FONT FACE="Arial,Helvetica"> -}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -return(0);</FONT> -<BR><FONT FACE="Arial,Helvetica">}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> When an object wants to do -work in the pool, it should call the enqueue() method.</FONT> -<BR><FONT FACE="Arial,Helvetica"> We introduce the ACE_Message_Block -here but, unfortunately, we seriously missuse it.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">int Thread_Pool::enqueue( ACE_Event_Handler -* _handler )</FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -An ACE_Message_Block is a chunk of data. You put them into an ACE_Message_Queue.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -ACE_Task<> has an ACE_Message_Queue built in. In fact, the parameter -to ACE_Task<></FONT> -<BR><FONT FACE="Arial,Helvetica"> -is passed directly to ACE_Message_Queue. If you look back at our -header file you'll</FONT> -<BR><FONT FACE="Arial,Helvetica"> -see that we used ACE_MT_SYNCH as the parameter indicating that we want -MultiThread</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Synch safety. This allows us to safely put ACE_Message_Block objects -into the</FONT> -<BR><FONT FACE="Arial,Helvetica"> -message queue in one thread and take them out in another.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -An ACE_Message_Block wants to have char* data. We don't have that. -We could</FONT> -<BR><FONT FACE="Arial,Helvetica"> -cast our ACE_Event_Handler* directly to a char* but I wanted to be more -explicit.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Since casting pointers around is a dangerous thing, I've gone out of my -way here</FONT> -<BR><FONT FACE="Arial,Helvetica"> -to be very clear about what we're doing.</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -First: Cast the handler pointer to a void pointer. You can't -do any useful work</FONT> -<BR><FONT FACE="Arial,Helvetica"> -on a void pointer, so this is a clear message that we're making the</FONT> -<BR><FONT FACE="Arial,Helvetica"> -pointer unusable.</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -Next: Cast the void pointer to a char pointer that the ACE_Message_Block -will accept.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -void * v_data = (void*)_handler;</FONT> -<BR><FONT FACE="Arial,Helvetica"> -char * c_data = (char*)v_data;</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Construct a new ACE_Message_Block. For efficiency, you might want -to preallocate a</FONT> -<BR><FONT FACE="Arial,Helvetica"> -stack of these and reuse them. For simplicity, I'll just create what -I need as I need it.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -ACE_Message_Block * mb = new ACE_Message_Block( c_data );</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Our putq() method is a wrapper around one of the enqueue methods of the -ACE_Message_Queue</FONT> -<BR><FONT FACE="Arial,Helvetica"> -that we own. Like all good methods, it returns -1 if it fails for -some reason.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -if( this->putq(mb) == -1 )</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Another trait of the ACE_Message_Block objects is that they are reference -counted.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Since they're designed to be passed around between various objects in several -threads</FONT> -<BR><FONT FACE="Arial,Helvetica"> -we can't just delete them whenever we feel like it. The release() -method is similar</FONT> -<BR><FONT FACE="Arial,Helvetica"> -to the destroy() method we've used elsewhere. It watches the reference -count and will</FONT> -<BR><FONT FACE="Arial,Helvetica"> -delete the object when possible.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -mb->release();</FONT> -<BR><FONT FACE="Arial,Helvetica"> -return(-1);</FONT> -<BR><FONT FACE="Arial,Helvetica"> -}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -return(0);</FONT> -<BR><FONT FACE="Arial,Helvetica">}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> The "guard" concept is very -powerful and used throughout multi-threaded applications.</FONT> -<BR><FONT FACE="Arial,Helvetica"> A guard normally does some -operation on an object at construction and the "opposite"</FONT> -<BR><FONT FACE="Arial,Helvetica"> operation at destruction. -For instance, when you guard a mutex (lock) object, the guard</FONT> -<BR><FONT FACE="Arial,Helvetica"> will acquire the lock on -construction and release it on destruction. In this way, your</FONT> -<BR><FONT FACE="Arial,Helvetica"> method can simply let the -guard go out of scope and know that the lock is released.</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> Guards aren't only useful -for locks however. In this application I've created two guard</FONT> -<BR><FONT FACE="Arial,Helvetica"> objects for quite a different -purpose.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> The Counter_Guard is constructed -with a reference to the thread pool's active thread</FONT> -<BR><FONT FACE="Arial,Helvetica"> counter. The guard -increments the counter when it is created and decrements it at</FONT> -<BR><FONT FACE="Arial,Helvetica"> destruction. By creating -one of these in svc(), I know that the counter will be decremented</FONT> -<BR><FONT FACE="Arial,Helvetica"> no matter how or where svc() -returns.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">class Counter_Guard</FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica">public:</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Counter_Guard( Thread_Pool::counter_t & _counter )</FONT> -<BR><FONT FACE="Arial,Helvetica"> -: counter_(_counter)</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -++counter_;</FONT> -<BR><FONT FACE="Arial,Helvetica"> -}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -~Counter_Guard(void)</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> ---counter_;</FONT> -<BR><FONT FACE="Arial,Helvetica"> -}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">protected:</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Thread_Pool::counter_t & counter_;</FONT> -<BR><FONT FACE="Arial,Helvetica">};</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> My Message_Block_Guard is -also a little non-traditional. It doesn't do anything in the</FONT> -<BR><FONT FACE="Arial,Helvetica"> constructor but it's destructor -ensures that the message block's release() method is called.</FONT> -<BR><FONT FACE="Arial,Helvetica"> This is a cheap way to prevent -a memory leak if I need an additional exit point in svc().</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">class Message_Block_Guard</FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica">public:</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Message_Block_Guard( ACE_Message_Block * & _mb )</FONT> -<BR><FONT FACE="Arial,Helvetica"> -: mb_(_mb)</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -~Message_Block_Guard( void )</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -mb_->release();</FONT> -<BR><FONT FACE="Arial,Helvetica"> -}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">protected:</FONT> -<BR><FONT FACE="Arial,Helvetica"> -ACE_Message_Block * & mb_;</FONT> -<BR><FONT FACE="Arial,Helvetica">};</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica">/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> Now we come to the svc() -method. As I said, this is being executed in each thread of the</FONT> -<BR><FONT FACE="Arial,Helvetica"> Thread_Pool. Here, -we pull messages off of our built-in ACE_Message_Queue and cause them</FONT> -<BR><FONT FACE="Arial,Helvetica"> to do work.</FONT> -<BR><FONT FACE="Arial,Helvetica"> */</FONT> -<BR><FONT FACE="Arial,Helvetica">int Thread_Pool::svc(void)</FONT> -<BR><FONT FACE="Arial,Helvetica">{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -The getq() method takes a reference to a pointer. So... we need a -pointer to give it</FONT> -<BR><FONT FACE="Arial,Helvetica"> -a reference to.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -ACE_Message_Block * mb;</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Create the guard for our active thread counter object. No matter -where we choose to</FONT> -<BR><FONT FACE="Arial,Helvetica"> -return() from svc(), we no know that the counter will be decremented.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Counter_Guard counter_guard(active_threads_);</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Get messages from the queue until we have a failure. There's no real -good reason</FONT> -<BR><FONT FACE="Arial,Helvetica"> -for failure so if it happens, we leave immediately.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -while( this->getq(mb) != -1 )</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -A successful getq() will cause "mb" to point to a valid refernce-counted</FONT> -<BR><FONT FACE="Arial,Helvetica"> -ACE_Message_Block. We use our guard object here so that we're sure -to call</FONT> -<BR><FONT FACE="Arial,Helvetica"> -the release() method of that message block and reduce it's reference count.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Once the count reaches zero, it will be deleted.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Message_Block_Guard message_block_guard(mb);</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -As noted before, the ACE_Message_Block stores it's data as a char*. -We pull that</FONT> -<BR><FONT FACE="Arial,Helvetica"> -out here and later turn it into an ACE_Event_Handler*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -char * c_data = mb->base();</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -We've chosen to use a "null" value as an indication to leave. If -the data we got</FONT> -<BR><FONT FACE="Arial,Helvetica"> -from the queue is not null then we have some work to do.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -if( c_data )</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Once again, we go to great lengths to emphasize the fact that we're casting -pointers</FONT> -<BR><FONT FACE="Arial,Helvetica"> -around in rather impolite ways. We could have cast the char* directly -to an</FONT> -<BR><FONT FACE="Arial,Helvetica"> -ACE_Event_Handler* but then folks might think that's an OK thing to do.</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -(Note: The correct way to use an ACE_Message_Block is to write data -into it.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -What I should have done was create a message block big enough to hold an</FONT> -<BR><FONT FACE="Arial,Helvetica"> -event handler pointer and then written the pointer value into the block. -When</FONT> -<BR><FONT FACE="Arial,Helvetica"> -we got here, I would have to read that data back into a pointer. -While politically</FONT> -<BR><FONT FACE="Arial,Helvetica"> -correct, it is also a lot of work. If you're careful you can get -away with casting</FONT> -<BR><FONT FACE="Arial,Helvetica"> -pointers around.)</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -void * v_data = (void*)c_data;</FONT> -<BR><FONT FACE="Arial,Helvetica"> </FONT> -<BR><FONT FACE="Arial,Helvetica"> -ACE_Event_Handler * handler = (ACE_Event_Handler*)v_data;</FONT> -<BR><FONT FACE="Arial,Helvetica"> </FONT> -<BR><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Now that we finally have an event handler pointer, invoke it's handle_input() -method.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Since we don't know it's handle, we just give it a default. That's -OK because we</FONT> -<BR><FONT FACE="Arial,Helvetica"> -know that we're not using the handle in the method anyway.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -if( handler->handle_input(ACE_INVALID_HANDLE) == -1 )</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Tell the handler that it's time to go home. The "normal" method for -shutting</FONT> -<BR><FONT FACE="Arial,Helvetica"> -down a handler whose handler failed is to invoke handle_close(). -This will</FONT> -<BR><FONT FACE="Arial,Helvetica"> -take care of cleaning it up for us.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Notice how we use the handler's get_handle() method to populate it's "handle"</FONT> -<BR><FONT FACE="Arial,Helvetica"> -parameter. Convenient isn't it?</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -handler->handle_close(handler->get_handle(),0);</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -Also notice that we don't exit the svc() method here! The first time -I did</FONT> -<BR><FONT FACE="Arial,Helvetica"> -this, I was exiting. After a few clients disconnect you have an empty</FONT> -<BR><FONT FACE="Arial,Helvetica"> -thread pool. Hard to do any more work after that...</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -}</FONT> -<BR><FONT FACE="Arial,Helvetica"> -}</FONT> -<BR><FONT FACE="Arial,Helvetica"> -else</FONT> -<BR><FONT FACE="Arial,Helvetica"> -{</FONT> -<BR><FONT FACE="Arial,Helvetica"> -/*</FONT> -<BR><FONT FACE="Arial,Helvetica"> -If we get here, we were given a message block with "null" data. That -is our</FONT> -<BR><FONT FACE="Arial,Helvetica"> -signal to leave, so we return(0) to leave gracefully.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -*/</FONT> -<BR><FONT FACE="Arial,Helvetica"> -return(0); -// Ok, shutdown request</FONT> -<BR><FONT FACE="Arial,Helvetica"> -}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -// message_block_guard goes out of scope here</FONT> -<BR><FONT FACE="Arial,Helvetica"> -// and releases the message_block instance.</FONT> -<BR><FONT FACE="Arial,Helvetica"> -}</FONT><FONT FACE="Arial,Helvetica"></FONT> - -<P><FONT FACE="Arial,Helvetica"> -return(0);</FONT> -<BR><FONT FACE="Arial,Helvetica">}</FONT> - -<P> -<HR WIDTH="100%"> -<CENTER>[<A HREF="..">Tutorial -Index</A>] [<A HREF="page09.html">Continue -This Tutorial</A>]</CENTER> - -</BODY> -</HTML> diff --git a/docs/tutorials/007/page09.html b/docs/tutorials/007/page09.html deleted file mode 100644 index 00e9ff650b5..00000000000 --- a/docs/tutorials/007/page09.html +++ /dev/null @@ -1,85 +0,0 @@ -<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 006</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 WIDTH="100%"> - -<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> - -<LI> -<A HREF="fix.Makefile">fix.Makefile</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="..">Tutorial -Index</A>]</CENTER> - -</BODY> -</HTML> diff --git a/docs/tutorials/007/server.cpp b/docs/tutorials/007/server.cpp deleted file mode 100644 index 55fb69c58ef..00000000000 --- a/docs/tutorials/007/server.cpp +++ /dev/null @@ -1,119 +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[]) -{ -/* - 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 852b51ef232..00000000000 --- a/docs/tutorials/007/thread_pool.cpp +++ /dev/null @@ -1,277 +0,0 @@ - -// $Id$ - -#include "thread_pool.h" - -/* - We need this header so that we can invoke handle_input() on the objects we dequeue. - */ -#include "ace/Event_Handler.h" - - -/* - All we do here is initialize our active thread counter. - */ -Thread_Pool::Thread_Pool(void) - : active_threads_(0) -{ -} - -/* - Our open() method is a thin disguise around the ACE_Task<> activate() method. By - hiding activate() in this way, the users of Thread_Pool don't have to worry about - the thread configuration flags. - */ -int Thread_Pool::open( int _pool_size ) -{ - return this->activate(THR_NEW_LWP,_pool_size); -} - -/* - Closing the thread pool can be a tricky exercise. I've decided to take an easy approach - and simply enqueue a secret message for each thread we have active. - */ -int Thread_Pool::close( 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-second or so between tries. - */ - while( active_threads_.value() ) - { - ACE_OS::sleep( ACE_Time_Value(0.25) ); - } - - return(0); -} - -/* - When an object wants to do work in the pool, it should call the enqueue() method. - We introduce the ACE_Message_Block here but, unfortunately, we seriously missuse it. - */ -int Thread_Pool::enqueue( ACE_Event_Handler * _handler ) -{ - /* - An ACE_Message_Block is a chunk of data. You put them into an ACE_Message_Queue. - ACE_Task<> has an ACE_Message_Queue built in. In fact, the parameter to ACE_Task<> - is passed directly to ACE_Message_Queue. If you look back at our header file you'll - see that we used ACE_MT_SYNCH as the parameter indicating that we want MultiThread - Synch safety. This allows us to safely put ACE_Message_Block objects into the - message queue in one thread and take them out in another. - */ - - /* - An ACE_Message_Block wants to have char* data. We don't have that. We could - cast our ACE_Event_Handler* directly to a char* but I wanted to be more explicit. - Since casting pointers around is a dangerous thing, I've gone out of my way here - to be very clear about what we're doing. - - First: Cast the handler pointer to a void pointer. You can't do any useful work - on a void pointer, so this is a clear message that we're making the - pointer unusable. - - Next: Cast the void pointer to a char pointer that the ACE_Message_Block will accept. - */ - void * v_data = (void*)_handler; - char * c_data = (char*)v_data; - - /* - Construct a new ACE_Message_Block. For efficiency, you might want to preallocate a - stack of these and reuse them. For simplicity, I'll just create what I need as I need it. - */ - ACE_Message_Block * mb = new ACE_Message_Block( c_data ); - - /* - Our putq() method is a wrapper around one of the enqueue methods of the ACE_Message_Queue - that we own. Like all good methods, it returns -1 if it fails for some reason. - */ - if( this->putq(mb) == -1 ) - { - /* - Another trait of the ACE_Message_Block objects is that they are reference counted. - Since they're designed to be passed around between various objects in several threads - we can't just delete them whenever we feel like it. The release() method is similar - to the destroy() method we've used elsewhere. It watches the reference count and will - delete the object when possible. - */ - mb->release(); - return(-1); - } - - return(0); -} - -/* - The "guard" concept is very powerful and used throughout multi-threaded applications. - A guard normally does some operation on an object at construction and the "opposite" - operation at destruction. For instance, when you guard a mutex (lock) object, the guard - will acquire the lock on construction and release it on destruction. In this way, your - method can simply let the guard go out of scope and know that the lock is released. - - Guards aren't only useful for locks however. In this application I've created two guard - objects for quite a different purpose. - */ - -/* - The Counter_Guard is constructed with a reference to the thread pool's active thread - counter. The guard increments the counter when it is created and decrements it at - destruction. By creating one of these in svc(), I know that the counter will be decremented - no matter how or where svc() returns. - */ -class Counter_Guard -{ -public: - Counter_Guard( Thread_Pool::counter_t & _counter ) - : counter_(_counter) - { - ++counter_; - } - - ~Counter_Guard(void) - { - --counter_; - } - -protected: - Thread_Pool::counter_t & counter_; -}; - -/* - My Message_Block_Guard is also a little non-traditional. It doesn't do anything in the - constructor but it's destructor ensures that the message block's release() method is called. - This is a cheap way to prevent a memory leak if I need an additional exit point in svc(). - */ -class Message_Block_Guard -{ -public: - Message_Block_Guard( ACE_Message_Block * & _mb ) - : mb_(_mb) - { - } - - ~Message_Block_Guard( void ) - { - mb_->release(); - } - -protected: - ACE_Message_Block * & mb_; -}; - -/* - Now we come to the svc() method. As I said, this is being executed in each thread of the - Thread_Pool. Here, we pull messages off of our built-in ACE_Message_Queue and cause them - to do work. - */ -int Thread_Pool::svc(void) -{ - /* - The getq() method takes a reference to a pointer. So... we need a pointer to give it - a reference to. - */ - ACE_Message_Block * mb; - - /* - Create the guard for our active thread counter object. No matter where we choose to - return() from svc(), we no know that the counter will be decremented. - */ - Counter_Guard counter_guard(active_threads_); - - /* - Get messages from the queue until we have a failure. There's no real good reason - for failure so if it happens, we leave immediately. - */ - while( this->getq(mb) != -1 ) - { - /* - A successful getq() will cause "mb" to point to a valid refernce-counted - ACE_Message_Block. We use our guard object here so that we're sure to call - the release() method of that message block and reduce it's reference count. - Once the count reaches zero, it will be deleted. - */ - Message_Block_Guard message_block_guard(mb); - - /* - As noted before, the ACE_Message_Block stores it's data as a char*. We pull that - out here and later turn it into an ACE_Event_Handler* - */ - char * c_data = mb->base(); - - /* - We've chosen to use a "null" value as an indication to leave. If the data we got - from the queue is not null then we have some work to do. - */ - if( c_data ) - { - /* - Once again, we go to great lengths to emphasize the fact that we're casting pointers - around in rather impolite ways. We could have cast the char* directly to an - ACE_Event_Handler* but then folks might think that's an OK thing to do. - - (Note: The correct way to use an ACE_Message_Block is to write data into it. - What I should have done was create a message block big enough to hold an - event handler pointer and then written the pointer value into the block. When - we got here, I would have to read that data back into a pointer. While politically - correct, it is also a lot of work. If you're careful you can get away with casting - pointers around.) - */ - void * v_data = (void*)c_data; - - ACE_Event_Handler * handler = (ACE_Event_Handler*)v_data; - - /* - Now that we finally have an event handler pointer, invoke it's handle_input() method. - Since we don't know it's handle, we just give it a default. That's OK because we - know that we're not using the handle in the method anyway. - */ - if( handler->handle_input(ACE_INVALID_HANDLE) == -1 ) - { - /* - Tell the handler that it's time to go home. The "normal" method for shutting - down a handler whose handler failed is to invoke handle_close(). This will - take care of cleaning it up for us. - Notice how we use the handler's get_handle() method to populate it's "handle" - parameter. Convenient isn't it? - */ - handler->handle_close(handler->get_handle(),0); - - /* - Also notice that we don't exit the svc() method here! The first time I did - this, I was exiting. After a few clients disconnect you have an empty - thread pool. Hard to do any more work after that... - */ - } - } - else - { - /* - If we get here, we were given a message block with "null" data. That is our - signal to leave, so we return(0) to leave gracefully. - */ - return(0); // Ok, shutdown request - } - - // message_block_guard goes out of scope here - // and releases the message_block instance. - } - - return(0); -} - diff --git a/docs/tutorials/007/thread_pool.h b/docs/tutorials/007/thread_pool.h deleted file mode 100644 index 12b9f14a44f..00000000000 --- a/docs/tutorials/007/thread_pool.h +++ /dev/null @@ -1,102 +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); } - - /* - */ - 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 |