Motr  M0
atomic.c
Go to the documentation of this file.
1 /* -*- C -*- */
2 /*
3  * Copyright (c) 2011-2020 Seagate Technology LLC and/or its Affiliates
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * For any questions about this software or licensing,
18  * please email opensource@seagate.com or cortx-questions@seagate.com.
19  *
20  */
21 
22 
23 #include <time.h> /* nanosleep */
24 #include <stdlib.h>
25 #include <string.h>
26 #include <pthread.h> /* barrier */
27 
28 #include "lib/misc.h" /* M0_SET0 */
29 #include "ut/ut.h"
30 #include "lib/ub.h"
31 #include "lib/thread.h"
32 #include "lib/atomic.h"
33 #include "lib/assert.h"
34 
35 #ifdef HAVE_PTHREAD_BARRIER_T
36 enum {
37  NR = 64
38 };
39 
40 static struct m0_atomic64 atom;
41 static pthread_barrier_t bar[NR];
42 static pthread_barrier_t let[NR];
43 
44 static void wait(pthread_barrier_t *b)
45 {
46  int result;
47 
48  result = pthread_barrier_wait(b);
49  M0_ASSERT(result == 0 || result == PTHREAD_BARRIER_SERIAL_THREAD);
50 }
51 
52 static void worker(int id)
53 {
54  int i;
55  int j;
56  int k;
57 
58  struct timespec delay;
59 
60  for (i = 0; i < NR; ++i) {
61  wait(&let[i]);
62  for (j = 0; j < 2; ++j) {
63  for (k = 0; k < NR; ++k) {
64  if ((id + i + j) % 2 == 0)
65  m0_atomic64_sub(&atom, id);
66  else
67  m0_atomic64_add(&atom, id);
68  }
69  }
70  delay.tv_sec = 0;
71  delay.tv_nsec = (((id + i) % 4) + 1) * 1000;
72  nanosleep(&delay, NULL);
73  wait(&bar[i]);
74  M0_ASSERT(m0_atomic64_get(&atom) == 0);
75  }
76 }
77 
78 struct el {
79  struct el *next;
80  int datum;
81 };
82 
83 static struct el *list;
84 
85 static void cas_insert(struct el *e)
86 {
87  do
88  e->next = list;
89  while (!M0_ATOMIC64_CAS(&list, e->next, e));
90 }
91 
92 static void cas_delete(void)
93 {
94  struct el *e;
95 
96  do
97  e = list;
98  while (!M0_ATOMIC64_CAS(&list, e, e->next));
99 }
100 
101 static void breset(pthread_barrier_t *b, int n)
102 {
103  int result;
104 
105  result = pthread_barrier_destroy(b);
106  M0_ASSERT(result == 0);
107  result = pthread_barrier_init(b, NULL, n);
108  M0_ASSERT(result == 0);
109 }
110 
111 static void cas(int id)
112 {
113  int i;
114  int j;
115 
116  struct el e = {
117  .next = NULL,
118  .datum = id
119  };
120  struct el d[NR];
121 
122  wait(&bar[0]);
123  /* and all together now: non-blocking list insertion. */
124  for (i = 0; i < NR; ++i) {
125  cas_insert(&e);
126  wait(&bar[1]);
127  wait(&bar[2]);
128  }
129  for (i = 0; i < NR; ++i) {
130  for (j = 0; j < NR; ++j)
131  cas_insert(&d[j]);
132  for (j = 0; j < NR; ++j)
133  cas_delete();
134  wait(&bar[3]);
135  wait(&bar[4]);
136  }
137 }
138 #endif
139 
140 void test_atomic(void)
141 {
142 #ifdef HAVE_PTHREAD_BARRIER_T
143  int i;
144  int j;
145  int result;
146  uint64_t sum;
147  uint64_t sum1;
148  bool zero;
149  struct m0_thread t[NR];
150  struct el *e;
151 
152  M0_SET_ARR0(t);
153  m0_atomic64_set(&atom, 0);
154  sum = 0;
155  for (i = 0; i < NR; ++i) {
156  m0_atomic64_add(&atom, i);
157  sum += i;
158  M0_ASSERT(m0_atomic64_get(&atom) == sum);
159  }
160 
161  for (i = sum; i > 0; --i) {
163  M0_ASSERT(zero == (i == 1));
164  }
165 
166  for (i = 0; i < ARRAY_SIZE(bar); ++i) {
167  result = pthread_barrier_init(&bar[i], NULL, NR + 1);
168  M0_ASSERT(result == 0);
169  result = pthread_barrier_init(&let[i], NULL, NR + 1);
170  M0_ASSERT(result == 0);
171  }
172 
173  m0_atomic64_set(&atom, 0);
174 
175  for (i = 0; i < ARRAY_SIZE(t); ++i) {
176  result = M0_THREAD_INIT(&t[i], int, NULL, &worker, i, "worker");
177  M0_ASSERT(result == 0);
178  }
179 
180  for (i = 0; i < NR; ++i) {
181  wait(&let[i]);
182  wait(&bar[i]);
183  M0_ASSERT(m0_atomic64_get(&atom) == 0);
184  }
185 
186  for (i = 0; i < ARRAY_SIZE(t); ++i) {
187  m0_thread_join(&t[i]);
188  m0_thread_fini(&t[i]);
189  }
190 
191  /*
192  * m0_atomic64_cas() test.
193  */
194 
195  M0_CASSERT(ARRAY_SIZE(bar) > 5);
196 
197  /* bar[0] is reached when all cas() threads are created. */
198  breset(&bar[0], NR);
199  /* bar[1] is reached when every cas() thread insert its element in the
200  global lock-free linked list. */
201  breset(&bar[1], NR + 1);
202  /* bar[2] is reached when main thread checked that the list is correct
203  after the previous concurrent step and reset the list to NULL. */
204  breset(&bar[2], NR + 1);
205  /* bar[3] is reached after every cas() step inserted NR elements in the
206  global lock-free list and then removed NR entries from it. */
207  breset(&bar[3], NR + 1);
208  /* bar[4] is reached after main thread checked that the list is empty
209  after the previous step. */
210  breset(&bar[4], NR + 1);
211 
212  for (i = 0; i < ARRAY_SIZE(t); ++i) {
213  result = M0_THREAD_INIT(&t[i], int, NULL, &cas, i, "cas");
214  M0_ASSERT(result == 0);
215  }
216 
217  for (j = 0; j < NR; ++j) {
218  wait(&bar[1]);
219  breset(&bar[1], NR + 1);
220 
221  /* all threads inserted their identifiers in the list, check. */
222  for (i = 0, sum1 = 0, e = list; i < NR; ++i, e = e->next) {
223  M0_ASSERT(e != NULL);
224  M0_ASSERT(0 <= e->datum && e->datum < NR);
225  sum1 += e->datum;
226  }
227  M0_ASSERT(sum == sum1);
228  list = NULL;
229  wait(&bar[2]);
230  breset(&bar[2], NR + 1);
231  }
232  for (j = 0; j < NR; ++j) {
233  wait(&bar[3]);
234  breset(&bar[3], NR + 1);
235  M0_ASSERT(list == NULL);
236  wait(&bar[4]);
237  breset(&bar[4], NR + 1);
238  }
239 
240  for (i = 0; i < ARRAY_SIZE(bar); ++i) {
241  result = pthread_barrier_destroy(&bar[i]);
242  M0_ASSERT(result == 0);
243  result = pthread_barrier_destroy(&let[i]);
244  M0_ASSERT(result == 0);
245  }
246 
247  for (i = 0; i < ARRAY_SIZE(t); ++i) {
248  m0_thread_join(&t[i]);
249  m0_thread_fini(&t[i]);
250  }
251 #else
252  M0_IMPOSSIBLE("pthread barriers are not supported!");
253 #endif
254 
255 }
256 
257 enum {
258  UB_ITER = 1000
259 };
260 
261 static void atomic_ub(int i)
262 {
263  test_atomic();
264 }
265 
267  .us_name = "atomic-ub",
268  .us_init = NULL,
269  .us_fini = NULL,
270  .us_run = {
271  { .ub_name = "atomic",
272  .ub_iter = UB_ITER,
273  .ub_round = atomic_ub },
274 
275  { .ub_name = NULL }
276  }
277 };
278 
279 /*
280  * Local variables:
281  * c-indentation-style: "K&R"
282  * c-basic-offset: 8
283  * tab-width: 8
284  * fill-column: 80
285  * scroll-step: 1
286  * End:
287  */
static struct m0_semaphore wait
Definition: item.c:151
static struct m0_list list
Definition: list.c:144
#define NULL
Definition: misc.h:38
int m0_thread_join(struct m0_thread *q)
Definition: kthread.c:169
static void m0_atomic64_sub(struct m0_atomic64 *a, int64_t num)
#define M0_CASSERT(cond)
static void atomic_ub(int i)
Definition: atomic.c:261
static int delay
Definition: dump.c:174
static int sum
Definition: rwlock.c:53
#define M0_THREAD_INIT(thread, TYPE, init, func, arg, namefmt,...)
Definition: thread.h:139
static const struct m0_uint128 zero
Definition: misc.c:41
Definition: hash.c:34
int i
Definition: dir.c:1033
void test_atomic(void)
Definition: atomic.c:140
#define M0_SET_ARR0(arr)
Definition: misc.h:72
struct m0_ub_set m0_atomic_ub
Definition: atomic.c:266
static struct m0_reqh_service * cas
Definition: service_ut.c:61
#define M0_ASSERT(cond)
const char * us_name
Definition: ub.h:76
Definition: storage.c:317
static struct m0_thread t[8]
Definition: service_ut.c:1230
void m0_thread_fini(struct m0_thread *q)
Definition: thread.c:92
static int next[]
Definition: cp.c:248
static bool m0_atomic64_dec_and_test(struct m0_atomic64 *a)
static int64_t m0_atomic64_get(const struct m0_atomic64 *a)
uint64_t n
Definition: fops.h:107
#define M0_ATOMIC64_CAS(loc, oldval, newval)
Definition: atomic.h:136
static struct elevator el
Definition: chs_test.c:102
static void m0_atomic64_add(struct m0_atomic64 *a, int64_t num)
#define ARRAY_SIZE(a)
Definition: misc.h:45
Definition: ub.h:74
static void m0_atomic64_set(struct m0_atomic64 *a, int64_t num)
#define M0_IMPOSSIBLE(fmt,...)