File: packagedtask.yo

package info (click to toggle)
c%2B%2B-annotations 12.2.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 13,044 kB
  • sloc: cpp: 24,337; makefile: 1,517; ansic: 165; sh: 121; perl: 90
file content (208 lines) | stat: -rw-r--r-- 10,650 bytes parent folder | download | duplicates (3)
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
The class template hi(packaged_task)tt(std::packaged_task) allows a program to
`package' a function or functor and pass the package to a thread for further
processing. The processing thread then calls the packaged function, passing it
its arguments (if any). After completing the function the tt(packaged_task's)
future is ready, allowing the program to retrieve the results produced by
the function. Thus, functions and the results of function calls can be
transferred between threads.

Before using the class template tt(packaged_task) the tthi(future) header file
must be included.
 
Before describing the class's interface, let's first look at an example to get
an idea about how a tt(packaged_task) can be used. Remember that the essence
of tt(packaged_task) is that part of your program prepares (packages) a task
for another thread to complete, and that the program at some point needs the
result of the completed task. 

To clarify what's happening here, let's first look at a real-life
analogon. Every now and then I make an appointment with my garage to have my
car serviced. The `package' in this case are the details about my car: its
make and type determine the kind of actions my garage performs when servicing
it. My neighbor also has a car, which also needs to be serviced every now and
then. This also results in a `package' for the garage. At the appropriate time
me and my neighbor take our cars to the garage (i.e., the packages are passed
to another thread). The garage services the cars (i.e., calls the functions
stored in the tt(packaged_tasks) [note that the tasks differ, depending on the
types of the cars]), and performs some actions that are associated with it
(e.g., registering that my or my neighbor's car has been serviced, or order
replacement parts). In the meantime my neighbor and I perform our own
businesses (the program continues while a separate thread runs as well). But
by the end of the day we'd like to use our cars again (i.e., get the results
associated with the tt(packaged_task)). A common result in this example is the
garage's bill, which we have to pay (the program obtains the
tt(packaged_task's) results).

Here is a little bf(C++) program illustrating the use of a tt(packaged_task)
(assuming the required headers and tt(using namespace std) have been
specified):
    verbinclude(-ns4 //code examples/packagedtask.cc)
    itemization(
    it() Lines 1-3 define the variables used for synchronization;
    it() Line 4 defines a tt(packaged_task: serviceTask) is initialized with a
        function (or functor) expecting a tt(string), returning a tt(size_t);
    it() Lines 6-10 and 12-16 define such functions: tt(volkswagen) and
        tt(peugeot) represent the tasks to perform when cars of the provided
        types come in for service; presumably they return the bill.
    it() Lines 18-30 define the function tt(void garage), defining the actions
        performed by the garage when cars come in for service. These actions
        are performed by a separate detached thread, starting in line 34. In a
        continuous loop it waits until it obtains a lock on the
        tt(carDetailsMutex) and tt(carDetails) is no longer empty. Then, at
        line 27, it passes tt(carDetails) to the tt(packaged_task
        `serviceTask'). By itself this is not identical to calling the
        tt(packaged_task's) function, but eventually its function will be
        called. At this point the tt(packaged_task) receives its function's
        arguments, which it eventually will forward to its configured
        function. Finally, at line 28 it clears tt(carDetails), thus preparing
        itself for the next request.
    it() Lines 32-53 define tt(main):
        itemization(
        it() First, at line 34 the anonymous detached thread running
            tt(garage) is started.
        )
        Then the program's main loop starts (lines 36-52):
        itemization(
        it() The main thread reads commands from the standard input until an
            empty or no line is received (lines 38-40).
        it() By convention the line's first letter starts the car's brand
            (tt(volkswagen) or tt(peugeot)), and the tt(packaged_task), provided with
            the right servicing function, is constructed
            next (line 45).
        it() Then, at line 48 the results, stored in a tt(future), are
            retrieved. Although at this point the tt(future) might not be
            ready, the tt(future) object itself em(is), and it is simply
            returned as the bill.
        it() Now we're ready to inform the garage that it can service a car:
            the garage is notified in line 49.
        )
       Anything may happen next: the program may perform any actions,
        but eventually it requests the results produced by the garage.
       itemization(
        it() The main thread obtains the results by calling tt(bill.get())
            in line 51. If, by this time, the car is still being serviced, the
            bill isn't ready yet, and tt(bill.get()) blocks until it is, and
            the bill for servicing a car is shown.
        )
    )
    Now that we've seen an example of a program using a tt(packaged_task),
let's have a look at its interface. Note that the class tt(packaged_task) is a
class template: its template type parameter specifies the prototype of a
function or function object implementing the task performed by the
tt(packaged_task) object.

Constructors and destructor:
    itemization(
    ittq(packaged_task() noexcept)
       (The default constructor constructs a tt(packaged_task) object which is
        not associated with a function or shared state;)

    ittq(explicit packaged_task(ReturnType(Args...) &&function))
       (A tt(packaged_task) is constructed for a function or functor
        expecting arguments of types tt(Args...), and returning a value of
        type tt(ReturnType). The tt(packaged_task) class template specifies
        tt(ReturnType (Args...)) as its template type parameter. The
        constructed object contains a shared state, and a (move constructed)
        copy of tt(function).

       Optionally an tt(Allocator) may be specified as second template type
        parameter, in which case the first two arguments are
        hi(allocator_arg_t)tt(std::allocator_arg_t, Allocator const
        &alloc). The type tt(std::allocator_arg_t) is a type introduced to
        disambiguate constructor selections, and can simply be specified as
        tt(std::allocator_arg_t()).

        This constructor may throw a tt(std::bad_alloc) exception or
        exceptions thrown by tt(function's) copy or move constructors)

    ittq(packaged_task(packaged_task &&tmp) noexcept)
       (The move constructor moves  any existing shared state from tt(tmp) to
        the newly constructed object, removing the shared state from tt(tmp).)

    ittq(~packaged_task())
       (The object's shared state (if any) is abandoned)
    )

Member functions:
    itemization(
    ithtq(get_future)(future<ReturnType> get_future())
       (A tt(std::future) object is returned holding the results of the
        separately executed thread. When tt(get_future) is incorrectly called
        a tt(future_error) exception is thrown, containing one of the
        following values:
       itemization(
       itt(future_already_retrieved) if tt(get_future) was already called on a
            tt(packaged_task) object containing the same shared state as the
            current object;
        itt(no_state) if the current object has no shared state.
       )
       Note: Any tt(futures) that share the object's shared state may access
        the result returned by the object's task.)

    ittq(void make_ready_at_thread_exit(Args... args))
       (Calls tt(void operator()(Args... args)) (see below) when the current
        thread exits, once all objects of thread storage duration associated
        with the current thread have been destroyed.)

    ittq(packaged_task &operator=(packaged_task &&tmp))
       (The move assignment operator first releases the current object's
        shared state (if available), after which the current object and
        tt(tmp) are swapped;)

    ittq(void operator()(Args... args))
       (The tt(args) arguments are forwarded to the current object's
        stored task. When the stored task returns its return value is stored
        in the current object's shared state. Otherwise any exception thrown
        by the task is stored in the object's shared state. Following this the
        object's shared state is made ready, and any threads blocked in a
        function waiting for the object's shared state to become ready are
        unblocked. A tt(future_error) exception is thrown upon error,
        containing 
       itemization(
       itt(promise_already_satisfied) if the shared state has already been
            made ready;
        itt(no_state) if the current object does not have any shared state.
       )
       Calling this member synchronizes with calling any member function of a
        tt((shared_)future) object that provides access to the
        tt(packaged_task's) results.)

    ittq(void reset())
       (Abandons any available shared state, initializing the current object
        to tt(packaged_task(std::move(funct))), where tt(funct) is the
        object's stored task. This member may throw the following exceptions:
       itemization(
       itt(bad_alloc) if memory for the new shared state could not be
            allocated;
        it() any exception thrown by the move constructor of the task stored in
                the shared state;   
        itt(future_error) with a tt(no_state) error condition if the current
            object contains no shared state.
       ))

    ittq(void swap(packaged_task &other) noexcept)
       (The shared states and stored tasks of the current object  and other
        are swapped.) 

    ittq(bool valid() const noexcept)
       (Returns tt(true) if the current object contains a shared state,
        otherwise tt(false) is returned;)
    )

The following non-member (free) function operating on tt(packaged_task)
objects is available:
    itemization(
    it() tt(void swap+OPENPARpackaged_task<ReturnType(Args...)> &lhs,)
                                                            linebreak()
            tt(packaged_task<ReturnType(Args...)> &rhs+CLOSEPAR noexcept)
       quote(Calls tt(lhs.swap(rhs)))
    )

COMMENT(
    template <class R, class Alloc>
    struct uses_allocator<packaged_task<R>, Alloc>;

    Requires: Alloc shall be an Allocator (17.6.3.5).
END)