vppinfra: added performance test for clib_rwlock_t (test_rwlock.c)
[vpp.git] / src / vppinfra / lock.h
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
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
16 #ifndef included_clib_lock_h
17 #define included_clib_lock_h
18
19 #include <vppinfra/clib.h>
20
21 #if __x86_64__
22 #define CLIB_PAUSE() __builtin_ia32_pause ()
23 #else
24 #define CLIB_PAUSE()
25 #endif
26
27 #if CLIB_DEBUG > 1
28 #define CLIB_LOCK_DBG(_p)                               \
29 do {                                                    \
30     (*_p)->frame_address = __builtin_frame_address (0); \
31     (*_p)->pid = getpid ();                             \
32     (*_p)->thread_index = os_get_thread_index ();       \
33 } while (0)
34 #define CLIB_LOCK_DBG_CLEAR(_p)                         \
35 do {                                                    \
36     (*_p)->frame_address = 0;                           \
37     (*_p)->pid = 0;                                     \
38     (*_p)->thread_index = 0;                            \
39 } while (0)
40 #else
41 #define CLIB_LOCK_DBG(_p)
42 #define CLIB_LOCK_DBG_CLEAR(_p)
43 #endif
44
45 #define CLIB_SPINLOCK_IS_LOCKED(_p) (*(_p))->lock
46 #define CLIB_SPINLOCK_ASSERT_LOCKED(_p) ASSERT(CLIB_SPINLOCK_IS_LOCKED((_p)))
47
48 typedef struct
49 {
50   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
51   u32 lock;
52 #if CLIB_DEBUG > 0
53   pid_t pid;
54   uword thread_index;
55   void *frame_address;
56 #endif
57 } *clib_spinlock_t;
58
59 static inline void
60 clib_spinlock_init (clib_spinlock_t * p)
61 {
62   *p = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES, CLIB_CACHE_LINE_BYTES);
63   clib_memset ((void *) *p, 0, CLIB_CACHE_LINE_BYTES);
64 }
65
66 static inline void
67 clib_spinlock_free (clib_spinlock_t * p)
68 {
69   if (*p)
70     {
71       clib_mem_free ((void *) *p);
72       *p = 0;
73     }
74 }
75
76 static_always_inline void
77 clib_spinlock_lock (clib_spinlock_t * p)
78 {
79   while (clib_atomic_test_and_set (&(*p)->lock))
80     CLIB_PAUSE ();
81   CLIB_LOCK_DBG (p);
82 }
83
84 static_always_inline void
85 clib_spinlock_lock_if_init (clib_spinlock_t * p)
86 {
87   if (PREDICT_FALSE (*p != 0))
88     clib_spinlock_lock (p);
89 }
90
91 static_always_inline void
92 clib_spinlock_unlock (clib_spinlock_t * p)
93 {
94   CLIB_LOCK_DBG_CLEAR (p);
95   /* Make sure all reads/writes are complete before releasing the lock */
96   clib_atomic_release (&(*p)->lock);
97 }
98
99 static_always_inline void
100 clib_spinlock_unlock_if_init (clib_spinlock_t * p)
101 {
102   if (PREDICT_FALSE (*p != 0))
103     clib_spinlock_unlock (p);
104 }
105
106 /*
107  * Readers-Writer Lock
108  */
109
110 typedef struct clib_rw_lock_
111 {
112   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
113   volatile u32 n_readers;
114   volatile u32 n_readers_lock;
115   volatile u32 writer_lock;
116 #if CLIB_DEBUG > 0
117   pid_t pid;
118   uword thread_index;
119   void *frame_address;
120 #endif
121 } *clib_rwlock_t;
122
123 always_inline void
124 clib_rwlock_init (clib_rwlock_t * p)
125 {
126   *p = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES, CLIB_CACHE_LINE_BYTES);
127   clib_memset ((void *) *p, 0, CLIB_CACHE_LINE_BYTES);
128 }
129
130 always_inline void
131 clib_rwlock_free (clib_rwlock_t * p)
132 {
133   if (*p)
134     {
135       clib_mem_free ((void *) *p);
136       *p = 0;
137     }
138 }
139
140 always_inline void
141 clib_rwlock_reader_lock (clib_rwlock_t * p)
142 {
143   while (clib_atomic_test_and_set (&(*p)->n_readers_lock))
144     CLIB_PAUSE ();
145
146   (*p)->n_readers += 1;
147   if ((*p)->n_readers == 1)
148     {
149       while (clib_atomic_test_and_set (&(*p)->writer_lock))
150         CLIB_PAUSE ();
151     }
152   clib_atomic_release (&(*p)->n_readers_lock);
153   CLIB_LOCK_DBG (p);
154 }
155
156 always_inline void
157 clib_rwlock_reader_unlock (clib_rwlock_t * p)
158 {
159   ASSERT ((*p)->n_readers > 0);
160   CLIB_LOCK_DBG_CLEAR (p);
161
162   while (clib_atomic_test_and_set (&(*p)->n_readers_lock))
163     CLIB_PAUSE ();
164
165   (*p)->n_readers -= 1;
166   if ((*p)->n_readers == 0)
167     {
168       clib_atomic_release (&(*p)->writer_lock);
169     }
170   clib_atomic_release (&(*p)->n_readers_lock);
171 }
172
173 always_inline void
174 clib_rwlock_writer_lock (clib_rwlock_t * p)
175 {
176   while (clib_atomic_test_and_set (&(*p)->writer_lock))
177     CLIB_PAUSE ();
178   CLIB_LOCK_DBG (p);
179 }
180
181 always_inline void
182 clib_rwlock_writer_unlock (clib_rwlock_t * p)
183 {
184   CLIB_LOCK_DBG_CLEAR (p);
185   clib_atomic_release (&(*p)->writer_lock);
186 }
187
188 #endif
189
190 /*
191  * fd.io coding-style-patch-verification: ON
192  *
193  * Local Variables:
194  * eval: (c-set-style "gnu")
195  * End:
196  */