summaryrefslogtreecommitdiff
path: root/docs/tutorials/007/page08.html
blob: f3b7a1d1b6e5e8f58e4712a5bdd8a44797ec2a86 (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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
<HTML>
<HEAD>
   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
   <META NAME="GENERATOR" CONTENT="Mozilla/4.04 [en] (X11; I; Linux 2.0.32 i486) [Netscape]">
   <META NAME="Author" CONTENT="James CE Johnson">
   <META NAME="Description" CONTENT="A first step towards using ACE productively">
   <TITLE>ACE Tutorial 007</TITLE>
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">

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

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

<P>Finally, <A HREF="thread_pool.cpp">thread_pool.cpp</A>
where we have the Thread_Pool object implementation.

<P>
<HR WIDTH="100%">
<PRE>
<font color=red>// $Id$</font>

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

<font color=red>/* We need this header so that we can invoke handle_input() on the
   objects we dequeue.  */</font>
<font color=blue>#include</font> "<A HREF="../../../ace/Event_Handler.h">ace/Event_Handler.h</A>"

<font color=red>/* All we do here is initialize our active thread counter.  */</font>
<font color=#008888>Thread_Pool::Thread_Pool</font> (void)
  : active_threads_ (0)
{
}

<font color=red>/* Our open() method is a thin disguise around the ACE_Task&lt;>
   activate() method.  By hiding activate() in this way, the users of
   Thread_Pool don't have to worry about the thread configuration
   flags.  */</font>
int 
<font color=#008888>Thread_Pool::open</font> (int pool_size)
{
  return this->activate (THR_NEW_LWP, pool_size);
}

<font color=red>/* Closing the thread pool can be a tricky exercise.  I've decided to
   take an easy approach and simply enqueue a secret message for each
   thread we have active.  */</font>
int 
<font color=#008888>Thread_Pool::close</font> (u_long flags)
{
  ACE_UNUSED_ARG(flags);

  <font color=red>/* Find out how many threads are currently active */</font>
  int counter = active_threads_.value ();

  <font color=red>/* For each one of the active threads, enqueue a "<font color=green>null</font>" event
    handler.  Below, we'll teach our svc() method that "<font color=green>null</font>" means
    "<font color=green>shutdown</font>".  */</font>
  while (counter--)
    this->enqueue (0);

  <font color=red>/* As each svc() method exits, it will decrement the active thread
    counter.  We just wait here for it to reach zero.  Since we don't
    know how long it will take, we sleep for a quarter of a second
    between tries.  */</font>
  while (active_threads_.value ())
    <font color=#008888>ACE_OS::sleep</font> (ACE_Time_Value (0, 250000));

  return(0);
}

<font color=red>/* When an object wants to do work in the pool, it should call the
   enqueue() method.  We introduce the ACE_Message_Block here but,
   unfortunately, we seriously misuse it.  */</font>
int 
<font color=#008888>Thread_Pool::enqueue</font> (ACE_Event_Handler *handler)
{
  <font color=red>/* An ACE_Message_Block is a chunk of data.  You put them into an
    ACE_Message_Queue.  ACE_Task&lt;> has an ACE_Message_Queue built in.
    In fact, the parameter to ACE_Task&lt;> is passed directly to
    ACE_Message_Queue.  If you look back at our header file you'll see
    that we used ACE_MT_SYNCH as the parameter indicating that we want
    MultiThread Synch safety.  This allows us to safely put
    ACE_Message_Block objects into the message queue in one thread and
    take them out in another.  */</font>

  <font color=red>/* An ACE_Message_Block wants to have char* data.  We don't have
    that.  We could cast our ACE_Event_Handler* directly to a char*
    but I wanted to be more explicit.  Since casting pointers around
    is a dangerous thing, I've gone out of my way here to be very
    clear about what we're doing.

    First: Cast the handler pointer to a void pointer.  You can't do
    any useful work on a void pointer, so this is a clear message that
    we're making the pointer unusable.

    Next: Cast the void pointer to a char pointer that the ACE_Message_Block will accept.  */</font>
  void *v_data = (void *) handler;
  char *c_data = (char *) v_data;

  ACE_Message_Block *mb;

  <font color=red>/* Construct a new ACE_Message_Block.  For efficiency, you might
    want to preallocate a stack of these and reuse them.  For
    simplicity, I'll just create what I need as I need it.  */</font>
  ACE_NEW_RETURN (mb,
                  ACE_Message_Block (c_data),
                  -1);

  <font color=red>/* Our putq() method is a wrapper around one of the enqueue methods
    of the ACE_Message_Queue that we own.  Like all good methods, it
    returns -1 if it fails for some reason.  */</font>
  if (this->putq (mb) == -1)
    {
      <font color=red>/* Another trait of the ACE_Message_Block objects is that they
        are reference counted.  Since they're designed to be passed
        around between various objects in several threads we can't
        just delete them whenever we feel like it.  The release()
        method is similar to the destroy() method we've used
        elsewhere.  It watches the reference count and will delete the
        object when possible.  */</font>
      mb->release ();
      return -1;
    }

  return 0;
}

<font color=red>/* The "<font color=green>guard</font>" concept is very powerful and used throughout
   multi-threaded applications.  A guard normally does some operation
   on an object at construction and the "<font color=green>opposite</font>" operation at
   destruction.  For instance, when you guard a mutex (lock) object,
   the guard will acquire the lock on construction and release it on
   destruction.  In this way, your method can simply let the guard go
   out of scope and know that the lock is released.

   Guards aren't only useful for locks however.  In this application
   I've created two guard objects for quite a different purpose.  */</font>

<font color=red>/* The Counter_Guard is constructed with a reference to the thread
   pool's active thread counter.  The guard increments the counter
   when it is created and decrements it at destruction.  By creating
   one of these in svc(), I know that the counter will be decremented
   no matter how or where svc() returns.  */</font>
class Counter_Guard
{
public:
  Counter_Guard (<font color=#008888>Thread_Pool::counter_t</font> &counter)
    : counter_ (counter)
  {
    ++counter_;
  }

  ~Counter_Guard (void)
  {
    --counter_;
  }

protected:
  <font color=#008888>Thread_Pool::counter_t</font> &counter_;
};

<font color=red>/* My Message_Block_Guard is also a little non-traditional.  It
   doesn't do anything in the constructor but it's destructor ensures
   that the message block's release() method is called.  This is a
   cheap way to prevent a memory leak if I need an additional exit
   point in svc().  */</font>
class Message_Block_Guard
{
public:
  Message_Block_Guard (ACE_Message_Block *&mb)
    : mb_ (mb)
  {
  }

  ~Message_Block_Guard (void)
  {
    mb_->release ();
  }

protected:
  ACE_Message_Block *&mb_;
};

<font color=red>/* Now we come to the svc() method.  As I said, this is being executed
   in each thread of the Thread_Pool.  Here, we pull messages off of
   our built-in ACE_Message_Queue and cause them to do work.  */</font>
int 
<font color=#008888>Thread_Pool::svc</font> (void)
{
  <font color=red>/* The getq() method takes a reference to a pointer.  So... we need
    a pointer to give it a reference to.  */</font>
  ACE_Message_Block *mb;

  <font color=red>/* Create the guard for our active thread counter object.  No matter
    where we choose to return() from svc(), we now know that the
    counter will be decremented.  */</font>
  Counter_Guard counter_guard (active_threads_);

  <font color=red>/* Get messages from the queue until we have a failure.  There's no
    real good reason for failure so if it happens, we leave
    immediately.  */</font>
  while (this->getq (mb) != -1)
    {
      <font color=red>/* A successful getq() will cause "<font color=green>mb</font>" to point to a valid
        refernce-counted ACE_Message_Block.  We use our guard object
        here so that we're sure to call the release() method of that
        message block and reduce it's reference count.  Once the count
        reaches zero, it will be deleted.  */</font>
      Message_Block_Guard message_block_guard (mb);

      <font color=red>/* As noted before, the ACE_Message_Block stores it's data as a
        char*.  We pull that out here and later turn it into an
        ACE_Event_Handler* */</font>
      char *c_data = mb->base ();

      <font color=red>/* We've chosen to use a "<font color=green>null</font>" value as an indication to leave.
        If the data we got from the queue is not null then we have
        some work to do.  */</font>
      if (c_data)
        {
          <font color=red>/* Once again, we go to great lengths to emphasize the fact
            that we're casting pointers around in rather impolite
            ways.  We could have cast the char* directly to an
            ACE_Event_Handler* but then folks might think that's an OK
            thing to do.

            (Note: The correct way to use an ACE_Message_Block is to
            write data into it.  What I should have done was create a
            message block big enough to hold an event handler pointer
            and then written the pointer value into the block.  When
            we got here, I would have to read that data back into a
            pointer.  While politically correct, it is also a lot of
            work.  If you're careful you can get away with casting
            pointers around.)  */</font>
          void *v_data = (void *) c_data;
	
          ACE_Event_Handler *handler = (ACE_Event_Handler *) v_data;
	
          <font color=red>/* Now that we finally have an event handler pointer, invoke
            it's handle_input() method.  Since we don't know it's
            handle, we just give it a default.  That's OK because we
            know that we're not using the handle in the method anyway.  */</font>
          if (handler->handle_input (ACE_INVALID_HANDLE) == -1)
            {
              <font color=red>/* Tell the handler that it's time to go home.  The
                "<font color=green>normal</font>" method for shutting down a handler whose
                handler failed is to invoke handle_close().  This will
                take care of cleaning it up for us.  Notice how we use
                the handler's get_handle() method to populate it's
                "<font color=green>handle</font>" parameter.  Convenient isn't it?  */</font>
              handler->handle_close (handler->get_handle (), 0);

              <font color=red>/* Also notice that we don't exit the svc() method here!
                The first time I did this, I was exiting.  After a few
                clients disconnect you have an empty thread pool.
                Hard to do any more work after that...  */</font>
            }
        }
      else
        <font color=red>/* If we get here, we were given a message block with "<font color=green>null</font>"
           data.  That is our signal to leave, so we return(0) to
           leave gracefully.  */</font>
          return 0;		<font color=red>// Ok, shutdown request</font>

      <font color=red>// message_block_guard goes out of scope here and releases the</font>
      <font color=red>// message_block instance.</font>
    }

  return 0;
}

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