summaryrefslogtreecommitdiff
path: root/docs/tutorials/013/combine.shar
blob: 488951315503c470d5ed29ac54187299c7c794b4 (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
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.2).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 2000-03-19 15:00 EST by <jcej@chiroptera.tragus.org>.
# Source directory was `/home/jcej/projects/ACE_wrappers/docs/tutorials/013'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#    386 -rw-rw-r-- hdr
#     89 -rw-rw-r-- bodies
#   2412 -rw-rw-r-- page01.pre
#    432 -rw-rw-r-- page02.pre
#   1424 -rw-rw-r-- page03.pre
#   1048 -rw-rw-r-- page04.pre
#    268 -rw-rw-r-- page05.pre
#    914 -rw-rw-r-- page06.pre
#   1360 -rw-rw-r-- page07.pre
#    860 -rw-rw-r-- page08.pre
#    204 -rw-rw-r-- page02.pst
#    704 -rw-rw-r-- page04.pst
#    385 -rw-rw-r-- page06.pst
#    369 -rw-rw-r-- page07.pst
#
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
  if test "$gettext_dir" = FAILED && test -f $dir/gettext \
     && ($dir/gettext --version >/dev/null 2>&1)
  then
    set `$dir/gettext --version 2>&1`
    if test "$3" = GNU
    then
      gettext_dir=$dir
    fi
  fi
  if test "$locale_dir" = FAILED && test -f $dir/shar \
     && ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
  then
    locale_dir=`$dir/shar --print-text-domain-dir`
  fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
  echo=echo
else
  TEXTDOMAINDIR=$locale_dir
  export TEXTDOMAINDIR
  TEXTDOMAIN=sharutils
  export TEXTDOMAIN
  echo="$gettext_dir/gettext -s"
fi
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
  shar_touch=touch
else
  shar_touch=:
  echo
  $echo 'WARNING: not restoring timestamps.  Consider getting and'
  $echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 1231235999 $$.touch
#
if mkdir _sh32654; then
  $echo 'x -' 'creating lock directory'
else
  $echo 'failed to create lock directory'
  exit 1
fi
# ============= hdr ==============
if test -f 'hdr' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'hdr' '(file already exists)'
else
  $echo 'x -' extracting 'hdr' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'hdr' &&
<HTML>
<HEAD>
X   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
X   <META NAME="Author" CONTENT="James CE Johnson">
X   <TITLE>ACE Tutorial 013</TITLE>
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
X
<CENTER><B><FONT SIZE=+2>ACE Tutorial 013</FONT></B></CENTER>
X
<CENTER><B><FONT SIZE=+2>Multiple thread pools</FONT></B></CENTER>
SHAR_EOF
  $shar_touch -am 03191459100 'hdr' &&
  chmod 0664 'hdr' ||
  $echo 'restore of' 'hdr' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'hdr:' 'MD5 check failed'
abef9831eba4051526151ff2343730d7  hdr
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'hdr'`"
    test 386 -eq "$shar_count" ||
    $echo 'hdr:' 'original size' '386,' 'current size' "$shar_count!"
  fi
fi
# ============= bodies ==============
if test -f 'bodies' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'bodies' '(file already exists)'
else
  $echo 'x -' extracting 'bodies' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'bodies' &&
PAGE=2
message_queue.cpp
mld.h mld.cpp
block.h
block.cpp
task.h task.cpp
work.h work.cpp
SHAR_EOF
  $shar_touch -am 1114230198 'bodies' &&
  chmod 0664 'bodies' ||
  $echo 'restore of' 'bodies' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'bodies:' 'MD5 check failed'
826e1e15e593f64228b867cb6143f179  bodies
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'bodies'`"
    test 89 -eq "$shar_count" ||
    $echo 'bodies:' 'original size' '89,' 'current size' "$shar_count!"
  fi
fi
# ============= page01.pre ==============
if test -f 'page01.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page01.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page01.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page01.pre' &&
<P>
<HR WIDTH="100%">
<P>
My intent with this tutorial was to derive from ACE_Data_Block instead
of ACE_Message_Block so that we could leverage the reference counting
nature of that object.
<P>
Along the way, I sort of got distracted...  What I ended up with is a
poor excuse for ACE_Stream that implements a simple state machine.
<P>
The application is built around a thread pool where the pool's svc()
method takes work units from the message queue for processing.  As
each unit is taken from the queue, the process() method is invoked to
do some work.  The twist is that after processing the message, we
enqueue it into another thread pool to do more work.  This continues
through a chain of thread pools until the last where the unit's fini()
method is called for finishing up any outstanding work.
<P>
The chain of thread pools is uni-directional using a singly-linked
list of Task derivatives.  Each pool has the same number of tasks in
order to keep things simple.
<P>
Kirthika's abstract:
<UL>
In this tutorial, a singly linked list of thread-pools, each of which is
a subtask and which acts as the finite state machine node, is used to
simulate a finite state machine.
<P>
A task is created with a number of subtasks. Once the message block is
obtained from the queue, it is verified to see whether a task has a
subtask. If so, it is forwarded to the subtask. Thus the mesage
traverses over the whole list. As a safety measure for destroying the
block after it goes through the whole list, an effective and simple
Memory Leak Detector has been implemented. It is a counter which
increments when the object where it resides is created and decrements on
its deletion.
<P>
Another optimisation from the previous tutorials on Message Queues, is
the bundling of the Data block within the Message Block. The Data block
provides reference counting, so duplication of data is avoided. It is
deleted only when its reference count drops to zero. Now updating
this count between threads call for synchronisation and in comes the
ACE_Mutex, a lock which takes care that the counting is thread-safe.
<P>
Although the example isn't a full-fledged Finite State Machine,
i.e. it has to be tweaked to be able to jump states on different inputs,
it definitely proves to be a great lesson and introduces us to quite a
few new ACE classes and the ways they can be mixed and matched to
produce the end-system desired.
</ul>
SHAR_EOF
  $shar_touch -am 03191459100 'page01.pre' &&
  chmod 0664 'page01.pre' ||
  $echo 'restore of' 'page01.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page01.pre:' 'MD5 check failed'
a78a6c7a7841ee5d8ce2a87a55cd456f  page01.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page01.pre'`"
    test 2412 -eq "$shar_count" ||
    $echo 'page01.pre:' 'original size' '2412,' 'current size' "$shar_count!"
  fi
fi
# ============= page02.pre ==============
if test -f 'page02.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page02.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page02.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page02.pre' &&
X
X
<P>
<HR WIDTH="100%">
<P>
We'll go back to our tradition of looking at main() first.  The only
change here from our "normal" thread pool is the ability to specify
the number of subtasks for the pool.  (Each subtask is another thread
pool in the chain.  I suppose I should have named that better...)
I've still got the custom Message_Block so that, at this level, we
don't even know about custom Data_Blocks.
<P>
<HR WIDTH="100%">
SHAR_EOF
  $shar_touch -am 03191459100 'page02.pre' &&
  chmod 0664 'page02.pre' ||
  $echo 'restore of' 'page02.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page02.pre:' 'MD5 check failed'
6f4a2e24d7d776b1ec17a07f00f409f8  page02.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page02.pre'`"
    test 432 -eq "$shar_count" ||
    $echo 'page02.pre:' 'original size' '432,' 'current size' "$shar_count!"
  fi
fi
# ============= page03.pre ==============
if test -f 'page03.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page03.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page03.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page03.pre' &&
X
X
<P>
<HR WIDTH="100%">
<P>
I did eventually create that ACE_Data_Block derivative that I wanted.
My purpose in doing so was to use the reference-counting
that is provided by ACE_Data_Block and ACE_Message_Block interactions.
X  When you're working with an object in a single
thread, it's generally not so difficult to manage it's lifetime.
That is, it doesn't tend to go out of scope or get destroyed unless
you do it on purpose.
<P>
On the other hand, if you're passing data between several threads, it
is easy to loose track of who "owns" the data at any one time.  All
too frequently, data will be deleted by one thread while another is
still using it.  Reference counting can prevent that.  The rule of
thumb is that you increment the reference count of the object when you
hand it off to a new thread.  You then decrement the count when you're
done with the object and let the object delete itself when there are
no more references.
<P>
To prove that all of that works correctly in the tutorial, I've
created a cheap Memory Leak Detector object.  All mld instances
reference a thread-safe counter that is incremented when the mld is
constructed and decremented when destructed.  I then insert an mld
into each of my dynamically created objects.  If I get to the end of
main() and the counter isn't zero then I either didn't delete enough
or I deleted too many times.
<P>
Simple, cheap, effective.
<P>
<HR WIDTH="100%">
SHAR_EOF
  $shar_touch -am 03191459100 'page03.pre' &&
  chmod 0664 'page03.pre' ||
  $echo 'restore of' 'page03.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page03.pre:' 'MD5 check failed'
545e09ebf801bfaea60bb1fb07c7ec9f  page03.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page03.pre'`"
    test 1424 -eq "$shar_count" ||
    $echo 'page03.pre:' 'original size' '1424,' 'current size' "$shar_count!"
  fi
fi
# ============= page04.pre ==============
if test -f 'page04.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page04.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page04.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page04.pre' &&
X
X
<P>
<HR WIDTH="100%">
<P>
Let's look now at the changes to our ACE_Message_Block derivative and
the new ACE_Data_Block derivative.
<P>
The important thing to remember is that the data block (not the
message block) is reference counted.  When you instantiate a new
ACE_Message_Block, it will create one or more ACE_Data_Block objects
to contain the data you need.  Optionally, you can provide it with a
pointer to a data block.
<P>
When you finish with a message block, you should use the release()
method to make it go away.  Do not ever <em>delete</em> an instance of
a message block!  When you invoke release(), the message block will
invoke release() on the data block(s) it contains.  If the block's
reference count goes to zero as a result then the block will <em>delete</em>
itself.
<P>
To increment the reference count of a data block, use the
duplicate() method of the message block (or blocks) to get a new
message block referencing the same data block.  This is very efficient
since the actual data is not copied.
<P>
<HR WIDTH="100%">
SHAR_EOF
  $shar_touch -am 03191459100 'page04.pre' &&
  chmod 0664 'page04.pre' ||
  $echo 'restore of' 'page04.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page04.pre:' 'MD5 check failed'
c8b4750a824380f2effc43557c8540ad  page04.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page04.pre'`"
    test 1048 -eq "$shar_count" ||
    $echo 'page04.pre:' 'original size' '1048,' 'current size' "$shar_count!"
  fi
fi
# ============= page05.pre ==============
if test -f 'page05.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page05.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page05.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page05.pre' &&
X
X
<P>
<HR WIDTH="100%">
<P>
On this page we have the code for the Data_Block and Message_Block
objects.  As you probably suspect from the header on the previous
page, the complicated part is in the construction and destruction of
the Data_Block.
<P>
<HR WIDTH="100%">
SHAR_EOF
  $shar_touch -am 03191459100 'page05.pre' &&
  chmod 0664 'page05.pre' ||
  $echo 'restore of' 'page05.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page05.pre:' 'MD5 check failed'
a95fdcd3db2356b091228728f4f3f130  page05.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page05.pre'`"
    test 268 -eq "$shar_count" ||
    $echo 'page05.pre:' 'original size' '268,' 'current size' "$shar_count!"
  fi
fi
# ============= page06.pre ==============
if test -f 'page06.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page06.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page06.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page06.pre' &&
X
X
<P>
<HR WIDTH="100%">
<P>
Let's take a look now at the new Task object.  This will obviously be
different from the Tasks we've created before but I think you'll be
surprised at how relatively simple it actually is.
<P>
Remember that the goal of this tutorial was to use the reference
counting abilities of the ACE_Data_Block.  The only way to show that
effectively is to have a data block passed between different threads.
A thread pool isn't really going to do that so, instead, our new Task
can be part of a chain of tasks.  In that way, each Task can pass the
data on to another and satisfy our need for moving the ACE_Data_Block
around.
If we've done the reference counting correctly then none of our tasks
will be trying to work with deleted data and we won't have any memory
leaks at the end.
<P>
There's not much to the header, so I've included it and the cpp file
on this one page.
<P>
<HR WIDTH="100%">
SHAR_EOF
  $shar_touch -am 03191459100 'page06.pre' &&
  chmod 0664 'page06.pre' ||
  $echo 'restore of' 'page06.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page06.pre:' 'MD5 check failed'
a4c9b50df3240c5134733d2033fd5f03  page06.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page06.pre'`"
    test 914 -eq "$shar_count" ||
    $echo 'page06.pre:' 'original size' '914,' 'current size' "$shar_count!"
  fi
fi
# ============= page07.pre ==============
if test -f 'page07.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page07.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page07.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page07.pre' &&
X
X
<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.
X 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%">
SHAR_EOF
  $shar_touch -am 03191459100 'page07.pre' &&
  chmod 0664 'page07.pre' ||
  $echo 'restore of' 'page07.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page07.pre:' 'MD5 check failed'
17c1c426089b288e418c3f62fdc09744  page07.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page07.pre'`"
    test 1360 -eq "$shar_count" ||
    $echo 'page07.pre:' 'original size' '1360,' 'current size' "$shar_count!"
  fi
fi
# ============= page08.pre ==============
if test -f 'page08.pre' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page08.pre' '(file already exists)'
else
  $echo 'x -' extracting 'page08.pre' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page08.pre' &&
X
X
<P>
<HR WIDTH="100%">
<P>
And that's the end of another tutorial.  This one is probably the most
complicated so far because I've introduced or expanded upon
a number of different
concepts.  Namely:  state machines, reference counting and task
chaining.  I hope I didn't complicate things to the point where the
lesson got lost in the noise.  As always, feel free to drop a note to
the ACE-Users mailing list if you feel that some of this could use a
little more explaination.
X
<P>
<UL>
<LI><A HREF="Makefile">Makefile</A>
<LI><A HREF="block.cpp">block.cpp</A>
<LI><A HREF="block.h">block.h</A>
<LI><A HREF="message_queue.cpp">message_queue.cpp</A>
<LI><A HREF="mld.cpp">mld.cpp</A>
<LI><A HREF="mld.h">mld.h</A>
<LI><A HREF="task.cpp">task.cpp</A>
<LI><A HREF="task.h">task.h</A>
<LI><A HREF="work.cpp">work.cpp</A>
<LI><A HREF="work.h">work.h</A>
</UL>
<P>
SHAR_EOF
  $shar_touch -am 03191459100 'page08.pre' &&
  chmod 0664 'page08.pre' ||
  $echo 'restore of' 'page08.pre' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page08.pre:' 'MD5 check failed'
b761e40eff75cbf3174b53b4b0c5c172  page08.pre
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page08.pre'`"
    test 860 -eq "$shar_count" ||
    $echo 'page08.pre:' 'original size' '860,' 'current size' "$shar_count!"
  fi
fi
# ============= page02.pst ==============
if test -f 'page02.pst' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page02.pst' '(file already exists)'
else
  $echo 'x -' extracting 'page02.pst' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page02.pst' &&
<HR WIDTH="100%">
<P>
Nothing really surprising here...  Just remember that your total
number of threads is ( ( 1 + subtasks ) * threads ).  You probably
don't want to get too carried away with that!
<P>
SHAR_EOF
  $shar_touch -am 03191459100 'page02.pst' &&
  chmod 0664 'page02.pst' ||
  $echo 'restore of' 'page02.pst' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page02.pst:' 'MD5 check failed'
a8c43c5c68518f6eb8c03701d1603a92  page02.pst
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page02.pst'`"
    test 204 -eq "$shar_count" ||
    $echo 'page02.pst:' 'original size' '204,' 'current size' "$shar_count!"
  fi
fi
# ============= page04.pst ==============
if test -f 'page04.pst' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page04.pst' '(file already exists)'
else
  $echo 'x -' extracting 'page04.pst' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page04.pst' &&
<HR WIDTH="100%">
<P>
One of the most difficult parts of this to get right was the Lock
object.  I didn't even have it in the beginning but I soon realized
that the reference counts were getting weird.  A little careful
reading of the comments and the source informed me that some sort of
locking is necessary to keep the counter sane.  The simplest thing at
that point was to use the ACE_Lock_Adaptor&lt;&gt; to adapt ACE_Mutex
appropriately.  The next trick was to ensure that the lock object was
destroyed at the proper time to prevent both memory leaks and core
dumps.  The finaly product may be a little bit intimidating at first
but it's really quite simple once you understand the motivation.
<P>
SHAR_EOF
  $shar_touch -am 03191459100 'page04.pst' &&
  chmod 0664 'page04.pst' ||
  $echo 'restore of' 'page04.pst' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page04.pst:' 'MD5 check failed'
325565f3f72961b842b612caeb93b36a  page04.pst
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page04.pst'`"
    test 704 -eq "$shar_count" ||
    $echo 'page04.pst:' 'original size' '704,' 'current size' "$shar_count!"
  fi
fi
# ============= page06.pst ==============
if test -f 'page06.pst' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page06.pst' '(file already exists)'
else
  $echo 'x -' extracting 'page06.pst' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page06.pst' &&
<HR WIDTH="100%">
<P>
So you see... it wasn't really that much more complicated.  We really
just have to remember to pass to <i>next_</i> when we finish working
on the data.  If your Unit_Of_Work derivative is going to implement a
state machine be sure that you also implement a fini() method
<em>or</em> ensure that your chain of subtasks is large enough for all
possible states.
<P>
SHAR_EOF
  $shar_touch -am 03191459100 'page06.pst' &&
  chmod 0664 'page06.pst' ||
  $echo 'restore of' 'page06.pst' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page06.pst:' 'MD5 check failed'
65a1b8bc21034187e0885cc07fa7734f  page06.pst
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page06.pst'`"
    test 385 -eq "$shar_count" ||
    $echo 'page06.pst:' 'original size' '385,' 'current size' "$shar_count!"
  fi
fi
# ============= page07.pst ==============
if test -f 'page07.pst' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'page07.pst' '(file already exists)'
else
  $echo 'x -' extracting 'page07.pst' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'page07.pst' &&
<HR>
<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>
SHAR_EOF
  $shar_touch -am 03191459100 'page07.pst' &&
  chmod 0664 'page07.pst' ||
  $echo 'restore of' 'page07.pst' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'page07.pst:' 'MD5 check failed'
21f1bb3615bd4ba5efe5ec25bb895c0e  page07.pst
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page07.pst'`"
    test 369 -eq "$shar_count" ||
    $echo 'page07.pst:' 'original size' '369,' 'current size' "$shar_count!"
  fi
fi
rm -fr _sh32654
exit 0