1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2018. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20
21 /*
22 * Purpose: High performance atomics.
23 */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <stddef.h> /* offsetof */
30
31 #include "sys.h"
32 #include "export.h"
33 #include "bif.h"
34 #include "erl_threads.h"
35 #include "big.h"
36 #include "erl_binary.h"
37 #include "erl_bif_unique.h"
38 #include "erl_map.h"
39
40 typedef struct
41 {
42 int is_signed;
43 UWord vlen;
44 erts_atomic64_t v[1];
45 }AtomicsRef;
46
atomics_destructor(Binary * unused)47 static int atomics_destructor(Binary *unused)
48 {
49 return 1;
50 }
51
52 #define OPT_SIGNED (1 << 0)
53
erts_internal_atomics_new_2(BIF_ALIST_2)54 BIF_RETTYPE erts_internal_atomics_new_2(BIF_ALIST_2)
55 {
56 AtomicsRef* p;
57 Binary* mbin;
58 UWord i, cnt, opts;
59 Uint bytes;
60 Eterm* hp;
61
62 if (!term_to_UWord(BIF_ARG_1, &cnt)) {
63 BIF_ERROR(BIF_P, cnt);
64 }
65 if (cnt == 0) {
66 BIF_ERROR(BIF_P, BADARG);
67 }
68 if (!term_to_UWord(BIF_ARG_2, &opts)) {
69 BIF_ERROR(BIF_P, opts);
70 }
71
72 if (cnt > (ERTS_UWORD_MAX / sizeof(p->v[0])))
73 BIF_ERROR(BIF_P, SYSTEM_LIMIT);
74
75 bytes = offsetof(AtomicsRef, v) + cnt*sizeof(p->v[0]);
76 mbin = erts_create_magic_binary_x(bytes,
77 atomics_destructor,
78 ERTS_ALC_T_ATOMICS,
79 0);
80 p = ERTS_MAGIC_BIN_DATA(mbin);
81 p->is_signed = opts & OPT_SIGNED;
82 p->vlen = cnt;
83 for (i=0; i < cnt; i++)
84 erts_atomic64_init_nob(&p->v[i], 0);
85 hp = HAlloc(BIF_P, ERTS_MAGIC_REF_THING_SIZE);
86 return erts_mk_magic_ref(&hp, &MSO(BIF_P), mbin);
87 }
88
get_ref(Eterm ref,AtomicsRef ** pp)89 static ERTS_INLINE int get_ref(Eterm ref, AtomicsRef** pp)
90 {
91 Binary* mbin;
92 if (!is_internal_magic_ref(ref))
93 return 0;
94
95 mbin = erts_magic_ref2bin(ref);
96 if (ERTS_MAGIC_BIN_DESTRUCTOR(mbin) != atomics_destructor)
97 return 0;
98 *pp = ERTS_MAGIC_BIN_DATA(mbin);
99 return 1;
100 }
101
get_ref_ix(Eterm ref,Eterm ix,AtomicsRef ** pp,UWord * ixp)102 static ERTS_INLINE int get_ref_ix(Eterm ref, Eterm ix,
103 AtomicsRef** pp, UWord* ixp)
104 {
105 return (get_ref(ref, pp)
106 && term_to_UWord(ix, ixp)
107 && --(*ixp) < (*pp)->vlen);
108 }
109
get_value(AtomicsRef * p,Eterm term,erts_aint64_t * valp)110 static ERTS_INLINE int get_value(AtomicsRef* p, Eterm term, erts_aint64_t *valp)
111 {
112 return (p->is_signed ?
113 term_to_Sint64(term, (Sint64*)valp) :
114 term_to_Uint64(term, (Uint64*)valp));
115 }
116
get_incr(AtomicsRef * p,Eterm term,erts_aint64_t * valp)117 static ERTS_INLINE int get_incr(AtomicsRef* p, Eterm term, erts_aint64_t *valp)
118 {
119 return (term_to_Sint64(term, (Sint64*)valp)
120 || term_to_Uint64(term, (Uint64*)valp));
121 }
122
bld_atomic(Process * proc,AtomicsRef * p,erts_aint64_t val)123 static ERTS_INLINE Eterm bld_atomic(Process* proc, AtomicsRef* p,
124 erts_aint64_t val)
125 {
126 if (p->is_signed) {
127 if (IS_SSMALL(val))
128 return make_small((Sint) val);
129 else {
130 Uint hsz = ERTS_SINT64_HEAP_SIZE(val);
131 Eterm* hp = HAlloc(proc, hsz);
132 return erts_sint64_to_big(val, &hp);
133 }
134 }
135 else {
136 if ((Uint64)val <= MAX_SMALL)
137 return make_small((Sint) val);
138 else {
139 Uint hsz = ERTS_UINT64_HEAP_SIZE((Uint64)val);
140 Eterm* hp = HAlloc(proc, hsz);
141 return erts_uint64_to_big(val, &hp);
142 }
143 }
144 }
145
atomics_put_3(BIF_ALIST_3)146 BIF_RETTYPE atomics_put_3(BIF_ALIST_3)
147 {
148 AtomicsRef* p;
149 UWord ix;
150 erts_aint64_t val;
151
152 if (!get_ref_ix(BIF_ARG_1, BIF_ARG_2, &p, &ix)
153 || !get_value(p, BIF_ARG_3, &val)) {
154 BIF_ERROR(BIF_P, BADARG);
155 }
156 erts_atomic64_set_mb(&p->v[ix], val);
157 return am_ok;
158 }
159
atomics_get_2(BIF_ALIST_2)160 BIF_RETTYPE atomics_get_2(BIF_ALIST_2)
161 {
162 AtomicsRef* p;
163 UWord ix;
164
165 if (!get_ref_ix(BIF_ARG_1, BIF_ARG_2, &p, &ix)) {
166 BIF_ERROR(BIF_P, BADARG);
167 }
168 return bld_atomic(BIF_P, p, erts_atomic64_read_mb(&p->v[ix]));
169 }
170
atomics_add_3(BIF_ALIST_3)171 BIF_RETTYPE atomics_add_3(BIF_ALIST_3)
172 {
173 AtomicsRef* p;
174 UWord ix;
175 erts_aint64_t incr;
176
177 if (!get_ref_ix(BIF_ARG_1, BIF_ARG_2, &p, &ix)
178 || !get_incr(p, BIF_ARG_3, &incr)) {
179 BIF_ERROR(BIF_P, BADARG);
180 }
181 erts_atomic64_add_mb(&p->v[ix], incr);
182 return am_ok;
183 }
184
atomics_add_get_3(BIF_ALIST_3)185 BIF_RETTYPE atomics_add_get_3(BIF_ALIST_3)
186 {
187 AtomicsRef* p;
188 UWord ix;
189 erts_aint64_t incr;
190
191 if (!get_ref_ix(BIF_ARG_1, BIF_ARG_2, &p, &ix)
192 || !get_incr(p, BIF_ARG_3, &incr)) {
193 BIF_ERROR(BIF_P, BADARG);
194 }
195 return bld_atomic(BIF_P, p, erts_atomic64_add_read_mb(&p->v[ix], incr));
196 }
197
atomics_exchange_3(BIF_ALIST_3)198 BIF_RETTYPE atomics_exchange_3(BIF_ALIST_3)
199 {
200 AtomicsRef* p;
201 UWord ix;
202 erts_aint64_t desired, was;
203
204 if (!get_ref_ix(BIF_ARG_1, BIF_ARG_2, &p, &ix)
205 || !get_value(p, BIF_ARG_3, &desired)) {
206 BIF_ERROR(BIF_P, BADARG);
207 }
208 was = erts_atomic64_xchg_mb(&p->v[ix], desired);
209 return bld_atomic(BIF_P, p, was);
210 }
211
atomics_compare_exchange_4(BIF_ALIST_4)212 BIF_RETTYPE atomics_compare_exchange_4(BIF_ALIST_4)
213 {
214 AtomicsRef* p;
215 UWord ix;
216 erts_aint64_t expected, desired, was;
217
218 if (!get_ref_ix(BIF_ARG_1, BIF_ARG_2, &p, &ix)
219 || !get_value(p, BIF_ARG_3, &expected)
220 || !get_value(p, BIF_ARG_4, &desired)) {
221 BIF_ERROR(BIF_P, BADARG);
222 }
223 was = erts_atomic64_cmpxchg_mb(&p->v[ix], desired, expected);
224 return was == expected ? am_ok : bld_atomic(BIF_P, p, was);
225 }
226
atomics_info_1(BIF_ALIST_1)227 BIF_RETTYPE atomics_info_1(BIF_ALIST_1)
228 {
229 AtomicsRef* p;
230 Uint hsz = MAP4_SZ;
231 Eterm *hp;
232 Uint64 max;
233 Sint64 min;
234 UWord memory;
235 Eterm max_val, min_val, sz_val, mem_val;
236
237 if (!get_ref(BIF_ARG_1, &p))
238 BIF_ERROR(BIF_P, BADARG);
239
240 max = p->is_signed ? ERTS_SINT64_MAX : ERTS_UINT64_MAX;
241 min = p->is_signed ? ERTS_SINT64_MIN : 0;
242 memory = erts_magic_ref2bin(BIF_ARG_1)->orig_size;
243
244 erts_bld_uint64(NULL, &hsz, max);
245 erts_bld_sint64(NULL, &hsz, min);
246 erts_bld_uword(NULL, &hsz, p->vlen);
247 erts_bld_uword(NULL, &hsz, memory);
248
249 hp = HAlloc(BIF_P, hsz);
250 max_val = erts_bld_uint64(&hp, NULL, max);
251 min_val = erts_bld_sint64(&hp, NULL, min);
252 sz_val = erts_bld_uword(&hp, NULL, p->vlen);
253 mem_val = erts_bld_uword(&hp, NULL, memory);
254
255 return MAP4(hp, am_max, max_val,
256 am_memory, mem_val,
257 am_min, min_val,
258 am_size, sz_val);
259 }
260