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 | |
69 | NSYNC_CPP_START_ |
70 | |
71 | struct 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. */ |
81 | void 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. */ |
106 | int 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. */ |
123 | void nsync_mu_unlock_without_wakeup (nsync_mu *mu); |
124 | |
125 | NSYNC_MU_WAIT_CPP_OVERLOAD_ |
126 | NSYNC_CPP_END_ |
127 | |
128 | #endif /*NSYNC_PUBLIC_NSYNC_MU_WAIT_H_*/ |
129 | |