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
22NSYNC_CPP_START_
23
24struct 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); */
62typedef 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 }
71void 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. */
75void 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. */
79void 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. */
84int 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. */
88void 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. */
92void 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 */
98int nsync_mu_rtrylock (nsync_mu *mu);
99
100/* May abort if *mu is not held in write mode by the calling thread. */
101void 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. */
105void 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. */
109int nsync_mu_is_reader (const nsync_mu *mu);
110
111NSYNC_CPP_END_
112
113#endif /*NSYNC_PUBLIC_NSYNC_MU_H_*/
114