summaryrefslogtreecommitdiff
path: root/docs/tutorials/013/page07.html
blob: 415521d09e331a77e5d759eb13d41304c4032aaa (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
<HTML>
<HEAD>
   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
   <META NAME="Author" CONTENT="James CE Johnson">
   <TITLE>ACE Tutorial 013</TITLE>
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">

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

<CENTER><B><FONT SIZE=+2>Multiple thread pools</FONT></B></CENTER>


<P>
<HR WIDTH="100%">
<P>
I've been trying to justify the chain of tasks by talking about a
Work object that implements a state machine.  The idea is that your
Work object has to perform a series of discrete steps to complete it's 
function.  Traditionally, all of those steps would take place in one
thread of execution.  That thread would probably be one from a Task
thread pool.
<P>
Suppose, however, that some of those steps spend a lot of time waiting 
for disk IO.  You could find that all of your thread-pool threads 
are just sitting there waiting for the disk.  You might then be
tempted to increase the thread pool size to get more work through.
However, if some of the stages are memory intensive, you could run out 
of memory if all of the workers get to that state at the same time.
<P>
One solution might be to have different thread pools for each state.
Each pool could have it's size tuned appropriately for the work that
would be done there.  That's where the chain of Tasks comes in.  
 In this tutorial's implementation I've taken the 
easy route and set all of the thread pools to the same size but a more 
realistic solution would be to set each thread pool in the chain to a
specific size as needed by that state of operation.
<P>
There's not much to this header either so I've combined it with the
cpp file as with task.
<P>
<HR WIDTH="100%">
<PRE>
<HR width=50%><P><center>work.h</center><HR width=50%>

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

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

<font color=blue>#include</font> "<font color=green>ace/Log_Msg.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/Synch.h</font>"
<font color=blue>#include</font> "<font color=green>mld.h</font>"

<font color=red>/*
   Our specilized message queue and thread pool will know how to do "<font color=green>work</font>" on
   our Unit_Of_Work baseclass.
 */</font>
class Unit_Of_Work
{
public:
    Unit_Of_Work (void);

    virtual ~ Unit_Of_Work (void);

        <font color=red>// Display the object instance value</font>
    void who_am_i (void);

        <font color=red>// The baseclass can override this to show it's "<font color=green>type name</font>"</font>
    virtual void what_am_i (void);

        <font color=red>// This is where you do application level logic.  It will be</font>
        <font color=red>// called once for each thread pool it passes through.  It</font>
        <font color=red>// would typically implement a state machine and execute a</font>
        <font color=red>// different state on each call.</font>
    virtual int process (void);

        <font color=red>// This is called by the last Task in the series (see task.h)</font>
        <font color=red>// in case our process() didn't get through all of it's states.</font>
    virtual int fini (void);

protected:
    ACE_Atomic_Op &lt; ACE_Mutex, int >state_;
    MLD;
};

<font color=red>/*
   A fairly trivial work derivative that implements an equally trivial state
   machine in process()
 */</font>
class Work : public Unit_Of_Work
{
public:
    Work (void);

    Work (int message);

    virtual ~ Work (void);

    void what_am_i (void);

    int process (void);

    int fini (void);

protected:
    int message_;
    MLD;
};

<font color=blue>#endif</font>
</PRE>
<PRE>
<HR width=50%><P><center>work.cpp</center><HR width=50%>

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

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

<font color=red>/*
   Initialize the state to zero 
 */</font>
<font color=#008888>Unit_Of_Work::Unit_Of_Work</font> (void)
        : state_ (0)
{
    ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Unit_Of_Work ctor\n</font>", (void *) this));
}

<font color=#008888>Unit_Of_Work::~Unit_Of_Work</font> (void)
{
    ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Unit_Of_Work dtor\n</font>", (void *) this));
}

<font color=red>/*
   Display our instance value 
 */</font>
void <font color=#008888>Unit_Of_Work::who_am_i</font> (void)
{
    ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Unit_Of_Work instance\n</font>", (void *) this));
}

<font color=red>/*
   Dispay our type name 
 */</font>
void <font color=#008888>Unit_Of_Work::what_am_i</font> (void)
{
    ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x I am a Unit_Of_Work object\n</font>", (void *) this));
}

<font color=red>/*
   Return failure.  You should always derive from Unit_Of_Work... 
 */</font>
int <font color=#008888>Unit_Of_Work::process</font> (void)
{
    return -1;
}

<font color=red>/*
   ditto 
 */</font>
int <font color=#008888>Unit_Of_Work::fini</font> (void)
{
    return -1;
}

<font color=red>/*
   Default constructor has no "<font color=green>message number</font>" 
 */</font>
<font color=#008888>Work::Work</font> (void)
        :message_ (-1)
{
    ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Work ctor\n</font>", (void *) this));
}

<font color=red>/*
   The useful constructor remembers which message it is and will tell you if
   you ask. 
 */</font>
<font color=#008888>Work::Work</font> (int message)
        : message_ (message)
{
    ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Work ctor for message %d\n</font>", (void *) this, message_));
}

<font color=#008888>Work::~Work</font> (void)
{
    ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Work dtor\n</font>", (void *) this));
}

<font color=red>/*
   This objects type name is different from the baseclass 
 */</font>
void <font color=#008888>Work::what_am_i</font> (void)
{
    ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x I am a Work object for message %d\n</font>", (void *) this, message_));
}

<font color=red>/*
   A very simple state machine that just walks through three stages. If it is
   called more than that, it will tell you not to bother. 
 */</font>
int <font color=#008888>Work::process</font> (void)
{
    switch (++state_)
    {
        case 1:
            ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Stage One\n</font>", (void *) this));
            break;
        case 2:
            ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Stage Two\n</font>", (void *) this));
            break;
        case 3:
            ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Stage Three\n</font>", (void *) this));
            break;
        default:
            ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x No work to do in state %d\n</font>",
                        (void *) this, state_.value ()));
            break;
    }
    return (0);
}

<font color=red>/*
   If you don't have enough subtasks in the chain then the state machine won't
   progress to the end.  The fini() hook will allow us to  recover from that by 
   executing the remaining states in the final task of the chain. 
 */</font>
int <font color=#008888>Work::fini</font> (void)
{
    while (state_.value () &lt; 3)
    {
        if (this->process () == -1)
        {
            ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>process</font>"), -1);
        }
    }
    return (0);
}
</PRE>
<HR>
<P>
And that is that.  For a more complex machine that may want to "jump
states" you would have to set some "state information" (sorry, bad
choice of terminology again) so that process() could decide what to do 
at each call.  You might also modify Task::svc() so that it will
respect the return value of process() and do something useful with the 
information.
<P>
<P><HR WIDTH="100%">
<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page08.html">Continue This Tutorial</A>]</CENTER>