1/* Copyright 2016 Google Inc.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License. */
14
15#ifndef NSYNC_PUBLIC_NSYNC_MU_WAIT_H_
16#define NSYNC_PUBLIC_NSYNC_MU_WAIT_H_
17
18/* nsync_mu_wait() and nsync_mu_wait_with_deadline() can be used instead of condition
19 variables. In many straightforward situations they are of equivalent
20 performance and are somewhat easier to use, because unlike condition
21 variables, they do not require that the waits be placed in a loop, and they
22 do not require explicit wakeup calls. Example:
23
24 Definitions:
25 static nsync_mu mu = NSYNC_MU_INIT;
26 static int i = 0; // protected by mu
27 // Condition for use with nsync_mu_wait().
28 static int int_is_zero (const void *v) { return (*(const int *)v == 0); }
29
30 Waiter:
31 nsync_mu_lock (&mu);
32 // Wait until i is zero.
33 nsync_mu_wait (&mu, &int_is_zero, &i, NULL);
34 // i is known to be zero here.
35 // ...
36 nsync_mu_unlock (&mu);
37
38
39 Thread potentially making i zero:
40 nsync_mu_lock (&mu);
41 i--;
42 // No need to signal that i may have become zero. The unlock call below
43 // will evaluate waiters' conditions to decide which to wake.
44 nsync_mu_unlock (&mu);
45
46 It is legal to use conditional critical sections and condition variables
47 on the same mutex.
48
49 --------------
50
51 The implementation benefits from determining whether waiters are waiting for
52 the same condition; it may then evaluate a condition once on behalf
53 of several waiters. Two waiters have equal condition if their "condition"
54 pointers are equal, and either:
55 - their "condition_arg" pointers are equal, or
56 - "condition_arg_eq" is non-null and
57 (*condition_arg_eq) (condition_arg0, condition_arg1) returns non-zero.
58 *condition_arg_eq will not be invoked unless the "condition" pointers
59 are equal, and the "condition_arg" pointers are unequal.
60
61 If many waiters wait for distinct conditions simultaneously, condition
62 variables may be faster.
63 */
64
65#include "nsync_cpp.h"
66#include "nsync_mu.h"
67#include "nsync_time.h"
68
69NSYNC_CPP_START_
70
71struct nsync_note_s_; /* forward declaration for an nsync_note */
72
73/* Return when (*condition) (condition_arg) is true. Perhaps unlock and relock
74 *mu while blocked waiting for the condition to become true. nsync_mu_wait()
75 is equivalent to nsync_mu_wait_with_deadline() with
76 abs_deadline==nsync_time_no_deadline, and cancel_note==NULL.
77
78 Requires that *mu be held on entry.
79 See nsync_mu_wait_with_deadline() for more details on *condition and
80 *condition_arg_eq. */
81void nsync_mu_wait (nsync_mu *mu, int (*condition) (const void *condition_arg),
82 const void *condition_arg,
83 int (*condition_arg_eq) (const void *a, const void *b));
84
85/* Return when at least one of: (*condition) (condition_arg) is true, the
86 deadline expires, or *cancel_note is notified. Perhaps unlock and relock *mu
87 while blocked waiting for one of these events, but always return with *mu
88 held. Return 0 iff the (*condition) (condition_arg) is true on return, and
89 otherwise either ETIMEDOUT or ECANCELED, depending on why the call returned
90 early. Callers should use abs_deadline==nsync_time_no_deadline for no
91 deadline, and cancel_note==NULL for no cancellation.
92
93 Requires that *mu be held on entry.
94
95 The implementation may call *condition from any thread using the mutex, and
96 while holding *mu in either read or write mode; it guarantees that any
97 thread calling *condition will hold *mu in some mode.
98 Requires that (*condition) (condition_arg) neither modify state protected by
99 *mu, nor return a value dependent on state not protected by *mu. To depend
100 on time, use the abs_deadline parameter.
101 (Conventional use of condition variables have the same restrictions on the
102 conditions tested by the while-loop.)
103 If non-null, condition_arg_eq should return whether two condition_arg
104 calls with the same "condition" pointer are considered equivalent; it should
105 have no side-effects. */
106int nsync_mu_wait_with_deadline (nsync_mu *mu,
107 int (*condition) (const void *condition_arg),
108 const void *condition_arg,
109 int (*condition_arg_eq) (const void *a, const void *b),
110 nsync_time abs_deadline,
111 struct nsync_note_s_ *cancel_note);
112
113/* Unlock *mu, which must be held in write mode, and wake waiters, if
114 appropriate. Unlike nsync_mu_unlock(), this call is not required to wake
115 nsync_mu_wait/nsync_mu_wait_with_deadline calls on conditions that were
116 false before this thread acquired the lock. This call should be used only
117 at the end of critical sections for which:
118 - nsync_mu_wait and/or nsync_mu_wait_with_deadline are in use on the same
119 mutex,
120 - this critical section cannot make the condition true for any of those
121 nsync_mu_wait/nsync_mu_wait_with_deadline waits, and
122 - when performance is significantly improved by using this call. */
123void nsync_mu_unlock_without_wakeup (nsync_mu *mu);
124
125NSYNC_MU_WAIT_CPP_OVERLOAD_
126NSYNC_CPP_END_
127
128#endif /*NSYNC_PUBLIC_NSYNC_MU_WAIT_H_*/
129