28
28
#include "overflow_helpers.h"
29
29
#include "term.h"
30
30
31
+ #define ETS_NO_INDEX SIZE_MAX
32
+ #define ETS_ANY_PROCESS -1
33
+
31
34
#ifndef AVM_NO_SMP
32
35
#define SMP_RDLOCK (table ) smp_rwlock_rdlock(table->lock)
33
36
#define SMP_WRLOCK (table ) smp_rwlock_wrlock(table->lock)
@@ -81,51 +84,40 @@ static void ets_add_table(struct Ets *ets, struct EtsTable *ets_table)
81
84
synclist_unlock (& ets -> ets_tables );
82
85
}
83
86
84
- static struct EtsTable * ets_get_table_by_ref (struct Ets * ets , uint64_t ref , TableAccessType access_type )
87
+ static struct EtsTable * ets_acquire_table (struct Ets * ets , int32_t process_id , term name_or_ref , TableAccessType requested_access_type )
85
88
{
86
- struct ListHead * ets_tables_list = synclist_rdlock (& ets -> ets_tables );
87
- struct ListHead * item ;
88
- struct EtsTable * ret = NULL ;
89
- LIST_FOR_EACH (item , ets_tables_list ) {
90
- struct EtsTable * table = GET_LIST_ENTRY (item , struct EtsTable , head );
91
- if (table -> ref_ticks == ref ) {
92
- switch (access_type ) {
93
- case TableAccessRead :
94
- SMP_RDLOCK (table );
95
- break ;
96
- case TableAccessWrite :
97
- SMP_WRLOCK (table );
98
- break ;
99
- default :
100
- break ;
101
- }
102
- ret = table ;
103
- break ;
104
- }
89
+ uint64_t ref = 0 ;
90
+ term name = term_invalid_term ();
91
+ bool is_atom = term_is_atom (name_or_ref );
92
+ if (is_atom ) {
93
+ name = name_or_ref ;
94
+ } else {
95
+ ref = term_to_ref_ticks (name_or_ref );
105
96
}
106
- synclist_unlock (& ets -> ets_tables );
107
- return ret ;
108
- }
109
97
110
- static struct EtsTable * ets_get_table_by_name (struct Ets * ets , term name , TableAccessType access_type )
111
- {
98
+ struct EtsTable * ret = NULL ;
112
99
struct ListHead * ets_tables_list = synclist_rdlock (& ets -> ets_tables );
113
100
struct ListHead * item ;
114
- struct EtsTable * ret = NULL ;
115
101
LIST_FOR_EACH (item , ets_tables_list ) {
116
102
struct EtsTable * table = GET_LIST_ENTRY (item , struct EtsTable , head );
117
- if (table -> is_named && table -> name == name ) {
118
- switch (access_type ) {
119
- case TableAccessRead :
120
- SMP_RDLOCK (table );
121
- break ;
122
- case TableAccessWrite :
123
- SMP_WRLOCK (table );
124
- break ;
125
- default :
126
- break ;
103
+ bool found = is_atom ? table -> is_named && table -> name == name : table -> ref_ticks == ref ;
104
+ if (found ) {
105
+ bool is_owner = table -> owner_process_id == process_id ;
106
+ bool is_non_private = table -> access_type != EtsAccessPrivate ;
107
+ bool is_public = table -> access_type == EtsAccessPublic ;
108
+
109
+ bool can_read = requested_access_type == TableAccessRead && (is_non_private || is_owner );
110
+ bool can_write = requested_access_type == TableAccessWrite && (is_public || is_owner );
111
+ bool access_none = requested_access_type == TableAccessNone ;
112
+ if (can_read ) {
113
+ SMP_RDLOCK (table );
114
+ ret = table ;
115
+ } else if (can_write ) {
116
+ SMP_WRLOCK (table );
117
+ ret = table ;
118
+ } else if (access_none ) {
119
+ ret = table ;
127
120
}
128
- ret = table ;
129
121
break ;
130
122
}
131
123
}
@@ -147,7 +139,7 @@ void ets_destroy(struct Ets *ets, GlobalContext *global)
147
139
EtsErrorCode ets_create_table_maybe_gc (term name , bool is_named , EtsTableType table_type , EtsAccessType access_type , size_t keypos , term * ret , Context * ctx )
148
140
{
149
141
if (is_named ) {
150
- struct EtsTable * ets_table = ets_get_table_by_name (& ctx -> global -> ets , name , TableAccessNone );
142
+ struct EtsTable * ets_table = ets_acquire_table (& ctx -> global -> ets , ETS_ANY_PROCESS , name , TableAccessNone );
151
143
if (ets_table != NULL ) {
152
144
return EtsTableNameInUse ;
153
145
}
@@ -255,10 +247,6 @@ static void ets_delete_all_tables(struct Ets *ets, GlobalContext *global)
255
247
256
248
static EtsErrorCode ets_table_insert (struct EtsTable * ets_table , term entry , Context * ctx )
257
249
{
258
- if (ets_table -> access_type != EtsAccessPublic && ets_table -> owner_process_id != ctx -> process_id ) {
259
- return EtsPermissionDenied ;
260
- }
261
-
262
250
size_t keypos = ets_table -> keypos ;
263
251
264
252
if ((size_t ) term_get_tuple_arity (entry ) < keypos + 1 ) {
@@ -270,7 +258,7 @@ static EtsErrorCode ets_table_insert(struct EtsTable *ets_table, term entry, Con
270
258
return EtsAllocationFailure ;
271
259
}
272
260
273
- EtsHashtableErrorCode res = ets_hashtable_insert (ets_table -> hashtable , new_node , EtsHashtableAllowOverwrite , ctx -> global );
261
+ EtsHashtableStatus res = ets_hashtable_insert (ets_table -> hashtable , new_node , EtsHashtableAllowOverwrite , ctx -> global );
274
262
if (UNLIKELY (res != EtsHashtableOk )) {
275
263
return EtsAllocationFailure ;
276
264
}
@@ -280,10 +268,6 @@ static EtsErrorCode ets_table_insert(struct EtsTable *ets_table, term entry, Con
280
268
281
269
static EtsErrorCode ets_table_insert_list (struct EtsTable * ets_table , term list , Context * ctx )
282
270
{
283
- if (ets_table -> access_type != EtsAccessPublic && ets_table -> owner_process_id != ctx -> process_id ) {
284
- return EtsPermissionDenied ;
285
- }
286
-
287
271
term iter = list ;
288
272
size_t size = 0 ;
289
273
@@ -309,7 +293,9 @@ static EtsErrorCode ets_table_insert_list(struct EtsTable *ets_table, term list,
309
293
term tuple = term_get_list_head (list );
310
294
nodes [i ] = ets_hashtable_new_node (tuple , ets_table -> keypos );
311
295
if (IS_NULL_PTR (nodes [i ])) {
312
- ets_hashtable_free_node_array (nodes , i , ctx -> global );
296
+ for (size_t it = 0 ; it < i ; ++ it ) {
297
+ ets_hashtable_free_node (nodes [it ], ctx -> global );
298
+ }
313
299
free (nodes );
314
300
return EtsAllocationFailure ;
315
301
}
@@ -318,8 +304,7 @@ static EtsErrorCode ets_table_insert_list(struct EtsTable *ets_table, term list,
318
304
}
319
305
320
306
for (size_t i = 0 ; i < size ; ++ i ) {
321
-
322
- EtsHashtableErrorCode res = ets_hashtable_insert (ets_table -> hashtable , nodes [i ], EtsHashtableAllowOverwrite , ctx -> global );
307
+ EtsHashtableStatus res = ets_hashtable_insert (ets_table -> hashtable , nodes [i ], EtsHashtableAllowOverwrite , ctx -> global );
323
308
assert (res == EtsHashtableOk );
324
309
}
325
310
@@ -329,9 +314,9 @@ static EtsErrorCode ets_table_insert_list(struct EtsTable *ets_table, term list,
329
314
330
315
EtsErrorCode ets_insert (term name_or_ref , term entry , Context * ctx )
331
316
{
332
- struct EtsTable * ets_table = term_is_atom ( name_or_ref ) ? ets_get_table_by_name ( & ctx -> global -> ets , name_or_ref , TableAccessWrite ) : ets_get_table_by_ref ( & ctx -> global -> ets , term_to_ref_ticks ( name_or_ref ) , TableAccessWrite );
333
- if (ets_table == NULL ) {
334
- return EtsTableNotFound ;
317
+ struct EtsTable * ets_table = ets_acquire_table ( & ctx -> global -> ets , ctx -> process_id , name_or_ref , TableAccessWrite );
318
+ if (IS_NULL_PTR ( ets_table ) ) {
319
+ return EtsBadAccess ;
335
320
}
336
321
337
322
EtsErrorCode result ;
@@ -350,10 +335,6 @@ EtsErrorCode ets_insert(term name_or_ref, term entry, Context *ctx)
350
335
351
336
static EtsErrorCode ets_table_lookup_maybe_gc (struct EtsTable * ets_table , term key , term * ret , Context * ctx , int num_roots , term * roots )
352
337
{
353
- if (ets_table -> access_type == EtsAccessPrivate && ets_table -> owner_process_id != ctx -> process_id ) {
354
- return EtsPermissionDenied ;
355
- }
356
-
357
338
term res = ets_hashtable_lookup (ets_table -> hashtable , key , ets_table -> keypos , ctx -> global );
358
339
359
340
if (term_is_nil (res )) {
@@ -374,9 +355,9 @@ static EtsErrorCode ets_table_lookup_maybe_gc(struct EtsTable *ets_table, term k
374
355
375
356
EtsErrorCode ets_lookup_maybe_gc (term name_or_ref , term key , term * ret , Context * ctx )
376
357
{
377
- struct EtsTable * ets_table = term_is_atom ( name_or_ref ) ? ets_get_table_by_name ( & ctx -> global -> ets , name_or_ref , TableAccessRead ) : ets_get_table_by_ref ( & ctx -> global -> ets , term_to_ref_ticks ( name_or_ref ) , TableAccessRead );
378
- if (ets_table == NULL ) {
379
- return EtsTableNotFound ;
358
+ struct EtsTable * ets_table = ets_acquire_table ( & ctx -> global -> ets , ctx -> process_id , name_or_ref , TableAccessRead );
359
+ if (IS_NULL_PTR ( ets_table ) ) {
360
+ return EtsBadAccess ;
380
361
}
381
362
382
363
EtsErrorCode result = ets_table_lookup_maybe_gc (ets_table , key , ret , ctx , 0 , NULL );
@@ -391,14 +372,9 @@ EtsErrorCode ets_lookup_element_maybe_gc(term name_or_ref, term key, size_t pos,
391
372
return EtsBadPosition ;
392
373
}
393
374
394
- struct EtsTable * ets_table = term_is_atom (name_or_ref ) ? ets_get_table_by_name (& ctx -> global -> ets , name_or_ref , TableAccessRead ) : ets_get_table_by_ref (& ctx -> global -> ets , term_to_ref_ticks (name_or_ref ), TableAccessRead );
395
- if (ets_table == NULL ) {
396
- return EtsTableNotFound ;
397
- }
398
-
399
- if (ets_table -> access_type == EtsAccessPrivate && ets_table -> owner_process_id != ctx -> process_id ) {
400
- SMP_UNLOCK (ets_table );
401
- return EtsPermissionDenied ;
375
+ struct EtsTable * ets_table = ets_acquire_table (& ctx -> global -> ets , ctx -> process_id , name_or_ref , TableAccessRead );
376
+ if (IS_NULL_PTR (ets_table )) {
377
+ return EtsBadAccess ;
402
378
}
403
379
404
380
term entry = ets_hashtable_lookup (ets_table -> hashtable , key , ets_table -> keypos , ctx -> global );
@@ -426,29 +402,11 @@ EtsErrorCode ets_lookup_element_maybe_gc(term name_or_ref, term key, size_t pos,
426
402
return EtsOk ;
427
403
}
428
404
429
- static EtsErrorCode ets_table_delete (struct EtsTable * ets_table , term key , term * ret , Context * ctx )
430
- {
431
- if (ets_table -> access_type != EtsAccessPublic && ets_table -> owner_process_id != ctx -> process_id ) {
432
- return EtsPermissionDenied ;
433
- }
434
-
435
- bool _res = ets_hashtable_remove (ets_table -> hashtable , key , ets_table -> keypos , ctx -> global );
436
- UNUSED (_res );
437
-
438
- * ret = TRUE_ATOM ;
439
- return EtsOk ;
440
- }
441
-
442
405
EtsErrorCode ets_drop_table (term name_or_ref , term * ret , Context * ctx )
443
406
{
444
- struct EtsTable * ets_table = term_is_atom (name_or_ref )
445
- ? ets_get_table_by_name (& ctx -> global -> ets , name_or_ref , TableAccessWrite )
446
- : ets_get_table_by_ref (& ctx -> global -> ets , term_to_ref_ticks (name_or_ref ), TableAccessWrite );
407
+ struct EtsTable * ets_table = ets_acquire_table (& ctx -> global -> ets , ctx -> process_id , name_or_ref , TableAccessWrite );
447
408
if (IS_NULL_PTR (ets_table )) {
448
- return EtsTableNotFound ;
449
- }
450
- if (ets_table -> access_type != EtsAccessPublic && ets_table -> owner_process_id != ctx -> process_id ) {
451
- return EtsPermissionDenied ;
409
+ return EtsBadAccess ;
452
410
}
453
411
454
412
struct ListHead * ets_tables_list = synclist_wrlock (& ctx -> global -> ets .ets_tables );
@@ -464,17 +422,17 @@ EtsErrorCode ets_drop_table(term name_or_ref, term *ret, Context *ctx)
464
422
465
423
EtsErrorCode ets_delete (term name_or_ref , term key , term * ret , Context * ctx )
466
424
{
467
- struct EtsTable * ets_table = term_is_atom (name_or_ref )
468
- ? ets_get_table_by_name (& ctx -> global -> ets , name_or_ref , TableAccessRead )
469
- : ets_get_table_by_ref (& ctx -> global -> ets , term_to_ref_ticks (name_or_ref ), TableAccessRead );
425
+ struct EtsTable * ets_table = ets_acquire_table (& ctx -> global -> ets , ctx -> process_id , name_or_ref , TableAccessWrite );
470
426
if (IS_NULL_PTR (ets_table )) {
471
- return EtsTableNotFound ;
427
+ return EtsBadAccess ;
472
428
}
473
429
474
- EtsErrorCode res = ets_table_delete (ets_table , key , ret , ctx );
475
-
430
+ bool _found = ets_hashtable_remove (ets_table -> hashtable , key , ets_table -> keypos , ctx -> global );
431
+ UNUSED ( _found );
476
432
SMP_UNLOCK (ets_table );
477
- return res ;
433
+
434
+ * ret = TRUE_ATOM ;
435
+ return EtsOk ;
478
436
}
479
437
480
438
static bool operation_to_tuple4 (term operation , size_t default_pos , term * position , term * increment , term * threshold , term * set_value )
@@ -524,11 +482,9 @@ static bool operation_to_tuple4(term operation, size_t default_pos, term *positi
524
482
525
483
EtsErrorCode ets_update_counter_maybe_gc (term ref , term key , term operation , term default_value , term * ret , Context * ctx )
526
484
{
527
- struct EtsTable * ets_table = term_is_atom (ref )
528
- ? ets_get_table_by_name (& ctx -> global -> ets , ref , TableAccessWrite )
529
- : ets_get_table_by_ref (& ctx -> global -> ets , term_to_ref_ticks (ref ), TableAccessWrite );
485
+ struct EtsTable * ets_table = ets_acquire_table (& ctx -> global -> ets , ctx -> process_id , ref , TableAccessWrite );
530
486
if (IS_NULL_PTR (ets_table )) {
531
- return EtsTableNotFound ;
487
+ return EtsBadAccess ;
532
488
}
533
489
534
490
// do not use an invalid term as a root
@@ -588,7 +544,7 @@ EtsErrorCode ets_update_counter_maybe_gc(term ref, term key, term operation, ter
588
544
avm_int_t elem_value ;
589
545
if (BUILTIN_ADD_OVERFLOW_INT (increment , term_to_int (elem ), & elem_value )) {
590
546
SMP_UNLOCK (ets_table );
591
- return EtsOverlfow ;
547
+ return EtsOverflow ;
592
548
}
593
549
if (!term_is_invalid_term (threshold_term ) && !term_is_invalid_term (set_value_term )) {
594
550
avm_int_t threshold = term_to_int (threshold_term );
0 commit comments