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#include "nsync_cpp.h"
16#include "platform.h"
17#include "compiler.h"
18#include "cputype.h"
19#include "nsync.h"
20#include "atomic.h"
21#include "dll.h"
22#include "sem.h"
23#include "wait_internal.h"
24#include "common.h"
25
26NSYNC_CPP_START_
27
28/* Internal details of nsync_counter. */
29struct nsync_counter_s_ {
30 nsync_atomic_uint32_ waited; /* wait has been called */
31 nsync_mu counter_mu; /* protects fields below except reads of "value" */
32 nsync_atomic_uint32_ value; /* value of counter */
33 struct nsync_dll_element_s_ *waiters; /* list of waiters */
34};
35
36nsync_counter nsync_counter_new (uint32_t value) {
37 nsync_counter c = (nsync_counter) malloc (sizeof (*c));
38 if (c != NULL) {
39 memset ((void *) c, 0, sizeof (*c));
40 ATM_STORE (&c->value, value);
41 }
42 return (c);
43}
44
45void nsync_counter_free (nsync_counter c) {
46 nsync_mu_lock (&c->counter_mu);
47 ASSERT (nsync_dll_is_empty_ (c->waiters));
48 nsync_mu_unlock (&c->counter_mu);
49 free (c);
50}
51
52uint32_t nsync_counter_add (nsync_counter c, int32_t delta) {
53 uint32_t value;
54 IGNORE_RACES_START ();
55 if (delta == 0) {
56 value = ATM_LOAD_ACQ (&c->value);
57 } else {
58 nsync_mu_lock (&c->counter_mu);
59 do {
60 value = ATM_LOAD (&c->value);
61 } while (!ATM_CAS_RELACQ (&c->value, value, value+delta));
62 value += delta;
63 if (delta > 0) {
64 /* It's illegal to increase the count from zero if
65 there has been a waiter. */
66 ASSERT (value != (uint32_t) delta || !ATM_LOAD (&c->waited));
67 ASSERT (value > value - delta); /* Crash on overflow. */
68 } else {
69 ASSERT (value < value - delta); /* Crash on overflow. */
70 }
71 if (value == 0) {
72 nsync_dll_element_ *p;
73 while ((p = nsync_dll_first_ (c->waiters)) != NULL) {
74 struct nsync_waiter_s *nw = DLL_NSYNC_WAITER (p);
75 c->waiters = nsync_dll_remove_ (c->waiters, p);
76 ATM_STORE_REL (&nw->waiting, 0);
77 nsync_mu_semaphore_v (nw->sem);
78 }
79 }
80 nsync_mu_unlock (&c->counter_mu);
81 }
82 IGNORE_RACES_END ();
83 return (value);
84}
85
86uint32_t nsync_counter_value (nsync_counter c) {
87 uint32_t result;
88 IGNORE_RACES_START ();
89 result = ATM_LOAD_ACQ (&c->value);
90 IGNORE_RACES_END ();
91 return (result);
92}
93
94uint32_t nsync_counter_wait (nsync_counter c, nsync_time abs_deadline) {
95 struct nsync_waitable_s waitable;
96 struct nsync_waitable_s *pwaitable = &waitable;
97 uint32_t result = 0;
98 waitable.v = c;
99 waitable.funcs = &nsync_counter_waitable_funcs;
100 if (nsync_wait_n (NULL, NULL, NULL, abs_deadline, 1, &pwaitable) != 0) {
101 IGNORE_RACES_START ();
102 result = ATM_LOAD_ACQ (&c->value);
103 IGNORE_RACES_END ();
104 }
105 return (result);
106}
107
108static nsync_time counter_ready_time (void *v, struct nsync_waiter_s *nw UNUSED) {
109 nsync_counter c = (nsync_counter) v;
110 nsync_time r;
111 ATM_STORE (&c->waited, 1);
112 r = (ATM_LOAD_ACQ (&c->value) == 0? nsync_time_zero : nsync_time_no_deadline);
113 return (r);
114}
115
116static int counter_enqueue (void *v, struct nsync_waiter_s *nw) {
117 nsync_counter c = (nsync_counter) v;
118 int32_t value;
119 nsync_mu_lock (&c->counter_mu);
120 value = ATM_LOAD_ACQ (&c->value);
121 if (value != 0) {
122 c->waiters = nsync_dll_make_last_in_list_ (c->waiters, &nw->q);
123 ATM_STORE (&nw->waiting, 1);
124 } else {
125 ATM_STORE (&nw->waiting, 0);
126 }
127 nsync_mu_unlock (&c->counter_mu);
128 return (value != 0);
129}
130
131static int counter_dequeue (void *v, struct nsync_waiter_s *nw) {
132 nsync_counter c = (nsync_counter) v;
133 int32_t value;
134 nsync_mu_lock (&c->counter_mu);
135 value = ATM_LOAD_ACQ (&c->value);
136 if (ATM_LOAD_ACQ (&nw->waiting) != 0) {
137 c->waiters = nsync_dll_remove_ (c->waiters, &nw->q);
138 ATM_STORE (&nw->waiting, 0);
139 }
140 nsync_mu_unlock (&c->counter_mu);
141 return (value != 0);
142}
143
144const struct nsync_waitable_funcs_s nsync_counter_waitable_funcs = {
145 &counter_ready_time,
146 &counter_enqueue,
147 &counter_dequeue
148};
149
150NSYNC_CPP_END_
151