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
|
Section ref(MULTICOMP) describes the construction of a multi-threaded program
performing compilations. In that program separate threads were used for the
em(workers), who push their results on a results-queue. At the end of the
program the function tt(results) processes the queued results by showing the
names of the successfully compiled source files, and (if a compilation failed)
the name and error messages of the first source whose compilation failed.
The results-queue was used to store the results in a retrievable data
structure, using a mutex to ensure that the workers cannot simultaneously
push results on the results-queue.
Using tt(osyncstream) objects the results-queue and its mutexed protection
scheme is no longer required (the sources of the modified program are
available in the annotations()' directory
tt(yo/threading/examples/osyncmulticompile)).
Instead of using a results-queue the program uses a single destination stream
tt(fstream g_out{ "/tmp/out", ios::trunc | ios::in | ios::out }), and its
tt(compile) function defines a local a tt(osyncstream) object, ensuring that
its output is sent as a block to tt(g_out):
verbinsert(-ns4 //code examples/osyncmulticompile/compile.cc)
itemization(
it() at line 13 the tt(osyncstream out) object is defined, and the results
of the compilation are written to tt(out) at lines 14 and 18;
it() at line 14 the result of the compilation followed by the name of the
source file is inserted into tt(out);
it() if a compilation fails then, at line 18, the compiler's error
messages are inserted into tt(out) terminated by a marker,
used by tt(results) (see below), to recognize the end of the error
messages.
)
Since the results of the compilation are no longer transferred to another
thread, there's no need for defining a tt(shared_future<Result>). In fact,
since tt(compile) handles the results of a compilation itself, it defines
return type tt(void) and the tt(packaged_task) itself doesn't return anything
either. Therefore the tt(class Task) doesn't need a tt(result()) member
anymore. Instead, its function-call operator, having completed its task, calls
the task's tt(get_future) so exceptions that might have been generated by
the tt(packaged_tasks) are properly retrieved. Here's the simplified tt(class
Task):
verbinsert(-s4 //task examples/osyncmulticompile/main.ih)
At the end of tt(main) the function tt(results) is called:
verbinsert(-ns4 //code examples/osyncmulticompile/results.cc)
itemization(
it() Each compilation starts with a compilation result and a source
name. These are extracted in the tt(while) condition at line 9.
it() If the compilation was successful (line 13) the source's name is
displayed.
it() If not, only the info of the first failed compilation is displayed
(em(all) failed compilation messages could of course also be
displayed, but this program only shows the messages of the first
encountered failing compilation). If a compilation has already been
encountered then the next error messages are ignored (lines 19 thru
28).
it() The info of the first encountered compilation error is collected in
tt(errorDisplay) (lines 30 thru 39).
it() Once tt(g_out) has completely been read tt(errorDisplay) is displayed
(line 42), which is either empty or contains the error messages of the
first encountered compilation failure.
)
|