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_H_ |
16 | #define NSYNC_PUBLIC_NSYNC_MU_H_ |
17 | |
18 | #include <inttypes.h> |
19 | #include "nsync_cpp.h" |
20 | #include "nsync_atomic.h" |
21 | |
22 | NSYNC_CPP_START_ |
23 | |
24 | struct nsync_dll_element_s_; |
25 | |
26 | /* An nsync_mu is a lock. If initialized to all zeroes, it is valid and unlocked. |
27 | |
28 | An nsync_mu can be "free", held by a single thread (aka fiber, goroutine) in |
29 | "write" (exclusive) mode, or by many threads in "read" (shared) mode. A |
30 | thread that acquires it should eventually release it. It is illegal to |
31 | acquire an nsync_mu in one thread and release it in another. It is |
32 | illegal for a thread to reacquire an nsync_mu while holding it (even a |
33 | second share of a "read" lock). |
34 | |
35 | Example usage: |
36 | static struct foo { |
37 | nsync_mu mu; // protects invariant a+b==0 on fields below. |
38 | int a; |
39 | int b; |
40 | } p = { NSYNC_MU_INIT, 0, 0 }; |
41 | .... |
42 | nsync_mu_lock (&p.mu); |
43 | // The current thread now has exclusive access to p.a and p.b; invariant assumed true. |
44 | p.a++; |
45 | p.b--; // restore invariant p.a+p.b==0 before releasing p.mu |
46 | nsync_mu_unlock (&p.mu) |
47 | |
48 | Mutexes can be used with condition variables; see nsync_cv.h. |
49 | |
50 | nsync_mu_wait() and nsync_mu_wait_with_deadline() can be used instead of |
51 | condition variables. See nsync_mu_wait.h for more details. |
52 | Example use of nsync_mu_wait() to wait for p.a==0, using definition above: |
53 | int a_is_zero (const void *condition_arg) { |
54 | return (((const struct foo *)condition_arg)->a == 0); |
55 | } |
56 | ... |
57 | nsync_mu_lock (&p.mu); |
58 | nsync_mu_wait (&p.mu, &a_is_zero, &p, NULL); |
59 | // The current thread now has exclusive access to p.a and p.b, and p.a==0. |
60 | ... |
61 | nsync_mu_unlock (&p.mu); */ |
62 | typedef struct nsync_mu_s_ { |
63 | nsync_atomic_uint32_ word; /* internal use only */ |
64 | struct nsync_dll_element_s_ *waiters; /* internal use only */ |
65 | } nsync_mu; |
66 | |
67 | /* An nsync_mu should be zeroed to initialize, which can be accomplished by |
68 | initializing with static initializer NSYNC_MU_INIT, or by setting the entire |
69 | structure to all zeroes, or using nsync_mu_init(). */ |
70 | #define NSYNC_MU_INIT { NSYNC_ATOMIC_UINT32_INIT_, 0 } |
71 | void nsync_mu_init (nsync_mu *mu); |
72 | |
73 | /* Block until *mu is free and then acquire it in writer mode. |
74 | Requires that the calling thread not already hold *mu in any mode. */ |
75 | void nsync_mu_lock (nsync_mu *mu); |
76 | |
77 | /* Unlock *mu, which must have been acquired in write mode by the calling |
78 | thread, and wake waiters, if appropriate. */ |
79 | void nsync_mu_unlock (nsync_mu *mu); |
80 | |
81 | /* Attempt to acquire *mu in writer mode without blocking, and return non-zero |
82 | iff successful. Return non-zero with high probability if *mu was free |
83 | on entry. */ |
84 | int nsync_mu_trylock (nsync_mu *mu); |
85 | |
86 | /* Block until *mu can be acquired in reader mode and then acquire it. |
87 | Requires that the calling thread not already hold *mu in any mode. */ |
88 | void nsync_mu_rlock (nsync_mu *mu); |
89 | |
90 | /* Unlock *mu, which must have been acquired in read mode by the calling |
91 | thread, and wake waiters, if appropriate. */ |
92 | void nsync_mu_runlock (nsync_mu *mu); |
93 | |
94 | /* Attempt to acquire *mu in reader mode without blocking, and return non-zero |
95 | iff successful. Return non-zero with high probability if *mu was free on |
96 | entry. Perhaps fail to acquire if a writer is waiting, to avoid starvation. |
97 | */ |
98 | int nsync_mu_rtrylock (nsync_mu *mu); |
99 | |
100 | /* May abort if *mu is not held in write mode by the calling thread. */ |
101 | void nsync_mu_assert_held (const nsync_mu *mu); |
102 | |
103 | /* May abort if *mu is not held in read or write mode |
104 | by the calling thread. */ |
105 | void nsync_mu_rassert_held (const nsync_mu *mu); |
106 | |
107 | /* Return whether *mu is held in read mode. |
108 | Requires that the calling thread holds *mu in some mode. */ |
109 | int nsync_mu_is_reader (const nsync_mu *mu); |
110 | |
111 | NSYNC_CPP_END_ |
112 | |
113 | #endif /*NSYNC_PUBLIC_NSYNC_MU_H_*/ |
114 | |