File: locks.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 (182 lines) | stat: -rw-r--r-- 8,470 bytes parent folder | download | duplicates (2)
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
Locks are used to simplify the use of mutexes. Before locks can be used the
tthi(mutex) header file must be included.

Whenever threads share data, and at least one of the threads may change common
data, mutexes should be used to prevent threads from using the same data
synchronously.

Usually locks are released at the end of action blocks. This requires explicit
calls to the mutexes' tt(unlock) function, which introduces comparable
problems as we've seen with the thread's tt(join) member.

To simplify locking and unlocking two mutex wrapper classes are available:
    itemization(
    ithtq(lock_guard)(std::lock_guard) 
        (objects of this class offer the basic unlock-guarantee: their
destructors call the member tt(unlock) of the mutexes they control;)

    ithtq(unique_lock)(std::unique_lock)
        (objects of this class offer a more extensive interface, allowing
explicit unlocking and locking of the mutexes they control, while their
destructors preserve the unlock-guarantee also offered by tt(lock_guard);)
    )

    The class tt(lock_guard) offers a limited, but useful interface:
    itemization(

    ittq(lock_guard<Mutex>(Mutex &mutex))
        (when defining a tt(lock_guard) object the mutex type (e.g.,
tt(std::mutex, std::timed_mutex, std::shared_mutex)) is specified, and a mutex
of the indicated type is provided as its argument. The construction blocks
until the tt(lock_guard) object owns the lock. The tt(lock_guard's) destructor
automatically releases the mutex lock.)

    ittq(lock_guard<Mutex>(Mutex &mutex, std::adopt_lock_t))
        (this constructor is used to transfer control over the mutex from the
calling thread to the tt(lock_guard).  The mutex lock is released again by the
tt(lock_guard's) destructor. At construction time the mutex must already be
owned by the calling thread. Here is an illustration of how it can be used:
        verbinclude(-n //code examples/threadaction.cc)
            itemization(
            it() At line 1 tt(threadAction) receives a reference to a
mutex. Assume the mutex owns the lock;

            it() At line 3 control is transferred to the tt(lock_guard). Even
though we don't explicitly use the tt(lock_guard) object, an object should be
defined to prevent the compiler from destroying an anonymous object before the
function ends;
    
            it() When the function ends, at line 5, the mutex's lock is
released by the tt(lock_guard's) destructor.
            )
        )
    ittq(mutex_type)
        (in hi(mutex_type) addition to the constructors and destructor,
tt(lock_guard<Mutex>) types also define the type ti(mutex_type): it is a
synonym of the tt(Mutex) type that is passed to the tt(lock_guard)'s
constructor.)
    )

    Here is a simple example of a multi-threaded program using tt(lock_guards)
to prevent information inserted into tt(cout) from getting mixed.
        verbinclude(-s4 //code examples/insertguard.cc)

    As with tt(lock_guard), a mutex-type must be specified when defining
objects of the class hi(unique_lock)tt(std::unique_lock).  The class
tt(unique_lock) is much more elaborate than the basic tt(lock_guard) class
template.  Its interface does not define a copy constructor or overloaded
assignment operator, but it em(does) define a move constructor and a move
assignment operator. In the following overview of tt(unique_lock)'s inteface
tt(Mutex) refers to the mutex-type that is specified when defining a
tt(unique_lock):
    itemization(
    ittq(unique_lock() noexcept)
       (the default constructor is not yet associated with a tt(mutex)
object. It must be assigned a tt(mutex) (e.g., using move-assignment) before
it can do anything useful;)

    ittq(explicit unique_lock(Mutex &mutex))
       (initializes a tt(unique_lock) with an existing tt(Mutex) object, and
calls tt(mutex.lock());)

    ittq(unique_lock(Mutex &mutex, defer_lock_t) noexcept)
       (initializes a tt(unique_lock) with an existing tt(Mutex) object, but
does not call tt(mutex.lock()). Call it by passing a tt(defer_lock_t) object
as the constructor's second argument, e.g.,
            verb(unique_lock<mutex> ul(mutexObj, defer_lock_t()))

)

    ittq(unique_lock(Mutex &mutex, try_to_lock_t) noexcept)
       (initializes a tt(unique_lock) with an existing tt(Mutex) object, and
calls tt(mutex.try_lock()): the constructor won't block if the mutex cannot be
locked;)

    ittq(unique_lock(Mutex &mutex, adopt_lock_t) noexcept)
       (initializes a tt(unique_lock) with an existing tt(Mutex) object,
and assumes that the current thread has already locked the mutex;)

    ittq(unique_lock(Mutex &mutex, chrono::duration<Rep, Period> const
        &relTime) noexcept)
       (this constructor tries to obtain ownership of the tt(Mutex) object by
calling tt(mutex.try_lock_for(relTime)). The specified mutex type must
therefore support this member (e.g., it is a tt(std::timed_mutex)). It could
be called like this:
           verb(std::unique_lock<std::timed_mutex> ulock(timedMutex, 
                                         std::chrono::seconds(5));)

)

    ittq(unique_lock(Mutex &mutex, chrono::time_point<Clock, Duration> const
        &absTime) noexcept)
       (this constructor tries to obtain ownership of the tt(Mutex) object by
calling tt(mutex.try_lock_until(absTime)). The specified mutex type must
therefore support this member (e.g., it is a tt(std::timed_mutex)). 
This constructor could be called like this:
            verb(std::unique_lock<std::timed_mutex> ulock(
                timedMutex, 
                std::chrono::system_clock::now() + std::chrono::seconds(5)
            );)

)

    ithtq(lock)(void lock())
       (blocks the current thread until ownership of the mutex that is managed
by the tt(unique_lock) is obtained. If no mutex is currently managed, then a
tt(system_error) exception is thrown.)

    ithtq(mutex)(Mutex *mutex() const noexcept)
       (returns a pointer to the mutex object stored inside the
tt(unique_lock) (a tt(nullptr) is returned if no mutex object is currently
associated with the tt(unique_lock) object.))

    ittq(explicit operator bool() const noexcept)
       (returns tt(true) if the tt(unique_lock) owns a locked mutex, otherwise
tt(false) is returned;)

    ittq(unique_lock& operator=(unique_lock &&tmp) noexcept)
       (if the left-hand operand owns a lock, it will call its mutex's
tt(unlock) member, whereafter tt(tmp's) state is transferred to the left-hand
operand;)

    ithtq(owns_lock)(bool owns_lock() const noexcept)
       (returns tt(true) if the tt(unique_lock) owns the mutex, otherwise
tt(false) is returned;)

    ithtq(release)(Mutex *release() noexcept)
       (returns a pointer to the mutex object that is associated with the
tt(unique_lock) object, discarding that association;)

    ithtq(swap)(void swap(unique_lock& other) noexcept)
       (swaps the states of the current tt(unique_lock) and tt(other);)

    ithtq(try_lock)(bool try_lock())
       (tries to obtain ownership of the mutex that is associated witg the
tt(unique_lock), returning tt(true) if this succeeds, and tt(false)
otherwise. If no mutex is currently associated with the tt(unique_lock)
object, then a tt(system_error) exception is thrown;)

    ithtq(try_lock_for)(bool try_lock_for(chrono::duration<Rep, Period> const
        &relTime)) 
       (this member function tries to obtain ownership of the tt(Mutex) object
managed by the tt(unique_lock) object by calling the mutex's
tt(try_lock_for(relTime)) member. The specified mutex type must therefore
support this member (e.g., it is a tt(std::timed_mutex));)

    ithtq(try_lock_until)(bool try_lock_until(chrono::time_point<Clock,
        Duration> const &absTime))
       (this member function tries to obtain ownership of the tt(Mutex) object
managed by the tt(unique_lock) object by calling the mutex's
tt(mutex.try_lock_until(absTime)) member. The specified mutex type must
therefore support this member (e.g., it is a tt(std::timed_mutex));)

    ithtq(unlock)(void unlock())
       (releases ownership of the mutex (or reduces the mutex's lock count). A
tt(system_error) exception is thrown if the tt(unique_lock) object does not
own the mutex.)
    )

In addition to the members of the classes tt(std::lock_guard) and
tt(std::unique_lock) the functions tt(std::lock)hi(lock (function)) and
tt(std::try_lock)hi(try_lock (function)) are available. These functions can be
used to prevent em(deadlocks), the topic of the next section.