Skip to content

Commit b620e2c

Browse files
committed
Merge pull request atomvm#496 from bettio/propagate-tempstack-errors
Propagate temp_stack errors Some temp_stack functions (such as init and push) were using malloc, that might fail under low memory conditions. Rather than ignoring it, or using abort, try to propagate the error to the caller. A lot of API changes have been made, since those functions were used quite everywhere. Last but not least, remove dependency on Context and use GlobalContext while changing the API. These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents 79b8d0f + 85bad39 commit b620e2c

File tree

18 files changed

+635
-281
lines changed

18 files changed

+635
-281
lines changed

src/libAtomVM/bif.c

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,6 @@ term bif_erlang_is_map_1(Context *ctx, term arg1)
168168

169169
term bif_erlang_is_map_key_2(Context *ctx, term arg1, term arg2)
170170
{
171-
UNUSED(ctx);
172-
173171
if (UNLIKELY(!term_is_map(arg2))) {
174172
if (UNLIKELY(memory_ensure_free(ctx, 3) != MEMORY_GC_OK)) {
175173
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
@@ -181,7 +179,15 @@ term bif_erlang_is_map_key_2(Context *ctx, term arg1, term arg2)
181179

182180
RAISE_ERROR(err);
183181
}
184-
return (term_find_map_pos(ctx, arg2, arg1) != -1) ? TRUE_ATOM : FALSE_ATOM;
182+
183+
switch (term_find_map_pos(arg2, arg1, ctx->global)) {
184+
case TERM_MAP_NOT_FOUND:
185+
return FALSE_ATOM;
186+
case TERM_MAP_MEMORY_ALLOC_FAIL:
187+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
188+
default:
189+
return TRUE_ATOM;
190+
}
185191
}
186192

187193
term bif_erlang_length_1(Context *ctx, int live, term arg1)
@@ -256,8 +262,6 @@ term bif_erlang_map_size_1(Context *ctx, int live, term arg1)
256262

257263
term bif_erlang_map_get_2(Context *ctx, term arg1, term arg2)
258264
{
259-
UNUSED(ctx);
260-
261265
if (!UNLIKELY(term_is_map(arg2))) {
262266
if (UNLIKELY(memory_ensure_free(ctx, 3) != MEMORY_GC_OK)) {
263267
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
@@ -270,8 +274,8 @@ term bif_erlang_map_get_2(Context *ctx, term arg1, term arg2)
270274
RAISE_ERROR(err);
271275
}
272276

273-
int pos = term_find_map_pos(ctx, arg2, arg1);
274-
if (pos == -1) {
277+
int pos = term_find_map_pos(arg2, arg1, ctx->global);
278+
if (pos == TERM_MAP_NOT_FOUND) {
275279
if (UNLIKELY(memory_ensure_free(ctx, 3) != MEMORY_GC_OK)) {
276280
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
277281
}
@@ -284,6 +288,8 @@ term bif_erlang_map_get_2(Context *ctx, term arg1, term arg2)
284288
term_put_tuple_element(err, 1, UNSUPPORTED_ATOM);
285289
}
286290
RAISE_ERROR(err);
291+
} else if (UNLIKELY(pos == TERM_MAP_MEMORY_ALLOC_FAIL)) {
292+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
287293
}
288294
return term_get_map_value(arg2, pos);
289295
}
@@ -1325,97 +1331,111 @@ term bif_erlang_xor_2(Context *ctx, term arg1, term arg2)
13251331

13261332
term bif_erlang_equal_to_2(Context *ctx, term arg1, term arg2)
13271333
{
1328-
UNUSED(ctx);
1329-
1330-
if (term_equals(arg1, arg2, ctx)) {
1334+
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1335+
if (result == TermEquals) {
13311336
return TRUE_ATOM;
1332-
} else {
1337+
} else if (result & (TermLessThan | TermGreaterThan)) {
13331338
return FALSE_ATOM;
1339+
} else {
1340+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
13341341
}
13351342
}
13361343

13371344
term bif_erlang_not_equal_to_2(Context *ctx, term arg1, term arg2)
13381345
{
1339-
UNUSED(ctx);
1340-
13411346
//TODO: fix this implementation
13421347
//it should compare any kind of type, and 5.0 != 5 is false
1343-
if (!term_equals(arg1, arg2, ctx)) {
1348+
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1349+
if (result & (TermLessThan | TermGreaterThan)) {
13441350
return TRUE_ATOM;
1345-
} else {
1351+
} else if (result == TermEquals) {
13461352
return FALSE_ATOM;
1353+
} else {
1354+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
13471355
}
13481356
}
13491357

13501358
term bif_erlang_exactly_equal_to_2(Context *ctx, term arg1, term arg2)
13511359
{
1352-
UNUSED(ctx);
1353-
13541360
//TODO: 5.0 != 5
1355-
if (term_equals(arg1, arg2, ctx)) {
1361+
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1362+
if (result == TermEquals) {
13561363
return TRUE_ATOM;
1357-
} else {
1364+
} else if (result & (TermLessThan | TermGreaterThan)) {
13581365
return FALSE_ATOM;
1366+
} else {
1367+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
13591368
}
13601369
}
13611370

13621371
term bif_erlang_exactly_not_equal_to_2(Context *ctx, term arg1, term arg2)
13631372
{
1364-
UNUSED(ctx);
1365-
13661373
//TODO: 5.0 != 5
1367-
if (!term_equals(arg1, arg2, ctx)) {
1374+
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1375+
if (result & (TermLessThan | TermGreaterThan)) {
13681376
return TRUE_ATOM;
1369-
} else {
1377+
} else if (result == TermEquals) {
13701378
return FALSE_ATOM;
1379+
} else {
1380+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
13711381
}
13721382
}
13731383

13741384
term bif_erlang_greater_than_2(Context *ctx, term arg1, term arg2)
13751385
{
1376-
UNUSED(ctx);
1377-
1378-
if (term_compare(arg1, arg2, ctx) > 0) {
1386+
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1387+
if (result == TermGreaterThan) {
13791388
return TRUE_ATOM;
1380-
} else {
1389+
} else if (result & (TermEquals | TermLessThan)) {
13811390
return FALSE_ATOM;
1391+
} else {
1392+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
13821393
}
13831394
}
13841395

13851396
term bif_erlang_less_than_2(Context *ctx, term arg1, term arg2)
13861397
{
1387-
UNUSED(ctx);
1388-
1389-
if (term_compare(arg1, arg2, ctx) < 0) {
1398+
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1399+
if (result == TermLessThan) {
13901400
return TRUE_ATOM;
1391-
} else {
1401+
} else if (result & (TermEquals | TermGreaterThan)) {
13921402
return FALSE_ATOM;
1403+
} else {
1404+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
13931405
}
13941406
}
13951407

13961408
term bif_erlang_less_than_or_equal_2(Context *ctx, term arg1, term arg2)
13971409
{
1398-
UNUSED(ctx);
1399-
1400-
if (term_compare(arg1, arg2, ctx) <= 0) {
1410+
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1411+
if (result & (TermLessThan | TermEquals)) {
14011412
return TRUE_ATOM;
1402-
} else {
1413+
} else if (result == TermGreaterThan) {
14031414
return FALSE_ATOM;
1415+
} else {
1416+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
14041417
}
14051418
}
14061419

14071420
term bif_erlang_greater_than_or_equal_2(Context *ctx, term arg1, term arg2)
14081421
{
1409-
UNUSED(ctx);
1410-
1411-
if (term_compare(arg1, arg2, ctx) >= 0) {
1422+
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1423+
if (result & (TermGreaterThan | TermEquals)) {
14121424
return TRUE_ATOM;
1413-
} else {
1425+
} else if (result & TermLessThan) {
14141426
return FALSE_ATOM;
1427+
} else {
1428+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
14151429
}
14161430
}
14171431

14181432
term bif_erlang_get_1(Context *ctx, term arg1)
14191433
{
1420-
return dictionary_get(&ctx->dictionary, ctx, arg1);
1434+
term value;
1435+
DictionaryFunctionResult result = dictionary_get(&ctx->dictionary, arg1, &value, ctx->global);
1436+
if (UNLIKELY(result != DictionaryOk)) {
1437+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
1438+
}
1439+
1440+
return value;
14211441
}

src/libAtomVM/dictionary.c

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,55 +26,85 @@
2626

2727
#include <stdlib.h>
2828

29-
static struct DictEntry *dictionary_find(struct ListHead *dictionary, Context *ctx, term key)
29+
static DictionaryFunctionResult dictionary_find(
30+
struct ListHead *dictionary, term key, struct DictEntry **found, GlobalContext *global)
3031
{
3132
struct ListHead *item;
3233
LIST_FOR_EACH (item, dictionary) {
3334
struct DictEntry *entry = GET_LIST_ENTRY(item, struct DictEntry, head);
34-
if (term_compare(entry->key, key, ctx) == 0) {
35-
return entry;
35+
TermCompareResult result = term_compare(entry->key, key, global);
36+
if (result == TermEquals) {
37+
*found = entry;
38+
return DictionaryOk;
39+
} else if (UNLIKELY(result == TermCompareMemoryAllocFail)) {
40+
return DictionaryMemoryAllocFail;
3641
}
3742
}
3843

39-
return NULL;
44+
*found = NULL;
45+
return DictionaryOk;
4046
}
4147

42-
term dictionary_put(struct ListHead *dict, Context *ctx, term key, term value)
48+
DictionaryFunctionResult dictionary_put(
49+
struct ListHead *dict, term key, term value, term *old, GlobalContext *global)
4350
{
44-
struct DictEntry *entry = dictionary_find(dict, ctx, key);
51+
struct DictEntry *entry;
52+
DictionaryFunctionResult result = dictionary_find(dict, key, &entry, global);
53+
if (UNLIKELY(result != DictionaryOk)) {
54+
return result;
55+
}
56+
4557
if (entry) {
46-
term old = entry->value;
58+
*old = entry->value;
4759
entry->value = value;
4860

49-
return old;
5061
} else {
5162
entry = malloc(sizeof(struct DictEntry));
63+
if (IS_NULL_PTR(entry)) {
64+
return DictionaryMemoryAllocFail;
65+
}
5266
entry->key = key;
5367
entry->value = value;
5468
list_prepend(dict, &entry->head);
5569

56-
return UNDEFINED_ATOM;
70+
*old = UNDEFINED_ATOM;
5771
}
72+
73+
return DictionaryOk;
5874
}
5975

60-
term dictionary_get(struct ListHead *dict, Context *ctx, term key)
76+
DictionaryFunctionResult dictionary_get(
77+
struct ListHead *dict, term key, term *old, GlobalContext *global)
6178
{
62-
struct DictEntry *entry = dictionary_find(dict, ctx, key);
63-
return entry ? entry->value : UNDEFINED_ATOM;
79+
struct DictEntry *entry;
80+
DictionaryFunctionResult result = dictionary_find(dict, key, &entry, global);
81+
if (UNLIKELY(result != DictionaryOk)) {
82+
return result;
83+
}
84+
85+
*old = entry ? entry->value : UNDEFINED_ATOM;
86+
return DictionaryOk;
6487
}
6588

66-
term dictionary_erase(struct ListHead *dict, Context *ctx, term key)
89+
DictionaryFunctionResult dictionary_erase(
90+
struct ListHead *dict, term key, term *old, GlobalContext *global)
6791
{
68-
struct DictEntry *entry = dictionary_find(dict, ctx, key);
92+
struct DictEntry *entry;
93+
DictionaryFunctionResult result = dictionary_find(dict, key, &entry, global);
94+
if (UNLIKELY(result != DictionaryOk)) {
95+
return result;
96+
}
97+
6998
if (!entry) {
70-
return UNDEFINED_ATOM;
99+
*old = UNDEFINED_ATOM;
100+
return DictionaryOk;
71101
}
72-
term old = entry->value;
102+
*old = entry->value;
73103

74104
list_remove(&entry->head);
75105
free(entry);
76106

77-
return old;
107+
return DictionaryOk;
78108
}
79109

80110
void dictionary_destroy(struct ListHead *dict)

src/libAtomVM/dictionary.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,25 @@ extern "C" {
2828
#include "list.h"
2929
#include "term.h"
3030

31+
typedef enum
32+
{
33+
DictionaryOk,
34+
DictionaryMemoryAllocFail
35+
} DictionaryFunctionResult;
36+
3137
struct DictEntry
3238
{
3339
struct ListHead head;
3440
term key;
3541
term value;
3642
};
3743

38-
term dictionary_put(struct ListHead *dict, Context *ctx, term key, term value);
39-
term dictionary_get(struct ListHead *dict, Context *ctx, term key);
40-
term dictionary_erase(struct ListHead *dict, Context *ctx, term key);
44+
DictionaryFunctionResult dictionary_put(
45+
struct ListHead *dict, term key, term value, term *old, GlobalContext *ctx);
46+
DictionaryFunctionResult dictionary_get(
47+
struct ListHead *dict, term key, term *old, GlobalContext *ctx);
48+
DictionaryFunctionResult dictionary_erase(
49+
struct ListHead *dict, term key, term *old, GlobalContext *ctx);
4150
void dictionary_destroy(struct ListHead *dict);
4251

4352
#ifdef __cplusplus

0 commit comments

Comments
 (0)