summaryrefslogtreecommitdiff
path: root/docs/tutorials/007/page05.html
blob: f3b2774923201371eacc3e5156fc27f13d9e61c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
<HTML>
<HEAD>
   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
   <META NAME="GENERATOR" CONTENT="Mozilla/4.04 [en] (X11; I; Linux 2.0.32 i486) [Netscape]">
   <META NAME="Author" CONTENT="James CE Johnson">
   <META NAME="Description" CONTENT="A first step towards using ACE productively">
   <TITLE>ACE Tutorial 007</TITLE>
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">

<CENTER><B><FONT SIZE=+2>ACE Tutorial 007</FONT></B></CENTER>

<CENTER><B><FONT SIZE=+2>Creating a thread-pool server</FONT></B></CENTER>
<HR>

<P>As you might expect, <A HREF="client_handler.h">client_handler.h</A>
is next.

<P>
<HR WIDTH="100%">
<PRE>

<font color=red>// $Id$</font>

<font color=blue>#ifndef</font> <font color=purple>CLIENT_HANDLER_H</font>
<font color=blue>#define</font> <font color=purple>CLIENT_HANDLER_H</font>

<font color=red>/*
   Our client handler must exist somewhere in the ACE_Event_Handler object
   hierarchy.  This is a requirement of the ACE_Reactor because it maintains
   ACE_Event_Handler pointers for each registered event handler.  You could
   derive our Client_Handler directly from ACE_Event_Handler but you still have
   to have an ACE_SOCK_Stream for the actually connection.  With a direct
   derivative of ACE_Event_Handler, you'll have to contain and maintain an
   ACE_SOCK_Stream instance yourself.  With ACE_Svc_Handler (which is a
   derivative of ACE_Event_Handler) some of those details are handled for you.
 */</font>

<font color=blue>#include</font> "<font color=green>ace/Svc_Handler.h</font>"

<font color=blue>#if !defined</font> (<font color=purple>ACE_LACKS_PRAGMA_ONCE</font>)
# pragma once
<font color=blue>#endif</font> <font color=red>/* ACE_LACKS_PRAGMA_ONCE */</font>

<font color=blue>#include</font> "<font color=green>ace/SOCK_Stream.h</font>"

class Client_Acceptor;
class Thread_Pool;

<font color=red>/*
   Another feature of ACE_Svc_Handler is it's ability to present the ACE_Task&lt;>
   interface as well.  That's what the ACE_NULL_SYNCH parameter below is all
   about.  That's beyond our scope here but we'll come back to it in the next
   tutorial when we start looking at concurrency options.
 */</font>
class Client_Handler : public ACE_Svc_Handler &lt; ACE_SOCK_STREAM, ACE_NULL_SYNCH >
{
public:
  typedef ACE_Svc_Handler &lt; ACE_SOCK_STREAM, ACE_NULL_SYNCH > inherited;

  <font color=red>// Constructor...</font>
  Client_Handler (void);

  <font color=red>/*
     The destroy() method is our preferred method of destruction.  We could
     have overloaded the delete operator but that is neither easy nor
     intuitive (at least to me).  Instead, we provide a new method of
     destruction and we make our destructor protected so that only ourselves,
     our derivatives and our friends can delete us. It's a nice
     compromise.
   */</font>
  void destroy (void);

  <font color=red>/*
     Most ACE objects have an open() method.  That's how you make them ready
     to do work.  ACE_Event_Handler has a virtual open() method which allows us
     to create this overrride.  ACE_Acceptor&lt;> 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&lt;>* but since ACE_Event_Handler is generic, that
     would tie it too closely to the ACE_Acceptor&lt;> set of objects.  In our
     definition of open() you'll see how we get around that.
   */</font>
  int open (void *_acceptor);

  <font color=red>/*
     When an ACE_Task&lt;> object falls out of the svc() method, the framework
     will call the close() method.  That's where we want to cleanup ourselves
     if we're running in either thread-per-connection or thread-pool mode.
   */</font>
  int close(u_long flags = 0);

  <font color=red>/*
     When there is activity on a registered handler, the handle_input() method
     of the handler will be invoked.  If that method returns an error code (eg
     -- -1) then the reactor will invoke handle_close() to allow the object to
     clean itself up. Since an event handler can be registered for more than
     one type of callback, the callback mask is provided to inform
     handle_close() exactly which method failed.  That way, you don't have to
     maintain state information between your handle_* method calls. The _handle
     parameter is explained below...
     As a side-effect, the reactor will also invoke remove_handler()
     for the object on the mask that caused the -1 return.  This means 
     that we don't have to do that ourselves!
   */</font>
  int handle_close (ACE_HANDLE _handle, ACE_Reactor_Mask _mask);

  <font color=red>/*
     When we register with the reactor, we're going to tell it that we want to
     be notified of READ events.  When the reactor sees that there is read
     activity for us, our handle_input() will be invoked. The _handleg
     provided is the handle (file descriptor in Unix) of the actual connection
     causing the activity.  Since we're derived from ACE_Svc_Handler&lt;> 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.
   */</font>
  int handle_input (ACE_HANDLE _handle);

protected:

  <font color=red>/*
     If the Client_Acceptor which created us has chosen a thread-per-connection
     strategy then our open() method will activate us into a dedicate thread.
     The svc() method will then execute in that thread performing some of the
     functions we used to leave up to the reactor.
   */</font>
  int svc(void);

  <font color=red>/*
     This has nothing at all to do with ACE.  I've added this here as a worker
     function which I will call from handle_input().  That allows me to
     introduce concurrencly in later tutorials with a no changes to the worker
     function.  You can think of process() as application-level code and
     everything elase as application-framework code.
   */</font>
  int process (char *_rdbuf, int _rdbuf_len);

  <font color=red>/*
     We don't really do anything in our destructor but we've declared it to be
     protected to prevent casual deletion of this object.  As I said above, I
     really would prefer that everyone goes through the destroy() method to get
     rid of us.
   */</font>
   ~Client_Handler (void);

   <font color=red>/*
      When we get to the definition of Client_Handler we'll see that there are
          several places where we go back to the Client_Acceptor for information.
          It is generally a good idea to do that through an accesor rather than
          using the member variable directly.
    */</font>
   Client_Acceptor * client_acceptor( void )
                { return this->client_acceptor_; }

   <font color=red>/*
      And since you shouldn't access a member variable directly, neither should you
          set (mutate) it.  Although it might seem silly to do it this way, you'll thank
          yourself for it later.
    */</font>
   void client_acceptor( Client_Acceptor * _client_acceptor )
                { this->client_acceptor_ = _client_acceptor; }

   <font color=red>/*
      The concurrency() accessor tells us the current concurrency strategy.  It actually
          queries the Client_Acceptor for it but by having the accessor in place, we could
          change our implementation without affecting everything that needs to know.
    */</font>
   int concurrency(void);

   <font color=red>/*
      Likewise for access to the Thread_Pool that we belong to.
    */</font>
   Thread_Pool * thread_pool(void);


   Client_Acceptor * client_acceptor_;

   <font color=red>/*
      For some reason I didn't create accessor/mutator methods for this.  So much for
          consistency....

          This variable is used to remember the thread in which we were created:  the "<font color=green>creator</font>"
          thread in other words.  handle_input() needs to know if it is operating in the
          main reactor thread (which is the one that created us) or if it is operating in
          one of the thread pool threads.  More on this when we get to handle_input().
    */</font>
   ACE_thread_t      creator_;
};

<font color=blue>#endif</font> <font color=red>// CLIENT_HANDLER_H</font>
</PRE>
<HR WIDTH="100%">

<P>Still, we're just not seeing a lot of changes due to introduction of
the thread pool.&nbsp; That's a good thing! You don't want to go turning
your application upside down just because you changed thread models.

<P><HR WIDTH="100%">
<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page06.html">Continue This Tutorial</A>]</CENTER>