aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--bootstrap/lib/stdlib/ebin/ets.beambin22544 -> 22808 bytes
-rw-r--r--erts/emulator/beam/bif.c126
-rw-r--r--erts/emulator/beam/bif.tab3
-rw-r--r--erts/emulator/beam/break.c2
-rw-r--r--erts/emulator/beam/erl_alloc.types1
-rw-r--r--erts/emulator/beam/erl_bif_info.c8
-rw-r--r--erts/emulator/beam/erl_db.c1953
-rw-r--r--erts/emulator/beam/erl_db.h46
-rw-r--r--erts/emulator/beam/erl_db_hash.c242
-rw-r--r--erts/emulator/beam/erl_db_hash.h12
-rw-r--r--erts/emulator/beam/erl_db_tree.c63
-rw-r--r--erts/emulator/beam/erl_db_util.h93
-rw-r--r--erts/emulator/beam/erl_init.c1
-rw-r--r--erts/emulator/beam/erl_lock_check.c6
-rw-r--r--erts/emulator/beam/erl_node_tables.c41
-rw-r--r--erts/emulator/beam/erl_process.c88
-rw-r--r--erts/emulator/beam/erl_process.h32
-rw-r--r--erts/emulator/beam/erl_rbtree.h66
-rw-r--r--erts/preloaded/ebin/erlang.beambin106104 -> 106220 bytes
-rw-r--r--erts/preloaded/src/erlang.erl8
-rw-r--r--lib/asn1/c_src/asn1_erl_nif.c38
-rw-r--r--lib/asn1/src/asn1ct.erl129
-rw-r--r--lib/asn1/src/asn1ct_check.erl140
-rw-r--r--lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl111
-rw-r--r--lib/asn1/src/asn1ct_constructed_per.erl40
-rw-r--r--lib/asn1/src/asn1ct_func.erl2
-rw-r--r--lib/asn1/src/asn1ct_gen.erl89
-rw-r--r--lib/asn1/src/asn1ct_gen_ber_bin_v2.erl308
-rw-r--r--lib/asn1/src/asn1ct_gen_per.erl64
-rw-r--r--lib/asn1/src/asn1ct_name.erl2
-rw-r--r--lib/asn1/src/asn1ct_parser2.erl2
-rw-r--r--lib/asn1/src/asn1ct_value.erl51
-rw-r--r--lib/asn1/src/asn1rtt_ber.erl33
-rw-r--r--lib/asn1/test/asn1_SUITE.erl1
-rw-r--r--lib/asn1/test/asn1_SUITE_data/HighTagNumbers.asn117
-rw-r--r--lib/compiler/test/Makefile1
-rw-r--r--lib/crypto/src/crypto.erl2
-rw-r--r--lib/eunit/src/eunit_surefire.erl1
-rw-r--r--lib/kernel/doc/src/inet.xml6
-rw-r--r--lib/observer/src/cdv_ets_cb.erl19
-rw-r--r--lib/observer/src/crashdump_viewer.erl6
-rw-r--r--lib/observer/src/crashdump_viewer.hrl1
-rw-r--r--lib/observer/src/observer_tv_wx.erl8
-rw-r--r--lib/ssh/doc/src/ssh.xml15
-rw-r--r--lib/ssh/src/ssh_options.erl11
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl4
-rw-r--r--lib/ssl/doc/src/ssl.xml13
-rw-r--r--lib/ssl/src/ssl.erl67
-rw-r--r--lib/ssl/src/ssl_connection.erl40
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl41
-rw-r--r--lib/ssl/test/ssl_packet_SUITE.erl10
-rw-r--r--lib/ssl/test/ssl_test_lib.erl12
-rw-r--r--lib/stdlib/doc/src/proplists.xml2
-rw-r--r--lib/stdlib/doc/src/unicode_usage.xml31
-rw-r--r--lib/stdlib/src/erl_lint.erl15
-rw-r--r--lib/stdlib/src/ets.erl18
-rw-r--r--lib/stdlib/src/otp_internal.erl176
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl29
-rw-r--r--lib/stdlib/test/ets_SUITE.erl136
-rw-r--r--system/doc/reference_manual/character_set.xml8
61 files changed, 2273 insertions, 2219 deletions
diff --git a/README.md b/README.md
index ccfce03a96..b3d1aaaad1 100644
--- a/README.md
+++ b/README.md
@@ -52,7 +52,7 @@ Please visit [bugs.erlang.org](https://bugs.erlang.org/issues/?jql=project%20%3D
### Security Disclosure
-We take security bugs in Erlang/OTP seriously. Please disclose the issues regarding security by sending an email to [email protected] and not by creating a public issue.
+We take security bugs in Erlang/OTP seriously. Please disclose the issues regarding security by sending an email to **erlang-security [at] erlang [dot] org** and not by creating a public issue.
## Contributing
diff --git a/bootstrap/lib/stdlib/ebin/ets.beam b/bootstrap/lib/stdlib/ebin/ets.beam
index e66749048d..ee769b5dec 100644
--- a/bootstrap/lib/stdlib/ebin/ets.beam
+++ b/bootstrap/lib/stdlib/ebin/ets.beam
Binary files differ
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 614bedab12..2d37f977c0 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -4254,6 +4254,132 @@ BIF_RETTYPE list_to_pid_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
}
+BIF_RETTYPE list_to_ref_1(BIF_ALIST_1)
+{
+ /*
+ * A valid reference is on the format
+ * "#Ref<N.X.Y.Z>" where N, X, Y, and Z are
+ * 32-bit integers (i.e., max 10 characters).
+ */
+ Eterm *hp;
+ Eterm res;
+ Uint32 refn[ERTS_MAX_REF_NUMBERS];
+ int n = 0;
+ Uint ints[1 + ERTS_MAX_REF_NUMBERS] = {0};
+ char* cp;
+ Sint i;
+ DistEntry *dep = NULL;
+ char buf[5 /* #Ref< */
+ + (1 + ERTS_MAX_REF_NUMBERS)*(10 + 1) /* N.X.Y.Z> */
+ + 1 /* \0 */];
+
+ /* walk down the list and create a C string */
+ if ((i = intlist_to_buf(BIF_ARG_1, buf, sizeof(buf)-1)) < 0)
+ goto bad;
+
+ buf[i] = '\0'; /* null terminal */
+
+ cp = &buf[0];
+ if (*cp++ != '#') goto bad;
+ if (*cp++ != 'R') goto bad;
+ if (*cp++ != 'e') goto bad;
+ if (*cp++ != 'f') goto bad;
+ if (*cp++ != '<') goto bad;
+
+ for (i = 0; i < sizeof(ints)/sizeof(Uint); i++) {
+ if (*cp < '0' || *cp > '9') goto bad;
+
+ while (*cp >= '0' && *cp <= '9') {
+ ints[i] = 10*ints[i] + (*cp - '0');
+ cp++;
+ }
+
+ n++;
+ if (ints[i] > ~((Uint32) 0)) goto bad;
+ if (*cp == '>') break;
+ if (*cp++ != '.') goto bad;
+ }
+
+ if (*cp++ != '>') goto bad;
+ if (*cp != '\0') goto bad;
+
+ if (n < 2) goto bad;
+
+ for (n = 0; i > 0; i--)
+ refn[n++] = (Uint32) ints[i];
+
+ ASSERT(n <= ERTS_MAX_REF_NUMBERS);
+
+ dep = erts_channel_no_to_dist_entry(ints[0]);
+
+ if (!dep)
+ goto bad;
+
+ if(dep == erts_this_dist_entry) {
+ ErtsMagicBinary *mb;
+ Uint32 sid;
+ if (refn[0] > MAX_REFERENCE) goto bad;
+ if (n != ERTS_REF_NUMBERS) goto bad;
+ sid = erts_get_ref_numbers_thr_id(refn);
+ if (sid > erts_no_schedulers) goto bad;
+ mb = erts_magic_ref_lookup_bin(refn);
+ if (mb) {
+ hp = HAlloc(BIF_P, ERTS_MAGIC_REF_THING_SIZE);
+ res = erts_mk_magic_ref(&hp, &BIF_P->off_heap,
+ (Binary *) mb);
+ }
+ else {
+ hp = HAlloc(BIF_P, ERTS_REF_THING_SIZE);
+ write_ref_thing(hp, refn[0], refn[1], refn[2]);
+ res = make_internal_ref(hp);
+ }
+ }
+ else {
+ ExternalThing *etp;
+ ErlNode *enp;
+ Uint hsz;
+ int j;
+
+ if (is_nil(dep->cid))
+ goto bad;
+
+ enp = erts_find_or_insert_node(dep->sysname, dep->creation);
+ ASSERT(enp != erts_this_node);
+
+ hsz = EXTERNAL_THING_HEAD_SIZE;
+#if defined(ARCH_64)
+ hsz += n/2 + 1;
+#else
+ hsz += n;
+#endif
+
+ etp = (ExternalThing *) HAlloc(BIF_P, hsz);
+ etp->header = make_external_ref_header(n/2);
+ etp->next = BIF_P->off_heap.first;
+ etp->node = enp;
+ i = 0;
+#if defined(ARCH_64)
+ etp->data.ui32[i] = n;
+#endif
+ for (j = 0; j < n; j++) {
+ etp->data.ui32[i] = refn[j];
+ i++;
+ }
+
+ BIF_P->off_heap.first = (struct erl_off_heap_header*) etp;
+ res = make_external_ref(etp);
+ }
+
+ erts_deref_dist_entry(dep);
+ BIF_RET(res);
+
+ bad:
+ if (dep)
+ erts_deref_dist_entry(dep);
+ BIF_ERROR(BIF_P, BADARG);
+}
+
+
/**********************************************************************/
BIF_RETTYPE group_leader_0(BIF_ALIST_0)
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index ce2cffa498..6f50297fc5 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -88,6 +88,7 @@ bif erlang:list_to_binary/1
bif erlang:list_to_float/1
bif erlang:list_to_integer/1
bif erlang:list_to_pid/1
+bif erlang:list_to_ref/1
bif erlang:list_to_tuple/1
bif erlang:loaded/0
bif erlang:localtime/0
@@ -324,7 +325,7 @@ bif erlang:match_spec_test/3
# Bifs in ets module.
#
-bif ets:all/0
+bif ets:internal_request_all/0
bif ets:new/2
bif ets:delete/1
bif ets:delete/2
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index 4dad5736c5..0b40d70cb7 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -512,6 +512,8 @@ do_break(void)
erts_free_read_env(mode);
#endif /* __WIN32__ */
+ ASSERT(erts_smp_thr_progress_is_blocking());
+
erts_printf("\n"
"BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded\n"
" (v)ersion (k)ill (D)b-tables (d)istribution\n");
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index 3d5de72ee7..32f84c8593 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -227,6 +227,7 @@ type DB_DMC_ERROR ETS ETS db_dmc_error
type DB_DMC_ERR_INFO ETS ETS db_dmc_error_info
type DB_TERM ETS ETS db_term
type DB_PROC_CLEANUP SHORT_LIVED ETS db_proc_cleanup_state
+type ETS_ALL_REQ SHORT_LIVED ETS ets_all_request
type INSTR_INFO LONG_LIVED SYSTEM instr_info
type LOGGER_DSBUF TEMPORARY SYSTEM logger_dsbuf
type TMP_DSBUF TEMPORARY SYSTEM tmp_dsbuf
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 06a73ffea5..025f99330a 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -3634,10 +3634,6 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
BIF_RET(TUPLE2(hp, make_small((Uint) words),
erts_ets_hash_sizeof_ext_segtab()));
}
- else if (ERTS_IS_ATOM_STR("DbTable_meta", BIF_ARG_1)) {
- /* Used by ets_SUITE (stdlib) */
- BIF_RET(erts_ets_get_meta_state(BIF_P));
- }
else if (ERTS_IS_ATOM_STR("check_io_debug", BIF_ARG_1)) {
/* Used by driver_SUITE (emulator) */
Uint sz, *szp;
@@ -4398,10 +4394,6 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
}
BIF_RET(am_ok);
}
- else if (ERTS_IS_ATOM_STR("DbTable_meta", BIF_ARG_1)) {
- /* Used by ets_SUITE (stdlib) */
- BIF_RET(erts_ets_restore_meta_state(BIF_P, BIF_ARG_2));
- }
else if (ERTS_IS_ATOM_STR("make", BIF_ARG_1)) {
if (ERTS_IS_ATOM_STR("magic_ref", BIF_ARG_2)) {
Binary *bin = erts_create_magic_binary(0, empty_magic_ref_destructor);
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 9f077dd407..2f188b5391 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -19,9 +19,7 @@
*/
/*
- * This file contains the bif interface functions and
- * the handling of the "meta tables" ie the tables of
- * db tables.
+ * This file contains the 'ets' bif interface functions.
*/
/*
@@ -43,6 +41,7 @@
#include "erl_db.h"
#include "bif.h"
#include "big.h"
+#include "erl_binary.h"
erts_smp_atomic_t erts_ets_misc_mem_size;
@@ -74,62 +73,226 @@ enum DbIterSafety {
#define DID_TRAP(P,Ret) (!is_value(Ret) && ((P)->freason == TRAP))
+/*
+ * "fixed_tabs": list of all fixed tables for a process
+ */
+#ifdef DEBUG
+static int fixed_tabs_find(DbFixation* first, DbFixation* fix);
+#endif
-/*
-** The main meta table, containing all ets tables.
-*/
-#ifdef ERTS_SMP
+static void fixed_tabs_insert(Process* p, DbFixation* fix)
+{
+ DbFixation* first = erts_psd_get(p, ERTS_PSD_ETS_FIXED_TABLES);
+
+ if (!first) {
+ fix->tabs.next = fix->tabs.prev = fix;
+ erts_psd_set(p, ERTS_PSD_ETS_FIXED_TABLES, fix);
+ }
+ else {
+ ASSERT(!fixed_tabs_find(first, fix));
+ fix->tabs.prev = first->tabs.prev;
+ fix->tabs.next = first;
+ fix->tabs.prev->tabs.next = fix;
+ first->tabs.prev = fix;
+ }
+}
+
+static void fixed_tabs_delete(Process *p, DbFixation* fix)
+{
+ if (fix->tabs.next == fix) {
+ DbFixation* old;
+ ASSERT(fix->tabs.prev == fix);
+ old = erts_psd_set(p, ERTS_PSD_ETS_FIXED_TABLES, NULL);
+ ASSERT(old == fix); (void)old;
+ }
+ else {
+ DbFixation *first = (DbFixation*) erts_psd_get(p, ERTS_PSD_ETS_FIXED_TABLES);
-#define ERTS_META_MAIN_TAB_LOCK_TAB_BITS 8
-#define ERTS_META_MAIN_TAB_LOCK_TAB_SIZE (1 << ERTS_META_MAIN_TAB_LOCK_TAB_BITS)
-#define ERTS_META_MAIN_TAB_LOCK_TAB_MASK (ERTS_META_MAIN_TAB_LOCK_TAB_SIZE - 1)
+ ASSERT(fixed_tabs_find(first, fix));
+ fix->tabs.prev->tabs.next = fix->tabs.next;
+ fix->tabs.next->tabs.prev = fix->tabs.prev;
-typedef union {
- erts_smp_rwmtx_t rwmtx;
- byte cache_line_align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(
- sizeof(erts_smp_rwmtx_t))];
-} erts_meta_main_tab_lock_t;
+ if (fix == first)
+ erts_psd_set(p, ERTS_PSD_ETS_FIXED_TABLES, fix->tabs.next);
+ }
+}
-static erts_meta_main_tab_lock_t *meta_main_tab_locks;
+#ifdef DEBUG
+static int fixed_tabs_find(DbFixation* first, DbFixation* fix)
+{
+ DbFixation* p;
+ if (!first) {
+ first = (DbFixation*) erts_psd_get(fix->procs.p, ERTS_PSD_ETS_FIXED_TABLES);
+ }
+ p = first;
+ do {
+ if (p == fix)
+ return 1;
+ ASSERT(p->procs.p == fix->procs.p);
+ ASSERT(p->tabs.next->tabs.prev == p);
+ p = p->tabs.next;
+ } while (p != first);
+ return 0;
+}
#endif
-static struct {
- union {
- DbTable *tb; /* Only directly readable if slot is ALIVE */
- UWord next_free; /* (index<<2)|1 if slot is FREE */
- }u;
-} *meta_main_tab;
-/* A slot in meta_main_tab can have three states:
- * FREE : Free to use for new table. Part of linked free-list.
- * ALIVE: Contains a table
- * DEAD : Contains a table that is being removed.
+
+/*
+ * fixing_procs: tree of all processes fixating a table
*/
-#define IS_SLOT_FREE(i) (meta_main_tab[(i)].u.next_free & 1)
-#define IS_SLOT_DEAD(i) (meta_main_tab[(i)].u.next_free & 2)
-#define IS_SLOT_ALIVE(i) (!(meta_main_tab[(i)].u.next_free & (1|2)))
-#define GET_NEXT_FREE_SLOT(i) (meta_main_tab[(i)].u.next_free >> 2)
-#define SET_NEXT_FREE_SLOT(i,next) (meta_main_tab[(i)].u.next_free = ((next)<<2)|1)
-#define MARK_SLOT_DEAD(i) (meta_main_tab[(i)].u.next_free |= 2)
-#define GET_ANY_SLOT_TAB(i) ((DbTable*)(meta_main_tab[(i)].u.next_free & ~(1|2))) /* dead or alive */
+#define ERTS_RBT_PREFIX fixing_procs
+#define ERTS_RBT_T DbFixation
+#define ERTS_RBT_KEY_T Process*
+#define ERTS_RBT_FLAGS_T int
+#define ERTS_RBT_INIT_EMPTY_TNODE(T) \
+ do { \
+ (T)->procs.parent = NULL; \
+ (T)->procs.right = NULL; \
+ (T)->procs.left = NULL; \
+ } while (0)
+#define ERTS_RBT_IS_RED(T) ((T)->procs.is_red)
+#define ERTS_RBT_SET_RED(T) ((T)->procs.is_red = 1)
+#define ERTS_RBT_IS_BLACK(T) (!(T)->procs.is_red)
+#define ERTS_RBT_SET_BLACK(T) ((T)->procs.is_red = 0)
+#define ERTS_RBT_GET_FLAGS(T) ((T)->procs.is_red)
+#define ERTS_RBT_SET_FLAGS(T, F) ((T)->procs.is_red = (F))
+#define ERTS_RBT_GET_PARENT(T) ((T)->procs.parent)
+#define ERTS_RBT_SET_PARENT(T, P) ((T)->procs.parent = (P))
+#define ERTS_RBT_GET_RIGHT(T) ((T)->procs.right)
+#define ERTS_RBT_SET_RIGHT(T, R) ((T)->procs.right = (R))
+#define ERTS_RBT_GET_LEFT(T) ((T)->procs.left)
+#define ERTS_RBT_SET_LEFT(T, L) ((T)->procs.left = (L))
+#define ERTS_RBT_GET_KEY(T) ((T)->procs.p)
+#define ERTS_RBT_IS_LT(KX, KY) ((KX) < (KY))
+#define ERTS_RBT_IS_EQ(KX, KY) ((KX) == (KY))
+
+#define ERTS_RBT_WANT_INSERT
+#define ERTS_RBT_WANT_LOOKUP
+#define ERTS_RBT_WANT_DELETE
+#define ERTS_RBT_WANT_FOREACH
+#define ERTS_RBT_WANT_FOREACH_DESTROY
+#ifdef DEBUG
+# define ERTS_RBT_WANT_LOOKUP
+#endif
+#define ERTS_RBT_UNDEF
-static ERTS_INLINE erts_smp_rwmtx_t *
-get_meta_main_tab_lock(unsigned slot)
+#include "erl_rbtree.h"
+
+#ifdef HARDDEBUG
+# error Do something useful with CHECK_TABLES maybe
+#else
+# define CHECK_TABLES()
+#endif
+
+
+static void
+send_ets_transfer_message(Process *c_p, Process *proc,
+ ErtsProcLocks *locks,
+ DbTable *tb, Eterm heir_data);
+static void schedule_free_dbtable(DbTable* tb);
+static void delete_sched_table(Process *c_p, DbTable *tb);
+
+static void table_dec_refc(DbTable *tb, erts_aint_t min_val)
+{
+ if (erts_smp_refc_dectest(&tb->common.refc, min_val) == 0)
+ schedule_free_dbtable(tb);
+}
+
+static int
+db_table_tid_destructor(Binary *unused)
+{
+ return 1;
+}
+
+static ERTS_INLINE void
+make_btid(DbTable *tb)
+{
+ Binary *btid = erts_create_magic_indirection(db_table_tid_destructor);
+ erts_smp_atomic_t *tbref = erts_smp_binary_to_magic_indirection(btid);
+ erts_smp_atomic_init_nob(tbref, (erts_aint_t) tb);
+ tb->common.btid = btid;
+ /*
+ * Table and magic indirection refer eachother,
+ * and table is refered once by being alive...
+ */
+ erts_smp_refc_init(&tb->common.refc, 2);
+ erts_refc_inc(&btid->refc, 1);
+}
+
+static ERTS_INLINE DbTable* btid2tab(Binary* btid)
+{
+ erts_smp_atomic_t *tbref = erts_smp_binary_to_magic_indirection(btid);
+ return (DbTable *) erts_smp_atomic_read_nob(tbref);
+}
+
+static DbTable *
+tid2tab(Eterm tid)
+{
+ DbTable *tb;
+ Binary *btid;
+ erts_smp_atomic_t *tbref;
+ if (!is_internal_magic_ref(tid))
+ return NULL;
+
+ btid = erts_magic_ref2bin(tid);
+ if (ERTS_MAGIC_BIN_DESTRUCTOR(btid) != db_table_tid_destructor)
+ return NULL;
+
+ tbref = erts_smp_binary_to_magic_indirection(btid);
+ tb = (DbTable *) erts_smp_atomic_read_nob(tbref);
+
+ ASSERT(!tb || tb->common.btid == btid);
+
+ return tb;
+}
+
+static ERTS_INLINE int
+is_table_alive(DbTable *tb)
+{
+ erts_smp_atomic_t *tbref;
+ DbTable *rtb;
+
+ tbref = erts_smp_binary_to_magic_indirection(tb->common.btid);
+ rtb = (DbTable *) erts_smp_atomic_read_nob(tbref);
+
+ ASSERT(!rtb || rtb == tb);
+
+ return !!rtb;
+}
+
+static ERTS_INLINE int
+is_table_named(DbTable *tb)
{
#ifdef ERTS_SMP
- return &meta_main_tab_locks[slot & ERTS_META_MAIN_TAB_LOCK_TAB_MASK].rwmtx;
+ return tb->common.type & DB_NAMED_TABLE;
#else
- return NULL;
+ return tb->common.status & DB_NAMED_TABLE;
#endif
}
-static erts_smp_spinlock_t meta_main_tab_main_lock;
-static Uint meta_main_tab_first_free; /* Index of first free slot */
-static int meta_main_tab_cnt; /* Number of active tables */
-static int meta_main_tab_top; /* Highest ever used slot + 1 */
-static Uint meta_main_tab_slot_mask; /* The slot index part of an unnamed table id */
-static Uint meta_main_tab_seq_incr;
-static Uint meta_main_tab_seq_cnt = 0; /* To give unique(-ish) table identifiers */
+
+static ERTS_INLINE void
+tid_clear(Process *c_p, DbTable *tb)
+{
+ DbTable *rtb;
+ Binary *btid = tb->common.btid;
+ erts_smp_atomic_t *tbref = erts_smp_binary_to_magic_indirection(btid);
+ rtb = (DbTable *) erts_smp_atomic_xchg_nob(tbref, (erts_aint_t) NULL);
+ ASSERT(!rtb || tb == rtb);
+ if (rtb) {
+ table_dec_refc(tb, 1);
+ delete_sched_table(c_p, tb);
+ }
+}
+
+static ERTS_INLINE Eterm
+make_tid(Process *c_p, DbTable *tb)
+{
+ Eterm *hp = HAlloc(c_p, ERTS_MAGIC_REF_THING_SIZE);
+ return erts_mk_magic_ref(&hp, &c_p->off_heap, tb->common.btid);
+}
+
/*
** The meta hash table of all NAMED ets tables
@@ -181,8 +344,6 @@ int user_requested_db_max_tabs;
int erts_ets_realloc_always_moves;
int erts_ets_always_compress;
static int db_max_tabs;
-static DbTable *meta_pid_to_tab; /* Pid mapped to owned tables */
-static DbTable *meta_pid_to_fixed_tab; /* Pid mapped to fixed tables */
static Eterm ms_delete_all;
static Eterm ms_delete_all_buff[8]; /* To compare with for deletion
of all objects */
@@ -195,12 +356,9 @@ static void fix_table_locked(Process* p, DbTable* tb);
static void unfix_table_locked(Process* p, DbTable* tb, db_lock_kind_t* kind);
static void set_heir(Process* me, DbTable* tb, Eterm heir, UWord heir_data);
static void free_heir_data(DbTable*);
-static void free_fixations_locked(DbTable *tb);
+static SWord free_fixations_locked(Process* p, DbTable *tb);
-static int free_table_cont(Process *p,
- DbTable *tb,
- int first,
- int clean_meta_tab);
+static SWord free_table_continue(Process *p, DbTable *tb, SWord reds);
static void print_table(fmtfn_t to, void *to_arg, int show, DbTable* tb);
static BIF_RETTYPE ets_select_delete_1(BIF_ALIST_1);
static BIF_RETTYPE ets_select_count_1(BIF_ALIST_1);
@@ -235,23 +393,16 @@ free_dbtable(void *vtb)
erts_smp_atomic_read_nob(&tb->common.memory_size)-sizeof(DbTable),
tb->common.fixations);
}
- erts_fprintf(stderr, "ets: free_dbtable(%T) deleted!!!\r\n",
- tb->common.id);
-
- erts_fprintf(stderr, "ets: free_dbtable: meta_pid_to_tab common.memory_size = %ld\n",
- erts_smp_atomic_read_nob(&meta_pid_to_tab->common.memory_size));
- print_table(ERTS_PRINT_STDOUT, NULL, 1, meta_pid_to_tab);
-
-
- erts_fprintf(stderr, "ets: free_dbtable: meta_pid_to_fixed_tab common.memory_size = %ld\n",
- erts_smp_atomic_read_nob(&meta_pid_to_fixed_tab->common.memory_size));
- print_table(ERTS_PRINT_STDOUT, NULL, 1, meta_pid_to_fixed_tab);
#endif
#ifdef ERTS_SMP
erts_smp_rwmtx_destroy(&tb->common.rwlock);
erts_smp_mtx_destroy(&tb->common.fixlock);
#endif
ASSERT(is_immed(tb->common.heir_data));
+
+ if (tb->common.btid && erts_refc_dectest(&tb->common.btid->refc, 0) == 0)
+ erts_bin_free(tb->common.btid);
+
erts_db_free(ERTS_ALC_T_DB_TABLE, tb, (void *) tb, sizeof(DbTable));
}
@@ -266,13 +417,163 @@ static void schedule_free_dbtable(DbTable* tb)
* Caller is *not* allowed to access the specialized part
* (hash or tree) of *tb after this function has returned.
*/
- ASSERT(erts_smp_refc_read(&tb->common.ref, 0) == 0);
+ ASSERT(erts_smp_refc_read(&tb->common.refc, 0) == 0);
+ ASSERT(erts_smp_refc_read(&tb->common.fix_count, 0) == 0);
erts_schedule_thr_prgr_later_cleanup_op(free_dbtable,
(void *) tb,
&tb->release.data,
sizeof(DbTable));
}
+static ERTS_INLINE void
+save_sched_table(Process *c_p, DbTable *tb)
+{
+ ErtsSchedulerData *esdp = erts_proc_sched_data(c_p);
+ DbTable *first;
+
+ ASSERT(esdp);
+ esdp->ets_tables.count++;
+ erts_smp_refc_inc(&tb->common.refc, 1);
+
+ first = esdp->ets_tables.clist;
+ if (!first) {
+ tb->common.all.next = tb->common.all.prev = tb;
+ esdp->ets_tables.clist = tb;
+ }
+ else {
+ tb->common.all.prev = first->common.all.prev;
+ tb->common.all.next = first;
+ tb->common.all.prev->common.all.next = tb;
+ first->common.all.prev = tb;
+ }
+}
+
+static ERTS_INLINE void
+remove_sched_table(ErtsSchedulerData *esdp, DbTable *tb)
+{
+ ErtsEtsAllYieldData *eaydp;
+ ASSERT(esdp);
+ ASSERT(erts_get_ref_numbers_thr_id(ERTS_MAGIC_BIN_REFN(tb->common.btid))
+ == (Uint32) esdp->no);
+
+ ASSERT(esdp->ets_tables.count > 0);
+ esdp->ets_tables.count--;
+
+ eaydp = ERTS_SCHED_AUX_YIELD_DATA(esdp, ets_all);
+ if (eaydp->ongoing) {
+ /* ets:all() op process list from last to first... */
+ if (eaydp->tab == tb) {
+ if (eaydp->tab == esdp->ets_tables.clist)
+ eaydp->tab = NULL;
+ else
+ eaydp->tab = tb->common.all.prev;
+ }
+ }
+
+ if (tb->common.all.next == tb) {
+ ASSERT(tb->common.all.prev == tb);
+ ASSERT(esdp->ets_tables.clist == tb);
+ esdp->ets_tables.clist = NULL;
+ }
+ else {
+#ifdef DEBUG
+ DbTable *tmp = esdp->ets_tables.clist;
+ do {
+ if (tmp == tb) break;
+ tmp = tmp->common.all.next;
+ } while (tmp != esdp->ets_tables.clist);
+ ASSERT(tmp == tb);
+#endif
+ tb->common.all.prev->common.all.next = tb->common.all.next;
+ tb->common.all.next->common.all.prev = tb->common.all.prev;
+
+ if (esdp->ets_tables.clist == tb)
+ esdp->ets_tables.clist = tb->common.all.next;
+
+ }
+
+ table_dec_refc(tb, 0);
+}
+
+static void
+scheduled_remove_sched_table(void *vtb)
+{
+ remove_sched_table(erts_get_scheduler_data(), (DbTable *) vtb);
+}
+
+static void
+delete_sched_table(Process *c_p, DbTable *tb)
+{
+ ErtsSchedulerData *esdp = erts_proc_sched_data(c_p);
+ Uint32 sid;
+
+ ASSERT(esdp);
+
+ ASSERT(tb->common.btid);
+ sid = erts_get_ref_numbers_thr_id(ERTS_MAGIC_BIN_REFN(tb->common.btid));
+ ASSERT(1 <= sid && sid <= erts_no_schedulers);
+ if (sid == (Uint32) esdp->no)
+ remove_sched_table(esdp, tb);
+ else
+ erts_schedule_misc_aux_work((int) sid, scheduled_remove_sched_table, tb);
+}
+
+static ERTS_INLINE void
+save_owned_table(Process *c_p, DbTable *tb)
+{
+ DbTable *first;
+
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS);
+
+ first = (DbTable*) erts_psd_get(c_p, ERTS_PSD_ETS_OWNED_TABLES);
+
+ erts_smp_refc_inc(&tb->common.refc, 1);
+
+ if (!first) {
+ tb->common.owned.next = tb->common.owned.prev = tb;
+ erts_psd_set(c_p, ERTS_PSD_ETS_OWNED_TABLES, tb);
+ }
+ else {
+ tb->common.owned.prev = first->common.owned.prev;
+ tb->common.owned.next = first;
+ tb->common.owned.prev->common.owned.next = tb;
+ first->common.owned.prev = tb;
+ }
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS);
+}
+
+static ERTS_INLINE void
+delete_owned_table(Process *p, DbTable *tb)
+{
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ if (tb->common.owned.next == tb) {
+ DbTable* old;
+ ASSERT(tb->common.owned.prev == tb);
+ old = erts_psd_set(p, ERTS_PSD_ETS_OWNED_TABLES, NULL);
+ ASSERT(old == tb); (void)old;
+ }
+ else {
+ DbTable *first = (DbTable*) erts_psd_get(p, ERTS_PSD_ETS_OWNED_TABLES);
+#ifdef DEBUG
+ DbTable *tmp = first;
+ do {
+ if (tmp == tb) break;
+ tmp = tmp->common.owned.next;
+ } while (tmp != first);
+ ASSERT(tmp == tb);
+#endif
+ tb->common.owned.prev->common.owned.next = tb->common.owned.next;
+ tb->common.owned.next->common.owned.prev = tb->common.owned.prev;
+
+ if (tb == first)
+ erts_psd_set(p, ERTS_PSD_ETS_OWNED_TABLES, tb->common.owned.next);
+ }
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+
+ table_dec_refc(tb, 1);
+}
+
+
static ERTS_INLINE void db_init_lock(DbTable* tb, int use_frequent_read_lock,
char *rwname, char* fixname)
{
@@ -294,7 +595,6 @@ static ERTS_INLINE void db_init_lock(DbTable* tb, int use_frequent_read_lock,
static ERTS_INLINE void db_lock(DbTable* tb, db_lock_kind_t kind)
{
#ifdef ERTS_SMP
- ASSERT(tb != meta_pid_to_tab && tb != meta_pid_to_fixed_tab);
if (tb->common.type & DB_FINE_LOCKED) {
if (kind == LCK_WRITE) {
erts_smp_rwmtx_rwlock(&tb->common.rwlock);
@@ -327,8 +627,6 @@ static ERTS_INLINE void db_unlock(DbTable* tb, db_lock_kind_t kind)
* to follow the tb pointer!
*/
#ifdef ERTS_SMP
- ASSERT(tb != meta_pid_to_tab && tb != meta_pid_to_fixed_tab);
-
if (tb->common.type & DB_FINE_LOCKED) {
if (kind == LCK_WRITE) {
ASSERT(tb->common.is_thread_safe);
@@ -354,20 +652,6 @@ static ERTS_INLINE void db_unlock(DbTable* tb, db_lock_kind_t kind)
#endif
}
-
-static ERTS_INLINE void db_meta_lock(DbTable* tb, db_lock_kind_t kind)
-{
- ASSERT(tb == meta_pid_to_tab || tb == meta_pid_to_fixed_tab);
- ASSERT(kind != LCK_WRITE);
- /* As long as we only lock for READ we don't have to lock at all. */
-}
-
-static ERTS_INLINE void db_meta_unlock(DbTable* tb, db_lock_kind_t kind)
-{
- ASSERT(tb == meta_pid_to_tab || tb == meta_pid_to_fixed_tab);
- ASSERT(kind != LCK_WRITE);
-}
-
static ERTS_INLINE
DbTable* db_get_table_aux(Process *p,
Eterm id,
@@ -375,7 +659,7 @@ DbTable* db_get_table_aux(Process *p,
db_lock_kind_t kind,
int meta_already_locked)
{
- DbTable *tb = NULL;
+ DbTable *tb;
erts_smp_rwmtx_t *mtl = NULL;
/*
@@ -385,23 +669,7 @@ DbTable* db_get_table_aux(Process *p,
*/
ASSERT(erts_get_scheduler_data());
- if (is_small(id)) {
- Uint slot = unsigned_val(id) & meta_main_tab_slot_mask;
- if (!meta_already_locked) {
- mtl = get_meta_main_tab_lock(slot);
- erts_smp_rwmtx_rlock(mtl);
- }
-#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
- else {
- erts_smp_rwmtx_t *test_mtl = get_meta_main_tab_lock(slot);
- ERTS_SMP_LC_ASSERT(erts_lc_rwmtx_is_rlocked(test_mtl)
- || erts_lc_rwmtx_is_rwlocked(test_mtl));
- }
-#endif
- if (slot < db_max_tabs && IS_SLOT_ALIVE(slot))
- tb = meta_main_tab[slot].u.tb;
- }
- else if (is_atom(id)) {
+ if (is_atom(id)) {
struct meta_name_tab_entry* bucket = meta_name_tab_bucket(id,&mtl);
if (!meta_already_locked)
erts_smp_rwmtx_rlock(mtl);
@@ -410,7 +678,7 @@ DbTable* db_get_table_aux(Process *p,
|| erts_lc_rwmtx_is_rwlocked(mtl));
mtl = NULL;
}
-
+ tb = NULL;
if (bucket->pu.tb != NULL) {
if (is_atom(bucket->u.name_atom)) { /* single */
if (bucket->u.name_atom == id)
@@ -428,11 +696,13 @@ DbTable* db_get_table_aux(Process *p,
}
}
}
+ else
+ tb = tid2tab(id);
+
if (tb) {
db_lock(tb, kind);
- if (tb->common.id != id
- || ((tb->common.status & what) == 0
- && p->common.id != tb->common.owner)) {
+ if ((tb->common.status & what) == 0
+ && p->common.id != tb->common.owner) {
db_unlock(tb, kind);
tb = NULL;
}
@@ -451,18 +721,6 @@ DbTable* db_get_table(Process *p,
return db_get_table_aux(p, id, what, kind, 0);
}
-/* Requires meta_main_tab_locks[slot] locked.
-*/
-static ERTS_INLINE void free_slot(int slot)
-{
- ASSERT(!IS_SLOT_FREE(slot));
- erts_smp_spin_lock(&meta_main_tab_main_lock);
- SET_NEXT_FREE_SLOT(slot,meta_main_tab_first_free);
- meta_main_tab_first_free = slot;
- meta_main_tab_cnt--;
- erts_smp_spin_unlock(&meta_main_tab_main_lock);
-}
-
static int insert_named_tab(Eterm name_atom, DbTable* tb, int have_lock)
{
int ret = 0;
@@ -527,9 +785,10 @@ static int remove_named_tab(DbTable *tb, int have_lock)
{
int ret = 0;
erts_smp_rwmtx_t* rwlock;
- Eterm name_atom = tb->common.id;
+ Eterm name_atom = tb->common.the_name;
struct meta_name_tab_entry* bucket = meta_name_tab_bucket(name_atom,
&rwlock);
+ ASSERT(is_table_named(tb));
#ifdef ERTS_SMP
if (!have_lock && erts_smp_rwmtx_tryrwlock(rwlock) == EBUSY) {
db_unlock(tb, LCK_WRITE);
@@ -600,11 +859,11 @@ done:
*/
static ERTS_INLINE void local_fix_table(DbTable* tb)
{
- erts_smp_refc_inc(&tb->common.ref, 1);
+ erts_smp_refc_inc(&tb->common.fix_count, 1);
}
static ERTS_INLINE void local_unfix_table(DbTable* tb)
{
- if (erts_smp_refc_dectest(&tb->common.ref, 0) == 0) {
+ if (erts_smp_refc_dectest(&tb->common.fix_count, 0) == 0) {
ASSERT(IS_HASH_TABLE(tb->common.status));
db_unfix_table_hash(&(tb->hash));
}
@@ -1244,6 +1503,7 @@ BIF_RETTYPE ets_rename_2(BIF_ALIST_2)
{
DbTable* tb;
Eterm ret;
+ Eterm old_name;
erts_smp_rwmtx_t *lck1, *lck2;
#ifdef HARDDEBUG
@@ -1260,12 +1520,10 @@ BIF_RETTYPE ets_rename_2(BIF_ALIST_2)
(void) meta_name_tab_bucket(BIF_ARG_2, &lck1);
- if (is_small(BIF_ARG_1)) {
- Uint slot = unsigned_val(BIF_ARG_1) & meta_main_tab_slot_mask;
- lck2 = get_meta_main_tab_lock(slot);
- }
- else if (is_atom(BIF_ARG_1)) {
- (void) meta_name_tab_bucket(BIF_ARG_1, &lck2);
+ if (is_atom(BIF_ARG_1)) {
+ old_name = BIF_ARG_1;
+ named_tab:
+ (void) meta_name_tab_bucket(old_name, &lck2);
if (lck1 == lck2)
lck2 = NULL;
else if (lck1 > lck2) {
@@ -1275,7 +1533,16 @@ BIF_RETTYPE ets_rename_2(BIF_ALIST_2)
}
}
else {
- BIF_ERROR(BIF_P, BADARG);
+ tb = tid2tab(BIF_ARG_1);
+ if (!tb)
+ BIF_ERROR(BIF_P, BADARG);
+ else {
+ if (is_table_named(tb)) {
+ old_name = tb->common.the_name;
+ goto named_tab;
+ }
+ lck2 = NULL;
+ }
}
erts_smp_rwmtx_rwlock(lck1);
@@ -1286,21 +1553,19 @@ BIF_RETTYPE ets_rename_2(BIF_ALIST_2)
if (!tb)
goto badarg;
- if (is_not_atom(tb->common.id)) { /* Not a named table */
- tb->common.the_name = BIF_ARG_2;
- goto done;
- }
-
- if (!insert_named_tab(BIF_ARG_2, tb, 1))
- goto badarg;
+ if (is_table_named(tb)) {
+ if (!insert_named_tab(BIF_ARG_2, tb, 1))
+ goto badarg;
- if (!remove_named_tab(tb, 1))
- erts_exit(ERTS_ERROR_EXIT,"Could not find named tab %s", tb->common.id);
-
- tb->common.id = tb->common.the_name = BIF_ARG_2;
+ if (!remove_named_tab(tb, 1))
+ erts_exit(ERTS_ERROR_EXIT,"Could not find named tab %s", tb->common.the_name);
+ ret = BIF_ARG_2;
+ }
+ else { /* Not a named table */
+ ret = BIF_ARG_1;
+ }
+ tb->common.the_name = BIF_ARG_2;
- done:
- ret = tb->common.id;
db_unlock(tb, LCK_WRITE);
erts_smp_rwmtx_rwunlock(lck1);
if (lck2)
@@ -1324,7 +1589,6 @@ BIF_RETTYPE ets_rename_2(BIF_ALIST_2)
BIF_RETTYPE ets_new_2(BIF_ALIST_2)
{
DbTable* tb = NULL;
- int slot;
Eterm list;
Eterm val;
Eterm ret;
@@ -1339,9 +1603,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
#ifdef DEBUG
int cret;
#endif
- DeclareTmpHeap(meta_tuple,3,BIF_P);
DbTableMethod* meth;
- erts_smp_rwmtx_t *mmtl;
if (is_not_atom(BIF_ARG_1)) {
BIF_ERROR(BIF_P, BADARG);
@@ -1350,7 +1612,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
BIF_ERROR(BIF_P, BADARG);
}
- status = DB_NORMAL | DB_SET | DB_PROTECTED;
+ status = DB_SET | DB_PROTECTED;
keypos = 1;
is_named = 0;
#ifdef ERTS_SMP
@@ -1433,6 +1695,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
}
else if (val == am_named_table) {
is_named = 1;
+ status |= DB_NAMED_TABLE;
}
else if (val == am_compressed) {
is_compressed = 1;
@@ -1487,7 +1750,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
tb->common.type = status & ERTS_ETS_TABLE_TYPES;
/* Note, 'type' is *read only* from now on... */
#endif
- erts_smp_refc_init(&tb->common.ref, 0);
+ erts_smp_refc_init(&tb->common.fix_count, 0);
db_init_lock(tb, status & (DB_FINE_LOCKED|DB_FREQ_READ),
"db_tab", "db_tab_fix");
tb->common.keypos = keypos;
@@ -1496,7 +1759,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
erts_smp_atomic_init_nob(&tb->common.nitems, 0);
- tb->common.fixations = NULL;
+ tb->common.fixing_procs = NULL;
tb->common.compress = is_compressed;
#ifdef DEBUG
@@ -1505,87 +1768,36 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
meth->db_create(BIF_P, tb);
ASSERT(cret == DB_ERROR_NONE);
- erts_smp_spin_lock(&meta_main_tab_main_lock);
+ make_btid(tb);
- if (meta_main_tab_cnt >= db_max_tabs) {
- erts_smp_spin_unlock(&meta_main_tab_main_lock);
- erts_send_error_to_logger_str(BIF_P->group_leader,
- "** Too many db tables **\n");
- free_heir_data(tb);
- tb->common.meth->db_free_table(tb);
- free_dbtable((void *) tb);
- BIF_ERROR(BIF_P, SYSTEM_LIMIT);
- }
-
- slot = meta_main_tab_first_free;
- ASSERT(slot>=0 && slot<db_max_tabs);
- meta_main_tab_first_free = GET_NEXT_FREE_SLOT(slot);
- meta_main_tab_cnt++;
- if (slot >= meta_main_tab_top) {
- ASSERT(slot == meta_main_tab_top);
- meta_main_tab_top = slot + 1;
- }
-
- if (is_named) {
- ret = BIF_ARG_1;
- }
- else {
- ret = make_small(slot | meta_main_tab_seq_cnt);
- meta_main_tab_seq_cnt += meta_main_tab_seq_incr;
- ASSERT((unsigned_val(ret) & meta_main_tab_slot_mask) == slot);
- }
- erts_smp_spin_unlock(&meta_main_tab_main_lock);
-
- tb->common.id = ret;
- tb->common.slot = slot; /* store slot for erase */
+ if (is_named)
+ ret = BIF_ARG_1;
+ else
+ ret = make_tid(BIF_P, tb);
- mmtl = get_meta_main_tab_lock(slot);
- erts_smp_rwmtx_rwlock(mmtl);
- meta_main_tab[slot].u.tb = tb;
- ASSERT(IS_SLOT_ALIVE(slot));
- erts_smp_rwmtx_rwunlock(mmtl);
+ save_sched_table(BIF_P, tb);
if (is_named && !insert_named_tab(BIF_ARG_1, tb, 0)) {
- mmtl = get_meta_main_tab_lock(slot);
- erts_smp_rwmtx_rwlock(mmtl);
- free_slot(slot);
- erts_smp_rwmtx_rwunlock(mmtl);
+ tid_clear(BIF_P, tb);
db_lock(tb,LCK_WRITE);
free_heir_data(tb);
tb->common.meth->db_free_table(tb);
- schedule_free_dbtable(tb);
db_unlock(tb,LCK_WRITE);
+ table_dec_refc(tb, 0);
BIF_ERROR(BIF_P, BADARG);
}
BIF_P->flags |= F_USING_DB; /* So we can remove tb if p dies */
+ save_owned_table(BIF_P, tb);
#ifdef HARDDEBUG
erts_fprintf(stderr,
"ets:new(%T,%T)=%T; Process: %T, initial: %T:%T/%bpu\n",
BIF_ARG_1, BIF_ARG_2, ret, BIF_P->common.id,
BIF_P->u.initial[0], BIF_P->u.initial[1], BIF_P->u.initial[2]);
- erts_fprintf(stderr, "ets: new: meta_pid_to_tab common.memory_size = %ld\n",
- erts_smp_atomic_read_nob(&meta_pid_to_tab->common.memory_size));
- erts_fprintf(stderr, "ets: new: meta_pid_to_fixed_tab common.memory_size = %ld\n",
- erts_smp_atomic_read_nob(&meta_pid_to_fixed_tab->common.memory_size));
#endif
- UseTmpHeap(3,BIF_P);
-
- db_meta_lock(meta_pid_to_tab, LCK_WRITE_REC);
- if (db_put_hash(meta_pid_to_tab,
- TUPLE2(meta_tuple,
- BIF_P->common.id,
- make_small(slot)),
- 0) != DB_ERROR_NONE) {
- erts_exit(ERTS_ERROR_EXIT,"Could not update ets metadata.");
- }
- db_meta_unlock(meta_pid_to_tab, LCK_WRITE_REC);
-
- UnUseTmpHeap(3,BIF_P);
-
BIF_RET(ret);
}
@@ -1690,9 +1902,9 @@ BIF_RETTYPE ets_lookup_element_3(BIF_ALIST_3)
*/
BIF_RETTYPE ets_delete_1(BIF_ALIST_1)
{
- int trap;
+ SWord initial_reds = ERTS_BIF_REDS_LEFT(BIF_P);
+ SWord reds = initial_reds;
DbTable* tb;
- erts_smp_rwmtx_t *mmtl;
#ifdef HARDDEBUG
erts_fprintf(stderr,
@@ -1715,7 +1927,6 @@ BIF_RETTYPE ets_delete_1(BIF_ALIST_1)
tb->common.status |= DB_DELETE;
if (tb->common.owner != BIF_P->common.id) {
- DeclareTmpHeap(meta_tuple,3,BIF_P);
/*
* The table is being deleted by a process other than its owner.
@@ -1723,50 +1934,33 @@ BIF_RETTYPE ets_delete_1(BIF_ALIST_1)
* current process will be killed (e.g. by an EXIT signal), we will
* now transfer the ownership to the current process.
*/
- UseTmpHeap(3,BIF_P);
- db_meta_lock(meta_pid_to_tab, LCK_WRITE_REC);
- db_erase_bag_exact2(meta_pid_to_tab, tb->common.owner,
- make_small(tb->common.slot));
-
- BIF_P->flags |= F_USING_DB;
- tb->common.owner = BIF_P->common.id;
-
- db_put_hash(meta_pid_to_tab,
- TUPLE2(meta_tuple,
- BIF_P->common.id,
- make_small(tb->common.slot)),
- 0);
- db_meta_unlock(meta_pid_to_tab, LCK_WRITE_REC);
- UnUseTmpHeap(3,BIF_P);
- }
- mmtl = get_meta_main_tab_lock(tb->common.slot);
-#ifdef ERTS_SMP
- if (erts_smp_rwmtx_tryrwlock(mmtl) == EBUSY) {
- /*
- * We keep our increased refc over this op in order to
- * prevent the table from disapearing.
- */
- db_unlock(tb, LCK_WRITE);
- erts_smp_rwmtx_rwlock(mmtl);
- db_lock(tb, LCK_WRITE);
+ Process *rp = erts_proc_lookup_raw(tb->common.owner);
+ /*
+ * Process 'rp' might be exiting, but our table lock prevents it
+ * from terminating as it cannot complete erts_db_process_exiting().
+ */
+ ASSERT(!(ERTS_PSFLG_FREE & erts_smp_atomic32_read_nob(&rp->state)));
+
+ delete_owned_table(rp, tb);
+ BIF_P->flags |= F_USING_DB;
+ tb->common.owner = BIF_P->common.id;
+ save_owned_table(BIF_P, tb);
}
-#endif
- /* We must keep the slot, to be found by db_proc_dead() if process dies */
- MARK_SLOT_DEAD(tb->common.slot);
- erts_smp_rwmtx_rwunlock(mmtl);
- if (is_atom(tb->common.id))
+
+ tid_clear(BIF_P, tb);
+
+ if (is_table_named(tb))
remove_named_tab(tb, 0);
/* disable inheritance */
free_heir_data(tb);
tb->common.heir = am_none;
- free_fixations_locked(tb);
-
- trap = free_table_cont(BIF_P, tb, 1, 1);
+ reds -= free_fixations_locked(BIF_P, tb);
db_unlock(tb, LCK_WRITE);
- if (trap) {
+
+ if (free_table_continue(BIF_P, tb, reds) < 0) {
/*
* Package the DbTable* pointer into a bignum so that it can be safely
* passed through a trap. We used to pass the DbTable* pointer directly
@@ -1776,9 +1970,11 @@ BIF_RETTYPE ets_delete_1(BIF_ALIST_1)
Eterm *hp = HAlloc(BIF_P, 2);
hp[0] = make_pos_bignum_header(1);
hp[1] = (Eterm) tb;
+ BUMP_ALL_REDS(BIF_P);
BIF_TRAP1(&ets_delete_continue_exp, BIF_P, make_big(hp));
}
else {
+ BUMP_REDS(BIF_P, (initial_reds - reds));
BIF_RET(am_true);
}
}
@@ -1790,7 +1986,6 @@ BIF_RETTYPE ets_give_away_3(BIF_ALIST_3)
{
Process* to_proc = NULL;
ErtsProcLocks to_locks = ERTS_PROC_LOCK_MAIN;
- DeclareTmpHeap(buf,5,BIF_P);
Eterm to_pid = BIF_ARG_2;
Eterm from_pid;
DbTable* tb = NULL;
@@ -1812,26 +2007,14 @@ BIF_RETTYPE ets_give_away_3(BIF_ALIST_3)
goto badarg; /* or should we be idempotent? return false maybe */
}
- UseTmpHeap(5,BIF_P);
- db_meta_lock(meta_pid_to_tab, LCK_WRITE_REC);
- db_erase_bag_exact2(meta_pid_to_tab, tb->common.owner,
- make_small(tb->common.slot));
-
+ delete_owned_table(BIF_P, tb);
to_proc->flags |= F_USING_DB;
tb->common.owner = to_pid;
-
- db_put_hash(meta_pid_to_tab,
- TUPLE2(buf,to_pid,make_small(tb->common.slot)),
- 0);
- db_meta_unlock(meta_pid_to_tab, LCK_WRITE_REC);
+ save_owned_table(to_proc, tb);
db_unlock(tb,LCK_WRITE);
- erts_send_message(BIF_P, to_proc, &to_locks,
- TUPLE4(buf, am_ETS_TRANSFER,
- tb->common.id,
- from_pid,
- BIF_ARG_3),
- 0);
+ send_ets_transfer_message(BIF_P, to_proc, &to_locks,
+ tb, BIF_ARG_3);
erts_smp_proc_unlock(to_proc, to_locks);
UnUseTmpHeap(5,BIF_P);
BIF_RET(am_true);
@@ -2074,7 +2257,7 @@ BIF_RETTYPE ets_select_delete_2(BIF_ALIST_2)
if (safety == ITER_UNSAFE) {
local_fix_table(tb);
}
- cret = tb->common.meth->db_select_delete(BIF_P, tb, BIF_ARG_2, &ret);
+ cret = tb->common.meth->db_select_delete(BIF_P, tb, BIF_ARG_1, BIF_ARG_2, &ret);
if (DID_TRAP(BIF_P,ret) && safety != ITER_SAFE) {
fix_table_locked(BIF_P,tb);
@@ -2101,46 +2284,254 @@ BIF_RETTYPE ets_select_delete_2(BIF_ALIST_2)
return result;
}
-/*
-** Return a list of tables on this node
-*/
-BIF_RETTYPE ets_all_0(BIF_ALIST_0)
+/*
+ * ets:all/0
+ *
+ * ets:all() calls ets:internal_request_all/0 which
+ * requests information about all tables from
+ * each scheduler thread. Each scheduler replies
+ * to the calling process with information about
+ * existing tables created on that specific scheduler.
+ */
+
+struct ErtsEtsAllReq_ {
+ erts_smp_atomic32_t refc;
+ Process *proc;
+ ErtsOIRefStorage ref;
+ ErtsEtsAllReqList list[1]; /* one per scheduler */
+};
+
+#define ERTS_ETS_ALL_REQ_SIZE \
+ (sizeof(ErtsEtsAllReq) \
+ + (sizeof(ErtsEtsAllReqList) \
+ * (erts_no_schedulers - 1)))
+
+typedef struct {
+ ErtsEtsAllReq *ongoing;
+ ErlHeapFragment *hfrag;
+ DbTable *tab;
+ ErtsEtsAllReq *queue;
+} ErtsEtsAllData;
+
+/* Tables handled before yielding */
+#define ERTS_ETS_ALL_TB_YCNT 200
+/*
+ * Min yield count required before starting
+ * an operation that will require yield.
+ */
+#define ERTS_ETS_ALL_TB_YCNT_START 10
+
+#ifdef DEBUG
+/* Test yielding... */
+#undef ERTS_ETS_ALL_TB_YCNT
+#undef ERTS_ETS_ALL_TB_YCNT_START
+#define ERTS_ETS_ALL_TB_YCNT 10
+#define ERTS_ETS_ALL_TB_YCNT_START 1
+#endif
+
+static int
+ets_all_reply(ErtsSchedulerData *esdp, ErtsEtsAllReq **reqpp,
+ ErlHeapFragment **hfragpp, DbTable **tablepp,
+ int *yield_count_p)
{
- DbTable* tb;
- Eterm previous;
- int i;
- Eterm* hp;
- Eterm* hendp;
- int t_tabs_cnt;
- int t_top;
-
- erts_smp_spin_lock(&meta_main_tab_main_lock);
- t_tabs_cnt = meta_main_tab_cnt;
- t_top = meta_main_tab_top;
- erts_smp_spin_unlock(&meta_main_tab_main_lock);
-
- hp = HAlloc(BIF_P, 2*t_tabs_cnt);
- hendp = hp + 2*t_tabs_cnt;
-
- previous = NIL;
- for(i = 0; i < t_top; i++) {
- erts_smp_rwmtx_t *mmtl = get_meta_main_tab_lock(i);
- erts_smp_rwmtx_rlock(mmtl);
- if (IS_SLOT_ALIVE(i)) {
- if (hp == hendp) {
- /* Racing table creator, grab some more heap space */
- t_tabs_cnt = 10;
- hp = HAlloc(BIF_P, 2*t_tabs_cnt);
- hendp = hp + 2*t_tabs_cnt;
- }
- tb = meta_main_tab[i].u.tb;
- previous = CONS(hp, tb->common.id, previous);
- hp += 2;
- }
- erts_smp_rwmtx_runlock(mmtl);
+ ErtsEtsAllReq *reqp = *reqpp;
+ ErlHeapFragment *hfragp = *hfragpp;
+ int ycount = *yield_count_p;
+ DbTable *tb, *first;
+ Uint sz;
+ Eterm list, msg, ref, *hp;
+ ErlOffHeap *ohp;
+ ErtsMessage *mp;
+
+ /*
+ * - save_sched_table() inserts at end of circular list.
+ *
+ * - This function scans from the end so we know that
+ * the amount of tables to scan wont grow even if we
+ * yield.
+ *
+ * - remove_sched_table() updates the table we yielded
+ * on if it removes it.
+ */
+
+ if (hfragp) {
+ /* Restart of a yielded operation... */
+ ASSERT(hfragp->used_size < hfragp->alloc_size);
+ ohp = &hfragp->off_heap;
+ hp = &hfragp->mem[hfragp->used_size];
+ list = *hp;
+ hfragp->used_size = hfragp->alloc_size;
+ first = esdp->ets_tables.clist;
+ tb = *tablepp;
+ }
+ else {
+ /* A new operation... */
+ ASSERT(!*tablepp);
+
+ /* Max heap size needed... */
+ sz = esdp->ets_tables.count;
+ sz *= ERTS_MAGIC_REF_THING_SIZE + 2;
+ sz += 3 + ERTS_REF_THING_SIZE;
+ hfragp = new_message_buffer(sz);
+
+ hp = &hfragp->mem[0];
+ ohp = &hfragp->off_heap;
+ list = NIL;
+ first = esdp->ets_tables.clist;
+ tb = first ? first->common.all.prev : NULL;
+ }
+
+ if (tb) {
+ while (1) {
+ if (is_table_alive(tb)) {
+ Eterm tid;
+ if (is_table_named(tb))
+ tid = tb->common.the_name;
+ else
+ tid = erts_mk_magic_ref(&hp, ohp, tb->common.btid);
+ list = CONS(hp, tid, list);
+ hp += 2;
+ }
+
+ if (tb == first)
+ break;
+
+ tb = tb->common.all.prev;
+
+ if (--ycount <= 0) {
+ sz = hp - &hfragp->mem[0];
+ ASSERT(hfragp->alloc_size > sz + 1);
+ *hp = list;
+ hfragp->used_size = sz;
+ *hfragpp = hfragp;
+ *reqpp = reqp;
+ *tablepp = tb;
+ *yield_count_p = 0;
+ return 1; /* Yield! */
+ }
+ }
+ }
+
+ ref = erts_oiref_storage_make_ref(&reqp->ref, &hp);
+ msg = TUPLE2(hp, ref, list);
+ hp += 3;
+
+ sz = hp - &hfragp->mem[0];
+ ASSERT(sz <= hfragp->alloc_size);
+
+ hfragp = erts_resize_message_buffer(hfragp, sz, &msg, 1);
+
+ mp = erts_alloc_message(0, NULL);
+ mp->data.heap_frag = hfragp;
+
+ erts_queue_message(reqp->proc, 0, mp, msg, am_system);
+
+ erts_proc_dec_refc(reqp->proc);
+
+ if (erts_smp_atomic32_dec_read_nob(&reqp->refc) == 0)
+ erts_free(ERTS_ALC_T_ETS_ALL_REQ, reqp);
+
+ *reqpp = NULL;
+ *hfragpp = NULL;
+ *tablepp = NULL;
+ *yield_count_p = ycount;
+
+ return 0;
+}
+
+int
+erts_handle_yielded_ets_all_request(ErtsSchedulerData *esdp,
+ ErtsEtsAllYieldData *eaydp)
+{
+ int ix = (int) esdp->no - 1;
+ int yc = ERTS_ETS_ALL_TB_YCNT;
+
+ while (1) {
+ if (!eaydp->ongoing) {
+ ErtsEtsAllReq *ongoing;
+
+ if (!eaydp->queue)
+ return 0; /* All work completed! */
+
+ if (yc < ERTS_ETS_ALL_TB_YCNT_START && yc > esdp->ets_tables.count)
+ return 1; /* Yield! */
+
+ eaydp->ongoing = ongoing = eaydp->queue;
+ if (ongoing->list[ix].next == ongoing)
+ eaydp->queue = NULL;
+ else {
+ ongoing->list[ix].next->list[ix].prev = ongoing->list[ix].prev;
+ ongoing->list[ix].prev->list[ix].next = ongoing->list[ix].next;
+ eaydp->queue = ongoing->list[ix].next;
+ }
+ ASSERT(!eaydp->hfrag);
+ ASSERT(!eaydp->tab);
+ }
+
+ if (ets_all_reply(esdp, &eaydp->ongoing, &eaydp->hfrag, &eaydp->tab, &yc))
+ return 1; /* Yield! */
+ }
+}
+
+static void
+handle_ets_all_request(void *vreq)
+{
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ ErtsEtsAllYieldData *eayp = ERTS_SCHED_AUX_YIELD_DATA(esdp, ets_all);
+ ErtsEtsAllReq *req = (ErtsEtsAllReq *) vreq;
+
+ if (!eayp->ongoing && !eayp->queue) {
+ /* No ets:all() operations ongoing... */
+ ErlHeapFragment *hf = NULL;
+ DbTable *tb = NULL;
+ int yc = ERTS_ETS_ALL_TB_YCNT;
+ if (ets_all_reply(esdp, &req, &hf, &tb, &yc)) {
+ /* Yielded... */
+ ASSERT(hf);
+ eayp->ongoing = req;
+ eayp->hfrag = hf;
+ eayp->tab = tb;
+ erts_notify_new_aux_yield_work(esdp);
+ }
+ }
+ else {
+ /* Ongoing ets:all() operations; queue up this request... */
+ int ix = (int) esdp->no - 1;
+ if (!eayp->queue) {
+ req->list[ix].next = req;
+ req->list[ix].prev = req;
+ eayp->queue = req;
+ }
+ else {
+ req->list[ix].next = eayp->queue;
+ req->list[ix].prev = eayp->queue->list[ix].prev;
+ eayp->queue->list[ix].prev = req;
+ req->list[ix].prev->list[ix].next = req;
+ }
}
- HRelease(BIF_P, hendp, hp);
- BIF_RET(previous);
+}
+
+BIF_RETTYPE ets_internal_request_all_0(BIF_ALIST_0)
+{
+ Eterm ref = erts_make_ref(BIF_P);
+ ErtsEtsAllReq *req = erts_alloc(ERTS_ALC_T_ETS_ALL_REQ,
+ ERTS_ETS_ALL_REQ_SIZE);
+ erts_smp_atomic32_init_nob(&req->refc,
+ (erts_aint32_t) erts_no_schedulers);
+ erts_oiref_storage_save(&req->ref, ref);
+ req->proc = BIF_P;
+ erts_proc_add_refc(BIF_P, (Sint) erts_no_schedulers);
+
+#ifdef ERTS_SMP
+ if (erts_no_schedulers > 1)
+ erts_schedule_multi_misc_aux_work(1,
+ erts_no_schedulers,
+ handle_ets_all_request,
+ (void *) req);
+#endif
+
+ handle_ets_all_request((void *) req);
+ BIF_RET(ref);
}
@@ -2245,7 +2636,7 @@ ets_select3(Process* p, Eterm arg1, Eterm arg2, Eterm arg3)
if (safety == ITER_UNSAFE) {
local_fix_table(tb);
}
- cret = tb->common.meth->db_select_chunk(p, tb,
+ cret = tb->common.meth->db_select_chunk(p, tb, arg1,
arg2, chunk_size,
0 /* not reversed */,
&ret);
@@ -2414,8 +2805,7 @@ ets_select2(Process* p, Eterm arg1, Eterm arg2)
local_fix_table(tb);
}
- cret = tb->common.meth->db_select(p, tb, arg2,
- 0, &ret);
+ cret = tb->common.meth->db_select(p, tb, arg1, arg2, 0, &ret);
if (DID_TRAP(p,ret) && safety != ITER_SAFE) {
fix_table_locked(p, tb);
@@ -2506,7 +2896,7 @@ BIF_RETTYPE ets_select_count_2(BIF_ALIST_2)
if (safety == ITER_UNSAFE) {
local_fix_table(tb);
}
- cret = tb->common.meth->db_select_count(BIF_P,tb,BIF_ARG_2, &ret);
+ cret = tb->common.meth->db_select_count(BIF_P,tb, BIF_ARG_1, BIF_ARG_2, &ret);
if (DID_TRAP(BIF_P,ret) && safety != ITER_SAFE) {
fix_table_locked(BIF_P, tb);
@@ -2560,7 +2950,7 @@ BIF_RETTYPE ets_select_reverse_3(BIF_ALIST_3)
if (safety == ITER_UNSAFE) {
local_fix_table(tb);
}
- cret = tb->common.meth->db_select_chunk(BIF_P,tb,
+ cret = tb->common.meth->db_select_chunk(BIF_P,tb, BIF_ARG_1,
BIF_ARG_2, chunk_size,
1 /* reversed */, &ret);
if (DID_TRAP(BIF_P,ret) && safety != ITER_SAFE) {
@@ -2610,7 +3000,7 @@ BIF_RETTYPE ets_select_reverse_2(BIF_ALIST_2)
if (safety == ITER_UNSAFE) {
local_fix_table(tb);
}
- cret = tb->common.meth->db_select(BIF_P,tb,BIF_ARG_2,
+ cret = tb->common.meth->db_select(BIF_P,tb, BIF_ARG_1, BIF_ARG_2,
1 /*reversed*/, &ret);
if (DID_TRAP(BIF_P,ret) && safety != ITER_SAFE) {
@@ -2701,7 +3091,7 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1)
*/
if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ)) == NULL) {
- if (is_atom(BIF_ARG_1) || is_small(BIF_ARG_1)) {
+ if (is_atom(BIF_ARG_1) || is_ref(BIF_ARG_1)) {
BIF_RET(am_undefined);
}
BIF_ERROR(BIF_P, BADARG);
@@ -2763,7 +3153,7 @@ BIF_RETTYPE ets_info_2(BIF_ALIST_2)
Eterm ret = THE_NON_VALUE;
if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ)) == NULL) {
- if (is_atom(BIF_ARG_1) || is_small(BIF_ARG_1)) {
+ if (is_atom(BIF_ARG_1) || is_ref(BIF_ARG_1)) {
BIF_RET(am_undefined);
}
BIF_ERROR(BIF_P, BADARG);
@@ -2853,7 +3243,6 @@ int erts_ets_rwmtx_spin_count = -1;
void init_db(ErtsDbSpinCount db_spin_count)
{
- DbTable init_tb;
int i;
Eterm *hp;
unsigned bits;
@@ -2902,16 +3291,6 @@ void init_db(ErtsDbSpinCount db_spin_count)
if (erts_ets_rwmtx_spin_count >= 0)
rwmtx_opt.main_spincount = erts_ets_rwmtx_spin_count;
- meta_main_tab_locks =
- erts_alloc_permanent_cache_aligned(ERTS_ALC_T_DB_TABLES,
- sizeof(erts_meta_main_tab_lock_t)
- * ERTS_META_MAIN_TAB_LOCK_TAB_SIZE);
-
- for (i = 0; i < ERTS_META_MAIN_TAB_LOCK_TAB_SIZE; i++) {
- erts_smp_rwmtx_init_opt_x(&meta_main_tab_locks[i].rwmtx, &rwmtx_opt,
- "meta_main_tab_slot", make_small(i));
- }
- erts_smp_spinlock_init(&meta_main_tab_main_lock, "meta_main_tab_main");
for (i=0; i<META_NAME_TAB_LOCK_CNT; i++) {
erts_smp_rwmtx_init_opt_x(&meta_name_tab_rwlocks[i].lck, &rwmtx_opt,
"meta_name_tab", make_small(i));
@@ -2931,20 +3310,6 @@ void init_db(ErtsDbSpinCount db_spin_count)
erts_exit(ERTS_ERROR_EXIT,"Max limit for ets tabled too high %u (max %u).",
db_max_tabs, ((Uint)1)<<SMALL_BITS);
}
- meta_main_tab_slot_mask = (((Uint)1)<<bits) - 1;
- meta_main_tab_seq_incr = (((Uint)1)<<bits);
-
- size = sizeof(*meta_main_tab)*db_max_tabs;
- meta_main_tab = erts_db_alloc_nt(ERTS_ALC_T_DB_TABLES, size);
- ERTS_ETS_MISC_MEM_ADD(size);
-
- meta_main_tab_cnt = 0;
- meta_main_tab_top = 0;
- for (i=1; i<db_max_tabs; i++) {
- SET_NEXT_FREE_SLOT(i-1,i);
- }
- SET_NEXT_FREE_SLOT(db_max_tabs-1, (Uint)-1);
- meta_main_tab_first_free = 0;
meta_name_tab_mask = (((Uint) 1)<<(bits-1)) - 1; /* At least half the size of main tab */
size = sizeof(struct meta_name_tab_entry)*(meta_name_tab_mask+1);
@@ -2959,70 +3324,6 @@ void init_db(ErtsDbSpinCount db_spin_count)
db_initialize_hash();
db_initialize_tree();
- /*TT*/
- /* Create meta table invertion. */
- erts_smp_atomic_init_nob(&init_tb.common.memory_size, 0);
- meta_pid_to_tab = (DbTable*) erts_db_alloc(ERTS_ALC_T_DB_TABLE,
- &init_tb,
- sizeof(DbTable));
- erts_smp_atomic_init_nob(&meta_pid_to_tab->common.memory_size,
- erts_smp_atomic_read_nob(&init_tb.common.memory_size));
-
- meta_pid_to_tab->common.id = NIL;
- meta_pid_to_tab->common.the_name = am_true;
- meta_pid_to_tab->common.status = (DB_NORMAL | DB_BAG | DB_PUBLIC | DB_FINE_LOCKED);
-#ifdef ERTS_SMP
- meta_pid_to_tab->common.type
- = meta_pid_to_tab->common.status & ERTS_ETS_TABLE_TYPES;
- /* Note, 'type' is *read only* from now on... */
- meta_pid_to_tab->common.is_thread_safe = 0;
-#endif
- meta_pid_to_tab->common.keypos = 1;
- meta_pid_to_tab->common.owner = NIL;
- erts_smp_atomic_init_nob(&meta_pid_to_tab->common.nitems, 0);
- meta_pid_to_tab->common.slot = -1;
- meta_pid_to_tab->common.meth = &db_hash;
- meta_pid_to_tab->common.compress = 0;
-
- erts_smp_refc_init(&meta_pid_to_tab->common.ref, 0);
- /* Neither rwlock or fixlock used
- db_init_lock(meta_pid_to_tab, "meta_pid_to_tab", "meta_pid_to_tab_FIX");*/
-
- if (db_create_hash(NULL, meta_pid_to_tab) != DB_ERROR_NONE) {
- erts_exit(ERTS_ERROR_EXIT,"Unable to create ets metadata tables.");
- }
-
- erts_smp_atomic_set_nob(&init_tb.common.memory_size, 0);
- meta_pid_to_fixed_tab = (DbTable*) erts_db_alloc(ERTS_ALC_T_DB_TABLE,
- &init_tb,
- sizeof(DbTable));
- erts_smp_atomic_init_nob(&meta_pid_to_fixed_tab->common.memory_size,
- erts_smp_atomic_read_nob(&init_tb.common.memory_size));
-
- meta_pid_to_fixed_tab->common.id = NIL;
- meta_pid_to_fixed_tab->common.the_name = am_true;
- meta_pid_to_fixed_tab->common.status = (DB_NORMAL | DB_BAG | DB_PUBLIC | DB_FINE_LOCKED);
-#ifdef ERTS_SMP
- meta_pid_to_fixed_tab->common.type
- = meta_pid_to_fixed_tab->common.status & ERTS_ETS_TABLE_TYPES;
- /* Note, 'type' is *read only* from now on... */
- meta_pid_to_fixed_tab->common.is_thread_safe = 0;
-#endif
- meta_pid_to_fixed_tab->common.keypos = 1;
- meta_pid_to_fixed_tab->common.owner = NIL;
- erts_smp_atomic_init_nob(&meta_pid_to_fixed_tab->common.nitems, 0);
- meta_pid_to_fixed_tab->common.slot = -1;
- meta_pid_to_fixed_tab->common.meth = &db_hash;
- meta_pid_to_fixed_tab->common.compress = 0;
-
- erts_smp_refc_init(&meta_pid_to_fixed_tab->common.ref, 0);
- /* Neither rwlock or fixlock used
- db_init_lock(meta_pid_to_fixed_tab, "meta_pid_to_fixed_tab", "meta_pid_to_fixed_tab_FIX");*/
-
- if (db_create_hash(NULL, meta_pid_to_fixed_tab) != DB_ERROR_NONE) {
- erts_exit(ERTS_ERROR_EXIT,"Unable to create ets metadata tables.");
- }
-
/* Non visual BIF to trap to. */
erts_init_trap_export(&ets_select_delete_continue_exp,
am_ets, am_atom_put("delete_trap",11), 1,
@@ -3051,81 +3352,18 @@ void init_db(ErtsDbSpinCount db_spin_count)
ms_delete_all = CONS(hp, ms_delete_all,NIL);
}
-#define ARRAY_CHUNK 100
-
-typedef enum {
- ErtsDbProcCleanupProgressTables,
- ErtsDbProcCleanupProgressFixations,
- ErtsDbProcCleanupProgressDone,
-} ErtsDbProcCleanupProgress;
-
-typedef enum {
- ErtsDbProcCleanupOpGetTables,
- ErtsDbProcCleanupOpDeleteTables,
- ErtsDbProcCleanupOpGetFixations,
- ErtsDbProcCleanupOpDeleteFixations,
- ErtsDbProcCleanupOpDone
-} ErtsDbProcCleanupOperation;
-
-typedef struct {
- ErtsDbProcCleanupProgress progress;
- ErtsDbProcCleanupOperation op;
- struct {
- Eterm arr[ARRAY_CHUNK];
- int size;
- int ix;
- int clean_ix;
- } slots;
-} ErtsDbProcCleanupState;
-
-
-static void
-proc_exit_cleanup_tables_meta_data(Eterm pid, ErtsDbProcCleanupState *state)
+void
+erts_ets_sched_spec_data_init(ErtsSchedulerData *esdp)
{
- ASSERT(state->slots.clean_ix <= state->slots.ix);
- if (state->slots.clean_ix < state->slots.ix) {
- db_meta_lock(meta_pid_to_tab, LCK_WRITE_REC);
- if (state->slots.size < ARRAY_CHUNK
- && state->slots.ix == state->slots.size) {
- Eterm dummy;
- db_erase_hash(meta_pid_to_tab,pid,&dummy);
- }
- else {
- int ix;
- /* Need to erase each explicitly */
- for (ix = state->slots.clean_ix; ix < state->slots.ix; ix++)
- db_erase_bag_exact2(meta_pid_to_tab,
- pid,
- state->slots.arr[ix]);
- }
- db_meta_unlock(meta_pid_to_tab, LCK_WRITE_REC);
- state->slots.clean_ix = state->slots.ix;
- }
+ ErtsEtsAllYieldData *eaydp = ERTS_SCHED_AUX_YIELD_DATA(esdp, ets_all);
+ eaydp->ongoing = NULL;
+ eaydp->hfrag = NULL;
+ eaydp->tab = NULL;
+ eaydp->queue = NULL;
+ esdp->ets_tables.clist = NULL;
+ esdp->ets_tables.count = 0;
}
-static void
-proc_exit_cleanup_fixations_meta_data(Eterm pid, ErtsDbProcCleanupState *state)
-{
- ASSERT(state->slots.clean_ix <= state->slots.ix);
- if (state->slots.clean_ix < state->slots.ix) {
- db_meta_lock(meta_pid_to_fixed_tab, LCK_WRITE_REC);
- if (state->slots.size < ARRAY_CHUNK
- && state->slots.ix == state->slots.size) {
- Eterm dummy;
- db_erase_hash(meta_pid_to_fixed_tab,pid,&dummy);
- }
- else {
- int ix;
- /* Need to erase each explicitly */
- for (ix = state->slots.clean_ix; ix < state->slots.ix; ix++)
- db_erase_bag_exact2(meta_pid_to_fixed_tab,
- pid,
- state->slots.arr[ix]);
- }
- db_meta_unlock(meta_pid_to_fixed_tab, LCK_WRITE_REC);
- state->slots.clean_ix = state->slots.ix;
- }
-}
/* In: Table LCK_WRITE
** Return TRUE : ok, table not mine and NOT locked anymore.
@@ -3135,7 +3373,6 @@ static int give_away_to_heir(Process* p, DbTable* tb)
{
Process* to_proc;
ErtsProcLocks to_locks = ERTS_PROC_LOCK_MAIN;
- DeclareTmpHeap(buf,5,p);
Eterm to_pid;
UWord heir_data;
@@ -3179,19 +3416,12 @@ retry:
erts_smp_proc_unlock(to_proc, to_locks);
return 0; /* heir dead and pid reused, table still mine */
}
- UseTmpHeap(5,p);
- db_meta_lock(meta_pid_to_tab, LCK_WRITE_REC);
- db_erase_bag_exact2(meta_pid_to_tab, tb->common.owner,
- make_small(tb->common.slot));
+ delete_owned_table(p, tb);
to_proc->flags |= F_USING_DB;
tb->common.owner = to_pid;
-
- db_put_hash(meta_pid_to_tab,
- TUPLE2(buf,to_pid,make_small(tb->common.slot)),
- 0);
- db_meta_unlock(meta_pid_to_tab, LCK_WRITE_REC);
- UnUseTmpHeap(5,p);
+ save_owned_table(to_proc, tb);
+
db_unlock(tb,LCK_WRITE);
heir_data = tb->common.heir_data;
if (!is_immed(heir_data)) {
@@ -3199,17 +3429,100 @@ retry:
ASSERT(arityval(*tpv) == 1);
heir_data = tpv[1];
}
- erts_send_message(p, to_proc, &to_locks,
- TUPLE4(buf,
- am_ETS_TRANSFER,
- tb->common.id,
- p->common.id,
- heir_data),
- 0);
+ send_ets_transfer_message(p, to_proc, &to_locks, tb, heir_data);
erts_smp_proc_unlock(to_proc, to_locks);
return !0;
}
+static void
+send_ets_transfer_message(Process *c_p, Process *proc,
+ ErtsProcLocks *locks,
+ DbTable *tb, Eterm heir_data)
+{
+ Uint hsz, hd_sz;
+ ErtsMessage *mp;
+ Eterm *hp;
+ ErlOffHeap *ohp;
+ Eterm tid, hd_copy, msg, sender;
+
+ hsz = 5;
+ if (!is_table_named(tb))
+ hsz += ERTS_MAGIC_REF_THING_SIZE;
+ if (is_immed(heir_data))
+ hd_sz = 0;
+ else {
+ hd_sz = size_object(heir_data);
+ hsz += hd_sz;
+ }
+
+ mp = erts_alloc_message_heap(proc, locks, hsz, &hp, &ohp);
+ if (is_table_named(tb))
+ tid = tb->common.the_name;
+ else
+ tid = erts_mk_magic_ref(&hp, ohp, tb->common.btid);
+ if (!hd_sz)
+ hd_copy = heir_data;
+ else
+ hd_copy = copy_struct(heir_data, hd_sz, &hp, ohp);
+ sender = c_p->common.id;
+ msg = TUPLE4(hp, am_ETS_TRANSFER, tid, sender, hd_copy);
+ erts_queue_message(proc, *locks, mp, msg, sender);
+}
+
+
+/* Auto-release fixation from exiting process */
+static SWord proc_cleanup_fixed_table(Process* p, DbFixation* fix)
+{
+ DbTable* tb = btid2tab(fix->tabs.btid);
+ SWord work = 0;
+
+ ASSERT(fix->procs.p == p); (void)p;
+ if (tb) {
+ db_lock(tb, LCK_WRITE_REC);
+ if (!(tb->common.status & DB_DELETE)) {
+ erts_aint_t diff;
+ #ifdef ERTS_SMP
+ erts_smp_mtx_lock(&tb->common.fixlock);
+ #endif
+
+ ASSERT(fixing_procs_rbt_lookup(tb->common.fixing_procs, p));
+
+ diff = -((erts_aint_t) fix->counter);
+ erts_smp_refc_add(&tb->common.fix_count,diff,0);
+ fix->counter = 0;
+
+ fixing_procs_rbt_delete(&tb->common.fixing_procs, fix);
+
+ #ifdef ERTS_SMP
+ erts_smp_mtx_unlock(&tb->common.fixlock);
+ #endif
+ if (!IS_FIXED(tb) && IS_HASH_TABLE(tb->common.status)) {
+ work += db_unfix_table_hash(&(tb->hash));
+ }
+
+ ASSERT(sizeof(DbFixation) == ERTS_ALC_DBG_BLK_SZ(fix));
+ ERTS_DB_ALC_MEM_UPDATE_(tb, sizeof(DbFixation), 0);
+ }
+ else {
+ ASSERT(fix->counter == 0);
+ }
+ db_unlock(tb, LCK_WRITE_REC);
+ }
+ else {
+ ASSERT(fix->counter == 0);
+ }
+
+ if (erts_refc_dectest(&fix->tabs.btid->refc, 0) == 0) {
+ erts_bin_free(fix->tabs.btid);
+ }
+ erts_free(ERTS_ALC_T_DB_FIXATION, fix);
+ ERTS_ETS_MISC_MEM_ADD(-sizeof(DbFixation));
+ ++work;
+
+ return work;
+}
+
+
/*
* erts_db_process_exiting() is called when a process terminates.
* It returns 0 when completely done, and !0 when it wants to
@@ -3223,276 +3536,160 @@ retry:
int
erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks)
{
- ErtsDbProcCleanupState *state = (ErtsDbProcCleanupState *) c_p->u.terminate;
+ typedef struct {
+ enum {
+ GET_OWNED_TABLE,
+ FREE_OWNED_TABLE,
+ UNFIX_TABLES,
+ }op;
+ DbTable *tb;
+ } CleanupState;
+ CleanupState *state = (CleanupState *) c_p->u.terminate;
Eterm pid = c_p->common.id;
- ErtsDbProcCleanupState default_state;
- int ret;
+ CleanupState default_state;
+ SWord initial_reds = ERTS_BIF_REDS_LEFT(c_p);
+ SWord reds = initial_reds;
if (!state) {
state = &default_state;
- state->progress = ErtsDbProcCleanupProgressTables;
- state->op = ErtsDbProcCleanupOpGetTables;
+ state->op = GET_OWNED_TABLE;
+ state->tb = NULL;
}
- while (!0) {
+ do {
switch (state->op) {
- case ErtsDbProcCleanupOpGetTables:
- state->slots.size = ARRAY_CHUNK;
- db_meta_lock(meta_pid_to_tab, LCK_READ);
- ret = db_get_element_array(meta_pid_to_tab,
- pid,
- 2,
- state->slots.arr,
- &state->slots.size);
- db_meta_unlock(meta_pid_to_tab, LCK_READ);
- if (ret == DB_ERROR_BADKEY) {
- /* Done with tables; now fixations */
- state->progress = ErtsDbProcCleanupProgressFixations;
- state->op = ErtsDbProcCleanupOpGetFixations;
- break;
- } else if (ret != DB_ERROR_NONE) {
- ERTS_DB_INTERNAL_ERROR("Inconsistent ets table metadata");
- }
-
- state->slots.ix = 0;
- state->slots.clean_ix = 0;
- state->op = ErtsDbProcCleanupOpDeleteTables;
- /* Fall through */
-
- case ErtsDbProcCleanupOpDeleteTables:
-
- while (state->slots.ix < state->slots.size) {
- DbTable *tb = NULL;
- Sint ix = unsigned_val(state->slots.arr[state->slots.ix]);
- erts_smp_rwmtx_t *mmtl = get_meta_main_tab_lock(ix);
- erts_smp_rwmtx_rlock(mmtl);
- if (!IS_SLOT_FREE(ix)) {
- tb = GET_ANY_SLOT_TAB(ix);
- ASSERT(tb);
- }
- erts_smp_rwmtx_runlock(mmtl);
- if (tb) {
- int do_yield;
- db_lock(tb, LCK_WRITE);
- /* Ownership may have changed since
- we looked up the table. */
- if (tb->common.owner != pid) {
- do_yield = 0;
- db_unlock(tb, LCK_WRITE);
- }
- else if (tb->common.heir != am_none
- && tb->common.heir != pid
- && give_away_to_heir(c_p, tb)) {
- do_yield = 0;
- }
- else {
- int first_call;
-#ifdef HARDDEBUG
- erts_fprintf(stderr,
- "erts_db_process_exiting(); Table: %T, "
- "Process: %T\n",
- tb->common.id, pid);
-#endif
- first_call = (tb->common.status & DB_DELETE) == 0;
- if (first_call) {
- /* Clear all access bits. */
- tb->common.status &= ~(DB_PROTECTED
- | DB_PUBLIC
- | DB_PRIVATE);
- tb->common.status |= DB_DELETE;
-
- if (is_atom(tb->common.id))
- remove_named_tab(tb, 0);
-
- free_heir_data(tb);
- free_fixations_locked(tb);
- }
-
- do_yield = free_table_cont(c_p, tb, first_call, 0);
- db_unlock(tb, LCK_WRITE);
- }
- if (do_yield)
- goto yield;
- }
- state->slots.ix++;
- if (ERTS_BIF_REDS_LEFT(c_p) <= 0)
- goto yield;
- }
+ case GET_OWNED_TABLE: {
+ DbTable* tb;
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS);
+ tb = (DbTable*) erts_psd_get(c_p, ERTS_PSD_ETS_OWNED_TABLES);
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS);
+
+ if (!tb) {
+ /* Done with owned tables; now fixations */
+ state->op = UNFIX_TABLES;
+ break;
+ }
- proc_exit_cleanup_tables_meta_data(pid, state);
- state->op = ErtsDbProcCleanupOpGetTables;
- break;
+ ASSERT(tb != state->tb);
+ state->tb = tb;
+ db_lock(tb, LCK_WRITE);
+ /*
+ * Ownership may have changed since we looked up the table.
+ */
+ if (tb->common.owner != pid) {
+ db_unlock(tb, LCK_WRITE);
+ break;
+ }
+ if (tb->common.heir != am_none
+ && tb->common.heir != pid
+ && give_away_to_heir(c_p, tb)) {
+ break;
+ }
+ tid_clear(c_p, tb);
+ /* Clear all access bits. */
+ tb->common.status &= ~(DB_PROTECTED | DB_PUBLIC | DB_PRIVATE);
+ tb->common.status |= DB_DELETE;
+
+ if (is_table_named(tb))
+ remove_named_tab(tb, 0);
+
+ free_heir_data(tb);
+ reds -= free_fixations_locked(c_p, tb);
+ db_unlock(tb, LCK_WRITE);
+ state->op = FREE_OWNED_TABLE;
+ break;
+ }
+ case FREE_OWNED_TABLE:
+ reds = free_table_continue(c_p, state->tb, reds);
+ if (reds < 0)
+ goto yield;
- case ErtsDbProcCleanupOpGetFixations:
- state->slots.size = ARRAY_CHUNK;
- db_meta_lock(meta_pid_to_fixed_tab, LCK_READ);
- ret = db_get_element_array(meta_pid_to_fixed_tab,
- pid,
- 2,
- state->slots.arr,
- &state->slots.size);
- db_meta_unlock(meta_pid_to_fixed_tab, LCK_READ);
-
- if (ret == DB_ERROR_BADKEY) {
- /* Done */
- state->progress = ErtsDbProcCleanupProgressDone;
- state->op = ErtsDbProcCleanupOpDone;
- break;
- } else if (ret != DB_ERROR_NONE) {
- ERTS_DB_INTERNAL_ERROR("Inconsistent ets fix table metadata");
- }
+ state->op = GET_OWNED_TABLE;
+ break;
- state->slots.ix = 0;
- state->slots.clean_ix = 0;
- state->op = ErtsDbProcCleanupOpDeleteFixations;
- /* Fall through */
+ case UNFIX_TABLES: {
+ DbFixation* fix;
- case ErtsDbProcCleanupOpDeleteFixations:
+ fix = (DbFixation*) erts_psd_get(c_p, ERTS_PSD_ETS_FIXED_TABLES);
- while (state->slots.ix < state->slots.size) {
- DbTable *tb = NULL;
- Sint ix = unsigned_val(state->slots.arr[state->slots.ix]);
- erts_smp_rwmtx_t *mmtl = get_meta_main_tab_lock(ix);
- erts_smp_rwmtx_rlock(mmtl);
- if (IS_SLOT_ALIVE(ix)) {
- tb = meta_main_tab[ix].u.tb;
- ASSERT(tb);
- }
- erts_smp_rwmtx_runlock(mmtl);
- if (tb) {
- int reds = 0;
-
- db_lock(tb, LCK_WRITE_REC);
- if (!(tb->common.status & DB_DELETE)) {
- DbFixation** pp;
-
- #ifdef ERTS_SMP
- erts_smp_mtx_lock(&tb->common.fixlock);
- #endif
- reds = 10;
-
- for (pp = &tb->common.fixations; *pp != NULL;
- pp = &(*pp)->next) {
- if ((*pp)->pid == pid) {
- DbFixation* fix = *pp;
- erts_aint_t diff = -((erts_aint_t) fix->counter);
- erts_smp_refc_add(&tb->common.ref,diff,0);
- *pp = fix->next;
- erts_db_free(ERTS_ALC_T_DB_FIXATION,
- tb, fix, sizeof(DbFixation));
- ERTS_ETS_MISC_MEM_ADD(-sizeof(DbFixation));
- break;
- }
- }
- #ifdef ERTS_SMP
- erts_smp_mtx_unlock(&tb->common.fixlock);
- #endif
- if (!IS_FIXED(tb) && IS_HASH_TABLE(tb->common.status)) {
- db_unfix_table_hash(&(tb->hash));
- reds += 40;
- }
- }
- db_unlock(tb, LCK_WRITE_REC);
- BUMP_REDS(c_p, reds);
- }
- state->slots.ix++;
- if (ERTS_BIF_REDS_LEFT(c_p) <= 0)
- goto yield;
- }
+ if (!fix) {
+ /* Done */
- proc_exit_cleanup_fixations_meta_data(pid, state);
- state->op = ErtsDbProcCleanupOpGetFixations;
- break;
+ if (state != &default_state)
+ erts_free(ERTS_ALC_T_DB_PROC_CLEANUP, state);
+ c_p->u.terminate = NULL;
- case ErtsDbProcCleanupOpDone:
+ BUMP_REDS(c_p, (initial_reds - reds));
+ return 0;
+ }
- if (state != &default_state)
- erts_free(ERTS_ALC_T_DB_PROC_CLEANUP, state);
- c_p->u.terminate = NULL;
- return 0;
+ fixed_tabs_delete(c_p, fix);
+ reds -= proc_cleanup_fixed_table(c_p, fix);
+ break;
+ }
default:
ERTS_DB_INTERNAL_ERROR("Bad internal state");
- }
- }
-
- yield:
+ }
- switch (state->progress) {
- case ErtsDbProcCleanupProgressTables:
- proc_exit_cleanup_tables_meta_data(pid, state);
- break;
- case ErtsDbProcCleanupProgressFixations:
- proc_exit_cleanup_fixations_meta_data(pid, state);
- break;
- default:
- break;
- }
+ } while (reds > 0);
- ASSERT(c_p->u.terminate == (void *) state
- || state == &default_state);
+ yield:
if (state == &default_state) {
c_p->u.terminate = erts_alloc(ERTS_ALC_T_DB_PROC_CLEANUP,
- sizeof(ErtsDbProcCleanupState));
- sys_memcpy(c_p->u.terminate,
- (void*) state,
- sizeof(ErtsDbProcCleanupState));
+ sizeof(CleanupState));
+ sys_memcpy(c_p->u.terminate, (void*) state, sizeof(CleanupState));
}
+ else
+ ASSERT(state == c_p->u.terminate);
return !0;
}
+
/* SMP note: table only need to be LCK_READ locked */
static void fix_table_locked(Process* p, DbTable* tb)
{
DbFixation *fix;
- DeclareTmpHeap(meta_tuple,3,p);
#ifdef ERTS_SMP
erts_smp_mtx_lock(&tb->common.fixlock);
#endif
- erts_smp_refc_inc(&tb->common.ref,1);
- fix = tb->common.fixations;
+ erts_smp_refc_inc(&tb->common.fix_count,1);
+ fix = tb->common.fixing_procs;
if (fix == NULL) {
tb->common.time.monotonic
= erts_get_monotonic_time(erts_proc_sched_data(p));
tb->common.time.offset = erts_get_time_offset();
}
else {
- for (; fix != NULL; fix = fix->next) {
- if (fix->pid == p->common.id) {
- ++(fix->counter);
+ fix = fixing_procs_rbt_lookup(fix, p);
+ if (fix) {
+ ASSERT(fixed_tabs_find(NULL, fix));
+ ++(fix->counter);
+
#ifdef ERTS_SMP
- erts_smp_mtx_unlock(&tb->common.fixlock);
+ erts_smp_mtx_unlock(&tb->common.fixlock);
#endif
- return;
- }
+ return;
}
}
fix = (DbFixation *) erts_db_alloc(ERTS_ALC_T_DB_FIXATION,
tb, sizeof(DbFixation));
ERTS_ETS_MISC_MEM_ADD(sizeof(DbFixation));
- fix->pid = p->common.id;
+ fix->tabs.btid = tb->common.btid;
+ erts_refc_inc(&fix->tabs.btid->refc, 2);
+ fix->procs.p = p;
fix->counter = 1;
- fix->next = tb->common.fixations;
- tb->common.fixations = fix;
+ fixing_procs_rbt_insert(&tb->common.fixing_procs, fix);
+
#ifdef ERTS_SMP
erts_smp_mtx_unlock(&tb->common.fixlock);
#endif
- p->flags |= F_USING_DB;
- UseTmpHeap(3,p);
- db_meta_lock(meta_pid_to_fixed_tab, LCK_WRITE_REC);
- if (db_put_hash(meta_pid_to_fixed_tab,
- TUPLE2(meta_tuple,
- p->common.id,
- make_small(tb->common.slot)),
- 0) != DB_ERROR_NONE) {
- UnUseTmpHeap(3,p);
- erts_exit(ERTS_ERROR_EXIT,"Could not insert ets metadata in safe_fixtable.");
- }
- UnUseTmpHeap(3,p);
- db_meta_unlock(meta_pid_to_fixed_tab, LCK_WRITE_REC);
+ p->flags |= F_USING_DB;
+
+ fixed_tabs_insert(p, fix);
}
/* SMP note: May re-lock table
@@ -3500,28 +3697,26 @@ static void fix_table_locked(Process* p, DbTable* tb)
static void unfix_table_locked(Process* p, DbTable* tb,
db_lock_kind_t* kind_p)
{
- DbFixation** pp;
+ DbFixation* fix;
#ifdef ERTS_SMP
erts_smp_mtx_lock(&tb->common.fixlock);
#endif
- for (pp = &tb->common.fixations; *pp != NULL; pp = &(*pp)->next) {
- if ((*pp)->pid == p->common.id) {
- DbFixation* fix = *pp;
- erts_smp_refc_dec(&tb->common.ref,0);
- --(fix->counter);
- ASSERT(fix->counter >= 0);
- if (fix->counter > 0) {
- break;
- }
- *pp = fix->next;
+ fix = fixing_procs_rbt_lookup(tb->common.fixing_procs, p);
+
+ if (fix) {
+ erts_smp_refc_dec(&tb->common.fix_count,0);
+ --(fix->counter);
+ ASSERT(fix->counter >= 0);
+ if (fix->counter == 0) {
+ fixing_procs_rbt_delete(&tb->common.fixing_procs, fix);
#ifdef ERTS_SMP
erts_smp_mtx_unlock(&tb->common.fixlock);
#endif
- db_meta_lock(meta_pid_to_fixed_tab, LCK_WRITE_REC);
- db_erase_bag_exact2(meta_pid_to_fixed_tab,
- p->common.id, make_small(tb->common.slot));
- db_meta_unlock(meta_pid_to_fixed_tab, LCK_WRITE_REC);
+ fixed_tabs_delete(p, fix);
+
+ erts_refc_dec(&fix->tabs.btid->refc, 1);
+
erts_db_free(ERTS_ALC_T_DB_FIXATION,
tb, (void *) fix, sizeof(DbFixation));
ERTS_ETS_MISC_MEM_ADD(-sizeof(DbFixation));
@@ -3548,29 +3743,87 @@ unlocked:
}
}
-/* Assume that tb is WRITE locked */
-static void free_fixations_locked(DbTable *tb)
+struct free_fixations_ctx
{
- DbFixation *fix;
- DbFixation *next_fix;
+ Process* p;
+ DbTable* tb;
+ SWord cnt;
+};
+
+static void free_fixations_op(DbFixation* fix, void* vctx)
+{
+ struct free_fixations_ctx* ctx = (struct free_fixations_ctx*) vctx;
+ erts_aint_t diff;
+#ifdef DEBUG
+ DbTable* dbg_tb = btid2tab(fix->tabs.btid);
+#endif
- fix = tb->common.fixations;
- while (fix != NULL) {
- erts_aint_t diff = -((erts_aint_t) fix->counter);
- erts_smp_refc_add(&tb->common.ref,diff,0);
- next_fix = fix->next;
- db_meta_lock(meta_pid_to_fixed_tab, LCK_WRITE_REC);
- db_erase_bag_exact2(meta_pid_to_fixed_tab,
- fix->pid,
- make_small(tb->common.slot));
- db_meta_unlock(meta_pid_to_fixed_tab, LCK_WRITE_REC);
- erts_db_free(ERTS_ALC_T_DB_FIXATION,
- tb, (void *) fix, sizeof(DbFixation));
- ERTS_ETS_MISC_MEM_ADD(-sizeof(DbFixation));
+ ASSERT(!dbg_tb || dbg_tb == ctx->tb);
+ ASSERT(fix->counter > 0);
+ ASSERT(ctx->tb->common.status & DB_DELETE);
- fix = next_fix;
+ diff = -((erts_aint_t) fix->counter);
+ erts_smp_refc_add(&ctx->tb->common.fix_count, diff, 0);
+
+#ifdef ERTS_SMP
+ if (fix->procs.p != ctx->p) { /* Fixated by other process */
+ fix->counter = 0;
+
+ /* Fake memory stats for table */
+ ASSERT(sizeof(DbFixation) == ERTS_ALC_DBG_BLK_SZ(fix));
+ ERTS_DB_ALC_MEM_UPDATE_(ctx->tb, sizeof(DbFixation), 0);
+
+ erts_schedule_ets_free_fixation(fix->procs.p->common.id, fix);
+ /*
+ * Either sys task is scheduled and erts_db_execute_free_fixation()
+ * will remove 'fix' or process will exit, drop sys task and
+ * proc_cleanup_fixed_table() will remove 'fix'.
+ */
+ }
+ else
+#endif
+ {
+ fixed_tabs_delete(fix->procs.p, fix);
+
+ if (erts_refc_dectest(&fix->tabs.btid->refc, 0) == 0) {
+ erts_bin_free(fix->tabs.btid);
+ }
+
+ erts_db_free(ERTS_ALC_T_DB_FIXATION,
+ ctx->tb, (void *) fix, sizeof(DbFixation));
+ ERTS_ETS_MISC_MEM_ADD(-sizeof(DbFixation));
}
- tb->common.fixations = NULL;
+ ctx->cnt++;
+}
+
+#ifdef ERTS_SMP
+int erts_db_execute_free_fixation(Process* p, DbFixation* fix)
+{
+ ASSERT(fix->counter == 0);
+ fixed_tabs_delete(p, fix);
+
+ if (erts_refc_dectest(&fix->tabs.btid->refc, 0) == 0) {
+ erts_bin_free(fix->tabs.btid);
+ }
+ erts_free(ERTS_ALC_T_DB_FIXATION, fix);
+ ERTS_ETS_MISC_MEM_ADD(-sizeof(DbFixation));
+ return 1;
+}
+#endif
+
+static SWord free_fixations_locked(Process* p, DbTable *tb)
+{
+ struct free_fixations_ctx ctx;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rwlocked(&tb->common.rwlock));
+
+ ctx.p = p;
+ ctx.tb = tb;
+ ctx.cnt = 0;
+ fixing_procs_rbt_foreach_destroy(&tb->common.fixing_procs,
+ free_fixations_op, &ctx);
+ tb->common.fixing_procs = NULL;
+ return ctx.cnt;
}
static void set_heir(Process* me, DbTable* tb, Eterm heir, UWord heir_data)
@@ -3634,55 +3887,40 @@ static void free_heir_data(DbTable* tb)
static BIF_RETTYPE ets_delete_trap(BIF_ALIST_1)
{
- Process *p = BIF_P;
+ SWord initial_reds = ERTS_BIF_REDS_LEFT(BIF_P);
+ SWord reds = initial_reds;
Eterm cont = BIF_ARG_1;
- int trap;
Eterm* ptr = big_val(cont);
DbTable *tb = *((DbTable **) (UWord) (ptr + 1));
ASSERT(*ptr == make_pos_bignum_header(1));
- db_lock(tb, LCK_WRITE);
- trap = free_table_cont(p, tb, 0, 1);
- db_unlock(tb, LCK_WRITE);
-
- if (trap) {
- BIF_TRAP1(&ets_delete_continue_exp, p, cont);
+ if (free_table_continue(BIF_P, tb, reds) < 0) {
+ BUMP_ALL_REDS(BIF_P);
+ BIF_TRAP1(&ets_delete_continue_exp, BIF_P, cont);
}
else {
+ BUMP_REDS(BIF_P, (initial_reds - reds));
BIF_RET(am_true);
}
}
/*
- * free_table_cont() returns 0 when done and !0 when more work is needed.
+ * free_table_continue() returns reductions left
+ * done if >= 0
+ * yield if < 0
*/
-static int free_table_cont(Process *p,
- DbTable *tb,
- int first,
- int clean_meta_tab)
+static SWord free_table_continue(Process *p, DbTable *tb, SWord reds)
{
- Eterm result;
- erts_smp_rwmtx_t *mmtl;
-
-#ifdef HARDDEBUG
- if (!first) {
- erts_fprintf(stderr,"ets: free_table_cont %T (continue)\r\n",
- tb->common.id);
- }
-#endif
-
- result = tb->common.meth->db_free_table_continue(tb);
+ reds = tb->common.meth->db_free_table_continue(tb, reds);
- if (result == 0) {
+ if (reds < 0) {
#ifdef HARDDEBUG
erts_fprintf(stderr,"ets: free_table_cont %T (continue begin)\r\n",
tb->common.id);
#endif
/* More work to be done. Let other processes work and call us again. */
- BUMP_ALL_REDS(p);
- return !0;
}
else {
#ifdef HARDDEBUG
@@ -3690,27 +3928,28 @@ static int free_table_cont(Process *p,
tb->common.id);
#endif
/* Completely done - we will not get called again. */
- mmtl = get_meta_main_tab_lock(tb->common.slot);
-#ifdef ERTS_SMP
- if (erts_smp_rwmtx_tryrwlock(mmtl) == EBUSY) {
- erts_smp_rwmtx_rwunlock(&tb->common.rwlock);
- erts_smp_rwmtx_rwlock(mmtl);
- erts_smp_rwmtx_rwlock(&tb->common.rwlock);
- }
-#endif
- free_slot(tb->common.slot);
- erts_smp_rwmtx_rwunlock(mmtl);
-
- if (clean_meta_tab) {
- db_meta_lock(meta_pid_to_tab, LCK_WRITE_REC);
- db_erase_bag_exact2(meta_pid_to_tab,tb->common.owner,
- make_small(tb->common.slot));
- db_meta_unlock(meta_pid_to_tab, LCK_WRITE_REC);
- }
- schedule_free_dbtable(tb);
- BUMP_REDS(p, 100);
- return 0;
+ delete_owned_table(p, tb);
+ table_dec_refc(tb, 0);
}
+ return reds;
+}
+
+struct fixing_procs_info_ctx
+{
+ Process* p;
+ Eterm list;
+};
+
+static void fixing_procs_info_op(DbFixation* fix, void* vctx)
+{
+ struct fixing_procs_info_ctx* ctx = (struct fixing_procs_info_ctx*) vctx;
+ Eterm* hp;
+ Eterm tpl;
+
+ hp = HAllocX(ctx->p, 5, 100);
+ tpl = TUPLE2(hp, fix->procs.p->common.id, make_small(fix->counter));
+ hp += 3;
+ ctx->list = CONS(hp, tpl, ctx->list);
}
static Eterm table_info(Process* p, DbTable* tb, Eterm What)
@@ -3759,7 +3998,7 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
} else if (What == am_node) {
ret = erts_this_dist_entry->sysname;
} else if (What == am_named_table) {
- ret = is_atom(tb->common.id) ? am_true : am_false;
+ ret = is_table_named(tb) ? am_true : am_false;
} else if (What == am_compressed) {
ret = tb->common.compress ? am_true : am_false;
}
@@ -3784,9 +4023,9 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
if (IS_FIXED(tb)) {
Uint need;
Eterm *hp;
- Eterm tpl, lst;
- DbFixation *fix;
+ Eterm time;
Sint64 mtime;
+ struct fixing_procs_info_ctx ctx;
need = 3;
if (use_monotonic) {
@@ -3799,19 +4038,15 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
mtime = 0;
need += 4;
}
- for (fix = tb->common.fixations; fix != NULL; fix = fix->next) {
- need += 5;
- }
+ ctx.p = p;
+ ctx.list = NIL;
+ fixing_procs_rbt_foreach(tb->common.fixing_procs,
+ fixing_procs_info_op,
+ &ctx);
+
hp = HAlloc(p, need);
- lst = NIL;
- for (fix = tb->common.fixations; fix != NULL; fix = fix->next) {
- tpl = TUPLE2(hp,fix->pid,make_small(fix->counter));
- hp += 3;
- lst = CONS(hp,tpl,lst);
- hp += 2;
- }
if (use_monotonic)
- tpl = (IS_SSMALL(mtime)
+ time = (IS_SSMALL(mtime)
? make_small(mtime)
: erts_sint64_to_big(mtime, &hp));
else {
@@ -3819,10 +4054,10 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
erts_make_timestamp_value(&ms, &s, &us,
tb->common.time.monotonic,
tb->common.time.offset);
- tpl = TUPLE3(hp, make_small(ms), make_small(s), make_small(us));
+ time = TUPLE3(hp, make_small(ms), make_small(s), make_small(us));
hp += 4;
}
- ret = TUPLE2(hp, tpl, lst);
+ ret = TUPLE2(hp, time, ctx.list);
} else {
ret = am_false;
}
@@ -3867,7 +4102,19 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
static void print_table(fmtfn_t to, void *to_arg, int show, DbTable* tb)
{
- erts_print(to, to_arg, "Table: %T\n", tb->common.id);
+ Eterm tid;
+ Eterm heap[ERTS_MAGIC_REF_THING_SIZE];
+
+ if (is_table_named(tb)) {
+ tid = tb->common.the_name;
+ } else {
+ ErlOffHeap oh;
+ ERTS_INIT_OFF_HEAP(&oh);
+ write_magic_ref_thing(heap, &oh, (ErtsMagicBinary *) tb->common.btid);
+ tid = make_internal_ref(heap);
+ }
+
+ erts_print(to, to_arg, "Table: %T\n", tid);
erts_print(to, to_arg, "Name: %T\n", tb->common.the_name);
tb->common.meth->db_print(to, to_arg, show, tb);
@@ -3885,21 +4132,30 @@ static void print_table(fmtfn_t to, void *to_arg, int show, DbTable* tb)
erts_print(to, to_arg, "Read Concurrency: %T\n", table_info(NULL, tb, am_read_concurrency));
}
+typedef struct {
+ fmtfn_t to;
+ void *to_arg;
+ int show;
+} ErtsPrintDbInfo;
+
+static void
+db_info_print(DbTable *tb, void *vpdbip)
+{
+ ErtsPrintDbInfo *pdbip = (ErtsPrintDbInfo *) vpdbip;
+ erts_print(pdbip->to, pdbip->to_arg, "=ets:%T\n", tb->common.owner);
+ erts_print(pdbip->to, pdbip->to_arg, "Slot: %bpu\n", (Uint) tb);
+ print_table(pdbip->to, pdbip->to_arg, pdbip->show, tb);
+}
+
void db_info(fmtfn_t to, void *to_arg, int show) /* Called by break handler */
{
- int i;
- for (i=0; i < db_max_tabs; i++)
- if (IS_SLOT_ALIVE(i)) {
- erts_print(to, to_arg, "=ets:%T\n", meta_main_tab[i].u.tb->common.owner);
- erts_print(to, to_arg, "Slot: %d\n", i);
- print_table(to, to_arg, show, meta_main_tab[i].u.tb);
- }
-#ifdef DEBUG
- erts_print(to, to_arg, "=internal_ets: Process to table index\n");
- print_table(to, to_arg, show, meta_pid_to_tab);
- erts_print(to, to_arg, "=internal_ets: Process to fixation index\n");
- print_table(to, to_arg, show, meta_pid_to_fixed_tab);
-#endif
+ ErtsPrintDbInfo pdbi;
+
+ pdbi.to = to;
+ pdbi.to_arg = to_arg;
+ pdbi.show = show;
+
+ erts_db_foreach_table(db_info_print, &pdbi);
}
Uint
@@ -3914,15 +4170,22 @@ erts_get_ets_misc_mem_size(void)
void
erts_db_foreach_table(void (*func)(DbTable *, void *), void *arg)
{
- int i, j;
- j = 0;
- for(i = 0; (i < db_max_tabs && j < meta_main_tab_cnt); i++) {
- if (IS_SLOT_ALIVE(i)) {
- j++;
- (*func)(meta_main_tab[i].u.tb, arg);
- }
+ int ix;
+
+ ASSERT(erts_smp_thr_progress_is_blocking());
+
+ for (ix = 0; ix < erts_no_schedulers; ix++) {
+ ErtsSchedulerData *esdp = ERTS_SCHEDULER_IX(ix);
+ DbTable *first = esdp->ets_tables.clist;
+ if (first) {
+ DbTable *tb = first;
+ do {
+ if (is_table_alive(tb))
+ (*func)(tb, arg);
+ tb = tb->common.all.next;
+ } while (tb != first);
+ }
}
- ASSERT(j == meta_main_tab_cnt);
}
/* SMP Note: May only be used when system is locked */
@@ -3972,53 +4235,3 @@ erts_ets_colliding_names(Process* p, Eterm name, Uint cnt)
return list;
}
-/*
- * For testing only
- * Retreive meta table size state
- */
-Eterm erts_ets_get_meta_state(Process* p)
-{
- Eterm* hp = HAlloc(p, 3);
- return TUPLE2(hp,
- erts_ets_hash_get_memstate(p, &meta_pid_to_tab->hash),
- erts_ets_hash_get_memstate(p, &meta_pid_to_fixed_tab->hash));
-}
-/*
- * For testing only
- * Restore a previously retrieved meta table size state.
- * We do this to suppress failed memory checks
- * caused by the hysteresis of meta tables grow/shrink limits.
- */
-Eterm erts_ets_restore_meta_state(Process* p, Eterm meta_state)
-{
- Eterm* tv;
- Eterm* hp;
- if (!is_tuple_arity(meta_state, 2))
- return am_badarg;
-
- tv = tuple_val(meta_state);
- hp = HAlloc(p, 3);
- return TUPLE2(hp,
- erts_ets_hash_restore_memstate(&meta_pid_to_tab->hash, tv[1]),
- erts_ets_hash_restore_memstate(&meta_pid_to_fixed_tab->hash, tv[2]));
-}
-
-#ifdef HARDDEBUG /* Here comes some debug functions */
-
-void db_check_tables(void)
-{
-#ifdef ERTS_SMP
- return;
-#else
- int i;
-
- for (i = 0; i < db_max_tabs; i++) {
- if (IS_SLOT_ALIVE(i)) {
- DbTable* tb = meta_main_tab[i].t;
- tb->common.meth->db_check_table(tb);
- }
- }
-#endif
-}
-
-#endif /* HARDDEBUG */
diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h
index 852440ff31..cbf4b9e007 100644
--- a/erts/emulator/beam/erl_db.h
+++ b/erts/emulator/beam/erl_db.h
@@ -24,8 +24,37 @@
*
*/
-#ifndef __DB_H__
-#define __DB_H__
+#ifndef ERTS_DB_SCHED_SPEC_TYPES__
+#define ERTS_DB_SCHED_SPEC_TYPES__
+
+union db_table;
+typedef union db_table DbTable;
+
+typedef struct ErtsEtsAllReq_ ErtsEtsAllReq;
+
+typedef struct {
+ ErtsEtsAllReq *next;
+ ErtsEtsAllReq *prev;
+} ErtsEtsAllReqList;
+
+typedef struct {
+ ErtsEtsAllReq *ongoing;
+ ErlHeapFragment *hfrag;
+ DbTable *tab;
+ ErtsEtsAllReq *queue;
+} ErtsEtsAllYieldData;
+
+typedef struct {
+ Uint count;
+ DbTable *clist;
+} ErtsEtsTables;
+
+#endif /* ERTS_DB_SCHED_SPEC_TYPES__ */
+
+#ifndef ERTS_ONLY_SCHED_SPEC_ETS_DATA
+
+#ifndef ERL_DB_H__
+#define ERL_DB_H__
#include "sys.h"
#undef ERL_THR_PROGRESS_TSD_TYPE_ONLY
@@ -46,6 +75,12 @@ typedef struct {
ErtsThrPrgrLaterOp data;
} DbTableRelease;
+struct ErtsSchedulerData_;
+int erts_handle_yielded_ets_all_request(struct ErtsSchedulerData_ *esdp,
+ ErtsEtsAllYieldData *eadp);
+
+void erts_ets_sched_spec_data_init(struct ErtsSchedulerData_ *esdp);
+
/*
* So, the structure for a database table, NB this is only
* interesting in db.c.
@@ -74,6 +109,7 @@ typedef enum {
void init_db(ErtsDbSpinCount);
int erts_db_process_exiting(Process *, ErtsProcLocks);
+int erts_db_execute_free_fixation(Process*, DbFixation*);
void db_info(fmtfn_t, void *, int);
void erts_db_foreach_table(void (*)(DbTable *, void *), void *);
void erts_db_foreach_offheap(DbTable *,
@@ -90,12 +126,9 @@ extern Export ets_select_continue_exp;
extern erts_smp_atomic_t erts_ets_misc_mem_size;
Eterm erts_ets_colliding_names(Process*, Eterm name, Uint cnt);
-Eterm erts_ets_get_meta_state(Process* p);
-Eterm erts_ets_restore_meta_state(Process* p, Eterm target_state);
-
Uint erts_db_get_max_tabs(void);
-#endif
+#endif /* ERL_DB_H__ */
#if defined(ERTS_WANT_DB_INTERNAL__) && !defined(ERTS_HAVE_DB_INTERNAL__)
#define ERTS_HAVE_DB_INTERNAL__
@@ -271,3 +304,4 @@ erts_db_free_nt(ErtsAlcType_t type, void *ptr, Uint size)
#endif /* #if defined(ERTS_WANT_DB_INTERNAL__) && !defined(ERTS_HAVE_DB_INTERNAL__) */
+#endif /* !ERTS_ONLY_SCHED_SPEC_ETS_DATA */
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index bb29d56aa7..18405342da 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -393,20 +393,20 @@ static int db_erase_object_hash(DbTable *tbl, Eterm object,Eterm *ret);
static int db_slot_hash(Process *p, DbTable *tbl,
Eterm slot_term, Eterm *ret);
-static int db_select_chunk_hash(Process *p, DbTable *tbl,
+static int db_select_chunk_hash(Process *p, DbTable *tbl, Eterm tid,
Eterm pattern, Sint chunk_size,
int reverse, Eterm *ret);
-static int db_select_hash(Process *p, DbTable *tbl,
+static int db_select_hash(Process *p, DbTable *tbl, Eterm tid,
Eterm pattern, int reverse, Eterm *ret);
-static int db_select_count_hash(Process *p, DbTable *tbl,
+static int db_select_count_hash(Process *p, DbTable *tbl, Eterm tid,
Eterm pattern, Eterm *ret);
-static int db_select_delete_hash(Process *p, DbTable *tbl,
+static int db_select_delete_hash(Process *p, DbTable *tbl, Eterm tid,
Eterm pattern, Eterm *ret);
static int db_select_continue_hash(Process *p, DbTable *tbl,
Eterm continuation, Eterm *ret);
-static int db_select_count_continue_hash(Process *p, DbTable *tbl,
+static int db_select_count_continue_hash(Process *p, DbTable *tbl,
Eterm continuation, Eterm *ret);
static int db_select_delete_continue_hash(Process *p, DbTable *tbl,
@@ -418,7 +418,7 @@ static void db_print_hash(fmtfn_t to,
DbTable *tbl);
static int db_free_table_hash(DbTable *tbl);
-static int db_free_table_continue_hash(DbTable *tbl);
+static SWord db_free_table_continue_hash(DbTable *tbl, SWord reds);
static void db_foreach_offheap_hash(DbTable *,
@@ -529,11 +529,6 @@ DbTableMethod db_hash =
db_free_table_continue_hash,
db_print_hash,
db_foreach_offheap_hash,
-#ifdef HARDDEBUG
- db_check_table_hash,
-#else
- NULL,
-#endif
db_lookup_dbterm_hash,
db_finalize_dbterm_hash
};
@@ -581,9 +576,10 @@ static void restore_fixdel(DbTableHash* tb, FixedDeletion* fixdel)
** Table interface routines ie what's called by the bif's
*/
-void db_unfix_table_hash(DbTableHash *tb)
+SWord db_unfix_table_hash(DbTableHash *tb)
{
FixedDeletion* fixdel;
+ SWord work = 0;
ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rwlocked(&tb->common.rwlock)
|| (erts_smp_lc_rwmtx_is_rlocked(&tb->common.rwlock)
@@ -604,7 +600,7 @@ restart:
if (!IS_FIXED(tb)) {
goto restart; /* unfixed again! */
}
- return;
+ return work;
}
if (ix < NACTIVE(tb)) {
bp = &BUCKET(tb, ix);
@@ -614,6 +610,7 @@ restart:
if (b->hvalue == INVALID_HASH) {
*bp = b->next;
free_term(tb, b);
+ work++;
b = *bp;
} else {
bp = &b->next;
@@ -629,9 +626,12 @@ restart:
(void *) fx,
sizeof(FixedDeletion));
ERTS_ETS_MISC_MEM_ADD(-sizeof(FixedDeletion));
+ work++;
}
/* ToDo: Maybe try grow/shrink the table as well */
+
+ return work;
}
int db_create_hash(Process *p, DbTable *tbl)
@@ -846,7 +846,6 @@ Lnew:
grow(tb, nitems);
}
}
- CHECK_TABLES();
return DB_ERROR_NONE;
Ldone:
@@ -871,7 +870,6 @@ get_term_list(Process *p, DbTableHash *tb, Eterm key, HashValue hval,
}
}
copy = build_term_list(p, b1, b2, sz, tb);
- CHECK_TABLES();
if (bend) {
*bend = b2;
}
@@ -903,70 +901,6 @@ done:
RUNLOCK_HASH(lck);
return DB_ERROR_NONE;
}
-
-int db_get_element_array(DbTable *tbl,
- Eterm key,
- int ndex,
- Eterm *ret,
- int *num_ret)
-{
- DbTableHash *tb = &tbl->hash;
- HashValue hval;
- int ix;
- HashDbTerm* b1;
- int num = 0;
- int retval;
- erts_smp_rwmtx_t* lck;
-
- ASSERT(!IS_FIXED(tbl)); /* no support for fixed tables here */
-
- hval = MAKE_HASH(key);
- lck = RLOCK_HASH(tb, hval);
- ix = hash_to_ix(tb, hval);
- b1 = BUCKET(tb, ix);
-
- while(b1 != 0) {
- if (has_live_key(tb,b1,key,hval)) {
- if (tb->common.status & (DB_BAG | DB_DUPLICATE_BAG)) {
- HashDbTerm* b;
- HashDbTerm* b2 = b1->next;
-
- while(b2 != NULL && has_live_key(tb,b2,key,hval)) {
- if (ndex > arityval(b2->dbterm.tpl[0])) {
- retval = DB_ERROR_BADITEM;
- goto done;
- }
- b2 = b2->next;
- }
-
- b = b1;
- while(b != b2) {
- if (num < *num_ret) {
- ret[num++] = b->dbterm.tpl[ndex];
- } else {
- retval = DB_ERROR_NONE;
- goto done;
- }
- b = b->next;
- }
- *num_ret = num;
- }
- else {
- ASSERT(*num_ret > 0);
- ret[0] = b1->dbterm.tpl[ndex];
- *num_ret = 1;
- }
- retval = DB_ERROR_NONE;
- goto done;
- }
- b1 = b1->next;
- }
- retval = DB_ERROR_BADKEY;
-done:
- RUNLOCK_HASH(lck);
- return retval;
-}
-
static int db_member_hash(DbTable *tbl, Eterm key, Eterm *ret)
{
@@ -1059,54 +993,6 @@ done:
}
/*
- * Very internal interface, removes elements of arity two from
- * BAG. Used for the PID meta table
- */
-int db_erase_bag_exact2(DbTable *tbl, Eterm key, Eterm value)
-{
- DbTableHash *tb = &tbl->hash;
- HashValue hval;
- int ix;
- HashDbTerm** bp;
- HashDbTerm* b;
- erts_smp_rwmtx_t* lck;
- int found = 0;
-
- hval = MAKE_HASH(key);
- lck = WLOCK_HASH(tb,hval);
- ix = hash_to_ix(tb, hval);
- bp = &BUCKET(tb, ix);
- b = *bp;
-
- ASSERT(!IS_FIXED(tb));
- ASSERT((tb->common.status & DB_BAG));
- ASSERT(!tb->common.compress);
-
- while(b != 0) {
- if (has_live_key(tb,b,key,hval)) {
- found = 1;
- if ((arityval(b->dbterm.tpl[0]) == 2) &&
- EQ(value, b->dbterm.tpl[2])) {
- *bp = b->next;
- free_term(tb, b);
- erts_smp_atomic_dec_nob(&tb->common.nitems);
- b = *bp;
- break;
- }
- } else if (found) {
- break;
- }
- bp = &b->next;
- b = b->next;
- }
- WUNLOCK_HASH(lck);
- if (found) {
- try_shrink(tb);
- }
- return DB_ERROR_NONE;
-}
-
-/*
** NB, this is for the db_erase/2 bif.
*/
int db_erase_hash(DbTable *tbl, Eterm key, Eterm *ret)
@@ -1401,14 +1287,14 @@ trap:
}
-static int db_select_hash(Process *p, DbTable *tbl,
+static int db_select_hash(Process *p, DbTable *tbl, Eterm tid,
Eterm pattern, int reverse,
Eterm *ret)
{
- return db_select_chunk_hash(p, tbl, pattern, 0, reverse, ret);
+ return db_select_chunk_hash(p, tbl, tid, pattern, 0, reverse, ret);
}
-static int db_select_chunk_hash(Process *p, DbTable *tbl,
+static int db_select_chunk_hash(Process *p, DbTable *tbl, Eterm tid,
Eterm pattern, Sint chunk_size,
int reverse, /* not used */
Eterm *ret)
@@ -1556,7 +1442,7 @@ done:
mpb = erts_db_make_match_prog_ref(p,(mpi.mp),&hp);
if (mpi.all_objects)
(mpi.mp)->flags |= BIN_FLAG_ALL_OBJECTS;
- continuation = TUPLE6(hp, tb->common.id,make_small(slot_ix),
+ continuation = TUPLE6(hp, tid, make_small(slot_ix),
make_small(chunk_size),
mpb, rest,
make_small(rest_size));
@@ -1580,7 +1466,7 @@ trap:
(mpi.mp)->flags |= BIN_FLAG_ALL_OBJECTS;
hp = HAlloc(p,7+ERTS_MAGIC_REF_THING_SIZE);
mpb = erts_db_make_match_prog_ref(p,(mpi.mp),&hp);
- continuation = TUPLE6(hp, tb->common.id, make_small(slot_ix),
+ continuation = TUPLE6(hp, tid, make_small(slot_ix),
make_small(chunk_size),
mpb, match_list,
make_small(got));
@@ -1594,7 +1480,8 @@ trap:
}
static int db_select_count_hash(Process *p,
- DbTable *tbl,
+ DbTable *tbl,
+ Eterm tid,
Eterm pattern,
Eterm *ret)
{
@@ -1700,7 +1587,7 @@ trap:
hp += BIG_UINT_HEAP_SIZE;
}
mpb = erts_db_make_match_prog_ref(p,mpi.mp,&hp);
- continuation = TUPLE4(hp, tb->common.id, make_small(slot_ix),
+ continuation = TUPLE4(hp, tid, make_small(slot_ix),
mpb,
egot);
mpi.mp = NULL; /*otherwise the return macro will destroy it */
@@ -1713,6 +1600,7 @@ trap:
static int db_select_delete_hash(Process *p,
DbTable *tbl,
+ Eterm tid,
Eterm pattern,
Eterm *ret)
{
@@ -1845,7 +1733,7 @@ trap:
hp += BIG_UINT_HEAP_SIZE;
}
mpb = erts_db_make_match_prog_ref(p,mpi.mp,&hp);
- continuation = TUPLE4(hp, tb->common.id, make_small(slot_ix),
+ continuation = TUPLE4(hp, tid, make_small(slot_ix),
mpb,
egot);
mpi.mp = NULL; /*otherwise the return macro will destroy it */
@@ -1959,7 +1847,7 @@ trap:
egot = uint_to_big(got, hp);
hp += BIG_UINT_HEAP_SIZE;
}
- continuation = TUPLE4(hp, tb->common.id, make_small(slot_ix),
+ continuation = TUPLE4(hp, tptr[1], make_small(slot_ix),
tptr[3],
egot);
RET_TO_BIF(bif_trap1(&ets_select_delete_continue_exp, p,
@@ -2050,7 +1938,7 @@ trap:
egot = uint_to_big(got, hp);
hp += BIG_UINT_HEAP_SIZE;
}
- continuation = TUPLE4(hp, tb->common.id, make_small(slot_ix),
+ continuation = TUPLE4(hp, tptr[1], make_small(slot_ix),
tptr[3],
egot);
RET_TO_BIF(bif_trap1(&ets_select_count_continue_exp, p,
@@ -2197,19 +2085,17 @@ static void db_print_hash(fmtfn_t to, void *to_arg, int show, DbTable *tbl)
/* release all memory occupied by a single table */
static int db_free_table_hash(DbTable *tbl)
{
- while (!db_free_table_continue_hash(tbl))
+ while (db_free_table_continue_hash(tbl, ERTS_SWORD_MAX) < 0)
;
return 0;
}
-static int db_free_table_continue_hash(DbTable *tbl)
+static SWord db_free_table_continue_hash(DbTable *tbl, SWord reds)
{
DbTableHash *tb = &tbl->hash;
- int done;
FixedDeletion* fixdel = (FixedDeletion*) erts_smp_atomic_read_acqb(&tb->fixdel);
- ERTS_SMP_LC_ASSERT(IS_TAB_WLOCKED(tb));
+ ERTS_SMP_LC_ASSERT(IS_TAB_WLOCKED(tb) || (tb->common.status & DB_DELETE));
- done = 0;
while (fixdel != NULL) {
FixedDeletion *fx = fixdel;
@@ -2219,22 +2105,21 @@ static int db_free_table_continue_hash(DbTable *tbl)
(void *) fx,
sizeof(FixedDeletion));
ERTS_ETS_MISC_MEM_ADD(-sizeof(FixedDeletion));
- if (++done >= 2*DELETE_RECORD_LIMIT) {
+ if (--reds < 0) {
erts_smp_atomic_set_relb(&tb->fixdel, (erts_aint_t)fixdel);
- return 0; /* Not done */
+ return reds; /* Not done */
}
}
erts_smp_atomic_set_relb(&tb->fixdel, (erts_aint_t)NULL);
- done /= 2;
while(tb->nslots != 0) {
- done += 1 + EXT_SEGSZ/64 + free_seg(tb, 1);
+ reds -= EXT_SEGSZ/64 + free_seg(tb, 1);
/*
* If we have done enough work, get out here.
*/
- if (done >= DELETE_RECORD_LIMIT) {
- return 0; /* Not done */
+ if (reds < 0) {
+ return reds; /* Not done */
}
}
#ifdef ERTS_SMP
@@ -2249,7 +2134,7 @@ static int db_free_table_continue_hash(DbTable *tbl)
}
#endif
ASSERT(erts_smp_atomic_read_nob(&tb->common.memory_size) == sizeof(DbTable));
- return 1; /* Done */
+ return reds; /* Done */
}
@@ -3007,63 +2892,4 @@ Eterm erts_ets_hash_sizeof_ext_segtab(void)
{
return make_small(((SIZEOF_EXT_SEGTAB(0)-1) / sizeof(UWord)) + 1);
}
-/* For testing only */
-Eterm erts_ets_hash_get_memstate(Process* p, DbTableHash* tb)
-{
- Eterm seg_cnt;
- while (!begin_resizing(tb))
- /*spinn*/;
-
- seg_cnt = make_small(SLOT_IX_TO_SEG_IX(tb->nslots));
- done_resizing(tb);
- return seg_cnt;
-}
-/* For testing only */
-Eterm erts_ets_hash_restore_memstate(DbTableHash* tb, Eterm memstate)
-{
- int seg_cnt, target;
-
- if (!is_small(memstate))
- return make_small(__LINE__);
-
- target = signed_val(memstate);
- if (target < 1)
- return make_small(__LINE__);
- while (1) {
- while (!begin_resizing(tb))
- /*spin*/;
- seg_cnt = SLOT_IX_TO_SEG_IX(tb->nslots);
- done_resizing(tb);
-
- if (target == seg_cnt)
- return am_ok;
- if (IS_FIXED(tb))
- return make_small(__LINE__);
- if (target < seg_cnt)
- shrink(tb, 0);
- else
- grow(tb, INT_MAX);
- }
-}
-#ifdef HARDDEBUG
-
-void db_check_table_hash(DbTable *tbl)
-{
- DbTableHash *tb = &tbl->hash;
- HashDbTerm* list;
- int j;
-
- for (j = 0; j < NACTIVE(tb); j++) {
- if ((list = BUCKET(tb,j)) != 0) {
- while (list != 0) {
- if (!is_tuple(make_tuple(list->dbterm.tpl))) {
- erts_exit(ERTS_ERROR_EXIT, "Bad term in slot %d of ets table", j);
- }
- list = list->next;
- }
- }
- }
-}
-
-#endif
diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h
index 6d25c73549..c340c72311 100644
--- a/erts/emulator/beam/erl_db_hash.h
+++ b/erts/emulator/beam/erl_db_hash.h
@@ -75,7 +75,7 @@ typedef struct db_table_hash {
** table types. The process is always an [in out] parameter.
*/
void db_initialize_hash(void);
-void db_unfix_table_hash(DbTableHash *tb /* [in out] */);
+SWord db_unfix_table_hash(DbTableHash *tb);
Uint db_kept_items_hash(DbTableHash *tb);
/* Interface for meta pid table */
@@ -88,14 +88,6 @@ int db_get_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret);
int db_erase_hash(DbTable *tbl, Eterm key, Eterm *ret);
-int db_get_element_array(DbTable *tbl,
- Eterm key,
- int ndex,
- Eterm *ret,
- int *num_ret);
-
-int db_erase_bag_exact2(DbTable *tbl, Eterm key, Eterm value);
-
/* not yet in method table */
int db_mark_all_deleted_hash(DbTable *tbl);
@@ -110,7 +102,5 @@ typedef struct {
void db_calc_stats_hash(DbTableHash* tb, DbHashStats*);
Eterm erts_ets_hash_sizeof_ext_segtab(void);
-Eterm erts_ets_hash_get_memstate(Process*, DbTableHash* tb);
-Eterm erts_ets_hash_restore_memstate(DbTableHash* tb, Eterm memstate);
#endif /* _DB_HASH_H */
diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c
index c4ecd2ba37..14a7451267 100644
--- a/erts/emulator/beam/erl_db_tree.c
+++ b/erts/emulator/beam/erl_db_tree.c
@@ -180,7 +180,6 @@ static ERTS_INLINE TreeDbTerm* replace_dbterm(DbTableTree *tb, TreeDbTerm* old,
static TreeDbTerm *traverse_until(TreeDbTerm *t, int *current, int to);
static void check_slot_pos(DbTableTree *tb);
static void check_saved_stack(DbTableTree *tb);
-static int check_table_tree(DbTableTree* tb, TreeDbTerm *t);
#define TREE_DEBUG
#endif
@@ -283,7 +282,7 @@ struct select_delete_context {
static TreeDbTerm *linkout_tree(DbTableTree *tb, Eterm key);
static TreeDbTerm *linkout_object_tree(DbTableTree *tb,
Eterm object);
-static int do_free_tree_cont(DbTableTree *tb, int num_left);
+static SWord do_free_tree_continue(DbTableTree *tb, SWord reds);
static void free_term(DbTableTree *tb, TreeDbTerm* p);
static int balance_left(TreeDbTerm **this);
static int balance_right(TreeDbTerm **this);
@@ -369,18 +368,18 @@ static int db_erase_tree(DbTable *tbl, Eterm key, Eterm *ret);
static int db_erase_object_tree(DbTable *tbl, Eterm object,Eterm *ret);
static int db_slot_tree(Process *p, DbTable *tbl,
Eterm slot_term, Eterm *ret);
-static int db_select_tree(Process *p, DbTable *tbl,
+static int db_select_tree(Process *p, DbTable *tbl, Eterm tid,
Eterm pattern, int reversed, Eterm *ret);
-static int db_select_count_tree(Process *p, DbTable *tbl,
+static int db_select_count_tree(Process *p, DbTable *tbl, Eterm tid,
Eterm pattern, Eterm *ret);
-static int db_select_chunk_tree(Process *p, DbTable *tbl,
+static int db_select_chunk_tree(Process *p, DbTable *tbl, Eterm tid,
Eterm pattern, Sint chunk_size,
int reversed, Eterm *ret);
static int db_select_continue_tree(Process *p, DbTable *tbl,
Eterm continuation, Eterm *ret);
static int db_select_count_continue_tree(Process *p, DbTable *tbl,
Eterm continuation, Eterm *ret);
-static int db_select_delete_tree(Process *p, DbTable *tbl,
+static int db_select_delete_tree(Process *p, DbTable *tbl, Eterm tid,
Eterm pattern, Eterm *ret);
static int db_select_delete_continue_tree(Process *p, DbTable *tbl,
Eterm continuation, Eterm *ret);
@@ -389,7 +388,7 @@ static void db_print_tree(fmtfn_t to, void *to_arg,
int show, DbTable *tbl);
static int db_free_table_tree(DbTable *tbl);
-static int db_free_table_continue_tree(DbTable *tbl);
+static SWord db_free_table_continue_tree(DbTable *tbl, SWord);
static void db_foreach_offheap_tree(DbTable *,
void (*)(ErlOffHeap *, void *),
@@ -442,11 +441,6 @@ DbTableMethod db_tree =
db_free_table_continue_tree,
db_print_tree,
db_foreach_offheap_tree,
-#ifdef HARDDEBUG
- db_check_table_tree,
-#else
- NULL,
-#endif
db_lookup_dbterm_tree,
db_finalize_dbterm_tree
@@ -1058,7 +1052,7 @@ static int db_select_continue_tree(Process *p,
}
-static int db_select_tree(Process *p, DbTable *tbl,
+static int db_select_tree(Process *p, DbTable *tbl, Eterm tid,
Eterm pattern, int reverse, Eterm *ret)
{
/* Strategy: Traverse backwards to build resulting list from tail to head */
@@ -1151,7 +1145,7 @@ static int db_select_tree(Process *p, DbTable *tbl,
continuation = TUPLE8
(hp,
- tb->common.id,
+ tid,
key,
sc.end_condition, /* From the match program, needn't be copied */
make_small(0), /* Chunk size of zero means not chunked to the
@@ -1263,7 +1257,7 @@ static int db_select_count_continue_tree(Process *p,
}
-static int db_select_count_tree(Process *p, DbTable *tbl,
+static int db_select_count_tree(Process *p, DbTable *tbl, Eterm tid,
Eterm pattern, Eterm *ret)
{
DbTableTree *tb = &tbl->tree;
@@ -1349,7 +1343,7 @@ static int db_select_count_tree(Process *p, DbTable *tbl,
continuation = TUPLE5
(hp,
- tb->common.id,
+ tid,
key,
sc.end_condition, /* From the match program, needn't be copied */
mpb,
@@ -1363,7 +1357,7 @@ static int db_select_count_tree(Process *p, DbTable *tbl,
}
-static int db_select_chunk_tree(Process *p, DbTable *tbl,
+static int db_select_chunk_tree(Process *p, DbTable *tbl, Eterm tid,
Eterm pattern, Sint chunk_size,
int reverse,
Eterm *ret)
@@ -1474,7 +1468,7 @@ static int db_select_chunk_tree(Process *p, DbTable *tbl,
continuation = TUPLE8
(hp,
- tb->common.id,
+ tid,
key,
sc.end_condition, /* From the match program,
needn't be copied */
@@ -1499,7 +1493,7 @@ static int db_select_chunk_tree(Process *p, DbTable *tbl,
mpb = erts_db_make_match_prog_ref(p,mpi.mp,&hp);
continuation = TUPLE8
(hp,
- tb->common.id,
+ tid,
key,
sc.end_condition, /* From the match program, needn't be copied */
make_small(chunk_size),
@@ -1605,7 +1599,7 @@ static int db_select_delete_continue_tree(Process *p,
#undef RET_TO_BIF
}
-static int db_select_delete_tree(Process *p, DbTable *tbl,
+static int db_select_delete_tree(Process *p, DbTable *tbl, Eterm tid,
Eterm pattern, Eterm *ret)
{
DbTableTree *tb = &tbl->tree;
@@ -1691,7 +1685,7 @@ static int db_select_delete_tree(Process *p, DbTable *tbl,
continuation = TUPLE5
(hp,
- tb->common.id,
+ tid,
key,
sc.end_condition, /* From the match program, needn't be copied */
mpb,
@@ -1757,23 +1751,22 @@ static void db_print_tree(fmtfn_t to, void *to_arg,
/* release all memory occupied by a single table */
static int db_free_table_tree(DbTable *tbl)
{
- while (!db_free_table_continue_tree(tbl))
+ while (db_free_table_continue_tree(tbl, ERTS_SWORD_MAX) < 0)
;
return 1;
}
-static int db_free_table_continue_tree(DbTable *tbl)
+static SWord db_free_table_continue_tree(DbTable *tbl, SWord reds)
{
DbTableTree *tb = &tbl->tree;
- int result;
if (!tb->deletion) {
tb->static_stack.pos = 0;
tb->deletion = 1;
PUSH_NODE(&tb->static_stack, tb->root);
}
- result = do_free_tree_cont(tb, DELETE_RECORD_LIMIT);
- if (result) { /* Completely done. */
+ reds = do_free_tree_continue(tb, reds);
+ if (reds >= 0) { /* Completely done. */
erts_db_free(ERTS_ALC_T_DB_STK,
(DbTable *) tb,
(void *) tb->static_stack.array,
@@ -1781,7 +1774,7 @@ static int db_free_table_continue_tree(DbTable *tbl)
ASSERT(erts_smp_atomic_read_nob(&tb->common.memory_size)
== sizeof(DbTable));
}
- return result;
+ return reds;
}
static int db_delete_all_objects_tree(Process* p, DbTable* tbl)
@@ -2064,7 +2057,7 @@ static int analyze_pattern(DbTableTree *tb, Eterm pattern,
return DB_ERROR_NONE;
}
-static int do_free_tree_cont(DbTableTree *tb, int num_left)
+static SWord do_free_tree_continue(DbTableTree *tb, SWord reds)
{
TreeDbTerm *root;
TreeDbTerm *p;
@@ -2083,15 +2076,14 @@ static int do_free_tree_cont(DbTableTree *tb, int num_left)
root = p;
} else {
free_term(tb, root);
- if (--num_left > 0) {
- break;
- } else {
- return 0; /* Done enough for now */
- }
+ if (--reds < 0) {
+ return reds; /* Done enough for now */
+ }
+ break;
}
}
}
- return 1;
+ return reds;
}
/*
@@ -3130,6 +3122,9 @@ static void do_dump_tree2(DbTableTree* tb, int to, void *to_arg, int show,
#ifdef HARDDEBUG
+/*
+ * No called, but kept as it might come to use
+ */
void db_check_table_tree(DbTable *tbl)
{
DbTableTree *tb = &tbl->tree;
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 471fefe3cb..72298842d7 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -75,9 +75,6 @@ typedef struct db_term {
*/
} DbTerm;
-union db_table;
-typedef union db_table DbTable;
-
#define DB_MUST_RESIZE 1
#define DB_NEW_OBJECT 2
#define DB_INC_TRY_GROW 4
@@ -138,30 +135,34 @@ typedef struct db_table_method
Eterm slot,
Eterm* ret);
int (*db_select_chunk)(Process* p,
- DbTable* tb, /* [in out] */
+ DbTable* tb, /* [in out] */
+ Eterm tid,
Eterm pattern,
Sint chunk_size,
int reverse,
Eterm* ret);
int (*db_select)(Process* p,
- DbTable* tb, /* [in out] */
+ DbTable* tb, /* [in out] */
+ Eterm tid,
Eterm pattern,
int reverse,
Eterm* ret);
int (*db_select_delete)(Process* p,
- DbTable* tb, /* [in out] */
+ DbTable* tb, /* [in out] */
+ Eterm tid,
Eterm pattern,
Eterm* ret);
int (*db_select_continue)(Process* p,
- DbTable* tb, /* [in out] */
+ DbTable* tb, /* [in out] */
Eterm continuation,
Eterm* ret);
int (*db_select_delete_continue)(Process* p,
- DbTable* tb, /* [in out] */
+ DbTable* tb, /* [in out] */
Eterm continuation,
Eterm* ret);
int (*db_select_count)(Process* p,
- DbTable* tb, /* [in out] */
+ DbTable* tb, /* [in out] */
+ Eterm tid,
Eterm pattern,
Eterm* ret);
int (*db_select_count_continue)(Process* p,
@@ -174,7 +175,7 @@ typedef struct db_table_method
DbTable* db /* [in out] */ );
int (*db_free_table)(DbTable* db /* [in out] */ );
- int (*db_free_table_continue)(DbTable* db); /* [in out] */
+ SWord (*db_free_table_continue)(DbTable* db, SWord reds);
void (*db_print)(fmtfn_t to,
void* to_arg,
@@ -184,7 +185,6 @@ typedef struct db_table_method
void (*db_foreach_offheap)(DbTable* db, /* [in out] */
void (*func)(ErlOffHeap *, void *),
void *arg);
- void (*db_check_table)(DbTable* tb);
/* Lookup a dbterm for updating. Return false if not found. */
int (*db_lookup_dbterm)(Process *, DbTable *, Eterm key, Eterm obj,
@@ -198,11 +198,27 @@ typedef struct db_table_method
} DbTableMethod;
typedef struct db_fixation {
- Eterm pid;
+ /* Node in fixed_tabs list */
+ struct {
+ struct db_fixation *next, *prev;
+ Binary* btid;
+ } tabs;
+
+ /* Node in fixing_procs tree */
+ struct {
+ struct db_fixation *left, *right, *parent;
+ int is_red;
+ Process* p;
+ } procs;
+
Uint counter;
- struct db_fixation *next;
} DbFixation;
+typedef struct {
+ DbTable *next;
+ DbTable *prev;
+} DbTableList;
+
/*
* This structure contains data for all different types of database
* tables. Note that these fields must match the same fields
@@ -212,10 +228,13 @@ typedef struct db_fixation {
*/
typedef struct db_table_common {
- erts_smp_refc_t ref; /* fixation counter */
+ erts_smp_refc_t refc; /* reference count of table struct */
+ erts_smp_refc_t fix_count;/* fixation counter */
+ DbTableList all;
+ DbTableList owned;
#ifdef ERTS_SMP
erts_smp_rwmtx_t rwlock; /* rw lock on table */
- erts_smp_mtx_t fixlock; /* Protects fixations,megasec,sec,microsec */
+ erts_smp_mtx_t fixlock; /* Protects fixing_procs and time */
int is_thread_safe; /* No fine locking inside table needed */
Uint32 type; /* table type, *read only* after creation */
#endif
@@ -224,7 +243,7 @@ typedef struct db_table_common {
UWord heir_data; /* To send in ETS-TRANSFER (is_immed or (DbTerm*) */
Uint64 heir_started_interval; /* To further identify the heir */
Eterm the_name; /* an atom */
- Eterm id; /* atom | integer */
+ Binary *btid;
DbTableMethod* meth; /* table methods */
erts_smp_atomic_t nitems; /* Total number of items in table */
erts_smp_atomic_t memory_size;/* Total memory size. NOTE: in bytes! */
@@ -232,36 +251,35 @@ typedef struct db_table_common {
ErtsMonotonicTime monotonic;
ErtsMonotonicTime offset;
} time;
- DbFixation* fixations; /* List of processes who have done safe_fixtable,
+ DbFixation* fixing_procs; /* Tree of processes who have done safe_fixtable,
"local" fixations not included. */
/* All 32-bit fields */
Uint32 status; /* bit masks defined below */
- int slot; /* slot index in meta_main_tab */
int keypos; /* defaults to 1 */
int compress;
} DbTableCommon;
/* These are status bit patterns */
-#define DB_NORMAL (1 << 0)
-#define DB_PRIVATE (1 << 1)
-#define DB_PROTECTED (1 << 2)
-#define DB_PUBLIC (1 << 3)
-#define DB_BAG (1 << 4)
-#define DB_SET (1 << 5)
-/*#define DB_LHASH (1 << 6)*/
-#define DB_FINE_LOCKED (1 << 7) /* fine grained locking enabled */
-#define DB_DUPLICATE_BAG (1 << 8)
-#define DB_ORDERED_SET (1 << 9)
-#define DB_DELETE (1 << 10) /* table is being deleted */
-#define DB_FREQ_READ (1 << 11)
-
-#define ERTS_ETS_TABLE_TYPES (DB_BAG|DB_SET|DB_DUPLICATE_BAG|DB_ORDERED_SET|DB_FINE_LOCKED|DB_FREQ_READ)
+#define DB_PRIVATE (1 << 0)
+#define DB_PROTECTED (1 << 1)
+#define DB_PUBLIC (1 << 2)
+#define DB_DELETE (1 << 3) /* table is being deleted */
+#define DB_SET (1 << 4)
+#define DB_BAG (1 << 5)
+#define DB_DUPLICATE_BAG (1 << 6)
+#define DB_ORDERED_SET (1 << 7)
+#define DB_FINE_LOCKED (1 << 8) /* write_concurrency */
+#define DB_FREQ_READ (1 << 9) /* read_concurrency */
+#define DB_NAMED_TABLE (1 << 10)
+
+#define ERTS_ETS_TABLE_TYPES (DB_BAG|DB_SET|DB_DUPLICATE_BAG|DB_ORDERED_SET\
+ |DB_FINE_LOCKED|DB_FREQ_READ|DB_NAMED_TABLE)
#define IS_HASH_TABLE(Status) (!!((Status) & \
(DB_BAG | DB_SET | DB_DUPLICATE_BAG)))
#define IS_TREE_TABLE(Status) (!!((Status) & \
DB_ORDERED_SET))
-#define NFIXED(T) (erts_smp_refc_read(&(T)->common.ref,0))
+#define NFIXED(T) (erts_smp_refc_read(&(T)->common.fix_count,0))
#define IS_FIXED(T) (NFIXED(T) != 0)
/*
@@ -506,14 +524,5 @@ erts_db_get_match_prog_binary(Eterm term)
#define Binary2MatchProg(BP) \
(ASSERT(IsMatchProgBinary((BP))), \
((MatchProg *) ERTS_MAGIC_BIN_DATA((BP))))
-/*
-** Debugging
-*/
-#ifdef HARDDEBUG
-void db_check_tables(void); /* in db.c */
-#define CHECK_TABLES() db_check_tables()
-#else
-#define CHECK_TABLES()
-#endif
#endif /* _DB_UTIL_H */
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index b2c307e826..61477af316 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -2307,6 +2307,7 @@ erl_start(int argc, char **argv)
#endif
set_main_stack_size();
erts_sched_init_time_sup(esdp);
+ erts_ets_sched_spec_data_init(esdp);
process_main(esdp->x_reg_array, esdp->f_reg_array);
}
#endif
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index 6ff9aea5ab..da73469516 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -100,14 +100,12 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "dist_entry_links", "address" },
{ "code_write_permission", NULL },
{ "purge_state", NULL },
+ { "meta_name_tab", "address" },
+ { "db_tab", "address" },
{ "proc_status", "pid" },
{ "proc_trace", "pid" },
{ "ports_snapshot", NULL },
- { "meta_name_tab", "address" },
- { "meta_main_tab_slot", "address" },
- { "db_tab", "address" },
{ "db_tab_fix", "address" },
- { "meta_main_tab_main", NULL },
{ "db_hash_slot", "address" },
{ "node_table", NULL },
{ "dist_table", NULL },
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index e2072fe30f..89b627aaf5 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -850,14 +850,15 @@ static Eterm AM_timer;
static Eterm AM_delayed_delete_timer;
static void setup_reference_table(void);
-static Eterm reference_table_term(Uint **hpp, Uint *szp);
+static Eterm reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp);
static void delete_reference_table(void);
-#if BIG_UINT_HEAP_SIZE > 3 /* 2-tuple */
-#define ID_HEAP_SIZE BIG_UINT_HEAP_SIZE
-#else
-#define ID_HEAP_SIZE 3 /* 2-tuple */
-#endif
+#undef ERTS_MAX__
+#define ERTS_MAX__(A, B) ((A) > (B) ? (A) : (B))
+
+#define ID_HEAP_SIZE \
+ ERTS_MAX__(ERTS_MAGIC_REF_THING_SIZE, \
+ ERTS_MAX__(BIG_UINT_HEAP_SIZE, 3))
typedef struct node_referrer_ {
struct node_referrer_ *next;
@@ -870,6 +871,7 @@ typedef struct node_referrer_ {
int system_ref;
Eterm id;
Uint id_heap[ID_HEAP_SIZE];
+ ErlOffHeap off_heap;
} NodeReferrer;
typedef struct {
@@ -942,7 +944,7 @@ erts_get_node_and_dist_references(struct process *proc)
/* Get term size */
size = 0;
- (void) reference_table_term(NULL, &size);
+ (void) reference_table_term(NULL, NULL, &size);
hp = HAlloc(proc, size);
#ifdef DEBUG
@@ -951,7 +953,7 @@ erts_get_node_and_dist_references(struct process *proc)
#endif
/* Write term */
- res = reference_table_term(&hp, NULL);
+ res = reference_table_term(&hp, &proc->off_heap, NULL);
ASSERT(endp == hp);
@@ -1048,13 +1050,14 @@ insert_node_referrer(ReferredNode *referred_node, int type, Eterm id)
nrp = (NodeReferrer *) erts_alloc(ERTS_ALC_T_NC_TMP,
sizeof(NodeReferrer));
nrp->next = referred_node->referrers;
+ ERTS_INIT_OFF_HEAP(&nrp->off_heap);
referred_node->referrers = nrp;
if(IS_CONST(id))
nrp->id = id;
else {
Uint *hp = &nrp->id_heap[0];
- ASSERT(is_big(id) || is_tuple(id));
- nrp->id = copy_struct(id, size_object(id), &hp, NULL);
+ ASSERT(is_big(id) || is_tuple(id) || is_internal_magic_ref(id));
+ nrp->id = copy_struct(id, size_object(id), &hp, &nrp->off_heap);
}
nrp->heap_ref = 0;
nrp->link_ref = 0;
@@ -1211,10 +1214,20 @@ insert_links2(ErtsLink *lnk, Eterm id)
static void
insert_ets_table(DbTable *tab, void *unused)
{
+ ErlOffHeap off_heap;
+ Eterm heap[ERTS_MAGIC_REF_THING_SIZE];
struct insert_offheap2_arg a;
a.type = ETS_REF;
- a.id = tab->common.id;
+ if (tab->common.status & DB_NAMED_TABLE)
+ a.id = tab->common.the_name;
+ else {
+ Eterm *hp = &heap[0];
+ ERTS_INIT_OFF_HEAP(&off_heap);
+ a.id = erts_mk_magic_ref(&hp, &off_heap, tab->common.btid);
+ }
erts_db_foreach_offheap(tab, insert_offheap2, (void *) &a);
+ if (is_not_atom(a.id))
+ erts_cleanup_offheap(&off_heap);
}
static void
@@ -1518,7 +1531,7 @@ setup_reference_table(void)
*/
static Eterm
-reference_table_term(Uint **hpp, Uint *szp)
+reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp)
{
#undef MK_2TUP
#undef MK_3TUP
@@ -1573,12 +1586,11 @@ reference_table_term(Uint **hpp, Uint *szp)
nrid = nrp->id;
if (!IS_CONST(nrp->id)) {
-
Uint nrid_sz = size_object(nrp->id);
if (szp)
*szp += nrid_sz;
if (hpp)
- nrid = copy_struct(nrp->id, nrid_sz, hpp, NULL);
+ nrid = copy_struct(nrp->id, nrid_sz, hpp, ohp);
}
if (is_internal_pid(nrid) || nrid == am_error_logger) {
@@ -1713,6 +1725,7 @@ delete_reference_table(void)
NodeReferrer *tnrp;
nrp = referred_nodes[i].referrers;
while(nrp) {
+ erts_cleanup_offheap(&nrp->off_heap);
tnrp = nrp;
nrp = nrp->next;
erts_free(ERTS_ALC_T_NC_TMP, (void *) tnrp);
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 52d9f9ddf7..88fae30845 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -449,7 +449,8 @@ typedef enum {
ERTS_PSTT_CPC, /* Check Process Code */
ERTS_PSTT_CLA, /* Copy Literal Area */
ERTS_PSTT_COHMQ, /* Change off heap message queue */
- ERTS_PSTT_FTMQ /* Flush trace msg queue */
+ ERTS_PSTT_FTMQ, /* Flush trace msg queue */
+ ERTS_PSTT_ETS_FREE_FIXATION
} ErtsProcSysTaskType;
#define ERTS_MAX_PROC_SYS_TASK_ARGS 2
@@ -602,6 +603,7 @@ dbg_chk_aux_work_val(erts_aint32_t value)
valid |= ERTS_SSI_AUX_WORK_REAP_PORTS;
#endif
valid |= ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED;
+ valid |= ERTS_SSI_AUX_WORK_YIELD;
if (~valid & value)
erts_exit(ERTS_ABORT_EXIT,
@@ -690,6 +692,8 @@ erts_pre_init_process(void)
= "SET_TMO";
erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX]
= "MSEG_CACHE_CHECK";
+ erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_YIELD_IX]
+ = "YIELD";
erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_REAP_PORTS_IX]
= "REAP_PORTS";
erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED_IX]
@@ -726,6 +730,16 @@ erts_pre_init_process(void)
= ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS;
erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].set_locks
= ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS;
+
+ erts_psd_required_locks[ERTS_PSD_ETS_OWNED_TABLES].get_locks
+ = ERTS_PSD_ETS_OWNED_TABLES_GET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_ETS_OWNED_TABLES].set_locks
+ = ERTS_PSD_ETS_OWNED_TABLES_SET_LOCKS;
+
+ erts_psd_required_locks[ERTS_PSD_ETS_FIXED_TABLES].get_locks
+ = ERTS_PSD_ETS_FIXED_TABLES_GET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_ETS_FIXED_TABLES].set_locks
+ = ERTS_PSD_ETS_FIXED_TABLES_SET_LOCKS;
#endif
}
@@ -2557,6 +2571,48 @@ handle_reap_ports(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
return aux_work & ~ERTS_SSI_AUX_WORK_REAP_PORTS;
}
+void
+erts_notify_new_aux_yield_work(ErtsSchedulerData *esdp)
+{
+ ASSERT(esdp == erts_get_scheduler_data());
+ /* Always called by the scheduler itself... */
+ set_aux_work_flags_wakeup_nob(esdp->ssi, ERTS_SSI_AUX_WORK_YIELD);
+}
+
+static ERTS_INLINE erts_aint32_t
+handle_yield(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting)
+{
+ int yield = 0;
+ /*
+ * Yield operations are always requested by the scheduler itself.
+ *
+ * The following handlers should *not* set the ERTS_SSI_AUX_WORK_YIELD
+ * flag in order to indicate more work. They should instead return
+ * information so this "main handler" can manipulate the flag...
+ *
+ * The following handlers should be able to handle being called
+ * even though no work is to be done...
+ */
+
+ /* Various yielding operations... */
+
+ yield |= erts_handle_yielded_ets_all_request(awdp->esdp,
+ &awdp->yield.ets_all);
+
+ /*
+ * Other yielding operations...
+ *
+ */
+
+ if (!yield) {
+ unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_YIELD);
+ return aux_work & ~ERTS_SSI_AUX_WORK_YIELD;
+ }
+
+ return aux_work;
+}
+
+
#if HAVE_ERTS_MSEG
static ERTS_INLINE erts_aint32_t
@@ -2697,6 +2753,9 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting)
handle_mseg_cache_check);
#endif
+ HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_YIELD,
+ handle_yield);
+
HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_REAP_PORTS,
handle_reap_ports);
@@ -8727,6 +8786,8 @@ sched_thread_func(void *vesdp)
ERTS_VERIFY_UNUSED_TEMP_ALLOC(NULL);
#endif
+ erts_ets_sched_spec_data_init(esdp);
+
process_main(esdp->x_reg_array, esdp->f_reg_array);
/* No schedulers should *ever* terminate */
@@ -11148,6 +11209,12 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
st_res = am_true;
break;
#endif
+#ifdef ERTS_SMP
+ case ERTS_PSTT_ETS_FREE_FIXATION:
+ reds -= erts_db_execute_free_fixation(c_p, (DbFixation*)st->arg[0]);
+ st_res = am_true;
+ break;
+#endif
default:
ERTS_INTERNAL_ERROR("Invalid process sys task type");
st_res = am_false;
@@ -11199,7 +11266,8 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds)
case ERTS_PSTT_GC_MAJOR:
case ERTS_PSTT_GC_MINOR:
case ERTS_PSTT_CPC:
- case ERTS_PSTT_COHMQ:
+ case ERTS_PSTT_COHMQ:
+ case ERTS_PSTT_ETS_FREE_FIXATION:
st_res = am_false;
break;
case ERTS_PSTT_CLA:
@@ -11553,13 +11621,12 @@ erts_internal_request_system_task_4(BIF_ALIST_4)
}
static void
-erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type)
+erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type, void* arg)
{
Process *rp = erts_proc_lookup(pid);
if (rp) {
ErtsProcSysTask *st;
erts_aint32_t state, fail_state;
- int i;
st = erts_alloc(ERTS_ALC_T_PROC_SYS_TSK,
ERTS_PROC_SYS_TASK_SIZE(0));
@@ -11568,8 +11635,7 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type)
st->reply_tag = NIL;
st->req_id = NIL;
st->req_id_sz = 0;
- for (i = 0; i < ERTS_MAX_PROC_SYS_TASK_ARGS; i++)
- st->arg[i] = NIL;
+ st->arg[0] = (Eterm)arg;
ERTS_INIT_OFF_HEAP(&st->off_heap);
state = erts_smp_atomic32_read_nob(&rp->state);
@@ -11585,7 +11651,13 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type)
void
erts_schedule_complete_off_heap_message_queue_change(Eterm pid)
{
- erts_schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ);
+ erts_schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ, NULL);
+}
+
+void
+erts_schedule_ets_free_fixation(Eterm pid, DbFixation* fix)
+{
+ erts_schedule_generic_sys_task(pid, ERTS_PSTT_ETS_FREE_FIXATION, fix);
}
#ifdef ERTS_DIRTY_SCHEDULERS
@@ -11633,7 +11705,7 @@ erts_schedule_flush_trace_messages(Process *proc, int force_on_proc)
dhndl = erts_thr_progress_unmanaged_delay();
#endif
- erts_schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ);
+ erts_schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ, NULL);
#ifdef ERTS_SMP
erts_thr_progress_unmanaged_continue(dhndl);
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 8bf372dad5..baf830615d 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -63,6 +63,9 @@ typedef struct process Process;
#define ERTS_ONLY_INCLUDE_TRACE_FLAGS
#include "erl_trace.h"
#undef ERTS_ONLY_INCLUDE_TRACE_FLAGS
+#define ERTS_ONLY_SCHED_SPEC_ETS_DATA
+#include "erl_db.h"
+#undef ERTS_ONLY_SCHED_SPEC_ETS_DATA
#ifdef HIPE
#include "hipe_process.h"
@@ -312,6 +315,7 @@ typedef enum {
ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX,
ERTS_SSI_AUX_WORK_SET_TMO_IX,
ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX,
+ ERTS_SSI_AUX_WORK_YIELD_IX,
ERTS_SSI_AUX_WORK_REAP_PORTS_IX,
ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED_IX, /* SHOULD be last flag index */
@@ -348,6 +352,8 @@ typedef enum {
(((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_SET_TMO_IX)
#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK \
(((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX)
+#define ERTS_SSI_AUX_WORK_YIELD \
+ (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_YIELD_IX)
#define ERTS_SSI_AUX_WORK_REAP_PORTS \
(((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_REAP_PORTS_IX)
#define ERTS_SSI_AUX_WORK_DEBUG_WAIT_COMPLETED \
@@ -613,6 +619,10 @@ typedef struct {
} delayed_wakeup;
#endif
struct {
+ ErtsEtsAllYieldData ets_all;
+ /* Other yielding operations... */
+ } yield;
+ struct {
struct {
erts_aint32_t flags;
void (*callback)(void *);
@@ -621,6 +631,10 @@ typedef struct {
} debug;
} ErtsAuxWorkData;
+#define ERTS_SCHED_AUX_YIELD_DATA(ESDP, NAME) \
+ (&(ESDP)->aux_work_data.yield.NAME)
+void erts_notify_new_aux_yield_work(ErtsSchedulerData *esdp);
+
#ifdef ERTS_DIRTY_SCHEDULERS
typedef enum {
ERTS_DIRTY_CPU_SCHEDULER,
@@ -688,7 +702,7 @@ struct ErtsSchedulerData_ {
ErtsSchedWallTime sched_wall_time;
ErtsGCInfo gc_info;
ErtsPortTaskHandle nosuspend_port_task_handle;
-
+ ErtsEtsTables ets_tables;
#ifdef ERTS_DO_VERIFY_UNUSED_TEMP_ALLOC
erts_alloc_verify_func_t verify_unused_temp_alloc;
Allctr_t *verify_unused_temp_alloc_data;
@@ -822,14 +836,16 @@ erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi)
#define ERTS_PSD_CALL_TIME_BP 3
#define ERTS_PSD_DELAYED_GC_TASK_QS 4
#define ERTS_PSD_NIF_TRAP_EXPORT 5
-#define ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF 6
+#define ERTS_PSD_ETS_OWNED_TABLES 6
+#define ERTS_PSD_ETS_FIXED_TABLES 7
+#define ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF 8
-#define ERTS_PSD_SIZE 7
+#define ERTS_PSD_SIZE 9
#if !defined(HIPE)
# undef ERTS_PSD_SUSPENDED_SAVED_CALLS_BUF
# undef ERTS_PSD_SIZE
-# define ERTS_PSD_SIZE 6
+# define ERTS_PSD_SIZE 8
#endif
typedef struct {
@@ -857,6 +873,12 @@ typedef struct {
#define ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS ERTS_PROC_LOCK_MAIN
#define ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS ERTS_PROC_LOCK_MAIN
+#define ERTS_PSD_ETS_OWNED_TABLES_GET_LOCKS ERTS_PROC_LOCK_STATUS
+#define ERTS_PSD_ETS_OWNED_TABLES_SET_LOCKS ERTS_PROC_LOCK_STATUS
+
+#define ERTS_PSD_ETS_FIXED_TABLES_GET_LOCKS ERTS_PROC_LOCK_MAIN
+#define ERTS_PSD_ETS_FIXED_TABLES_SET_LOCKS ERTS_PROC_LOCK_MAIN
+
typedef struct {
ErtsProcLocks get_locks;
ErtsProcLocks set_locks;
@@ -1782,6 +1804,8 @@ void erts_schedule_thr_prgr_later_cleanup_op(void (*)(void *),
ErtsThrPrgrLaterOp *,
UWord);
void erts_schedule_complete_off_heap_message_queue_change(Eterm pid);
+struct db_fixation;
+void erts_schedule_ets_free_fixation(Eterm pid, struct db_fixation*);
void erts_schedule_flush_trace_messages(Process *proc, int force_on_proc);
int erts_flush_trace_messages(Process *c_p, ErtsProcLocks locks);
diff --git a/erts/emulator/beam/erl_rbtree.h b/erts/emulator/beam/erl_rbtree.h
index 5fefaea978..6a42853957 100644
--- a/erts/emulator/beam/erl_rbtree.h
+++ b/erts/emulator/beam/erl_rbtree.h
@@ -105,7 +105,10 @@
* <ERTS_RBT_PREFIX>_rbt_yield_state_t.
*
* The yield state should be statically initialized by
- * ERTS_RBT_YIELD_STAT_INITER.
+ * ERTS_RBT_YIELD_STAT_INITER
+ *
+ * or dynamically initialized with
+ * ERTS_RBT_YIELD_STAT_INIT(<ERTS_RBT_PREFIX>_rbt_yield_state_t *ystate)
*
*
* The following API functions are implemented if corresponding
@@ -178,8 +181,8 @@
* Operate by calling the operator 'op' on each element.
* Order is undefined.
*
- * Yield when 'ylimit' elements has been processed. Zero is
- * returned when yielding, and a non-zero value is returned when
+ * Yield when 'ylimit' elements has been processed. True is
+ * returned when yielding, and false is returned when
* the whole tree has been processed. The tree should not be
* modified until all of it has been processed.
*
@@ -195,8 +198,8 @@
* Order is undefined. Each element should be destroyed
* by 'op'.
*
- * Yield when 'ylimit' elements has been processed. Zero is
- * returned when yielding, and a non-zero value is returned when
+ * Yield when 'ylimit' elements has been processed. True is
+ * returned when yielding, and false is returned when
* the whole tree has been processed.
*
* 'arg' is passed as argument to 'op'.
@@ -228,8 +231,8 @@
* Operate by calling the operator 'op' on each element from
* smallest towards larger elements.
*
- * Yield when 'ylimit' elements has been processed. Zero is
- * returned when yielding, and a non-zero value is returned when
+ * Yield when 'ylimit' elements has been processed. True is
+ * returned when yielding, and false is returned when
* the whole tree has been processed. The tree should not be
* modified until all of it has been processed.
*
@@ -244,8 +247,8 @@
* Operate by calling the operator 'op' on each element from
* largest towards smaller elements.
*
- * Yield when 'ylimit' elements has been processed. Zero is
- * returned when yielding, and a non-zero value is returned when
+ * Yield when 'ylimit' elements has been processed. True is
+ * returned when yielding, and false is returned when
* the whole tree has been processed. The tree should not be
* modified until all of it has been processed.
*
@@ -296,8 +299,8 @@
* Note that elements are often destroyed in another order
* than the order that the elements are operated on.
*
- * Yield when 'ylimit' elements has been processed. Zero is
- * returned when yielding, and a non-zero value is returned when
+ * Yield when 'ylimit' elements has been processed. True is
+ * returned when yielding, and false is returned when
* the whole tree has been processed. The tree should not be
* modified until all of it has been processed.
*
@@ -318,8 +321,8 @@
* Note that elements are often destroyed in another order
* than the order that the elements are operated on.
*
- * Yield when 'ylimit' elements has been processed. Zero is
- * returned when yielding, and a non-zero value is returned when
+ * Yield when 'ylimit' elements has been processed. True is
+ * returned when yielding, and false is returned when
* the whole tree has been processed. The tree should not be
* modified until all of it has been processed.
*
@@ -422,6 +425,13 @@
#ifndef ERTS_RBT_YIELD_STAT_INITER
# define ERTS_RBT_YIELD_STAT_INITER {NULL, 0}
#endif
+#ifndef ERTS_RBT_YIELD_STAT_INIT
+# define ERTS_RBT_YIELD_STAT_INIT(YS) \
+ do { \
+ (YS)->x = NULL; \
+ (YS)->up = 0; \
+ } while (0)
+#endif
#define ERTS_RBT_CONCAT_MACRO_VALUES___(X, Y) \
X ## Y
@@ -476,12 +486,12 @@ typedef struct {
#if defined(ERTS_RBT_HARD_DEBUG) \
&& (defined(ERTS_RBT_WANT_DELETE) \
|| defined(ERTS_RBT_NEED_INSERT__))
-static void ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root);
+static void ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *node);
# define ERTS_RBT_NEED_HDBG_CHECK_TREE__
-# define ERTS_RBT_HDBG_CHECK_TREE__(R) \
- ERTS_RBT_FUNC__(hdbg_check_tree)((R))
+# define ERTS_RBT_HDBG_CHECK_TREE__(R,N) \
+ ERTS_RBT_FUNC__(hdbg_check_tree)((R),(N))
#else
-# define ERTS_RBT_HDBG_CHECK_TREE__(R) ((void) 1)
+# define ERTS_RBT_HDBG_CHECK_TREE__(R,N) ((void) 1)
#endif
#ifdef ERTS_RBT_NEED_ROTATE__
@@ -634,7 +644,7 @@ ERTS_RBT_FUNC__(delete)(ERTS_RBT_T **root, ERTS_RBT_T *n)
ERTS_RBT_T null_x; /* null_x is used to get the fixup started when we
splice out a node without children. */
- ERTS_RBT_HDBG_CHECK_TREE__(*root);
+ ERTS_RBT_HDBG_CHECK_TREE__(*root, n);
ERTS_RBT_INIT_EMPTY_TNODE(&null_x);
@@ -852,7 +862,7 @@ ERTS_RBT_FUNC__(delete)(ERTS_RBT_T **root, ERTS_RBT_T *n)
}
}
- ERTS_RBT_HDBG_CHECK_TREE__(*root);
+ ERTS_RBT_HDBG_CHECK_TREE__(*root, NULL);
}
@@ -982,7 +992,7 @@ ERTS_RBT_FUNC__(insert_aux__)(ERTS_RBT_T **root, ERTS_RBT_T *n, int lookup)
{
ERTS_RBT_KEY_T kn = ERTS_RBT_GET_KEY(n);
- ERTS_RBT_HDBG_CHECK_TREE__(*root);
+ ERTS_RBT_HDBG_CHECK_TREE__(*root, NULL);
ERTS_RBT_INIT_EMPTY_TNODE(n);
@@ -1004,7 +1014,7 @@ ERTS_RBT_FUNC__(insert_aux__)(ERTS_RBT_T **root, ERTS_RBT_T *n, int lookup)
if (lookup && ERTS_RBT_IS_EQ(kn, kx)) {
- ERTS_RBT_HDBG_CHECK_TREE__(*root);
+ ERTS_RBT_HDBG_CHECK_TREE__(*root, NULL);
return x;
}
@@ -1038,7 +1048,7 @@ ERTS_RBT_FUNC__(insert_aux__)(ERTS_RBT_T **root, ERTS_RBT_T *n, int lookup)
ERTS_RBT_FUNC__(insert_fixup__)(root, n);
}
- ERTS_RBT_HDBG_CHECK_TREE__(*root);
+ ERTS_RBT_HDBG_CHECK_TREE__(*root, n);
return NULL;
}
@@ -1364,7 +1374,7 @@ ERTS_RBT_FUNC__(foreach_ordered__)(ERTS_RBT_T **root,
ystate->x = NULL;
ystate->up = 0;
}
- return 1; /* Done */
+ return 0; /* Done */
}
x = p;
}
@@ -1579,15 +1589,17 @@ ERTS_RBT_FUNC__(debug_print)(FILE *filep, ERTS_RBT_T *x, int indent,
#ifdef ERTS_RBT_NEED_HDBG_CHECK_TREE__
static void
-ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root)
+ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n)
{
int black_depth = -1, no_black = 0;
ERTS_RBT_T *c, *p, *x = root;
ERTS_RBT_KEY_T kx;
ERTS_RBT_KEY_T kc;
- if (!x)
+ if (!x) {
+ ERTS_RBT_ASSERT(!n);
return;
+ }
ERTS_RBT_ASSERT(!ERTS_RBT_GET_PARENT(x));
@@ -1597,6 +1609,9 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root)
while (1) {
+ if (x == n)
+ n = NULL;
+
if (ERTS_RBT_IS_BLACK(x))
no_black++;
else {
@@ -1668,6 +1683,7 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root)
if (!p) {
ERTS_RBT_ASSERT(root == x);
ERTS_RBT_ASSERT(no_black == 0);
+ ERTS_RBT_ASSERT(!n);
return; /* Done */
}
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 149d30d299..5d6f36b222 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 2b0c9ff2af..888d2beee0 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -129,7 +129,7 @@
-export([list_to_atom/1, list_to_binary/1]).
-export([list_to_bitstring/1, list_to_existing_atom/1, list_to_float/1]).
-export([list_to_integer/1, list_to_integer/2]).
--export([list_to_pid/1, list_to_tuple/1, loaded/0]).
+-export([list_to_pid/1, list_to_ref/1, list_to_tuple/1, loaded/0]).
-export([localtime/0, make_ref/0]).
-export([map_size/1, match_spec_test/3, md5/1, md5_final/1]).
-export([md5_init/0, md5_update/2, module_loaded/1, monitor/2]).
@@ -1159,6 +1159,12 @@ list_to_integer(_String,_Base) ->
String :: string().
list_to_pid(_String) ->
erlang:nif_error(undefined).
+
+%% list_to_ref/1
+-spec erlang:list_to_ref(String) -> reference() when
+ String :: string().
+list_to_ref(_String) ->
+ erlang:nif_error(undefined).
%% list_to_tuple/1
-spec list_to_tuple(List) -> tuple() when
diff --git a/lib/asn1/c_src/asn1_erl_nif.c b/lib/asn1/c_src/asn1_erl_nif.c
index b29c9a7ed3..7b7e11b02d 100644
--- a/lib/asn1/c_src/asn1_erl_nif.c
+++ b/lib/asn1/c_src/asn1_erl_nif.c
@@ -901,31 +901,35 @@ static int ber_decode_tag(ErlNifEnv* env, ERL_NIF_TERM *tag, unsigned char *in_b
/* then get the tag number */
if ((tmp_tag = (int) INVMASK(in_buf[*ib_index],ASN1_CLASSFORM)) < 31) {
- *tag = enif_make_uint(env, tag_no + tmp_tag);
+ *tag = enif_make_uint(env, tag_no | tmp_tag);
(*ib_index)++;
} else {
- int n = 0; /* n is used to check that the 64K limit is not
- exceeded*/
-
/* should check that at least three bytes are left in
in-buffer,at least two tag byte and at least one length byte */
if ((*ib_index + 3) > in_buf_len)
return ASN1_VALUE_ERROR;
(*ib_index)++;
- /* The tag is in the following bytes in in_buf as
- 1ttttttt 1ttttttt ... 0ttttttt, where the t-bits
- is the tag number*/
- /* In practice is the tag size limited to 64K, i.e. 16 bits. If
- the tag is greater then 64K return an error */
- while (((tmp_tag = (int) in_buf[*ib_index]) >= 128) && n < 2) {
- /* m.s.b. = 1 */
- tag_no = tag_no + (MASK(tmp_tag,ASN1_LONG_TAG) << 7);
+ /*
+ * The tag is in the following bytes in in_buf as:
+ *
+ * 1ttttttt 0ttttttt
+ *
+ * or
+ *
+ * 0ttttttt
+ *
+ * where the t-bits is the tag number. If the tag does not
+ * fit in two tag bytes (16K), return an error.
+ */
+ if ((tmp_tag = (int) in_buf[*ib_index]) >= 128) {
+ tag_no = tag_no | (MASK(tmp_tag,ASN1_LONG_TAG) << 7);
(*ib_index)++;
- n++;
- };
- if ((n == 2) && in_buf[*ib_index] > 3)
- return ASN1_TAG_ERROR; /* tag number > 64K */
- tag_no = tag_no + in_buf[*ib_index];
+ }
+ tmp_tag = (int) in_buf[*ib_index];
+ if (tmp_tag >= 128) {
+ return ASN1_TAG_ERROR; /* tag number > 16K */
+ }
+ tag_no = tag_no | tmp_tag;
(*ib_index)++;
*tag = enif_make_uint(env, tag_no);
}
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index 9f77a557e5..58cbc89db5 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -23,10 +23,10 @@
%% Compile Time functions for ASN.1 (e.g ASN.1 compiler).
-%%-compile(export_all).
%% Public exports
-export([compile/1, compile/2]).
-export([test/1, test/2, test/3, value/2, value/3]).
+
%% Application internal exports
-export([compile_asn/3,compile_asn1/3,compile_py/3,compile/3,
vsn/0,
@@ -75,12 +75,9 @@
-define(ALTERNATIVE,alt).
-define(ALTERNATIVE_UNDECODED,alt_undec).
-define(ALTERNATIVE_PARTS,alt_parts).
-%-define(BINARY,bin).
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This is the interface to the compiler
-%%
-%%
compile(File) ->
compile(File,[]).
@@ -751,7 +748,6 @@ remove_import_doubles([]) ->
remove_import_doubles(ImportList) ->
MergedImportList =
merge_symbols_from_module(ImportList,[]),
-%% io:format("MergedImportList: ~p~n",[MergedImportList]),
delete_double_of_symbol(MergedImportList,[]).
merge_symbols_from_module([Imp|Imps],Acc) ->
@@ -769,7 +765,6 @@ merge_symbols_from_module([Imp|Imps],Acc) ->
end,
Imps),
NewImps = lists:subtract(Imps,IfromModName),
-%% io:format("Imp: ~p~nIfromModName: ~p~n",[Imp,IfromModName]),
NewImp =
Imp#'SymbolsFromModule'{
symbols = lists:append(
@@ -835,7 +830,6 @@ generate({M,CodeTuple}, OutFile, EncodingRule, Options) ->
Code = #abst{name=M#module.name,
types=Types,values=Values,ptypes=Ptypes,
classes=Classes,objects=Objects,objsets=ObjectSets},
- debug_on(Options),
setup_bit_string_format(Options),
setup_legacy_erlang_types(Options),
asn1ct_table:new(check_functions),
@@ -855,7 +849,6 @@ generate({M,CodeTuple}, OutFile, EncodingRule, Options) ->
end,
asn1ct_gen:pgen(OutFile, Gen, Code),
- debug_off(Options),
cleanup_bit_string_format(),
erase(tlv_format), % used in ber
erase(class_default_type),% used in ber
@@ -990,12 +983,8 @@ get_input_file(Module,[]) ->
get_input_file(Module,[I|Includes]) ->
case (catch input_file_type(filename:join([I,Module]))) of
{single_file,FileName} ->
-%% case file:read_file_info(FileName) of
-%% {ok,_} ->
{file,FileName};
-%% _ -> get_input_file(Module,Includes)
-%% end;
- _ ->
+ _ ->
get_input_file(Module,Includes)
end.
@@ -1151,20 +1140,8 @@ is_asn1_flag(verbose) -> true;
%% 'warnings_as_errors' is intentionally passed through to the compiler.
is_asn1_flag(_) -> false.
-debug_on(Options) ->
- case lists:member(debug,Options) of
- true ->
- put(asndebug,true);
- _ ->
- true
- end.
-
-debug_off(_Options) ->
- erase(asndebug).
-
outfile(Base, Ext, Opts) ->
-% io:format("Opts. ~p~n",[Opts]),
Obase = case lists:keysearch(outdir, 1, Opts) of
{value, {outdir, Odir}} -> filename:join(Odir, Base);
_NotFound -> Base % Not found or bad format
@@ -1215,9 +1192,6 @@ compile_py(File,OutFile,Options) ->
compile(File, _OutFile, Options) ->
case compile(File, make_erl_options(Options)) of
{error,_Reason} ->
- %% case occurs due to error in asn1ct_parser2,asn1ct_check
-%% io:format("~p~n",[_Reason]),
-%% io:format("~p~n~s~n",[_Reason,"error"]),
error;
ok ->
ok;
@@ -1512,7 +1486,8 @@ create_pdec_inc_command(_ModName,_,[],Acc) ->
create_pdec_inc_command(ModName,{Comps1,Comps2},TNL,Acc)
when is_list(Comps1),is_list(Comps2) ->
create_pdec_inc_command(ModName,Comps1 ++ Comps2,TNL,Acc);
-%% The following two functionclauses matches on the type after the top type. This one if the top type had no tag, i.e. a CHOICE
+%% The following two clauses match on the type after the top
+%% type. This one if the top type had no tag, i.e. a CHOICE.
create_pdec_inc_command(ModN,Clist,[CL|_Rest],[[]]) when is_list(CL) ->
create_pdec_inc_command(ModN,Clist,CL,[]);
create_pdec_inc_command(ModN,Clist,[CL|_Rest],Acc) when is_list(CL) ->
@@ -1523,17 +1498,14 @@ create_pdec_inc_command(ModName,
prop=Prop}|Comps],
TNL=[C1|Cs],Acc) ->
case C1 of
-% Name ->
-% %% In this case C1 is an atom
-% TagCommand = get_tag_command(TS,?MANDATORY,Prop),
-% create_pdec_inc_command(ModName,get_components(TS#type.def),Cs,[TagCommand|Acc]);
{Name,undecoded} ->
TagCommand = get_tag_command(TS,?UNDECODED,Prop),
create_pdec_inc_command(ModName,Comps,Cs,concat_sequential(TagCommand,Acc));
{Name,parts} ->
TagCommand = get_tag_command(TS,?PARTS,Prop),
create_pdec_inc_command(ModName,Comps,Cs,concat_sequential(TagCommand,Acc));
- L when is_list(L) -> % I guess this never happens due to previous function clause
+ L when is_list(L) ->
+ %% I guess this never happens due to previous clause.
%% This case is only possible as the first element after
%% the top type element, when top type is SEGUENCE or SET.
%% Follow each element in L. Must note every tag on the
@@ -1555,8 +1527,6 @@ create_pdec_inc_command(ModName,
RestPartsList,[]),
create_pdec_inc_command(ModName,Comps,Cs,
[[?MANDATORY,InnerDirectives]|Acc]);
-% create_pdec_inc_command(ModName,Comps,Cs,
-% [InnerDirectives,?MANDATORY|Acc]);
[Opt,EncTag] ->
InnerDirectives =
create_pdec_inc_command(ModName,TS#type.def,
@@ -1564,9 +1534,8 @@ create_pdec_inc_command(ModName,
create_pdec_inc_command(ModName,Comps,Cs,
[[Opt,EncTag,InnerDirectives]|Acc])
end;
-% create_pdec_inc_command(ModName,CList,RestPartsList,Acc);
-%% create_pdec_inc_command(ModName,TS#type.def,RestPartsList,Acc);
- _ -> %% this component may not be in the config list
+ _ ->
+ %% this component may not be in the config list
TagCommand = get_tag_command(TS,?MANDATORY,Prop),
create_pdec_inc_command(ModName,Comps,TNL,concat_sequential(TagCommand,Acc))
end;
@@ -1577,7 +1546,6 @@ create_pdec_inc_command(ModName,
[{C1,Directive}|Rest],Acc) ->
case Directive of
List when is_list(List) ->
-% [Command,Tag] = get_tag_command(TS,?ALTERNATIVE,Prop),
TagCommand = get_tag_command(TS,?ALTERNATIVE,Prop),
CompAcc =
create_pdec_inc_command(ModName,
@@ -1586,9 +1554,6 @@ create_pdec_inc_command(ModName,
[Command,Tag] when is_atom(Command) ->
[[Command,Tag,CompAcc]|Acc];
[L1,_L2|Rest] when is_list(L1) ->
-% [LastComm|Comms] = lists:reverse(TagCommand),
-% [concat_sequential(lists:reverse(Comms),
-% [LastComm,CompAcc])|Acc]
case lists:reverse(TagCommand) of
[Atom|Comms] when is_atom(Atom) ->
[concat_sequential(lists:reverse(Comms),
@@ -1597,12 +1562,8 @@ create_pdec_inc_command(ModName,
[concat_sequential(lists:reverse(Comms),
[[Command2,Tag2,CompAcc]])|Acc]
end
-% [concat_sequential(lists:reverse(Comms),
-% InnerCommand)|Acc]
-
end,
create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest,
-% [[Command,Tag,CompAcc]|Acc]);
NewAcc);
undecoded ->
TagCommand = get_tag_command(TS,?ALTERNATIVE_UNDECODED,Prop),
@@ -1658,7 +1619,6 @@ create_partial_decode_gen_info(_M1,{M2,_}) ->
throw({error,{"wrong module name in asn1 config file",
M2}}).
-%create_partial_decode_gen_info1(ModName,{ModName,TypeList}) ->
create_partial_decode_gen_info1(ModName,{FuncName,TypeList}) ->
case TypeList of
[TopType|Rest] ->
@@ -1678,11 +1638,6 @@ create_partial_decode_gen_info1(ModName,{FuncName,TypeList}) ->
end;
create_partial_decode_gen_info1(_,_) ->
ok.
-% create_partial_decode_gen_info1(_,[]) ->
-% [];
-% create_partial_decode_gen_info1(_M1,{M2,_}) ->
-% throw({error,{"wrong module name in asn1 config file",
-% M2}}).
%% create_pdec_command/4 for each name (type or component) in the
%% third argument, TypeNameList, a command is created. The command has
@@ -1698,7 +1653,6 @@ create_pdec_command(_ModName,_,[],Acc) ->
Fun(L,[H|Res],Fun)
end,
Remove_empty_lists(Acc,[],Remove_empty_lists);
-% lists:reverse(Acc);
create_pdec_command(ModName,[#'ComponentType'{name=C1,typespec=TS}|_Comps],
[C1|Cs],Acc) ->
%% this component is a constructed type or the last in the
@@ -1747,9 +1701,7 @@ create_pdec_command(ModName,TS=#type{def=Def},[C1|Cs],Acc) ->
create_pdec_command(_,_,TNL,_) ->
throw({error,{"unexpected error when creating partial "
"decode command",TNL}}).
-
-% get_components({'CHOICE',Components}) ->
-% Components;
+
get_components(#'SEQUENCE'{components={C1,C2}}) when is_list(C1),is_list(C2) ->
C1++C2;
get_components(#'SEQUENCE'{components=Components}) ->
@@ -1820,8 +1772,6 @@ get_tag_command(#type{tag=[Tag]},Command) ->
[Command,encode_tag_val(decode_class(Tag#tag.class),Tag#tag.form,
Tag#tag.number)];
get_tag_command(T=#type{tag=[Tag|Tags]},Command) ->
-% [get_tag_command(T#type{tag=[Tag]},Command)|
-% [get_tag_command(T#type{tag=Tags},Command)]].
TC = get_tag_command(T#type{tag=[Tag]},Command),
TCs = get_tag_command(T#type{tag=Tags},Command),
case many_tags(TCs) of
@@ -1849,7 +1799,6 @@ get_tag_command(#type{tag=Tag},Command,Prop) when is_record(Tag,tag) ->
get_tag_command(#type{tag=[Tag]},Command,Prop);
get_tag_command(T=#type{tag=[Tag|Tags]},Command,Prop) ->
[get_tag_command(T#type{tag=[Tag]},Command,Prop)|[
-% get_tag_command(T#type{tag=Tags},?MANDATORY,Prop)]].
get_tag_command(T#type{tag=Tags},Command,Prop)]].
anonymous_dec_command(?UNDECODED,'OPTIONAL') ->
@@ -1964,8 +1913,8 @@ read_config_data(Key) ->
true ->
case asn1ct_table:lookup(asn1_general,{asn1_config,Key}) of
[{_,Data}] -> Data;
- Err -> % Err is [] when nothing was saved in the ets table
-%% io:format("strange data from config file ~w~n",[Err]),
+ Err ->
+ %% Err is [] when nothing was saved in the ets table
Err
end
end.
@@ -1978,7 +1927,6 @@ read_config_data(Key) ->
%% saves input data in a new gen_state record
save_gen_state(exclusive_decode,{_,ConfList},PartIncTlvTagList) ->
- %ConfList=[{FunctionName,PatternList}|Rest]
State =
case get_gen_state() of
S when is_record(S,gen_state) -> S;
@@ -1988,14 +1936,12 @@ save_gen_state(exclusive_decode,{_,ConfList},PartIncTlvTagList) ->
inc_type_pattern=ConfList},
save_config(gen_state,StateRec);
save_gen_state(_,_,_) ->
-%% ok.
case get_gen_state() of
S when is_record(S,gen_state) -> ok;
_ -> save_config(gen_state,#gen_state{})
end.
save_gen_state(selective_decode,{_,Type_component_name_list}) ->
-%% io:format("Selective_decode: ~p~n",[Type_component_name_list]),
State =
case get_gen_state() of
S when is_record(S,gen_state) -> S;
@@ -2077,11 +2023,6 @@ update_gen_state(type_pattern,State,Data) ->
update_gen_state(func_name,State,Data) ->
save_gen_state(State#gen_state{func_name=Data});
update_gen_state(namelist,State,Data) ->
-% SData =
-% case Data of
-% [D] when is_list(D) -> D;
-% _ -> Data
-% end,
save_gen_state(State#gen_state{namelist=Data});
update_gen_state(tobe_refed_funcs,State,Data) ->
save_gen_state(State#gen_state{tobe_refed_funcs=Data});
@@ -2136,7 +2077,6 @@ get_tobe_refed_func(Name) ->
%% tuple. Do not save if it exists in generated_functions, because
%% then it will be or already is generated.
add_tobe_refed_func(Data) ->
- %%
{Name,SI,Pattern} =
fun({N,Si,P,_}) -> {N,Si,P};
(D) -> D end (Data),
@@ -2144,8 +2084,6 @@ add_tobe_refed_func(Data) ->
case SI of
I when is_integer(I) ->
fun(D) -> D end(Data);
-% fun({N,Ix,P}) -> {N,Ix+1,P};
-% ({N,Ix,P,T}) -> {N,Ix+1,P,T} end (Data);
_ ->
fun({N,_,P}) -> {N,0,P};
({N,_,P,T}) -> {N,0,P,T} end (Data)
@@ -2153,12 +2091,13 @@ add_tobe_refed_func(Data) ->
L = get_gen_state_field(generated_functions),
case generated_functions_member(get(currmod),Name,L,Pattern) of
- true -> % it exists in generated_functions, it has already
- % been generated or saved in tobe_refed_func
+ true ->
+ %% it exists in generated_functions, it has already
+ %% been generated or saved in tobe_refed_func
ok;
_ ->
add_once_tobe_refed_func(NewData),
- %%only to get it saved in generated_functions
+ %% only to get it saved in generated_functions
maybe_rename_function(tobe_refed,Name,Pattern)
end.
@@ -2173,16 +2112,13 @@ add_once_tobe_refed_func(Data) ->
({N,I,_,_}) when N==Name,I==Index -> true;
(_) -> false end,TRFL) of
[] ->
-%% case lists:keysearch(element(1,Data),1,TRFL) of
-%% false ->
update_gen_state(tobe_refed_funcs,[Data|TRFL]);
_ ->
ok
end.
-
-%% moves Name from the to be list to the generated list.
+%% Moves Name from the to be list to the generated list.
generated_refed_func(Name) ->
L = get_gen_state_field(tobe_refed_funcs),
NewL = lists:keydelete(Name,1,L),
@@ -2190,7 +2126,7 @@ generated_refed_func(Name) ->
L2 = get_gen_state_field(gen_refed_funcs),
update_gen_state(gen_refed_funcs,[Name|L2]).
-%% adds Data to gen_refed_funcs field in gen_state.
+%% Adds Data to gen_refed_funcs field in gen_state.
add_generated_refed_func(Data) ->
case is_function_generated(Data) of
true ->
@@ -2212,7 +2148,7 @@ next_refed_func() ->
reset_gen_state() ->
save_gen_state(#gen_state{}).
-%% adds Data to generated_functions field in gen_state.
+%% Adds Data to generated_functions field in gen_state.
add_generated_function(Data) ->
L = get_gen_state_field(generated_functions),
update_gen_state(generated_functions,[Data|L]).
@@ -2231,16 +2167,18 @@ maybe_rename_function(Mode,Name,Pattern) ->
{_,true} ->
L2 = generated_functions_filter(get(currmod),Name,L),
case lists:keysearch(Pattern,3,L2) of
- false -> %name existed, but not pattern
+ false ->
+ %% name existed, but not pattern
NextIndex = length(L2),
- %%rename function
+ %% rename function
Suffix = lists:concat(["_",NextIndex]),
NewName =
maybe_rename_function2(type_check(Name),Name,
Suffix),
add_generated_function({Name,NextIndex,Pattern}),
NewName;
- Value -> % name and pattern existed
+ Value ->
+ %% name and pattern existed
%% do not save any new index
Suffix = make_suffix(Value),
Name2 =
@@ -2250,9 +2188,9 @@ maybe_rename_function(Mode,Name,Pattern) ->
end,
lists:concat([Name2,Suffix])
end;
- {inc_disp,_} -> %% this is when
- %% decode_partial_inc_disp/2 is
- %% generated
+ {inc_disp,_} ->
+ %% this is when decode_partial_inc_disp/2 is
+ %% generated
add_generated_function({Name,0,Pattern}),
Name;
_ -> % this if call from add_tobe_refed_func
@@ -2298,23 +2236,12 @@ generated_functions_member(M,Name,[_|T]) ->
generated_functions_member(_,_,[]) ->
false.
-% generated_functions_member(M,Name,L) ->
-% case lists:keymember(Name,1,L) of
-% true ->
-% true;
-% _ ->
-% generated_functions_member1(M,Name,L)
-% end.
-% generated_functions_member1(M,#'Externaltypereference'{module=M,type=Name},L) ->
-% lists:keymember(Name,1,L);
-% generated_functions_member1(_,_,_) -> false.
-
generated_functions_filter(_,Name,L) when is_atom(Name);is_list(Name) ->
lists:filter(fun({N,_,_}) when N==Name -> true;
(_) -> false
end, L);
generated_functions_filter(M,#'Externaltypereference'{module=M,type=Name},L)->
- % remove toptypename from patterns
+ %% remove top typename from patterns
RemoveTType =
fun({N,I,[N,P]}) when N == Name ->
{N,I,P};
@@ -2351,8 +2278,6 @@ set_current_sindex(Index) ->
type_check(A) when is_atom(A) ->
atom;
-%% type_check(I) when is_integer(I) ->
-%% integer;
type_check(L) when is_list(L) ->
Pred = fun(X) when X=<255 ->
false;
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index 4f04b78241..e867b9606a 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -23,10 +23,9 @@
%% Main Module for ASN.1 compile time functions
-%-compile(export_all).
-export([check/2,storeindb/2,format_error/1]).
-%-define(debug,1).
-include("asn1_records.hrl").
+
%%% The tag-number for universal types
-define(N_BOOLEAN, 1).
-define(N_INTEGER, 2).
@@ -63,7 +62,8 @@
-define(TAG_CONSTRUCTED(Num),
#tag{class='UNIVERSAL',number=Num,type='IMPLICIT',form=32}).
--record(newt,{type=unchanged,tag=unchanged,constraint=unchanged,inlined=no}). % used in check_type to update type and tag
+%% used in check_type to update type and tag
+-record(newt,{type=unchanged,tag=unchanged,constraint=unchanged,inlined=no}).
check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) ->
%%Predicates used to filter errors
@@ -561,7 +561,6 @@ check_class_fields(S,[F|Fields],Acc) ->
D;
{undefined,user} ->
%% neither of {primitive,bif} or {constructed,bif}
-
{_,D} = get_referenced_type(S,#'Externaltypereference'{module=S#state.mname,type=Type#type.def}),
D;
_ ->
@@ -623,7 +622,6 @@ if_current_checked_type(S,#type{def=Def}) ->
CurrentModule = S#state.mname,
CurrentCheckedName = S#state.tname,
MergedModules = S#state.inputmodules,
- % CurrentCheckedModule = S#state.mname,
case Def of
#'Externaltypereference'{module=CurrentModule,
type=CurrentCheckedName} ->
@@ -656,7 +654,6 @@ check_pobjectset(S,PObjSet) ->
ClassName = #'Externaltypereference'{module=Mod,
type=get_datastr_name(Def)},
{valueset,Set} = ValueSet,
-% ObjectSet = #'ObjectSet'{class={objectclassname,ClassName},
ObjectSet = #'ObjectSet'{class=ClassName,
set=Set},
#pobjectsetdef{pos=Pos,name=Name,args=Args,class=Type#type.def,
@@ -1696,7 +1693,7 @@ check_value(OldS,V) when is_record(V,typedef) ->
%% reference to class
check_value(OldS,V#typedef{typespec=TS#'ObjectSet'{class=Eref}});
#typedef{typespec=HostType} ->
- % an ordinary value set with a type in #typedef.typespec
+ %% an ordinary value set with a type in #typedef.typespec
ValueSet0 = TS#'ObjectSet'.set,
Constr = check_constraints(OldS, HostType, [ValueSet0]),
Type = check_type(OldS,TSDef,TSDef#typedef.typespec),
@@ -2381,15 +2378,6 @@ normalize_s_of(SorS,S,Value,Type,NameList)
%% normalize_restrictedstring handles all format of restricted strings.
-%% tuple case
-% normalize_restrictedstring(_S,[Int1,Int2],_) when is_integer(Int1),is_integer(Int2) ->
-% {Int1,Int2};
-% %% quadruple case
-% normalize_restrictedstring(_S,[Int1,Int2,Int3,Int4],_) when is_integer(Int1),
-% is_integer(Int2),
-% is_integer(Int3),
-% is_integer(Int4) ->
-% {Int1,Int2,Int3,Int4};
%% character string list case
normalize_restrictedstring(S,[H|T],CType) when is_list(H);is_tuple(H) ->
[normalize_restrictedstring(S,H,CType)|normalize_restrictedstring(S,T,CType)];
@@ -2491,7 +2479,7 @@ check_ptype(S,Type,Ts) when is_record(Ts,type) ->
Ts#type{def=TDef}
end,
Ts2;
-%parameterized class
+%% parameterized class
check_ptype(_S,_PTDef,Ts) when is_record(Ts,objectclass) ->
throw({asn1_param_class,Ts}).
@@ -2506,8 +2494,6 @@ check_formal_parameter(_, #'Externaltypereference'{}) ->
check_formal_parameter(S, #'Externalvaluereference'{value=Name}) ->
asn1_error(S, {illegal_typereference,Name}).
-% check_type(S,Type,ObjSpec={{objectclassname,_},_}) ->
- % check_class(S,ObjSpec);
check_type(_S,Type,Ts) when is_record(Type,typedef),
(Type#typedef.checked==true) ->
Ts;
@@ -2606,7 +2592,6 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) ->
constraint = NewC};
_ ->
%% Here we only expand the tags and keep the ext ref.
-
NewExt = ExtRef#'Externaltypereference'{module=merged_mod(S,RefMod,Ext)},
TempNewDef#newt{
type = check_externaltypereference(S,NewExt),
@@ -2749,7 +2734,6 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) ->
case TopName of
[] ->
[get_datastr_name(Type)];
-% [Type#typedef.name];
_ ->
TopName
end,
@@ -2773,7 +2757,6 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) ->
case TopName of
[] ->
[get_datastr_name(Type)];
-% [Type#typedef.name];
_ ->
TopName
end,
@@ -2898,8 +2881,6 @@ tablecinf_choose(#'SEQUENCE'{tablecinf=TCI}) ->
get_innertag(_S,#'ObjectClassFieldType'{type=Type}) ->
case Type of
-% #type{tag=Tag} -> Tag;
-% {fixedtypevaluefield,_,#type{tag=[]}=T} -> get_taglist(S,T);
{fixedtypevaluefield,_,#type{tag=Tag}} -> Tag;
{TypeFieldName,_} when is_atom(TypeFieldName) -> [];
_ -> []
@@ -3754,14 +3735,8 @@ check_reference(S,#'Externaltypereference'{pos=Pos,module=Emod,type=Name}) ->
{ok,Imodule} ->
check_imported(S,Imodule,Name),
#'Externaltypereference'{module=Imodule,type=Name};
-%% case check_imported(S,Imodule,Name) of
-%% ok ->
-%% #'Externaltypereference'{module=Imodule,type=Name};
-%% Err ->
-%% Err
-%% end;
_ ->
- %may be a renamed type in multi file compiling!
+ %% may be a renamed type in multi file compiling!
{M,T}=get_renamed_reference(S,Name,Emod),
NewName = asn1ct:get_name_of_def(T),
NewPos = asn1ct:get_pos_of_def(T),
@@ -4170,7 +4145,6 @@ iof_associated_type(S,[]) ->
def=AssociateSeq}},
asn1_db:dbput(S#state.mname,'INSTANCE OF',TypeDef),
instance_of_decl(S#state.mname);
-%% put(instance_of,{generate,S#state.mname});
_ ->
instance_of_decl(S#state.mname),
ok
@@ -4199,14 +4173,12 @@ iof_associated_type1(S,C) ->
ObjectIdentifier =
#'ObjectClassFieldType'{classname=TypeIdentifierRef,
class=[],
-%% fieldname=[{valuefieldreference,id}],
fieldname={id,[]},
type={fixedtypevaluefield,id,
#type{def='OBJECT IDENTIFIER'}}},
Typefield =
#'ObjectClassFieldType'{classname=TypeIdentifierRef,
class=[],
-%% fieldname=[{typefieldreference,'Type'}],
fieldname={'Type',[]},
type=Typefield_type},
IOFComponents0 =
@@ -4360,11 +4332,11 @@ check_boolean(_S,_Constr) ->
check_octetstring(_S,_Constr) ->
ok.
-% check all aspects of a SEQUENCE
-% - that all component names are unique
-% - that all TAGS are ok (when TAG default is applied)
-% - that each component is of a valid type
-% - that the extension marks are valid
+%% check all aspects of a SEQUENCE
+%% - that all component names are unique
+%% - that all TAGS are ok (when TAG default is applied)
+%% - that each component is of a valid type
+%% - that the extension marks are valid
check_sequence(S,Type,Comps) ->
Components = expand_components(S,Comps),
@@ -4705,11 +4677,11 @@ check_objectidentifier(_S,_Constr) ->
check_relative_oid(_S,_Constr) ->
ok.
-% check all aspects of a CHOICE
-% - that all alternative names are unique
-% - that all TAGS are ok (when TAG default is applied)
-% - that each alternative is of a valid type
-% - that the extension marks are valid
+%% check all aspects of a CHOICE
+%% - that all alternative names are unique
+%% - that all TAGS are ok (when TAG default is applied)
+%% - that each alternative is of a valid type
+%% - that the extension marks are valid
check_choice(S,Type,Components) when is_list(Components) ->
Components1 = [C||C = #'ComponentType'{} <- Components],
case check_unique(Components1,#'ComponentType'.name) of
@@ -5063,12 +5035,12 @@ remove_doubles1(El,L) ->
%% referred to in the ObjectClassFieldType, and the name of the unique
%% field of the class of the ObjectClassFieldType.
%%
-% %% The level information outermost/innermost must be kept. There are
-% %% at least two possibilities to cover here for an outermost case: 1)
-% %% Both the simple table and the component relation have a common path
-% %% at least one step below the outermost level, i.e. the leading
-% %% information shall be on a sub level. 2) They don't have any common
-% %% path.
+%% The level information outermost/innermost must be kept. There are
+%% at least two possibilities to cover here for an outermost case: 1)
+%% Both the simple table and the component relation have a common path
+%% at least one step below the outermost level, i.e. the leading
+%% information shall be on a sub level. 2) They don't have any common
+%% path.
get_simple_table_info(S, Cs, AtLists) ->
[get_simple_table_info1(S, Cs, AtList, []) || AtList <- AtLists].
@@ -5109,10 +5081,10 @@ simple_table_info(S,#'ObjectClassFieldType'{classname=ClRef,
{_FirstFieldName,FieldNames} ->
lists:last(FieldNames)
end,
- %%ObjectClassFieldName is the last element in the dotted
- %%list of the ObjectClassFieldType. The last element may
- %%be of another class, that is referenced from the class
- %%of the ObjectClassFieldType
+ %% ObjectClassFieldName is the last element in the dotted list of
+ %% the ObjectClassFieldType. The last element may be of another
+ %% class, that is referenced from the class of the
+ %% ObjectClassFieldType
ClassDef =
case ObjectClass of
[] ->
@@ -5128,7 +5100,7 @@ simple_table_info(S,#'ObjectClassFieldType'{classname=ClRef,
%% the "name path" in the at-list to the component relation constraint
%% that must refer to a simple table constraint. The list is empty if
%% no component relation constraints were found.
-%%
+%%
%% NamePath has the names of all components that are followed from the
%% beginning of the search. CNames holds the names of all components
%% of the start level, this info is used if an outermost at-notation
@@ -5141,6 +5113,7 @@ any_component_relation(S,[#'ComponentType'{name=CName,typespec=Type}|Cs],CNames,
%% whether this constraint is relevant for the level
%% where the search started
AtNot = extract_at_notation(AtNotation),
+
%% evaluate_atpath returns the relative path to the
%% simple table constraint from where the component
%% relation is found.
@@ -5246,12 +5219,10 @@ get_components(_,#'SET'{components=Cs}) ->
tuple2complist(Cs);
get_components(_,{'CHOICE',Cs}) ->
tuple2complist(Cs);
-%do not step in inlined structures
+%%do not step in inlined structures
get_components(any,{'SEQUENCE OF',T = #type{def=_Def,inlined=no}}) ->
-% get_components(any,Def);
T;
get_components(any,{'SET OF',T = #type{def=_Def,inlined=no}}) ->
-% get_components(any,Def);
T;
get_components(_,_) ->
[].
@@ -5281,15 +5252,12 @@ extract_at_notation([{Level,ValueRefs}]) ->
componentrelation1(S,C = #type{def=Def,constraint=Constraint,tablecinf=TCI},
Path) ->
Ret =
-% case Constraint of
-% [{componentrelation,{_,_,ObjectSet},AtList}|_Rest] ->
case lists:keyfind(componentrelation, 1, Constraint) of
{_,{_,_,ObjectSet},AtList} ->
[{_,AL=[#'Externalvaluereference'{}|_R1]}|_R2] = AtList,
%% Note: if Path is longer than one,i.e. it is within
%% an inner type of the actual level, then the only
%% relevant at-list is of "outermost" type.
-%% #'ObjectClassFieldType'{class=ClassDef} = Def,
ClassDef = get_ObjectClassFieldType_classdef(S,Def),
AtPath =
lists:map(fun(#'Externalvaluereference'{value=V})->V end,
@@ -5375,7 +5343,6 @@ innertype_comprel1(S,T = #type{def=Def,constraint=Cons,tablecinf=TCI},Path) ->
%% relevent here.
[{_,AL=[#'Externalvaluereference'{value=_Attr}|_R1]}|_R2]
= AtList,
-%% #'ObjectClassFieldType'{class=ClassDef} = Def,
ClassDef = get_ObjectClassFieldType_classdef(S,Def),
AtPath =
lists:map(fun(#'Externalvaluereference'{value=V})->V end,
@@ -5444,7 +5411,7 @@ leading_attr_index1(S,[C|Cs],Arg={ObjectSet,_,CDef,P},
value_match(S,C,Name,SubAttr) ->
value_match(S,C,Name,SubAttr,[]). % C has name Name
value_match(_S,#'ComponentType'{},_Name,[],Acc) ->
- Acc;% do not reverse, indexes in reverse order
+ Acc; % do not reverse, indexes in reverse order
value_match(S,#'ComponentType'{typespec=Type},Name,[At|Ats],Acc) ->
InnerType = asn1ct_gen:get_inner(Type#type.def),
Components =
@@ -5514,8 +5481,6 @@ get_tableconstraint_info(S,Type,[C=#'ComponentType'{typespec=CheckedTs}|Cs],Acc)
CheckedTs#type{
def=NewOCFT
}};
-% constraint=[{tableconstraint_info,
-% FieldRef}]}};
{'SEQUENCE OF',SOType} when is_record(SOType,type),
(element(1,SOType#type.def)=='CHOICE') ->
CTypeList = element(2,SOType#type.def),
@@ -5618,51 +5583,6 @@ get_taglist1(S,[_H|Rest]) -> % skip EXTENSIONMARK
get_taglist1(_S,[]) ->
[].
-%% def_to_tag(S,Def) ->
-%% case asn1ct_gen:def_to_tag(Def) of
-%% {'UNIVERSAL',T} ->
-%% case asn1ct_gen:prim_bif(T) of
-%% true ->
-%% ?TAG_PRIMITIVE(tag_number(T));
-%% _ ->
-%% ?TAG_CONSTRUCTED(tag_number(T))
-%% end;
-%% _ -> []
-%% end.
-%% tag_number('BOOLEAN') -> 1;
-%% tag_number('INTEGER') -> 2;
-%% tag_number('BIT STRING') -> 3;
-%% tag_number('OCTET STRING') -> 4;
-%% tag_number('NULL') -> 5;
-%% tag_number('OBJECT IDENTIFIER') -> 6;
-%% tag_number('ObjectDescriptor') -> 7;
-%% tag_number('EXTERNAL') -> 8;
-%% tag_number('INSTANCE OF') -> 8;
-%% tag_number('REAL') -> 9;
-%% tag_number('ENUMERATED') -> 10;
-%% tag_number('EMBEDDED PDV') -> 11;
-%% tag_number('UTF8String') -> 12;
-%% %%tag_number('RELATIVE-OID') -> 13;
-%% tag_number('SEQUENCE') -> 16;
-%% tag_number('SEQUENCE OF') -> 16;
-%% tag_number('SET') -> 17;
-%% tag_number('SET OF') -> 17;
-%% tag_number('NumericString') -> 18;
-%% tag_number('PrintableString') -> 19;
-%% tag_number('TeletexString') -> 20;
-%% %%tag_number('T61String') -> 20;
-%% tag_number('VideotexString') -> 21;
-%% tag_number('IA5String') -> 22;
-%% tag_number('UTCTime') -> 23;
-%% tag_number('GeneralizedTime') -> 24;
-%% tag_number('GraphicString') -> 25;
-%% tag_number('VisibleString') -> 26;
-%% %%tag_number('ISO646String') -> 26;
-%% tag_number('GeneralString') -> 27;
-%% tag_number('UniversalString') -> 28;
-%% tag_number('CHARACTER STRING') -> 29;
-%% tag_number('BMPString') -> 30.
-
merge_tags(T1, T2) when is_list(T2) ->
merge_tags2(T1 ++ T2, []);
merge_tags(T1, T2) ->
diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
index 16af09bca9..bfb69a09b3 100644
--- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
@@ -32,17 +32,17 @@
-include("asn1_records.hrl").
--import(asn1ct_gen, [emit/1,demit/1,get_record_name_prefix/1]).
+-import(asn1ct_gen, [emit/1,get_record_name_prefix/1]).
-define(ASN1CT_GEN_BER,asn1ct_gen_ber_bin_v2).
-% the encoding of class of tag bits 8 and 7
+%% the encoding of class of tag bits 8 and 7
-define(UNIVERSAL, 0).
-define(APPLICATION, 16#40).
-define(CONTEXT, 16#80).
-define(PRIVATE, 16#C0).
-% primitive or constructed encoding % bit 6
+%% primitive or constructed encoding % bit 6
-define(PRIMITIVE, 0).
-define(CONSTRUCTED, 2#00100000).
@@ -103,7 +103,6 @@ gen_encode_sequence(Gen, Typename, #type{}=D) ->
uniqueclassfield=Unique} when Used /= Unique ->
false;
%% ObjectSet, name of the object set in constraints
- %%
#simpletableattributes{objectsetname=ObjectSetRef,
c_name=AttrN,
c_index=N,
@@ -230,7 +229,6 @@ gen_decode_sequence(Gen, Typename, #type{}=D) ->
usedclassfield=UniqueFieldName,
uniqueclassfield=UniqueFieldName,
valueindex=ValIndex} ->
-% {ObjectSetRef,AttrN,_N,UniqueFieldName} ->%% N is index of attribute that determines constraint
F = fun(#'ComponentType'{typespec=CT})->
case {asn1ct_gen:get_constraint(CT#type.constraint,componentrelation),CT#type.tablecinf} of
{no,[{objfun,_}|_]} -> true;
@@ -279,12 +277,12 @@ gen_decode_sequence(Gen, Typename, #type{}=D) ->
ValueMatch,"),",nl]),
gen_dec_postponed_decs(DecObj,PostponedDecArgs)
end,
- demit(["Result = "]), %dbg
%% return value as record
case Ext of
{ext,_,_} ->
emit(["case ",{prev,tlv}," of [] -> true; _ -> true end, % ... extra fields skipped",nl]);
- _ -> % noext | extensible
+ _ ->
+ %% noext | extensible
emit(["case ",{prev,tlv}," of",nl,
"[] -> true;",
"_ -> exit({error,{asn1, {unexpected,",{prev,tlv},
@@ -431,7 +429,6 @@ gen_decode_set(Gen, Typename, #type{}=D) ->
{DecObjInf,ValueIndex} =
case TableConsInfo of
-%% {ObjectSetRef,AttrN,_N,UniqueFieldName} ->%% N is index of attribute that determines constraint
#simpletableattributes{objectsetname=ObjectSetRef,
c_name=AttrN,
usedclassfield=UniqueFieldName,
@@ -446,7 +443,8 @@ gen_decode_set(Gen, Typename, #type{}=D) ->
end
end,
case lists:any(F,CompList) of
- true -> % when component relation constraint establish
+ true ->
+ %% when component relation constraint establish
%% relation from a component to another components
%% subtype component
{{AttrN,{deep,ObjectSetRef,UniqueFieldName,ValIndex}},
@@ -503,7 +501,6 @@ gen_decode_set(Gen, Typename, #type{}=D) ->
ValueMatch,"),",nl]),
gen_dec_postponed_decs(DecObj,PostponedDecArgs)
end,
- demit(["Result = "]), %dbg
%% return value as record
case Ext of
Extnsn when Extnsn =/= noext ->
@@ -722,7 +719,7 @@ gen_dec_sequence_call2(Erules,TopType,{Root1,EList,Root2},_Ext,DecObjInf) ->
length(Root1)+length(EList),noext,
DecObjInf,LA,ArgsAcc).
-%% returns a list of tags of the elements in the component (second
+%% Returns a list of tags of the elements in the component (second
%% root) list up to and including the first mandatory tag. See 24.6 in
%% X.680 (7/2002)
get_root2_taglist([],Acc) ->
@@ -811,8 +808,6 @@ gen_dec_set_cases(Erules,TopType,[Comp|RestComps],Pos) ->
[FirstTag|_] ->
[(?ASN1CT_GEN_BER:decode_class(FirstTag#tag.class) bsl 10) + FirstTag#tag.number]
end,
-% emit([indent(6),"%Tags: ",Tags,nl]),
-% emit([indent(6),"%Type#type.tag: ",Type#type.tag,nl]),
CaseFun = fun(TagList=[H|T],Fun,N) ->
Semicolon = case TagList of
[_Tag1,_|_] -> [";",nl];
@@ -827,7 +822,6 @@ gen_dec_set_cases(Erules,TopType,[Comp|RestComps],Pos) ->
emit([";",nl])
end,
CaseFun(Tags,CaseFun,0),
-%% emit([";",nl]),
gen_dec_set_cases(Erules,TopType,RestComps,Pos+1).
@@ -1007,14 +1001,6 @@ gen_enc_line(Erules,TopType,Cname,
["{",{curr,encBytes},",",{curr,encLen},"} = "],
EncObj)
end;
-% gen_enc_line(Erules,TopType,Cname,
-% Type=#type{constraint=[{componentrelation,_,_}],
-% def=#'ObjectClassFieldType'{type={typefield,_}}},
-% Element,Indent,OptOrMand=mandatory,EncObj)
-% when is_list(Element) ->
-% asn1ct_name:new(tmpBytes),
-% gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,
-% ["{",{curr,tmpBytes},",_} = "],EncObj);
gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,EncObj)
when is_list(Element) ->
gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,
@@ -1035,37 +1021,30 @@ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,Assign,EncObj)
gen_optormand_case(OptOrMand, Erules, TopType, Cname, Type, Element),
case {Type,asn1ct_gen:get_constraint(Type#type.constraint,
componentrelation)} of
-% #type{constraint=[{tableconstraint_info,RefedFieldName}],
-% def={typefield,_}} ->
{#type{def=#'ObjectClassFieldType'{type={typefield,_},
fieldname=RefedFieldName}},
{componentrelation,_,_}} ->
{_LeadingAttrName,Fun} = EncObj,
- case RefedFieldName of
- {Name,RestFieldNames} when is_atom(Name) ->
- case OptOrMand of
- mandatory -> ok;
- _ ->
-% emit(["{",{curr,tmpBytes},",",{curr,tmpLen},
- emit(["{",{curr,tmpBytes},",_ } = "])
-% "} = "])
- end,
- emit([Fun,"(",{asis,Name},", ",Element,", ",
- {asis,RestFieldNames},"),",nl]),
- emit(IndDeep),
- case OptOrMand of
- mandatory ->
- emit(["{",{curr,encBytes},",",{curr,encLen},
- "} = ",
- {call,ber,encode_open_type,
- [{curr,tmpBytes},{asis,Tag}]},nl]);
- _ ->
- emit([{call,ber,encode_open_type,
- [{curr,tmpBytes},{asis,Tag}]}])
- end;
- Err ->
- throw({asn1,{'internal error',Err}})
- end;
+ {Name,RestFieldNames} = RefedFieldName,
+ true = is_atom(Name), %Assertion.
+ case OptOrMand of
+ mandatory -> ok;
+ _ ->
+ emit(["{",{curr,tmpBytes},",_ } = "])
+ end,
+ emit([Fun,"(",{asis,Name},", ",Element,", ",
+ {asis,RestFieldNames},"),",nl]),
+ emit(IndDeep),
+ case OptOrMand of
+ mandatory ->
+ emit(["{",{curr,encBytes},",",{curr,encLen},
+ "} = ",
+ {call,ber,encode_open_type,
+ [{curr,tmpBytes},{asis,Tag}]},nl]);
+ _ ->
+ emit([{call,ber,encode_open_type,
+ [{curr,tmpBytes},{asis,Tag}]}])
+ end;
_ ->
case WhatKind of
{primitive,bif} ->
@@ -1166,7 +1145,9 @@ gen_dec_line(Erules,TopType,Cname,CTags,Type,OptOrMand,DecObjInf) ->
gen_dec_call(InnerType,Erules,TopType,Cname,Type,
BytesVar,Tag,
mandatory,", mandatory, ",DecObjInf,OptOrMand);
- _ -> %optional or default or a mandatory component after an extensionmark
+ _ ->
+ %% optional or default, or a mandatory component after
+ %% an extension marker
{FirstTag,RestTag} =
case Tag of
[] ->
@@ -1241,9 +1222,9 @@ gen_dec_line(Erules,TopType,Cname,CTags,Type,OptOrMand,DecObjInf) ->
PostponedDec
end,
case DecObjInf of
- {Cname,ObjSet} -> % this must be the component were an object is
- %% choosen from the object set according to the table
- %% constraint.
+ {Cname,ObjSet} ->
+ %% This must be the component were an object is chosen
+ %% from the object set according to the table constraint.
ObjSetName = case ObjSet of
{deep,OSName,_,_} ->
OSName;
@@ -1280,10 +1261,7 @@ gen_dec_call({typefield,_},_,_,_Cname,Type,BytesVar,Tag,_,_,false,_) ->
[];
gen_dec_call({typefield,_},_,_,Cname,Type,BytesVar,Tag,_,_,_DecObjInf,OptOrMandComp) ->
call(decode_open_type, [BytesVar,{asis,Tag}]),
- RefedFieldName =
-% asn1ct_gen:get_constraint(Type#type.constraint,
-% tableconstraint_info),
- (Type#type.def)#'ObjectClassFieldType'.fieldname,
+ RefedFieldName = (Type#type.def)#'ObjectClassFieldType'.fieldname,
[{Cname,RefedFieldName,asn1ct_gen:mk_var(asn1ct_name:curr(term)),
asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),Tag,OptOrMandComp}];
gen_dec_call(InnerType, Gen, TopType, Cname, Type, BytesVar,
@@ -1339,8 +1317,6 @@ gen_dec_call1(WhatKind, _, TopType, Cname, Type, BytesVar, Tag) ->
emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',",
BytesVar,"}"]);
_ ->
-% {DecFunName, _DecMod, _DecFun} =
-% case {asn1ct:get_gen_state_field(namelist),WhatKind} of
EmitDecFunCall =
fun(FuncName) ->
case {WhatKind,Type#type.tablecinf} of
@@ -1356,14 +1332,11 @@ gen_dec_call1(WhatKind, _, TopType, Cname, Type, BytesVar, Tag) ->
Sindex =
case WhatKind of
#'Externaltypereference'{} ->
-% asn1ct:maybe_rename_function(WhatKind,List),
SI = asn1ct:maybe_saved_sindex(WhatKind,List),
Saves = {WhatKind,SI,List},
asn1ct:add_tobe_refed_func(Saves),
SI;
_ ->
-% asn1ct:maybe_rename_function([Cname|TopType],
-% List),
SI = asn1ct:maybe_saved_sindex([Cname|TopType],List),
Saves = {[Cname|TopType],SI,List,Type},
asn1ct:add_tobe_refed_func(Saves),
@@ -1371,8 +1344,6 @@ gen_dec_call1(WhatKind, _, TopType, Cname, Type, BytesVar, Tag) ->
end,
asn1ct:update_gen_state(namelist,Rest),
Prefix=asn1ct:get_gen_state_field(prefix),
-% Suffix =
-% lists:concat(["_",asn1ct:latest_sindex()]),
Suffix =
case Sindex of
I when is_integer(I),I>0 -> lists:concat(["_",I]);
@@ -1380,8 +1351,6 @@ gen_dec_call1(WhatKind, _, TopType, Cname, Type, BytesVar, Tag) ->
end,
{DecFunName,_,_}=
mkfuncname(TopType,Cname,WhatKind,Prefix,Suffix),
-% SuffixedName =
-% lists:concat([DecFunName,asn1ct:latest_sindex()]),
EmitDecFunCall(DecFunName);
[{Cname,parts}|Rest] ->
asn1ct:update_gen_state(namelist,Rest),
@@ -1401,13 +1370,6 @@ gen_dec_call1(WhatKind, _, TopType, Cname, Type, BytesVar, Tag) ->
mkfuncname(TopType,Cname,WhatKind,"dec_",""),
EmitDecFunCall(DecFunName)
end
-% case {WhatKind,Type#type.tablecinf} of
-% {{constructed,bif},[{objfun,_}|_Rest]} ->
-% emit([DecFunName,"(",BytesVar,", ",{asis,Tag},
-% ", ObjFun)"]);
-% _ ->
-% emit([DecFunName,"(",BytesVar,", ",{asis,Tag},")"])
-% end
end.
@@ -1464,6 +1426,9 @@ print_attribute_comment(InnerType,Pos,Cname,Prop) ->
case InnerType of
#'Externaltypereference'{module=XModule,type=Name} ->
emit([nl,"%% attribute ",Cname,"(",Pos,") External ",XModule,":",Name]);
+ _ when is_tuple(InnerType) ->
+ emit([nl,"%% attribute ",Cname,"(",Pos,") with type "|
+ tuple_to_list(InnerType)]);
_ ->
emit([nl,"%% attribute ",Cname,"(",Pos,") with type ",InnerType])
end,
diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl
index 9cd9864b80..986d88b677 100644
--- a/lib/asn1/src/asn1ct_constructed_per.erl
+++ b/lib/asn1/src/asn1ct_constructed_per.erl
@@ -30,9 +30,8 @@
-export([gen_decode_choice/3]).
-include("asn1_records.hrl").
-%-compile(export_all).
--import(asn1ct_gen, [emit/1,demit/1,get_record_name_prefix/1]).
+-import(asn1ct_gen, [emit/1,get_record_name_prefix/1]).
-type type_name() :: any().
@@ -357,7 +356,6 @@ gen_dec_constructed_imm(Erule, Typename, #type{}=D) ->
#'SEQUENCE'{tablecinf=TCI,components=CL} ->
{add_textual_order(CL),TCI};
#'SET'{tablecinf=TCI,components=CL} ->
-%% {add_textual_order(CL),TCI}
{CL,TCI} % the textual order is already taken care of
end,
Ext = extensible_dec(CompList),
@@ -375,13 +373,11 @@ gen_dec_constructed_imm(Erule, Typename, #type{}=D) ->
end,
ObjSetInfo =
case TableConsInfo of
-%% {ObjectSet,AttrN,N,UniqueFieldName} ->%% N is index of attribute that determines constraint
#simpletableattributes{objectsetname=ObjectSet,
c_name=AttrN,
usedclassfield=UniqueFieldName,
uniqueclassfield=UniqueFieldName,
valueindex=ValIndex} ->
-%% {AttrN,ObjectSet};
F = fun(#'ComponentType'{typespec=CT})->
case {asn1ct_gen:get_constraint(CT#type.constraint,componentrelation),CT#type.tablecinf} of
{no,[{objfun,_}|_R]} -> true;
@@ -686,10 +682,10 @@ gen_decode_choice(Erules,Typename,D) when is_record(D,type) ->
{'CHOICE',CompList} = D#type.def,
Ext = extensible_enc(CompList),
gen_dec_choice(Erules,Typename,CompList,Ext),
- emit({".",nl}).
+ emit([".",nl]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% Encode generator for SEQUENCE OF type
+%% Encode generator for SEQUENCE OF type
gen_encode_sof(Erule, Typename, SeqOrSetOf, D) ->
asn1ct_name:start(),
@@ -781,20 +777,20 @@ gen_decode_sof_components(Erule, Name, Typename, SeqOrSetOf, Cont) ->
case asn1ct_gen:type(Conttype) of
{primitive,bif} ->
asn1ct_gen_per:gen_dec_prim(Erule, Cont, "Bytes"),
- emit({com,nl});
+ emit([com,nl]);
{constructed,bif} ->
NewTypename = [Constructed_Suffix|Typename],
- emit({"'dec_",asn1ct_gen:list2name(NewTypename),
- "'(Bytes",ObjFun,"),",nl});
+ emit([{asis,dec_func(asn1ct_gen:list2name(NewTypename))},
+ "(Bytes",ObjFun,"),",nl]);
#'Externaltypereference'{}=Etype ->
asn1ct_gen_per:gen_dec_external(Etype, "Bytes"),
emit([com,nl]);
'ASN1_OPEN_TYPE' ->
asn1ct_gen_per:gen_dec_prim(Erule, #type{def='ASN1_OPEN_TYPE'},
"Bytes"),
- emit({com,nl});
+ emit([com,nl]);
_ ->
- emit({"'dec_",Conttype,"'(Bytes),",nl})
+ emit([{asis,dec_func(Conttype)},"(Bytes),",nl])
end,
emit([{asis,Name},"(Num-1, Remain",ObjFun,", [Term|Acc]).",nl]).
@@ -934,9 +930,7 @@ add_textual_order({R1,Ext,R2}) ->
{NewExt,Num2} = add_textual_order1(Ext,Num1),
{NewR2,_} = add_textual_order1(R2,Num2),
{NewR1,NewExt,NewR2}.
-%%add_textual_order1(Cs=[#'ComponentType'{textual_order=Int}|_],I)
-%% when is_integer(Int) ->
-%% {Cs,I};
+
add_textual_order1(Cs,NumIn) ->
lists:mapfoldl(fun(C=#'ComponentType'{},Num) ->
{C#'ComponentType'{textual_order=Num},
@@ -1494,9 +1488,9 @@ gen_dec_component_no_val(_, Type, {'DEFAULT',DefVal0}) ->
DefVal = asn1ct_gen:conform_value(Type, DefVal0),
emit([{asis,DefVal}]);
gen_dec_component_no_val(_, _, 'OPTIONAL') ->
- emit({"asn1_NOVALUE"});
+ emit(["asn1_NOVALUE"]);
gen_dec_component_no_val({ext,_,_}, _, mandatory) ->
- emit({"asn1_NOVALUE"}).
+ emit(["asn1_NOVALUE"]).
dec_map_extaddgroup_no_val(Ext, Type, Comp) ->
L0 = [dec_map_extaddgroup_no_val_1(N, P, Ext, Type) ||
@@ -1693,16 +1687,15 @@ gen_dec_line_other(Erule, Atype, TopType, Comp) ->
end;
{constructed,bif} ->
NewTypename = [Cname|TopType],
+ DecFunc = dec_func(asn1ct_gen:list2name(NewTypename)),
case Type#type.tablecinf of
[{objfun,_}|_R] ->
fun(BytesVar) ->
- emit({"'dec_",asn1ct_gen:list2name(NewTypename),
- "'(",BytesVar,", ObjFun)"})
+ emit([{asis,DecFunc},"(",BytesVar,", ObjFun)"])
end;
_ ->
fun(BytesVar) ->
- emit({"'dec_",asn1ct_gen:list2name(NewTypename),
- "'(",BytesVar,")"})
+ emit([{asis,DecFunc},"(",BytesVar,")"])
end
end
end.
@@ -1908,7 +1901,7 @@ emit_extaddgroupTerms(VarSeries,[_]) ->
ok;
emit_extaddgroupTerms(VarSeries,[_|Rest]) ->
asn1ct_name:new(VarSeries),
- emit({{curr,VarSeries},","}),
+ emit([{curr,VarSeries},","]),
emit_extaddgroupTerms(VarSeries,Rest);
emit_extaddgroupTerms(_,[]) ->
ok.
@@ -1990,3 +1983,6 @@ attribute_comment(InnerType, TextPos, Cname) ->
end,
Comment = ["attribute ",Cname,"(",TextPos,") with type ",DispType],
lists:concat(Comment).
+
+dec_func(Tname) ->
+ list_to_atom(lists:concat(["dec_",Tname])).
diff --git a/lib/asn1/src/asn1ct_func.erl b/lib/asn1/src/asn1ct_func.erl
index 0cd72acf9d..016161fcaf 100644
--- a/lib/asn1/src/asn1ct_func.erl
+++ b/lib/asn1/src/asn1ct_func.erl
@@ -65,7 +65,7 @@ generate(Fd) ->
Funcs = sofs:to_external(Funcs0),
ok = file:write(Fd, Funcs).
-is_used({_,_,_}=MFA) ->
+is_used({M,F,A}=MFA) when is_atom(M), is_atom(F), is_integer(A) ->
req({is_used,MFA}).
diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl
index 9f628c7b04..fa312ed052 100644
--- a/lib/asn1/src/asn1ct_gen.erl
+++ b/lib/asn1/src/asn1ct_gen.erl
@@ -22,8 +22,7 @@
-include("asn1_records.hrl").
--export([demit/1,
- emit/1,
+-export([emit/1,
open_output_file/1,close_output_file/0,
get_inner/1,type/1,def_to_tag/1,prim_bif/1,
list2name/1,
@@ -191,13 +190,9 @@ pgen_partial_decode(_, _, _) ->
ok.
pgen_partial_inc_dec(Rtmod,Erules,Module) ->
-% io:format("Start partial incomplete decode gen?~n"),
case asn1ct:get_gen_state_field(inc_type_pattern) of
undefined ->
-% io:format("Partial incomplete decode gen not started: ~w~n",[asn1ct:get_gen_state_field(active)]),
ok;
-% [] ->
-% ok;
ConfList ->
PatternLists=lists:map(fun({_,P}) -> P end,ConfList),
pgen_partial_inc_dec1(Rtmod,Erules,Module,PatternLists),
@@ -215,11 +210,9 @@ pgen_partial_inc_dec1(Rtmod,Erules,Module,[P|Ps]) ->
asn1ct:update_gen_state(prefix,"dec-inc-"),
case asn1ct:maybe_saved_sindex(TopTypeName,P) of
I when is_integer(I),I > 0 ->
-% io:format("Index:~p~n",[I]),
asn1ct:set_current_sindex(I);
_I ->
asn1ct:set_current_sindex(0),
-% io:format("Index=~p~n",[_I]),
ok
end,
Rtmod:gen_decode(Erules,TypeDef),
@@ -250,8 +243,8 @@ gen_partial_inc_dec_refed_funcs(Rtmod, #gen{erule=ber}=Gen) ->
pgen_partial_dec(_Rtmod,Erules,_Module) ->
Type_pattern = asn1ct:get_gen_state_field(type_pattern),
-% io:format("Type_pattern: ~w~n",[Type_pattern]),
- %% Get the typedef of the top type and follow into the choosen components until the last type/component.
+ %% Get the typedef of the top type and follow into the choosen
+ %% components until the last type/component.
pgen_partial_types(Erules,Type_pattern),
ok.
@@ -266,7 +259,6 @@ pgen_partial_types(#gen{options=Options}=Gen, TypePattern) ->
pgen_partial_types1(Erules,[{FuncName,[TopType|RestTypes]}|Rest]) ->
-% emit([FuncName,"(Bytes) ->",nl]),
CurrMod = get(currmod),
TypeDef = asn1_db:dbget(CurrMod,TopType),
traverse_type_structure(Erules,TypeDef,RestTypes,FuncName,
@@ -291,8 +283,9 @@ traverse_type_structure(Erules,Type,[],FuncName,TopTypeName) ->
end,
Ctmod:gen_decode_selected(Erules,TypeDef,FuncName); % what if Type is #type{}
traverse_type_structure(Erules,#type{def=Def},[[N]],FuncName,TopTypeName)
- when is_integer(N) -> % this case a decode of one of the elements in
- % the SEQUENCE OF is required.
+ when is_integer(N) ->
+ %% In this case a decode of one of the elements in the SEQUENCE OF is
+ %% required.
InnerType = asn1ct_gen:get_inner(Def),
case InnerType of
'SEQUENCE OF' ->
@@ -368,8 +361,9 @@ traverse_type_structure(Erules,#typedef{typespec=Def},[T|Ts],FuncName,
TypeDef = asn1_db:dbget(M,TName),
traverse_type_structure(Erules,TypeDef,[T|Ts],FuncName,
[TypeDef#typedef.name]);
- _ -> %this may be a referenced type that shall be traversed or
- %the selected type
+ _ ->
+ %% This may be a referenced type that shall be traversed
+ %% or the selected type
traverse_type_structure(Erules,Def,Ts,FuncName,[T|TopTypeName])
end.
@@ -384,9 +378,7 @@ get_component(Name,{C1,C2}) when is_list(C1),is_list(C2) ->
get_component(Name,[C=#'ComponentType'{name=Name}|_Cs]) ->
C;
get_component(Name,[_C|Cs]) ->
- get_component(Name,Cs);
-get_component(Name,_) ->
- throw({error,{asn1,{internal_error,Name}}}).
+ get_component(Name,Cs).
%% generate code for all inner types that are called from the top type
%% of the partial incomplete decode and are defined within the top
@@ -451,7 +443,6 @@ pgen_partial_incomplete_decode1(#gen{erule=ber}) ->
lists:foreach(fun emit_partial_incomplete_decode/1,Data)
end,
GeneratedFs= asn1ct:get_gen_state_field(gen_refed_funcs),
-% io:format("GeneratedFs :~n~p~n",[GeneratedFs]),
gen_part_decode_funcs(GeneratedFs,0);
pgen_partial_incomplete_decode1(#gen{}) -> ok.
@@ -604,9 +595,7 @@ gen_encode_constructed(Erules,Typename,InnerType,D) when is_record(D,type) ->
Rtmod:gen_encode_sof(Erules,Typename,InnerType,D),
{_,Type} = D#type.def,
NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
- gen_types(Erules, [NameSuffix|Typename], Type, gen_encode);
- _ ->
- exit({nyi,InnerType})
+ gen_types(Erules, [NameSuffix|Typename], Type, gen_encode)
end;
gen_encode_constructed(Erules,Typename,InnerType,D)
when is_record(D,typedef) ->
@@ -879,7 +868,6 @@ gen_partial_inc_dispatcher(#gen{erule=ber}) ->
{_,undefined} ->
ok;
{Data1,Data2} ->
-% io:format("partial_incomplete_decode: ~p~ninc_type_pattern: ~p~n",[Data,Data2]),
gen_partial_inc_dispatcher(Data1, Data2, "")
end;
gen_partial_inc_dispatcher(#gen{}) ->
@@ -954,71 +942,39 @@ hrl_protector(OutFile) ->
end || C <- P].
-%% EMIT functions ************************
-%% ***************************************
-
- % debug generation
-demit(Term) ->
- case get(asndebug) of
- true -> emit(Term);
- _ ->true
- end.
-
- % always generation
emit(Term) ->
ok = file:write(get(gen_file_out), do_emit(Term)).
-do_emit({external,_M,T}) ->
- do_emit(T);
-
do_emit({prev,Variable}) when is_atom(Variable) ->
do_emit({var,asn1ct_name:prev(Variable)});
-
do_emit({next,Variable}) when is_atom(Variable) ->
do_emit({var,asn1ct_name:next(Variable)});
-
do_emit({curr,Variable}) when is_atom(Variable) ->
do_emit({var,asn1ct_name:curr(Variable)});
-
do_emit({var,Variable}) when is_atom(Variable) ->
[Head|V] = atom_to_list(Variable),
[Head-32|V];
-
-do_emit({var,Variable}) ->
- [Head|V] = Variable,
- [Head-32|V];
-
do_emit({asis,What}) ->
io_lib:format("~w", [What]);
-
do_emit({call,M,F,A}) ->
MFA = {M,F,length(A)},
asn1ct_func:need(MFA),
[atom_to_list(F),"(",call_args(A, "")|")"];
-
do_emit(nl) ->
"\n";
-
do_emit(com) ->
",";
-
-do_emit(tab) ->
- " ";
-
+do_emit([C|_]=Str) when is_integer(C) ->
+ Str;
+do_emit([_|_]=L) ->
+ [do_emit(E) || E <- L];
+do_emit([]) ->
+ [];
do_emit(What) when is_integer(What) ->
integer_to_list(What);
-
-do_emit(What) when is_list(What), is_integer(hd(What)) ->
- What;
-
do_emit(What) when is_atom(What) ->
- atom_to_list(What);
+ atom_to_list(What).
-do_emit(What) when is_tuple(What) ->
- [do_emit(E) || E <- tuple_to_list(What)];
-
-do_emit(What) when is_list(What) ->
- [do_emit(E) || E <- What].
call_args([A|As], Sep) ->
[Sep,do_emit(A)|call_args(As, ", ")];
@@ -1124,8 +1080,6 @@ gen_record(Gen, TorPtype, Name, #type{}=Type, Num) ->
case Seq#'SEQUENCE'.pname of
false ->
{record,Seq#'SEQUENCE'.components};
-%% _Pname when TorPtype == type ->
-%% false;
_ ->
{record,Seq#'SEQUENCE'.components}
end;
@@ -1138,8 +1092,6 @@ gen_record(Gen, TorPtype, Name, #type{}=Type, Num) ->
_ ->
{record,to_textual_order(Set#'SET'.components)}
end;
-% {'SET',{_,_CompList}} ->
-% {record,_CompList};
{'CHOICE',_CompList} -> {inner,Def};
{'SEQUENCE OF',_CompList} -> {['SEQOF'|Name],Def};
{'SET OF',_CompList} -> {['SETOF'|Name],Def};
@@ -1345,7 +1297,6 @@ get_inner({fixedtypevaluefield,_,Type}) ->
get_inner({typefield,TypeName}) ->
TypeName;
get_inner(#'ObjectClassFieldType'{type=Type}) ->
-% get_inner(Type);
Type;
get_inner(T) when is_tuple(T) ->
case element(1,T) of
@@ -1354,9 +1305,7 @@ get_inner(T) when is_tuple(T) ->
{valuefieldreference,FieldName} ->
get_fieldtype(element(2,Tuple),FieldName);
{typefieldreference,FieldName} ->
- get_fieldtype(element(2,Tuple),FieldName);
- {'EXIT',Reason} ->
- throw({asn1,{'internal error in get_inner/1',Reason}})
+ get_fieldtype(element(2,Tuple),FieldName)
end;
_ -> element(1,T)
end.
diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
index 6c6d4193f3..948566a6fc 100644
--- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
@@ -35,21 +35,21 @@
-export([extaddgroup2sequence/1]).
-export([dialyzer_suppressions/1]).
--import(asn1ct_gen, [emit/1,demit/1]).
+-import(asn1ct_gen, [emit/1]).
- % the encoding of class of tag bits 8 and 7
+%% The encoding of class of tag bits 8 and 7
-define(UNIVERSAL, 0).
-define(APPLICATION, 16#40).
-define(CONTEXT, 16#80).
-define(PRIVATE, 16#C0).
- % primitive or constructed encoding % bit 6
+%% Primitive or constructed encoding % bit 6
-define(PRIMITIVE, 0).
-define(CONSTRUCTED, 2#00100000).
-define(T_ObjectDescriptor, ?UNIVERSAL bor ?PRIMITIVE bor 7).
- % restricted character string types
+%% Restricted character string types
-define(T_NumericString, ?UNIVERSAL bor ?PRIMITIVE bor 18). %can be constructed
-define(T_PrintableString, ?UNIVERSAL bor ?PRIMITIVE bor 19). %can be constructed
-define(T_TeletexString, ?UNIVERSAL bor ?PRIMITIVE bor 20). %can be constructed
@@ -107,20 +107,12 @@ gen_encode(Erules,Typename,Type) when is_record(Type,type) ->
case asn1ct_gen:type(InnerType) of
{constructed,bif} ->
- emit([nl,nl,nl,"%%================================"]),
- emit([nl,"%% ",asn1ct_gen:list2name(Typename)]),
- emit([nl,"%%================================",nl]),
- case length(Typename) of
- 1 -> % top level type
- emit(["'enc_",asn1ct_gen:list2name(Typename),
- "'(Val",ObjFun,") ->",nl]),
- emit([" 'enc_",asn1ct_gen:list2name(Typename),
- "'(Val, ", {asis,lists:reverse(Type#type.tag)},ObjFun,").",nl,nl]);
- _ -> % embedded type with constructed name
- true
- end,
- emit(["'enc_",asn1ct_gen:list2name(Typename),
- "'(Val, TagIn",ObjFun,") ->",nl," "]),
+ Func = {asis,enc_func(asn1ct_gen:list2name(Typename))},
+ emit([nl,nl,nl,"%%================================",nl,
+ "%% ",asn1ct_gen:list2name(Typename),nl,
+ "%%================================",nl,
+ Func,"(Val, TagIn",ObjFun,") ->",nl,
+ " "]),
asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
_ ->
true
@@ -146,7 +138,7 @@ gen_encode_user(Erules, #typedef{}=D, Wrapper) ->
emit([nl,nl,"%%================================"]),
emit([nl,"%% ",Typename]),
emit([nl,"%%================================",nl]),
- FuncName = "'enc_" ++ asn1ct_gen:list2name(Typename) ++ "'",
+ FuncName = {asis,enc_func(asn1ct_gen:list2name(Typename))},
case Wrapper of
true ->
%% This is a top-level type. Generate an 'enc_Type'/1
@@ -169,9 +161,10 @@ gen_encode_user(Erules, #typedef{}=D, Wrapper) ->
gen_encode_prim(ber,Type,"TagIn","Val"),
emit([".",nl]);
#'Externaltypereference'{module=CurrentMod,type=Etype} ->
- emit([" 'enc_",Etype,"'(Val, TagIn).",nl]);
+ emit([" ",{asis,enc_func(Etype)},"(Val, TagIn).",nl]);
#'Externaltypereference'{module=Emod,type=Etype} ->
- emit([" '",Emod,"':'enc_",Etype,"'(Val, TagIn).",nl]);
+ emit([" ",{asis,Emod},":",{asis,enc_func(Etype)},
+ "(Val, TagIn).",nl]);
'ASN1_OPEN_TYPE' ->
emit(["%% OPEN TYPE",nl]),
gen_encode_prim(ber,
@@ -326,40 +319,39 @@ gen_decode(Erules,Type) when is_record(Type,typedef) ->
Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- InnerTag],
- FunctionName =
+ FuncName0 =
case {asn1ct:get_gen_state_field(active),
asn1ct:get_gen_state_field(prefix)} of
{true,Pref} ->
%% prevent duplicated function definitions
-% Pattern = asn1ct:get_gen_state_field(namelist),
-% FuncName=asn1ct:maybe_rename_function(Type#typedef.name,
-% Pattern),
case asn1ct:current_sindex() of
- I when is_integer(I),I>0 ->
- lists:concat([Pref,Type#typedef.name,"_",I]);
+ I when is_integer(I), I > 0 ->
+ [Pref,Type#typedef.name,"_",I];
_->
- lists:concat([Pref,Type#typedef.name])
- end; % maybe the current_sindex must be reset
- _ -> lists:concat(["dec_",Type#typedef.name])
+ [Pref,Type#typedef.name]
+ end;
+ {_,_} ->
+ ["dec_",Type#typedef.name]
end,
- emit({nl,nl}),
- emit(["'",FunctionName,"'(Tlv) ->",nl]),
- emit([" '",FunctionName,"'(Tlv, ",{asis,Tag},").",nl,nl]),
- emit(["'",FunctionName,"'(Tlv, TagIn) ->",nl]),
- dbdec(Type#typedef.name,"Tlv"),
+ FuncName = {asis,list_to_atom(lists:concat(FuncName0))},
+ emit([nl,nl,
+ FuncName,"(Tlv) ->",nl,
+ " ",FuncName,"(Tlv, ",{asis,Tag},").",nl,nl,
+ FuncName,"(Tlv, TagIn) ->",nl]),
gen_decode_user(Erules,Type).
gen_inc_decode(Erules,Type) when is_record(Type,typedef) ->
Prefix = asn1ct:get_gen_state_field(prefix),
Suffix = asn1ct_gen:index2suffix(asn1ct:current_sindex()),
- emit({nl,nl}),
- emit(["'",Prefix,Type#typedef.name,Suffix,"'(Tlv, TagIn) ->",nl]),
+ FuncName0 = [Prefix,Type#typedef.name,Suffix],
+ FuncName = {asis,list_to_atom(lists:concat(FuncName0))},
+ emit([nl,nl,
+ FuncName,"(Tlv, TagIn) ->",nl]),
gen_decode_user(Erules,Type).
%% gen_decode_selected exported function for selected decode
gen_decode_selected(Erules,Type,FuncName) ->
emit([FuncName,"(Bin) ->",nl]),
-% Pattern = asn1ct:get_gen_state_field(tag_pattern),
Patterns = asn1ct:read_config_data(partial_decode),
Pattern =
case lists:keysearch(FuncName,1,Patterns) of
@@ -398,12 +390,10 @@ gen_decode_selected_type(_Erules,TypeDef) ->
asn1ct_gen:list2name(TopType),"'"]),
emit([DecFunName,"(",BytesVar,
", ",{asis,Tag},")"]);
-% emit([";",nl]);
TheType ->
DecFunName = mkfuncname(TheType,dec),
emit([DecFunName,"(",BytesVar,
", ",{asis,Tag},")"])
-% emit([";",nl])
end.
%%===============================================================================
@@ -418,7 +408,6 @@ gen_decode(Erules,Typename,Type) when is_record(Type,type) ->
FunctionName =
case asn1ct:get_gen_state_field(active) of
true ->
-% Suffix = asn1ct_gen:index2suffix(SIndex),
Pattern = asn1ct:get_gen_state_field(namelist),
Suffix =
case asn1ct:maybe_saved_sindex(Typename,Pattern) of
@@ -431,8 +420,6 @@ gen_decode(Erules,Typename,Type) when is_record(Type,type) ->
_ ->
lists:concat(["'dec_",asn1ct_gen:list2name(Typename)])
end,
-% io:format("Typename: ~p,~n",[Typename]),
-% io:format("FunctionName: ~p~n",[FunctionName]),
case asn1ct_gen:type(InnerType) of
{constructed,bif} ->
ObjFun =
@@ -442,9 +429,7 @@ gen_decode(Erules,Typename,Type) when is_record(Type,type) ->
_ ->
""
end,
-% emit([Prefix,asn1ct_gen:list2name(Typename),"'(Tlv, TagIn",ObjFun,") ->",nl]),
emit([FunctionName,"'(Tlv, TagIn",ObjFun,") ->",nl]),
- dbdec(Typename,"Tlv"),
asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,Type);
Rec when is_record(Rec,'Externaltypereference') ->
case {Typename,asn1ct:get_gen_state_field(namelist)} of
@@ -476,10 +461,10 @@ gen_decode(Erules,Typename,Type) when is_record(Type,type) ->
gen_decode(Erules,Tname,#'ComponentType'{name=Cname,typespec=Type}) ->
NewTname = [Cname|Tname],
- %% The tag is set to [] to avoid that it is
- %% taken into account twice, both as a component/alternative (passed as
- %% argument to the encode decode function and within the encode decode
- %% function it self.
+ %% The tag is set to [] to avoid that it is taken into account
+ %% twice, both as a component/alternative (passed as argument to
+ %% the encode/decode function), and within the encode decode
+ %% function itself.
NewType = Type#type{tag=[]},
case {asn1ct:get_gen_state_field(active),
asn1ct:get_tobe_refed_func(NewTname)} of
@@ -504,7 +489,7 @@ gen_decode_user(Erules,D) when is_record(D,typedef) ->
asn1ct_name:new(len),
gen_dec_prim(Def#type{def='ASN1_OPEN_TYPE'},
BytesVar, {string,"TagIn"}),
- emit({".",nl,nl});
+ emit([".",nl,nl]);
{primitive,bif} ->
asn1ct_name:new(len),
gen_dec_prim(Def, BytesVar, {string,"TagIn"}),
@@ -515,8 +500,7 @@ gen_decode_user(Erules,D) when is_record(D,typedef) ->
TheType ->
DecFunName = mkfuncname(TheType,dec),
emit([DecFunName,"(",BytesVar,
- ", TagIn)"]),
- emit([".",nl,nl])
+ ", TagIn).",nl,nl])
end.
@@ -746,9 +730,10 @@ gen_obj_code(Erules,_Module,Obj) when is_record(Obj,typedef) ->
#'Externaltypereference'{module=M,type=ClName} = Def#'Object'.classname,
Class = asn1_db:dbget(M,ClName),
{object,_,Fields} = Def#'Object'.def,
- emit({nl,nl,nl,"%%================================"}),
- emit({nl,"%% ",ObjName}),
- emit({nl,"%%================================",nl}),
+ emit([nl,nl,nl,
+ "%%================================",nl,
+ "%% ",ObjName,nl,
+ "%%================================",nl]),
EncConstructed =
gen_encode_objectfields(ClName,get_class_fields(Class),
ObjName,Fields,[]),
@@ -766,11 +751,9 @@ gen_encode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
ObjName,ObjectFields,ConstrAcc) ->
EmitFuncClause =
fun(Arg) ->
- emit(["'enc_",ObjName,"'(",{asis,Name},
- ", ",Arg,", _RestPrimFieldName) ->",nl])
+ emit([{asis,enc_func(ObjName)},"(",{asis,Name},
+ ", ",Arg,", _RestPrimFieldName) ->",nl])
end,
-% emit(["'enc_",ObjName,"'(",{asis,Name},
-% ", Val, RestPrimFieldName) ->",nl]),
MaybeConstr=
case {get_object_field(Name,ObjectFields),OptOrMand} of
{false,'OPTIONAL'} ->
@@ -799,11 +782,9 @@ gen_encode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
CurrentMod = get(currmod),
EmitFuncClause =
fun(Args) ->
- emit(["'enc_",ObjName,"'(",{asis,Name},
+ emit([{asis,enc_func(ObjName)},"(",{asis,Name},
", ",Args,") ->",nl])
end,
-% emit(["'enc_",ObjName,"'(",{asis,Name},
-% ", Val,[H|T]) ->",nl]),
case {get_object_field(Name,ObjectFields),OptOrMand} of
{false,'OPTIONAL'} ->
EmitFuncClause("_,_"),
@@ -814,19 +795,14 @@ gen_encode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
{{Name,#'Externalvaluereference'{module=CurrentMod,
value=TypeName}},_} ->
EmitFuncClause(" Val, [H|T]"),
- emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"});
+ emit([indent(3),{asis,enc_func(TypeName)},"(H, Val, T)"]);
{{Name,#'Externalvaluereference'{module=M,value=TypeName}},_} ->
EmitFuncClause(" Val, [H|T]"),
- emit({indent(3),"'",M,"':'enc_",TypeName,"'(H, Val, T)"});
- {{Name,TypeSpec},_} ->
+ emit([indent(3),{asis,M},":",{asis,enc_func(TypeName)},
+ "(H, Val, T)"]);
+ {{Name,#typedef{name=TypeName}},_} when is_atom(TypeName) ->
EmitFuncClause(" Val, [H|T]"),
- case TypeSpec#typedef.name of
- {ExtMod,TypeName} ->
- emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
- "'(H, Val, T)"});
- TypeName ->
- emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"})
- end
+ emit([indent(3),{asis,enc_func(TypeName)},"(H, Val, T)"])
end,
case more_genfields(Rest) of
true ->
@@ -862,10 +838,11 @@ gen_encode_field_call(_ObjName,_FieldName,
X <- OTag],
if
M == CurrentMod ->
- emit({" 'enc_",T,"'(Val, ",{asis,Tag},")"}),
+ emit([" ",{asis,enc_func(T)},"(Val, ",{asis,Tag},")"]),
[];
true ->
- emit({" '",M,"':'enc_",T,"'(Val, ",{asis,Tag},")"}),
+ emit([" ",{asis,M},":",{asis,enc_func(T)},
+ "(Val, ",{asis,Tag},")"]),
[]
end;
gen_encode_field_call(ObjName,FieldName,Type) ->
@@ -875,24 +852,21 @@ gen_encode_field_call(ObjName,FieldName,Type) ->
X#tag.form,X#tag.number)||
X <- OTag],
case Type#typedef.name of
- {primitive,bif} -> %%tag should be the primitive tag
-% OTag = Def#type.tag,
-% Tag = [encode_tag_val(decode_class(X#tag.class),
-% X#tag.form,X#tag.number)||
-% X <- OTag],
+ {primitive,bif} -> %tag should be the primitive tag
gen_encode_prim(ber,Def,{asis,lists:reverse(Tag)},
"Val"),
[];
{constructed,bif} ->
- emit({" 'enc_",ObjName,'_',FieldName,
- "'(Val,",{asis,Tag},")"}),
- [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ Name = lists:concat([ObjName,'_',FieldName]),
+ emit([" ",{asis,enc_func(Name)},"(Val,",{asis,Tag},")"]),
+ [Type#typedef{name=list_to_atom(Name)}];
{ExtMod,TypeName} ->
- emit({" '",ExtMod,"':'enc_",TypeName,
- "'(Val,",{asis,Tag},")"}),
+ emit([" ",{asis,ExtMod},":",{asis,enc_func(TypeName)},
+ "(Val,",{asis,Tag},")"]),
[];
TypeName ->
- emit({" 'enc_",TypeName,"'(Val,",{asis,Tag},")"}),
+ emit([" ",{asis,enc_func(TypeName)},
+ "(Val,",{asis,Tag},")"]),
[]
end.
@@ -903,10 +877,10 @@ gen_encode_default_call(ClassName,FieldName,Type) ->
Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
case asn1ct_gen:type(InnerType) of
{constructed,bif} ->
- emit([" 'enc_",ClassName,'_',FieldName,"'",
+ Name = lists:concat([ClassName,'_',FieldName]),
+ emit([" ",{asis,enc_func(Name)},
"(Val, ",{asis,Tag},")"]),
- [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])),
- typespec=Type}];
+ [#typedef{name=list_to_atom(Name),typespec=Type}];
{primitive,bif} ->
gen_encode_prim(ber,Type,{asis,lists:reverse(Tag)},"Val"),
[];
@@ -916,12 +890,6 @@ gen_encode_default_call(ClassName,FieldName,Type) ->
#'Externaltypereference'{module=Emod,type=Etype} ->
emit([" '",Emod,"':'enc_",Etype,"'(Val, ",{asis,Tag},")",nl]),
[]
-% 'ASN1_OPEN_TYPE' ->
-% emit(["%% OPEN TYPE",nl]),
-% gen_encode_prim(ber,
-% Type#type{def='ASN1_OPEN_TYPE'},
-% "TagIn","Val"),
-% emit([".",nl])
end.
%%%%%%%%%%%%%%%%
@@ -930,11 +898,9 @@ gen_decode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
ObjName,ObjectFields,ConstrAcc) ->
EmitFuncClause =
fun(Arg) ->
- emit(["'dec_",ObjName,"'(",{asis,Name},
+ emit([{asis,dec_func(ObjName)},"(",{asis,Name},
", ",Arg,",_) ->",nl])
end,
-% emit(["'dec_",ObjName,"'(",{asis,Name},
-% ", Bytes, RestPrimFieldName) ->",nl]),
MaybeConstr=
case {get_object_field(Name,ObjectFields),OptOrMand} of
{false,'OPTIONAL'} ->
@@ -964,12 +930,9 @@ gen_decode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
CurrentMod = get(currmod),
EmitFuncClause =
fun(Args) ->
- emit(["'dec_",ObjName,"'(",{asis,Name},
+ emit([{asis,dec_func(ObjName)},"(",{asis,Name},
", ",Args,") ->",nl])
end,
-% emit(["'dec_",ObjName,"'(",{asis,Name},
-% ", Bytes,[H|T]) ->",nl]),
-% emit_tlv_format("Bytes"),
case {get_object_field(Name,ObjectFields),OptOrMand} of
{false,'OPTIONAL'} ->
EmitFuncClause("_,_"),
@@ -980,21 +943,14 @@ gen_decode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
{{Name,#'Externalvaluereference'{module=CurrentMod,
value=TypeName}},_} ->
EmitFuncClause("Bytes,[H|T]"),
- emit({indent(3),"'dec_",TypeName,"'(H, Bytes, T)"});
+ emit([indent(3),{asis,dec_func(TypeName)},"(H, Bytes, T)"]);
{{Name,#'Externalvaluereference'{module=M,value=TypeName}},_} ->
EmitFuncClause("Bytes,[H|T]"),
- emit({indent(3),"'",M,"':'dec_",TypeName,
- "'(H, Bytes, T)"});
- {{Name,TypeSpec},_} ->
- EmitFuncClause("Bytes,[H|T]"),
-% emit_tlv_format("Bytes"),
- case TypeSpec#typedef.name of
- {ExtMod,TypeName} ->
- emit({indent(3),"'",ExtMod,"':'dec_",TypeName,
- "'(H, Bytes, T)"});
- TypeName ->
- emit({indent(3),"'dec_",TypeName,"'(H, Bytes, T)"})
- end
+ emit([indent(3),{asis,M},":",{asis,dec_func(TypeName)},
+ "(H, Bytes, T)"]);
+ {{Name,#typedef{name=TypeName}},_} when is_atom(TypeName) ->
+ EmitFuncClause("Bytes,[H|T]"),
+ emit([indent(3),{asis,dec_func(TypeName)},"(H, Bytes, T)"])
end,
case more_genfields(Rest) of
true ->
@@ -1014,24 +970,20 @@ emit_tlv_format(Bytes) ->
notice_tlv_format_gen() ->
Module = get(currmod),
-% io:format("Noticed: ~p~n",[Module]),
case get(tlv_format) of
{done,Module} ->
ok;
- _ -> % true or undefined
+ _ -> % true or undefined
put(tlv_format,true)
end.
emit_tlv_format_function() ->
Module = get(currmod),
-% io:format("Tlv formated: ~p",[Module]),
case get(tlv_format) of
true ->
-% io:format(" YES!~n"),
emit_tlv_format_function1(),
put(tlv_format,{done,Module});
_ ->
-% io:format(" NO!~n"),
ok
end.
emit_tlv_format_function1() ->
@@ -1066,12 +1018,12 @@ gen_decode_field_call(_ObjName,_FieldName,Bytes,
X <- OTag],
if
M == CurrentMod ->
- emit({" 'dec_",T,"'(",Bytes,
- ", ",{asis,Tag},")"}),
+ emit([" ",{asis,dec_func(T)},"(",Bytes,
+ ", ",{asis,Tag},")"]),
[];
true ->
- emit({" '",M,"':'dec_",T,
- "'(",Bytes,", ",{asis,Tag},")"}),
+ emit([" ",{asis,M},":",{asis,dec_func(T)},
+ "(",Bytes,", ",{asis,Tag},")"]),
[]
end;
gen_decode_field_call(ObjName,FieldName,Bytes,Type) ->
@@ -1084,15 +1036,17 @@ gen_decode_field_call(ObjName,FieldName,Bytes,Type) ->
gen_dec_prim(Def, Bytes, Tag),
[];
{constructed,bif} ->
- emit({" 'dec_",ObjName,'_',FieldName,
- "'(",Bytes,",",{asis,Tag},")"}),
- [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ Name = lists:concat([ObjName,"_",FieldName]),
+ emit([" ",{asis,dec_func(Name)},
+ "(",Bytes,",",{asis,Tag},")"]),
+ [Type#typedef{name=list_to_atom(Name)}];
{ExtMod,TypeName} ->
- emit({" '",ExtMod,"':'dec_",TypeName,
- "'(",Bytes,",",{asis,Tag},")"}),
+ emit([" ",{asis,ExtMod},":",{asis,dec_func(TypeName)},
+ "(",Bytes,",",{asis,Tag},")"]),
[];
TypeName ->
- emit({" 'dec_",TypeName,"'(",Bytes,",",{asis,Tag},")"}),
+ emit([" ",{asis,dec_func(TypeName)},
+ "(",Bytes,",",{asis,Tag},")"]),
[]
end.
@@ -1118,12 +1072,6 @@ gen_decode_default_call(ClassName,FieldName,Bytes,Type) ->
emit([" '",Emod,"':'dec_",Etype,"'(",Bytes,", ",
{asis,Tag},")",nl]),
[]
-% 'ASN1_OPEN_TYPE' ->
-% emit(["%% OPEN TYPE",nl]),
-% gen_encode_prim(ber,
-% Type#type{def='ASN1_OPEN_TYPE'},
-% "TagIn","Val"),
-% emit([".",nl])
end.
%%%%%%%%%%%
@@ -1162,15 +1110,15 @@ more_genfields([Field|Fields]) ->
gen_objectset_code(Erules,ObjSet) ->
ObjSetName = ObjSet#typedef.name,
Def = ObjSet#typedef.typespec,
-% {ClassName,ClassDef} = Def#'ObjectSet'.class,
#'Externaltypereference'{module=ClassModule,
type=ClassName} = Def#'ObjectSet'.class,
ClassDef = asn1_db:dbget(ClassModule,ClassName),
UniqueFName = Def#'ObjectSet'.uniquefname,
Set = Def#'ObjectSet'.set,
- emit({nl,nl,nl,"%%================================"}),
- emit({nl,"%% ",ObjSetName}),
- emit({nl,"%%================================",nl}),
+ emit([nl,nl,nl,
+ "%%================================",nl,
+ "%% ",ObjSetName,nl,
+ "%%================================",nl]),
case ClassName of
{_Module,ExtClassName} ->
gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ExtClassName,ClassDef);
@@ -1200,19 +1148,20 @@ gen_objset_enc(Erules, ObjSetName, UniqueName,
{no_mod,no_name} ->
gen_inlined_enc_funs(Fields, ClFields, ObjSetName, Val, NthObj);
{CurrMod,Name} ->
- emit(["'getenc_",ObjSetName,"'(Id) when Id =:= ",
- {asis,Val}," ->",nl,
- " fun 'enc_",Name,"'/3;",nl]),
+ emit([asis_atom(["getenc_",ObjSetName]),
+ "(Id) when Id =:= ",{asis,Val}," ->",nl,
+ " fun ",asis_atom(["enc_",Name]),"/3;",nl]),
{[],NthObj};
{ModuleName,Name} ->
- emit(["'getenc_",ObjSetName,"'(Id) when Id =:= ",
- {asis,Val}," ->",nl]),
+ emit([asis_atom(["getenc_",ObjSetName]),
+ "(Id) when Id =:= ",{asis,Val}," ->",nl]),
emit_ext_fun(enc,ModuleName,Name),
emit([";",nl]),
{[],NthObj};
_ ->
- emit(["'getenc_",ObjSetName,"'(",{asis,Val},") ->",nl,
- " fun 'enc_",ObjName,"'/3;",nl]),
+ emit([asis_atom(["getenc_",ObjSetName]),
+ "(",{asis,Val},") ->",nl,
+ " fun ",asis_atom(["enc_",ObjName]),"/3;",nl]),
{[],NthObj}
end,
gen_objset_enc(Erules, ObjSetName, UniqueName, T, ClName, ClFields,
@@ -1220,7 +1169,7 @@ gen_objset_enc(Erules, ObjSetName, UniqueName,
%% See X.681 Annex E for the following case
gen_objset_enc(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
_ClFields,_NthObj,Acc) ->
- emit(["'getenc_",ObjSetName,"'(_) ->",nl,
+ emit([asis_atom(["getenc_",ObjSetName]),"(_) ->",nl,
indent(2),"fun(_, Val, _RestPrimFieldName) ->",nl]),
emit_enc_open_type(4),
emit([nl,
@@ -1228,7 +1177,7 @@ gen_objset_enc(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
Acc;
gen_objset_enc(_, ObjSetName, UniqueName, [], _, _, _, Acc) ->
emit_default_getenc(ObjSetName, UniqueName),
- emit({".",nl,nl}),
+ emit([".",nl,nl]),
Acc.
emit_ext_fun(EncDec,ModuleName,Name) ->
@@ -1236,14 +1185,15 @@ emit_ext_fun(EncDec,ModuleName,Name) ->
Name,"'(T,V,O) end"]).
emit_default_getenc(ObjSetName,UniqueName) ->
- emit(["'getenc_",ObjSetName,"'(ErrV) ->",nl]),
- emit([indent(3),"fun(C,V,_) -> exit({'Type not compatible with table constraint',{component,C},{value,V}, {unique_name_and_value,",{asis,UniqueName},", ErrV}}) end"]).
+ emit([asis_atom(["getenc_",ObjSetName]),"(ErrV) ->",nl,
+ indent(3),"fun(C,V,_) ->",nl,
+ "exit({'Type not compatible with table constraint',{component,C},{value,V}, {unique_name_and_value,",{asis,UniqueName},", ErrV}}) end"]).
%% gen_inlined_enc_funs for each object iterates over all fields of a
%% class, and for each typefield it checks if the object has that
%% field and emits the proper code.
gen_inlined_enc_funs(Fields, [{typefield,_,_}|_]=T, ObjSetName, Val, NthObj) ->
- emit(["'getenc_",ObjSetName,"'(",{asis,Val},") ->",nl,
+ emit([asis_atom(["getenc_",ObjSetName]),"(",{asis,Val},") ->",nl,
indent(3),"fun(Type, Val, _RestPrimFieldName) ->",nl,
indent(6),"case Type of",nl]),
gen_inlined_enc_funs1(Fields, T, ObjSetName, [], NthObj, []);
@@ -1283,8 +1233,8 @@ gen_inlined_enc_funs1(Fields, [{typefield,Name,_}|Rest], ObjSetName,
end,
{Acc0,0};
false ->
- %% This field was not present in the object thus there
- %% were no type in the table and we therefore generate
+ %% This field was not present in the object; thus, there
+ %% was no type in the table and we therefore generate
%% code that returns the input for application
%% treatment.
emit([indent(9),{asis,Name}," ->",nl]),
@@ -1322,7 +1272,6 @@ emit_inner_of_fun(TDef=#typedef{name={ExtMod,Name},typespec=Type},
InternalDefFunName) ->
OTag = Type#type.tag,
Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
-% remove Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
case {ExtMod,Name} of
{primitive,bif} ->
emit(indent(12)),
@@ -1333,20 +1282,14 @@ emit_inner_of_fun(TDef=#typedef{name={ExtMod,Name},typespec=Type},
InternalDefFunName,"'(Val, ",{asis,Tag},")"]),
{[TDef#typedef{name=InternalDefFunName}],1};
_ ->
- emit({indent(12),"'",ExtMod,"':'enc_",Name,"'(Val",{asis,Tag},")"}),
+ emit([indent(12),"'",ExtMod,"':'enc_",Name,"'(Val",{asis,Tag},")"]),
{[],0}
end;
emit_inner_of_fun(#typedef{name=Name},_) ->
-% OTag = Type#type.tag,
-% remove Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
-% Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
- emit({indent(12),"'enc_",Name,"'(Val)"}),
+ emit([indent(12),"'enc_",Name,"'(Val)"]),
{[],0};
emit_inner_of_fun(Type,_) when is_record(Type,type) ->
CurrMod = get(currmod),
-% OTag = Type#type.tag,
-% remove Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
-% Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
case Type#type.def of
Def when is_atom(Def) ->
OTag = Type#type.tag,
@@ -1384,18 +1327,19 @@ gen_objset_dec(Erules, ObjSName, UniqueName, [{ObjName,Val,Fields}|T],
{no_mod,no_name} ->
gen_inlined_dec_funs(Fields,ClFields,ObjSName,Val,NthObj);
{CurrMod,Name} ->
- emit(["'getdec_",ObjSName,"'(Id) when Id =:= ",
- {asis,Val}," ->",nl,
+ emit([asis_atom(["getdec_",ObjSName]),
+ "(Id) when Id =:= ",{asis,Val}," ->",nl,
" fun 'dec_",Name,"'/3;", nl]),
NthObj;
{ModuleName,Name} ->
- emit(["'getdec_",ObjSName,"'(Id) when Id =:= ",
- {asis,Val}," ->",nl]),
+ emit([asis_atom(["getdec_",ObjSName]),
+ "(Id) when Id =:= ",{asis,Val}," ->",nl]),
emit_ext_fun(dec,ModuleName,Name),
emit([";",nl]),
NthObj;
_ ->
- emit(["'getdec_",ObjSName,"'(",{asis,Val},") ->",nl,
+ emit([asis_atom(["getdec_",ObjSName]),
+ "(",{asis,Val},") ->",nl,
" fun 'dec_",ObjName,"'/3;", nl]),
NthObj
end,
@@ -1403,8 +1347,8 @@ gen_objset_dec(Erules, ObjSName, UniqueName, [{ObjName,Val,Fields}|T],
ClFields, NewNthObj);
gen_objset_dec(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
_ClFields,_NthObj) ->
- emit(["'getdec_",ObjSetName,"'(_) ->",nl]),
- emit([indent(2),"fun(_,Bytes, _RestPrimFieldName) ->",nl]),
+ emit([asis_atom(["getdec_",ObjSetName]),"(_) ->",nl,
+ indent(2),"fun(_,Bytes, _RestPrimFieldName) ->",nl]),
emit_dec_open_type(4),
emit([nl,
indent(2),"end.",nl,nl]),
@@ -1495,7 +1439,6 @@ emit_dec_open_type(I) ->
emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type}, _Prop,
InternalDefFunName) ->
OTag = Type#type.tag,
-%% Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- OTag],
case {ExtName,Name} of
{primitive,bif} ->
@@ -1504,8 +1447,6 @@ emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type}, _Prop,
0;
{constructed,bif} ->
emit([indent(12),"'dec_",
-% asn1ct_gen:list2name(InternalDefFunName),"'(Bytes, ",Prop,
-% ", ",{asis,Tag},")"]),
asn1ct_gen:list2name(InternalDefFunName),"'(Bytes, ",
{asis,Tag},")"]),
1;
@@ -1519,7 +1460,6 @@ emit_inner_of_decfun(#typedef{name=Name},_Prop,_) ->
0;
emit_inner_of_decfun(#type{}=Type, _Prop, _) ->
OTag = Type#type.tag,
-%% Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- OTag],
CurrMod = get(currmod),
Def = Type#type.def,
@@ -1531,11 +1471,9 @@ emit_inner_of_decfun(#type{}=Type, _Prop, _) ->
gen_dec_prim(Type, "Bytes", Tag);
#'Externaltypereference'{module=CurrMod,type=T} ->
emit([indent(9),T," ->",nl,indent(12),"'dec_",T,
-% "'(Bytes, ",Prop,")"]);
"'(Bytes)"]);
#'Externaltypereference'{module=ExtMod,type=T} ->
emit([indent(9),T," ->",nl,indent(12),ExtMod,":'dec_",
-% T,"'(Bytes, ",Prop,")"])
T,"'(Bytes, ",{asis,Tag},")"])
end,
0.
@@ -1550,10 +1488,6 @@ gen_internal_funcs(Erules,[TypeDef|Rest]) ->
gen_internal_funcs(Erules,Rest).
-dbdec(Type,Arg) ->
- demit({"io:format(\"decoding: ",{asis,Type},"~w~n\",[",Arg,"]),",nl}).
-
-
decode_class('UNIVERSAL') ->
?UNIVERSAL;
decode_class('APPLICATION') ->
@@ -1605,7 +1539,7 @@ encode_tag_val(Class, Form, TagNo) ->
%%%%%%%%%%%
%% mk_object_val(Value) -> {OctetList, Len}
-%% returns a Val as a list of octets, the 8 bit is allways set to one except
+%% returns a Val as a list of octets, the 8 bit is always set to one except
%% for the last octet, where its 0
%%
@@ -1619,8 +1553,9 @@ mk_object_val(0, Ack, Len) ->
mk_object_val(Val, Ack, Len) ->
mk_object_val(Val bsr 7, [((Val band 127) bor 128) | Ack], Len + 1).
-%% For BER the ExtensionAdditionGroup notation has no impact on the encoding/decoding
-%% and therefore we only filter away the ExtensionAdditionGroup start and end markers
+%% For BER the ExtensionAdditionGroup notation has no impact on the
+%% encoding/decoding. Therefore we can filter away the
+%% ExtensionAdditionGroup start and end markers.
extaddgroup2sequence(ExtList) when is_list(ExtList) ->
lists:filter(fun(#'ExtensionAdditionGroup'{}) ->
false;
@@ -1632,3 +1567,12 @@ extaddgroup2sequence(ExtList) when is_list(ExtList) ->
call(F, Args) ->
asn1ct_func:call(ber, F, Args).
+
+enc_func(Tname) ->
+ list_to_atom(lists:concat(["enc_",Tname])).
+
+dec_func(Tname) ->
+ list_to_atom(lists:concat(["dec_",Tname])).
+
+asis_atom(List) ->
+ {asis,list_to_atom(lists:concat(List))}.
diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl
index 9671a566bf..22719bba74 100644
--- a/lib/asn1/src/asn1ct_gen_per.erl
+++ b/lib/asn1/src/asn1ct_gen_per.erl
@@ -24,7 +24,6 @@
%% all types in an ASN.1 module
-include("asn1_records.hrl").
-%-compile(export_all).
-export([gen_dec_imm/2]).
-export([gen_dec_prim/3,gen_encode_prim_imm/3]).
@@ -35,15 +34,20 @@
-export([extaddgroup2sequence/1]).
-export([dialyzer_suppressions/1]).
--import(asn1ct_gen, [emit/1,demit/1]).
+-import(asn1ct_gen, [emit/1]).
-import(asn1ct_func, [call/3]).
-%% Generate ENCODING ******************************
-%%****************************************x
+%%****************************************
+%% Generate ENCODING
+%%****************************************
-dialyzer_suppressions(Erules) ->
- case asn1ct_func:is_used({Erules,complete,1}) of
+dialyzer_suppressions(#gen{erule=per,aligned=Aligned}) ->
+ Mod = case Aligned of
+ false -> uper;
+ true -> per
+ end,
+ case asn1ct_func:is_used({Mod,complete,1}) of
false ->
ok;
true ->
@@ -54,14 +58,6 @@ dialyzer_suppressions(Erules) ->
gen_encode(Erules,Type) when is_record(Type,typedef) ->
gen_encode_user(Erules,Type).
-%% case Type#typedef.typespec of
-%% Def when is_record(Def,type) ->
-%% gen_encode_user(Erules,Type);
-%% Def when is_tuple(Def),(element(1,Def) == 'Object') ->
-%% gen_encode_object(Erules,Type);
-%% Other ->
-%% exit({error,{asn1,{unknown,Other}}})
-%% end.
gen_encode(Erules,Typename,#'ComponentType'{name=Cname,typespec=Type}) ->
NewTypename = [Cname|Typename],
@@ -72,15 +68,14 @@ gen_encode(Erules,Typename,Type) when is_record(Type,type) ->
ObjFun =
case lists:keysearch(objfun,1,Type#type.tablecinf) of
{value,{_,_Name}} ->
-%% lists:concat([", ObjFun",Name]);
", ObjFun";
false ->
""
end,
case asn1ct_gen:type(InnerType) of
{constructed,bif} ->
- emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val",ObjFun,
- ") ->",nl}),
+ Func = enc_func(asn1ct_gen:list2name(Typename)),
+ emit([{asis,Func},"(Val",ObjFun,") ->",nl]),
asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
_ ->
true
@@ -92,20 +87,21 @@ gen_encode_user(Erules,D) when is_record(D,typedef) ->
Typename = [D#typedef.name],
Def = D#typedef.typespec,
InnerType = asn1ct_gen:get_inner(Def#type.def),
- emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val) ->",nl}),
+ Func = enc_func(asn1ct_gen:list2name(Typename)),
+ emit([{asis,Func},"(Val) ->",nl]),
case asn1ct_gen:type(InnerType) of
{primitive,bif} ->
gen_encode_prim(Erules, Def),
- emit({".",nl});
+ emit([".",nl]);
'ASN1_OPEN_TYPE' ->
gen_encode_prim(Erules, Def#type{def='ASN1_OPEN_TYPE'}),
- emit({".",nl});
+ emit([".",nl]);
{constructed,bif} ->
asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,D);
#'Externaltypereference'{module=CurrMod,type=Etype} ->
- emit({"'enc_",Etype,"'(Val).",nl,nl});
+ emit([{asis,enc_func(Etype)},"(Val).",nl]);
#'Externaltypereference'{module=Emod,type=Etype} ->
- emit({"'",Emod,"':'enc_",Etype,"'(Val).",nl,nl})
+ emit([{asis,Emod},":",enc_func(Etype),"(Val).",nl])
end.
@@ -220,7 +216,6 @@ gen_objectset_code(_Erules, _ObjSet) ->
gen_decode(Erules, #typedef{}=Type) ->
DecFunc = dec_func(Type#typedef.name),
emit([nl,nl,{asis,DecFunc},"(Bytes) ->",nl]),
- dbdec(Type#typedef.name),
gen_decode_user(Erules, Type).
gen_decode(Erules,Tname,#'ComponentType'{name=Cname,typespec=Type}) ->
@@ -241,17 +236,11 @@ gen_decode(Erules,Typename,Type) when is_record(Type,type) ->
emit([nl,
{asis,dec_func(asn1ct_gen:list2name(Typename))},
"(Bytes",ObjFun,") ->",nl]),
- dbdec(Typename),
asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,Type);
_ ->
true
end.
-dbdec(Type) when is_list(Type)->
- demit({"io:format(\"decoding: ",asn1ct_gen:list2name(Type),"~w~n\",[Bytes]),",nl});
-dbdec(Type) ->
- demit({"io:format(\"decoding: ",{asis,Type},"~w~n\",[Bytes]),",nl}).
-
gen_decode_user(Erules,D) when is_record(D,typedef) ->
Typename = [D#typedef.name],
Def = D#typedef.typespec,
@@ -259,17 +248,15 @@ gen_decode_user(Erules,D) when is_record(D,typedef) ->
case asn1ct_gen:type(InnerType) of
{primitive,bif} ->
gen_dec_prim(Erules,Def,"Bytes"),
- emit({".",nl,nl});
+ emit([".",nl,nl]);
'ASN1_OPEN_TYPE' ->
gen_dec_prim(Erules,Def#type{def='ASN1_OPEN_TYPE'},"Bytes"),
- emit({".",nl,nl});
+ emit([".",nl,nl]);
{constructed,bif} ->
asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,D);
#'Externaltypereference'{}=Etype ->
gen_dec_external(Etype, "Bytes"),
- emit([".",nl,nl]);
- Other ->
- exit({error,{asn1,{unknown,Other}}})
+ emit([".",nl,nl])
end.
gen_dec_external(Ext, BytesVar) ->
@@ -398,10 +385,11 @@ gen_dec_prim(Erule, Type, BytesVar) ->
asn1ct_imm:dec_code_gen(Imm, BytesVar).
-%% For PER the ExtensionAdditionGroup notation has significance for the encoding and decoding
-%% the components within the ExtensionAdditionGroup is treated in a similar way as if they
-%% have been specified within a SEQUENCE, therefore we construct a fake sequence type here
-%% so that we can generate code for it
+%% For PER the ExtensionAdditionGroup notation has significance for
+%% the encoding and decoding. The components within the
+%% ExtensionAdditionGroup is treated in a similar way as if they have
+%% been specified within a SEQUENCE. Therefore we construct a fake
+%% sequence type here so that we can generate code for it.
extaddgroup2sequence(ExtList) ->
extaddgroup2sequence(ExtList,0,[]).
diff --git a/lib/asn1/src/asn1ct_name.erl b/lib/asn1/src/asn1ct_name.erl
index 72d541cbbc..06f6604a26 100644
--- a/lib/asn1/src/asn1ct_name.erl
+++ b/lib/asn1/src/asn1ct_name.erl
@@ -20,7 +20,6 @@
%%
-module(asn1ct_name).
-%%-compile(export_all).
-export([start/0,
curr/1,
clear/0,
@@ -44,7 +43,6 @@ start() ->
end.
name_server_loop({Ref, Parent} = Monitor,Vars) ->
-%% io:format("name -- ~w~n",[Vars]),
receive
{_From,clear} ->
name_server_loop(Monitor, []);
diff --git a/lib/asn1/src/asn1ct_parser2.erl b/lib/asn1/src/asn1ct_parser2.erl
index 2de9b0e2f0..3f1819b660 100644
--- a/lib/asn1/src/asn1ct_parser2.erl
+++ b/lib/asn1/src/asn1ct_parser2.erl
@@ -1496,7 +1496,7 @@ parse_ContentsConstraint([{'ENCODED',_},{'BY',_}|Rest]) ->
parse_ContentsConstraint(Tokens) ->
parse_error(Tokens).
-% X.683 Parameterization of ASN.1 specifications
+%% X.683 Parameterization of ASN.1 specifications
parse_Governor(Tokens) ->
Flist = [fun parse_Type/1,
diff --git a/lib/asn1/src/asn1ct_value.erl b/lib/asn1/src/asn1ct_value.erl
index 8bd99d995b..f7d986aa91 100644
--- a/lib/asn1/src/asn1ct_value.erl
+++ b/lib/asn1/src/asn1ct_value.erl
@@ -24,12 +24,12 @@
%% The value is randomized within it's constraints
-include("asn1_records.hrl").
-%-compile(export_all).
-export([from_type/2]).
-%% Generate examples of values ******************************
-%%****************************************x
+%%****************************************
+%% Generate examples of values
+%%****************************************
from_type(M,Typename) ->
@@ -92,9 +92,6 @@ get_inner(T) when is_tuple(T) ->
Other ->
Other
end.
-%%get_inner(T) when is_tuple(T) -> element(1,T).
-
-
from_type_constructed(M,Typename,InnerType,D) when is_record(D,type) ->
case InnerType of
@@ -111,9 +108,7 @@ from_type_constructed(M,Typename,InnerType,D) when is_record(D,type) ->
'SET OF' ->
{_,Type} = D#type.def,
NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
- get_sequence_of(M,Typename,D,NameSuffix);
- _ ->
- exit({nyi,InnerType})
+ get_sequence_of(M,Typename,D,NameSuffix)
end.
get_sequence(M,Typename,Type) ->
@@ -147,7 +142,8 @@ get_choice(M,Typename,Type) ->
case TCompList of
[] ->
{asn1_EMPTY,asn1_EMPTY};
- {CompList,ExtList} -> % Should be enhanced to handle extensions too
+ {CompList,ExtList} ->
+ %% should be enhanced to handle extensions too.
CList = CompList ++ ExtList,
C = lists:nth(random(length(CList)),CList),
{C#'ComponentType'.name,from_type(M,Typename,C)};
@@ -247,14 +243,6 @@ from_type_prim(M, D) ->
_ ->
{2#11111111,2,2}
end;
-%% Sign1 = random_sign(integer),
-%% Sign2 = random_sign(integer),
-%% {Sign1*random(10000),2,Sign2*random(1028)};
-%% 2 ->
-%% %% base 10 tuple format
-%% Sign1 = random_sign(integer),
-%% Sign2 = random_sign(integer),
-%% {Sign1*random(10000),10,Sign2*random(1028)};
_ ->
%% base 10 string format, NR3 format
case random(2) of
@@ -302,9 +290,7 @@ from_type_prim(M, D) ->
16#ffff,16#ffee,16#10ffff,16#ffff,16#fff]),
unicode:characters_to_binary(L);
'UniversalString' ->
- adjust_list(size_random(C),c_string(C,"UniversalString"));
- XX ->
- exit({asn1_error,nyi,XX})
+ adjust_list(size_random(C),c_string(C,"UniversalString"))
end.
c_string(C,Default) ->
@@ -343,22 +329,6 @@ random_unnamed_bit_string(M, C) ->
{PadLen,<<BitString/bitstring,0:PadLen>>}
end.
-%% FIXME:
-%% random_sign(integer) ->
-%% case random(2) of
-%% 2 ->
-%% -1;
-%% _ ->
-%% 1
-%% end;
-%% random_sign(string) ->
-%% case random(2) of
-%% 2 ->
-%% "-";
-%% _ ->
-%% ""
-%% end.
-
random(Upper) ->
rand:uniform(Upper).
@@ -409,13 +379,6 @@ c_random(VRange,Single) ->
S;
{_,S} when is_list(S) ->
lists:nth(random(length(S)),S)
-%% {S1,S2} ->
-%% io:format("asn1ct_value: hejsan hoppsan~n");
-%% _ ->
-%% io:format("asn1ct_value: hejsan hoppsan 2~n")
-%% io:format("asn1ct_value: c_random/2: S1 = ~w~n"
-%% "S2 = ~w,~n",[S1,S2])
-%% exit(self(),goodbye)
end.
adjust_list(Len,Orig) ->
diff --git a/lib/asn1/src/asn1rtt_ber.erl b/lib/asn1/src/asn1rtt_ber.erl
index fdb9b9061f..882a25c332 100644
--- a/lib/asn1/src/asn1rtt_ber.erl
+++ b/lib/asn1/src/asn1rtt_ber.erl
@@ -92,7 +92,7 @@
-define(N_BMPString, 30).
-% the complete tag-word of built-in types
+%% The complete tag-word of built-in types
-define(T_BOOLEAN, ?UNIVERSAL bor ?PRIMITIVE bor 1).
-define(T_INTEGER, ?UNIVERSAL bor ?PRIMITIVE bor 2).
-define(T_BIT_STRING, ?UNIVERSAL bor ?PRIMITIVE bor 3). % can be CONSTRUCTED
@@ -137,11 +137,11 @@ ber_decode_erlang(Tlv) ->
decode_primitive(Bin) ->
{Form,TagNo,V,Rest} = decode_tag_and_length(Bin),
case Form of
- 1 -> % constructed
+ 1 -> % constructed
{{TagNo,decode_constructed(V)},Rest};
- 0 -> % primitive
+ 0 -> % primitive
{{TagNo,V},Rest};
- 2 -> % constructed indefinite
+ 2 -> % constructed indefinite
{Vlist,Rest2} = decode_constructed_indefinite(V,[]),
{{TagNo,Vlist},Rest2}
end.
@@ -165,31 +165,30 @@ decode_primitive_incomplete([[default,TagNo]],Bin) -> %default
{Form,TagNo,V,Rest} ->
decode_incomplete2(Form,TagNo,V,[],Rest);
_ ->
- %{asn1_DEFAULT,Bin}
asn1_NOVALUE
end;
-decode_primitive_incomplete([[default,TagNo,Directives]],Bin) -> %default, constructed type, Directives points into this type
+decode_primitive_incomplete([[default,TagNo,Directives]],Bin) ->
+ %% default, constructed type, Directives points into this type
case decode_tag_and_length(Bin) of
{Form,TagNo,V,Rest} ->
decode_incomplete2(Form,TagNo,V,Directives,Rest);
_ ->
- %{asn1_DEFAULT,Bin}
asn1_NOVALUE
end;
-decode_primitive_incomplete([[opt,TagNo]],Bin) -> %optional
+decode_primitive_incomplete([[opt,TagNo]],Bin) ->
+ %% optional
case decode_tag_and_length(Bin) of
{Form,TagNo,V,Rest} ->
decode_incomplete2(Form,TagNo,V,[],Rest);
_ ->
- %{{TagNo,asn1_NOVALUE},Bin}
asn1_NOVALUE
end;
-decode_primitive_incomplete([[opt,TagNo,Directives]],Bin) -> %optional
+decode_primitive_incomplete([[opt,TagNo,Directives]],Bin) ->
+ %% optional
case decode_tag_and_length(Bin) of
{Form,TagNo,V,Rest} ->
decode_incomplete2(Form,TagNo,V,Directives,Rest);
_ ->
- %{{TagNo,asn1_NOVALUE},Bin}
asn1_NOVALUE
end;
%% An optional that shall be undecoded
@@ -236,7 +235,8 @@ decode_primitive_incomplete([[alt_parts,TagNo]|RestAlts],Bin) ->
_ ->
decode_primitive_incomplete(RestAlts,Bin)
end;
-decode_primitive_incomplete([[undec,_TagNo]|_RestTag],Bin) -> %incomlete decode
+decode_primitive_incomplete([[undec,_TagNo]|_RestTag],Bin) ->
+ %% incomlete decode
decode_incomplete_bin(Bin);
decode_primitive_incomplete([[parts,TagNo]|_RestTag],Bin) ->
case decode_tag_and_length(Bin) of
@@ -301,7 +301,8 @@ decode_constructed_incomplete(Directives=[[Alt,_]|_],Bin)
{TagNo,Tlv};
{alt_parts,_} ->
[{TagNo,decode_parts_incomplete(V)}];
- no_match -> %% if a choice alternative was encoded that
+ no_match ->
+ %% if a choice alternative was encoded that
%% was not specified in the config file,
%% thus decode component anonomous.
{Tlv,_}=decode_primitive(Bin),
@@ -546,7 +547,7 @@ decode_tag_and_length(<<Class:2, Form:1, 31:5, Buffer/binary>>) ->
decode_tag(<<0:1,PartialTag:7, Buffer/binary>>, TagAck) ->
TagNo = (TagAck bsl 7) bor PartialTag,
{TagNo, Buffer};
-% more tags
+%% more tags
decode_tag(<<_:1,PartialTag:7, Buffer/binary>>, TagAck) ->
TagAck1 = (TagAck bsl 7) bor PartialTag,
decode_tag(Buffer, TagAck1).
@@ -941,12 +942,12 @@ encode_bit_string_bits(C, BitListVal, _NamedBitList, TagIn) when is_list(BitList
case length(BitListVal) of
BitSize when BitSize == Size ->
{Len, Unused, OctetList} = encode_bitstring(BitListVal),
- %%add unused byte to the Len
+ %% add unused byte to the Len
encode_tags(TagIn, [Unused | OctetList], Len+1);
BitSize when BitSize < Size ->
PaddedList = pad_bit_list(Size-BitSize,BitListVal),
{Len, Unused, OctetList} = encode_bitstring(PaddedList),
- %%add unused byte to the Len
+ %% add unused byte to the Len
encode_tags(TagIn, [Unused | OctetList], Len+1);
BitSize ->
exit({error,{asn1,
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index 580c919b9d..d99190b6b0 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -1108,6 +1108,7 @@ test_modules() ->
"From",
"H235-SECURITY-MESSAGES",
"H323-MESSAGES",
+ "HighTagNumbers",
"Import",
"Int",
"MAP-commonDataTypes",
diff --git a/lib/asn1/test/asn1_SUITE_data/HighTagNumbers.asn1 b/lib/asn1/test/asn1_SUITE_data/HighTagNumbers.asn1
new file mode 100644
index 0000000000..b681063965
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/HighTagNumbers.asn1
@@ -0,0 +1,17 @@
+HighTagNumbers DEFINITIONS ::=
+BEGIN
+
+S ::= SEQUENCE {
+ a [127] INTEGER,
+ b [128] INTEGER,
+ c [150] INTEGER,
+ d [207] INTEGER,
+ e [255] INTEGER,
+ f [256] INTEGER,
+ g [7777] INTEGER,
+ h [9999] INTEGER,
+ i [16382] INTEGER,
+ j [16383] INTEGER
+}
+
+END
diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile
index e338dbb4e3..63763f31b2 100644
--- a/lib/compiler/test/Makefile
+++ b/lib/compiler/test/Makefile
@@ -185,6 +185,7 @@ release_tests_spec: make_emakefile
echo "-module($$module). %% dummy .erl file" >$$file; \
done
$(INSTALL_DATA) $(ERL_DUMMY_FILES) "$(RELSYSDIR)"
+ rm $(ERL_DUMMY_FILES)
chmod -R u+w "$(RELSYSDIR)"
@tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 631af62615..ce8add6559 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -40,6 +40,8 @@
-export([ec_curve/1, ec_curves/0]).
-export([rand_seed/1]).
+-deprecated({rand_uniform, 2, next_major_release}).
+
%% This should correspond to the similar macro in crypto.c
-define(MAX_BYTES_TO_NIF, 20000). %% Current value is: erlang:system_info(context_reductions) * 10
diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl
index 6b306c51d3..2b9f82b075 100644
--- a/lib/eunit/src/eunit_surefire.erl
+++ b/lib/eunit/src/eunit_surefire.erl
@@ -424,6 +424,7 @@ escape_suitename(String) ->
escape_suitename([], Acc) -> lists:reverse(Acc);
escape_suitename([$ | Tail], Acc) -> escape_suitename(Tail, [$_ | Acc]);
escape_suitename([$' | Tail], Acc) -> escape_suitename(Tail, Acc);
+escape_suitename([$" | Tail], Acc) -> escape_suitename(Tail, Acc);
escape_suitename([$/ | Tail], Acc) -> escape_suitename(Tail, [$: | Acc]);
escape_suitename([$\\ | Tail], Acc) -> escape_suitename(Tail, [$: | Acc]);
escape_suitename([Char | Tail], Acc) when Char < $! -> escape_suitename(Tail, Acc);
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index 4c4a5c39cb..d557efb6a8 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -659,7 +659,8 @@ get_tcpi_sacked(Sock) ->
<tag><c>{buffer, Size}</c></tag>
<item>
<p>The size of the user-level software buffer used by
- the driver. Not to be confused with options <c>sndbuf</c>
+ the driver.
+ Not to be confused with options <c>sndbuf</c>
and <c>recbuf</c>, which correspond to the
Kernel socket buffers. It is recommended
to have <c>val(buffer) &gt;= max(val(sndbuf),val(recbuf))</c> to
@@ -670,6 +671,9 @@ get_tcpi_sacked(Sock) ->
usually become larger, you are encouraged to use
<seealso marker="#getopts/2"><c>getopts/2</c></seealso>
to analyze the behavior of your operating system.</p>
+ <p>Note that this is also the maximum amount of data that can be
+ received from a single recv call. If you are using higher than
+ normal MTU consider setting buffer higher.</p>
</item>
<tag><c>{delay_send, Boolean}</c></tag>
<item>
diff --git a/lib/observer/src/cdv_ets_cb.erl b/lib/observer/src/cdv_ets_cb.erl
index ddd2d42df6..18f0c86fd3 100644
--- a/lib/observer/src/cdv_ets_cb.erl
+++ b/lib/observer/src/cdv_ets_cb.erl
@@ -30,26 +30,23 @@
-include("crashdump_viewer.hrl").
%% Defines
--define(COL_ID, 0).
--define(COL_NAME, ?COL_ID+1).
--define(COL_SLOT, ?COL_NAME+1).
--define(COL_OWNER, ?COL_SLOT+1).
+-define(COL_NAME, 0).
+-define(COL_IS_NAMED, ?COL_NAME+1).
+-define(COL_OWNER, ?COL_IS_NAMED+1).
-define(COL_OBJ, ?COL_OWNER+1).
-define(COL_MEM, ?COL_OBJ+1).
%% Callbacks for cdv_virtual_list_wx
-col_to_elem(id) -> col_to_elem(?COL_ID);
-col_to_elem(?COL_ID) -> #ets_table.id;
+col_to_elem(id) -> col_to_elem(?COL_NAME);
+col_to_elem(?COL_IS_NAMED) -> #ets_table.is_named;
col_to_elem(?COL_NAME) -> #ets_table.name;
-col_to_elem(?COL_SLOT) -> #ets_table.slot;
col_to_elem(?COL_OWNER) -> #ets_table.pid;
col_to_elem(?COL_OBJ) -> #ets_table.size;
col_to_elem(?COL_MEM) -> #ets_table.memory.
col_spec() ->
- [{"Id", ?wxLIST_FORMAT_LEFT, 200},
- {"Name", ?wxLIST_FORMAT_LEFT, 200},
- {"Slot", ?wxLIST_FORMAT_RIGHT, 50},
+ [{"Name", ?wxLIST_FORMAT_LEFT, 200},
+ {"Is Named", ?wxLIST_FORMAT_CENTRE, 70},
{"Owner", ?wxLIST_FORMAT_CENTRE, 120},
{"Objects", ?wxLIST_FORMAT_RIGHT, 80},
{"Memory", ?wxLIST_FORMAT_RIGHT, 80}
@@ -68,7 +65,7 @@ get_details(Id, Data) ->
{ok,{"Table:" ++ Id,Proplist,""}}.
get_detail_cols(all) ->
- {[{ets, ?COL_ID}, {process, ?COL_OWNER}],true};
+ {[{ets, ?COL_NAME}, {process, ?COL_OWNER}],true};
get_detail_cols(_W) ->
{[],true}.
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index 13e73f027d..e21f1c501b 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -1555,10 +1555,14 @@ split_pid_list_no_space([],[],Pids) ->
%% Page with external ets tables
get_ets_tables(File,Pid,WS) ->
ParseFun = fun(Fd,Id) ->
- get_etsinfo(Fd,#ets_table{pid=list_to_pid(Id)},WS)
+ ET = get_etsinfo(Fd,#ets_table{pid=list_to_pid(Id)},WS),
+ ET#ets_table{is_named=tab_is_named(ET)}
end,
lookup_and_parse_index(File,{?ets,Pid},ParseFun,"ets").
+tab_is_named(#ets_table{id=Name,name=Name}) -> "yes";
+tab_is_named(#ets_table{}) -> "no".
+
get_etsinfo(Fd,EtsTable = #ets_table{details=Ds},WS) ->
case line_head(Fd) of
"Slot" ->
diff --git a/lib/observer/src/crashdump_viewer.hrl b/lib/observer/src/crashdump_viewer.hrl
index a08659efd6..742e145641 100644
--- a/lib/observer/src/crashdump_viewer.hrl
+++ b/lib/observer/src/crashdump_viewer.hrl
@@ -118,6 +118,7 @@
slot,
id,
name,
+ is_named,
data_type="hash",
buckets="-",
size,
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index 4356cb890c..d04fb839c8 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -78,11 +78,11 @@ init([Notebook, Parent]) ->
Col + 1
end,
ListItems = [{"Table Name", ?wxLIST_FORMAT_LEFT, 200},
- {"Table Id", ?wxLIST_FORMAT_RIGHT, 100},
{"Objects", ?wxLIST_FORMAT_RIGHT, 100},
{"Size (kB)", ?wxLIST_FORMAT_RIGHT, 100},
{"Owner Pid", ?wxLIST_FORMAT_CENTER, 150},
- {"Owner Name", ?wxLIST_FORMAT_LEFT, 200}
+ {"Owner Name", ?wxLIST_FORMAT_LEFT, 200},
+ {"Table Id", ?wxLIST_FORMAT_LEFT, 250}
],
lists:foldl(AddListEntry, 0, ListItems),
wxListItem:destroy(Li),
@@ -387,8 +387,8 @@ update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Tables) ->
({Col, Val}) ->
wxListCtrl:setItem(Grid, Row, Col, observer_lib:to_str(Val))
end,
- [{0,Name}, {1,Id}, {2,Size}, {3, Memory div 1024},
- {4,Owner}, {5,RegName}]),
+ [{0,Name}, {1,Size}, {2, Memory div 1024},
+ {3,Owner}, {4,RegName}, {5,Id}]),
Row + 1
end,
ProcInfo = case Dir of
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 1f07e826ce..968983c862 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -243,21 +243,6 @@
<p><c>Peer</c> is in the format of <c>{Host,Port}</c>.</p>
</item>
- <tag><c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></tag>
- <item>
- <note>
- <p>This option will be removed in OTP 20, but is kept for compatibility. It is ignored if
- the preferred <c>pref_public_key_algs</c> option is used.</p>
- </note>
- <p>Sets the preferred public key algorithm to use for user
- authentication. If the preferred algorithm fails,
- the other algorithm is tried. If <c>{public_key_alg, 'ssh-rsa'}</c> is set, it is translated
- to <c>{pref_public_key_algs, ['ssh-rsa','ssh-dss']}</c>. If it is
- <c>{public_key_alg, 'ssh-dss'}</c>, it is translated
- to <c>{pref_public_key_algs, ['ssh-dss','ssh-rsa']}</c>.
- </p>
- </item>
-
<tag><c><![CDATA[{pref_public_key_algs, list()}]]></c></tag>
<item>
<p>List of user (client) public key algorithms to try to use.</p>
diff --git a/lib/ssh/src/ssh_options.erl b/lib/ssh/src/ssh_options.erl
index 395be6b220..a882a01eaf 100644
--- a/lib/ssh/src/ssh_options.erl
+++ b/lib/ssh/src/ssh_options.erl
@@ -200,17 +200,6 @@ save({K,V}, _, _) when K == reuseaddr ;
save({allow_user_interaction,V}, Opts, Vals) ->
save({user_interaction,V}, Opts, Vals);
-save({public_key_alg,V}, Defs, Vals) -> % To remove in OTP-20
- New = case V of
- 'ssh-rsa' -> ['ssh-rsa', 'ssh-dss'];
- ssh_rsa -> ['ssh-rsa', 'ssh-dss'];
- 'ssh-dss' -> ['ssh-dss', 'ssh-rsa'];
- ssh_dsa -> ['ssh-dss', 'ssh-rsa'];
- _ -> error({eoptions, {public_key_alg,V},
- "Unknown algorithm, try pref_public_key_algs instead"})
- end,
- save({pref_public_key_algs,New}, Defs, Vals);
-
%% Special case for socket options 'inet' and 'inet6'
save(Inet, Defs, OptMap) when Inet==inet ; Inet==inet6 ->
save({inet,Inet}, Defs, OptMap);
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index 687e6efaf3..7eda009552 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -333,7 +333,7 @@ erlang_client_openssh_server_publickey_rsa(Config) when is_list(Config) ->
[{_,_, not_encrypted}] ->
ConnectionRef =
ssh_test_lib:connect(?SSH_DEFAULT_PORT,
- [{public_key_alg, ssh_rsa},
+ [{pref_public_key_algs, ['ssh-rsa','ssh-dss']},
{user_interaction, false},
silently_accept_hosts]),
{ok, Channel} =
@@ -354,7 +354,7 @@ erlang_client_openssh_server_publickey_dsa() ->
erlang_client_openssh_server_publickey_dsa(Config) when is_list(Config) ->
ConnectionRef =
ssh_test_lib:connect(?SSH_DEFAULT_PORT,
- [{public_key_alg, ssh_dsa},
+ [{pref_public_key_algs, ['ssh-dss','ssh-rsa']},
{user_interaction, false},
silently_accept_hosts]),
{ok, Channel} =
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 916b41742e..91c590c247 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -935,13 +935,14 @@ fun(srp, Username :: string(), UserState :: term()) ->
<fsummary>Returns all the connection information.
</fsummary>
<type>
- <v>Item = protocol | cipher_suite | sni_hostname | ecc | atom()</v>
+ <v>Item = protocol | cipher_suite | sni_hostname | ecc | session_id | atom()</v>
<d>Meaningful atoms, not specified above, are the ssl option names.</d>
<v>Result = [{Item::atom(), Value::term()}]</v>
<v>Reason = term()</v>
</type>
- <desc><p>Returns all relevant information about the connection, ssl options that
- are undefined will be filtered out.</p>
+ <desc><p>Returns the most relevant information about the connection, ssl options that
+ are undefined will be filtered out. Note that values that affect the security of the
+ connection will only be returned if explicitly requested by connection_information/2.</p>
</desc>
</func>
@@ -952,8 +953,10 @@ fun(srp, Username :: string(), UserState :: term()) ->
</fsummary>
<type>
<v>Items = [Item]</v>
- <v>Item = protocol | cipher_suite | sni_hostname | atom()</v>
- <d>Meaningful atoms, not specified above, are the ssl option names.</d>
+ <v>Item = protocol | cipher_suite | sni_hostname | ecc | session_id | client_random
+ | server_random | master_secret | atom()</v>
+ <d>Note that client_random, server_random and master_secret are values
+ that affect the security of connection. Meaningful atoms, not specified above, are the ssl option names.</d>
<v>Result = [{Item::atom(), Value::term()}]</v>
<v>Reason = term()</v>
</type>
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 45fc29723f..b3d08bdfbe 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -38,16 +38,13 @@
getopts/2, setopts/2, getstat/1, getstat/2
]).
%% SSL/TLS protocol handling
--export([cipher_suites/0, cipher_suites/1, eccs/0, eccs/1,
- connection_info/1, versions/0, session_info/1, format_error/1,
- renegotiate/1, prf/5, negotiated_protocol/1, negotiated_next_protocol/1,
+
+-export([cipher_suites/0, cipher_suites/1, eccs/0, eccs/1, versions/0,
+ format_error/1, renegotiate/1, prf/5, negotiated_protocol/1,
connection_information/1, connection_information/2]).
%% Misc
-export([handle_options/2, tls_version/1]).
--deprecated({negotiated_next_protocol, 1, next_major_release}).
--deprecated({connection_info, 1, next_major_release}).
-
-include("ssl_api.hrl").
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
@@ -307,7 +304,7 @@ controlling_process(#sslsocket{pid = {Listen,
%% Description: Return SSL information for the connection
%%--------------------------------------------------------------------
connection_information(#sslsocket{pid = Pid}) when is_pid(Pid) ->
- case ssl_connection:connection_information(Pid) of
+ case ssl_connection:connection_information(Pid, false) of
{ok, Info} ->
{ok, [Item || Item = {_Key, Value} <- Info, Value =/= undefined]};
Error ->
@@ -323,8 +320,8 @@ connection_information(#sslsocket{pid = {udp,_}}) ->
%%
%% Description: Return SSL information for the connection
%%--------------------------------------------------------------------
-connection_information(#sslsocket{} = SSLSocket, Items) ->
- case connection_information(SSLSocket) of
+connection_information(#sslsocket{pid = Pid}, Items) when is_pid(Pid) ->
+ case ssl_connection:connection_information(Pid, include_security_info(Items)) of
{ok, Info} ->
{ok, [Item || Item = {Key, Value} <- Info, lists:member(Key, Items),
Value =/= undefined]};
@@ -333,21 +330,6 @@ connection_information(#sslsocket{} = SSLSocket, Items) ->
end.
%%--------------------------------------------------------------------
-%% Deprecated
--spec connection_info(#sslsocket{}) -> {ok, {tls_record:tls_atom_version(), ssl_cipher:erl_cipher_suite()}} |
- {error, reason()}.
-%%
-%% Description: Returns ssl protocol and cipher used for the connection
-%%--------------------------------------------------------------------
-connection_info(#sslsocket{} = SSLSocket) ->
- case connection_information(SSLSocket) of
- {ok, Result} ->
- {ok, {proplists:get_value(protocol, Result), proplists:get_value(cipher_suite, Result)}};
- Error ->
- Error
- end.
-
-%%--------------------------------------------------------------------
-spec peername(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}.
%%
%% Description: same as inet:peername/1.
@@ -392,20 +374,6 @@ negotiated_protocol(#sslsocket{pid = Pid}) ->
ssl_connection:negotiated_protocol(Pid).
%%--------------------------------------------------------------------
--spec negotiated_next_protocol(#sslsocket{}) -> {ok, binary()} | {error, reason()}.
-%%
-%% Description: Returns the next protocol that has been negotiated. If no
-%% protocol has been negotiated will return {error, next_protocol_not_negotiated}
-%%--------------------------------------------------------------------
-negotiated_next_protocol(Socket) ->
- case negotiated_protocol(Socket) of
- {error, protocol_not_negotiated} ->
- {error, next_protocol_not_negotiated};
- Res ->
- Res
- end.
-
-%%--------------------------------------------------------------------
-spec cipher_suites() -> [ssl_cipher:erl_cipher_suite()] | [string()].
%%--------------------------------------------------------------------
cipher_suites() ->
@@ -555,19 +523,6 @@ sockname(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}) when is_pid(Pid)
tls_socket:sockname(Transport, Socket).
%%---------------------------------------------------------------
--spec session_info(#sslsocket{}) -> {ok, list()} | {error, reason()}.
-%%
-%% Description: Returns list of session info currently [{session_id, session_id(),
-%% {cipher_suite, cipher_suite()}]
-%%--------------------------------------------------------------------
-session_info(#sslsocket{pid = Pid}) when is_pid(Pid) ->
- ssl_connection:session_info(Pid);
-session_info(#sslsocket{pid = {udp,_}}) ->
- {error, enotconn};
-session_info(#sslsocket{pid = {Listen,_}}) when is_port(Listen) ->
- {error, enotconn}.
-
-%%---------------------------------------------------------------
-spec versions() -> [{ssl_app, string()} | {supported, [tls_record:tls_atom_version()]} |
{available, [tls_record:tls_atom_version()]}].
%%
@@ -1480,3 +1435,13 @@ default_cb_info(tls) ->
{gen_tcp, tcp, tcp_closed, tcp_error};
default_cb_info(dtls) ->
{gen_udp, udp, udp_closed, udp_error}.
+
+include_security_info([]) ->
+ false;
+include_security_info([Item | Items]) ->
+ case lists:member(Item, [client_random, server_random, master_secret]) of
+ true ->
+ true;
+ false ->
+ include_security_info(Items)
+ end.
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index ea139ac4b1..df9b9e8a63 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -42,9 +42,9 @@
%% User Events
-export([send/2, recv/3, close/2, shutdown/2,
- new_user/2, get_opts/2, set_opts/2, session_info/1,
+ new_user/2, get_opts/2, set_opts/2,
peer_certificate/1, renegotiation/1, negotiated_protocol/1, prf/5,
- connection_information/1, handle_common_event/5
+ connection_information/2, handle_common_event/5
]).
%% General gen_statem state functions with extra callback argument
@@ -185,12 +185,12 @@ recv(Pid, Length, Timeout) ->
call(Pid, {recv, Length, Timeout}).
%%--------------------------------------------------------------------
--spec connection_information(pid()) -> {ok, list()} | {error, reason()}.
+-spec connection_information(pid(), boolean()) -> {ok, list()} | {error, reason()}.
%%
%% Description: Get the SNI hostname
%%--------------------------------------------------------------------
-connection_information(Pid) when is_pid(Pid) ->
- call(Pid, connection_information).
+connection_information(Pid, IncludeSecrityInfo) when is_pid(Pid) ->
+ call(Pid, {connection_information, IncludeSecrityInfo}).
%%--------------------------------------------------------------------
-spec close(pid(), {close, Timeout::integer() |
@@ -247,14 +247,6 @@ set_opts(ConnectionPid, Options) ->
call(ConnectionPid, {set_opts, Options}).
%%--------------------------------------------------------------------
--spec session_info(pid()) -> {ok, list()} | {error, reason()}.
-%%
-%% Description: Returns info about the ssl session
-%%--------------------------------------------------------------------
-session_info(ConnectionPid) ->
- call(ConnectionPid, session_info).
-
-%%--------------------------------------------------------------------
-spec peer_certificate(pid()) -> {ok, binary()| undefined} | {error, reason()}.
%%
%% Description: Returns the peer cert
@@ -775,14 +767,12 @@ connection({call, From}, renegotiate, #state{protocol_cb = Connection} = State,
connection({call, From}, peer_certificate,
#state{session = #session{peer_certificate = Cert}} = State, _) ->
hibernate_after(connection, State, [{reply, From, {ok, Cert}}]);
-connection({call, From}, connection_information, State, _) ->
+connection({call, From}, {connection_information, true}, State, _) ->
+ Info = connection_info(State) ++ security_info(State),
+ hibernate_after(connection, State, [{reply, From, {ok, Info}}]);
+connection({call, From}, {connection_information, false}, State, _) ->
Info = connection_info(State),
hibernate_after(connection, State, [{reply, From, {ok, Info}}]);
-connection({call, From}, session_info, #state{session = #session{session_id = Id,
- cipher_suite = Suite}} = State, _) ->
- SessionInfo = [{session_id, Id},
- {cipher_suite, ssl_cipher:erl_suite_definition(Suite)}],
- hibernate_after(connection, State, [{reply, From, SessionInfo}]);
connection({call, From}, negotiated_protocol,
#state{negotiated_protocol = undefined} = State, _) ->
hibernate_after(connection, State, [{reply, From, {error, protocol_not_negotiated}}]);
@@ -1195,7 +1185,8 @@ handle_alert(#alert{level = ?WARNING} = Alert, StateName,
%%% Internal functions
%%--------------------------------------------------------------------
connection_info(#state{sni_hostname = SNIHostname,
- session = #session{cipher_suite = CipherSuite, ecc = ECCCurve},
+ session = #session{session_id = SessionId,
+ cipher_suite = CipherSuite, ecc = ECCCurve},
protocol_cb = Connection,
negotiated_version = {_,_} = Version,
ssl_options = Opts}) ->
@@ -1210,9 +1201,18 @@ connection_info(#state{sni_hostname = SNIHostname,
[]
end,
[{protocol, RecordCB:protocol_version(Version)},
+ {session_id, SessionId},
{cipher_suite, CipherSuiteDef},
{sni_hostname, SNIHostname} | CurveInfo] ++ ssl_options_list(Opts).
+security_info(#state{connection_states = ConnectionStates}) ->
+ #{security_parameters :=
+ #security_parameters{client_random = ClientRand,
+ server_random = ServerRand,
+ master_secret = MasterSecret}} =
+ ssl_record:current_connection_state(ConnectionStates, read),
+ [{client_random, ClientRand}, {server_random, ServerRand}, {master_secret, MasterSecret}].
+
do_server_hello(Type, #hello_extensions{next_protocol_negotiation = NextProtocols} =
ServerHelloExt,
#state{negotiated_version = Version,
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 86426bdb60..4eabe544d7 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -148,6 +148,7 @@ options_tests_tls() ->
api_tests() ->
[connection_info,
+ secret_connection_info,
connection_information,
peercert,
peercert_with_client_cert,
@@ -611,7 +612,7 @@ prf(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
connection_info() ->
- [{doc,"Test the API function ssl:connection_information/1"}].
+ [{doc,"Test the API function ssl:connection_information/2"}].
connection_info(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
@@ -645,6 +646,38 @@ connection_info(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
+secret_connection_info() ->
+ [{doc,"Test the API function ssl:connection_information/2"}].
+secret_connection_info(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, secret_connection_info_result, []}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, secret_connection_info_result, []}},
+ {options, ClientOpts}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ Version = ssl_test_lib:protocol_version(Config),
+
+ ssl_test_lib:check_result(Server, true, Client, true),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+
connection_information() ->
[{doc,"Test the API function ssl:connection_information/1"}].
connection_information(Config) when is_list(Config) ->
@@ -3414,7 +3447,6 @@ listen_socket(Config) ->
{error, enotconn} = ssl:connection_information(ListenSocket),
{error, enotconn} = ssl:peername(ListenSocket),
{error, enotconn} = ssl:peercert(ListenSocket),
- {error, enotconn} = ssl:session_info(ListenSocket),
{error, enotconn} = ssl:renegotiate(ListenSocket),
{error, enotconn} = ssl:prf(ListenSocket, 'master_secret', <<"Label">>, client_random, 256),
{error, enotconn} = ssl:shutdown(ListenSocket, read_write),
@@ -4638,6 +4670,11 @@ version_info_result(Socket) ->
{ok, [{version, Version}]} = ssl:connection_information(Socket, [version]),
{ok, Version}.
+secret_connection_info_result(Socket) ->
+ {ok, [{client_random, ClientRand}, {server_random, ServerRand}, {master_secret, MasterSecret}]}
+ = ssl:connection_information(Socket, [client_random, server_random, master_secret]),
+ is_binary(ClientRand) andalso is_binary(ServerRand) andalso is_binary(MasterSecret).
+
connect_dist_s(S) ->
Msg = term_to_binary({erlang,term}),
ok = ssl:send(S, Msg).
diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl
index 3446a566c4..c8caa9c11a 100644
--- a/lib/ssl/test/ssl_packet_SUITE.erl
+++ b/lib/ssl/test/ssl_packet_SUITE.erl
@@ -1973,14 +1973,14 @@ passive_recv_packet(Socket, _, 0) ->
{error, timeout} = ssl:recv(Socket, 0, 500),
ok;
Other ->
- {other, Other, ssl:session_info(Socket), 0}
+ {other, Other, ssl:connection_information(Socket, [session_id, cipher_suite]), 0}
end;
passive_recv_packet(Socket, Data, N) ->
case ssl:recv(Socket, 0) of
{ok, Data} ->
passive_recv_packet(Socket, Data, N-1);
Other ->
- {other, Other, ssl:session_info(Socket), N}
+ {other, Other, ssl:connection_information(Socket, [session_id, cipher_suite]), N}
end.
send(Socket,_, 0) ->
@@ -2032,7 +2032,7 @@ active_once_packet(Socket,_, 0) ->
{ssl, Socket, []} ->
ok;
{ssl, Socket, Other} ->
- {other, Other, ssl:session_info(Socket), 0}
+ {other, Other, ssl:connection_information(Socket, [session_id, cipher_suite]), 0}
end;
active_once_packet(Socket, Data, N) ->
receive
@@ -2077,7 +2077,7 @@ active_packet(Socket, _, 0) ->
{ssl, Socket, []} ->
ok;
Other ->
- {other, Other, ssl:session_info(Socket), 0}
+ {other, Other, ssl:connection_information(Socket, [session_id, cipher_suite]), 0}
end;
active_packet(Socket, Data, N) ->
receive
@@ -2089,7 +2089,7 @@ active_packet(Socket, Data, N) ->
{ssl, Socket, Data} ->
active_packet(Socket, Data, N -1);
Other ->
- {other, Other, ssl:session_info(Socket),N}
+ {other, Other, ssl:connection_information(Socket, [session_id, cipher_suite]),N}
end.
assert_packet_opt(Socket, Type) ->
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 4b740c79db..3768fc2259 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -782,18 +782,18 @@ no_result(_) ->
no_result_msg.
trigger_renegotiate(Socket, [ErlData, N]) ->
- [{session_id, Id} | _ ] = ssl:session_info(Socket),
+ {ok, [{session_id, Id}]} = ssl:connection_information(Socket, [session_id]),
trigger_renegotiate(Socket, ErlData, N, Id).
trigger_renegotiate(Socket, _, 0, Id) ->
ct:sleep(1000),
- case ssl:session_info(Socket) of
- [{session_id, Id} | _ ] ->
+ case ssl:connection_information(Socket, [session_id]) of
+ {ok, [{session_id, Id}]} ->
fail_session_not_renegotiated;
%% Tests that uses this function will not reuse
%% sessions so if we get a new session id the
%% renegotiation has succeeded.
- [{session_id, _} | _ ] ->
+ {ok, [{session_id, _}]} ->
ok;
{error, closed} ->
fail_session_fatal_alert_during_renegotiation;
@@ -998,8 +998,8 @@ cipher_result(Socket, Result) ->
end.
session_info_result(Socket) ->
- ssl:session_info(Socket).
-
+ {ok, Info} = ssl:connection_information(Socket, [session_id, cipher_suite]),
+ Info.
public_key(#'PrivateKeyInfo'{privateKeyAlgorithm =
#'PrivateKeyInfo_privateKeyAlgorithm'{algorithm = ?rsaEncryption},
diff --git a/lib/stdlib/doc/src/proplists.xml b/lib/stdlib/doc/src/proplists.xml
index fe6b8cc3bf..990d47b313 100644
--- a/lib/stdlib/doc/src/proplists.xml
+++ b/lib/stdlib/doc/src/proplists.xml
@@ -344,7 +344,7 @@ split([{c, 2}, {e, 1}, a, {c, 3, 4}, d, {b, 5}, b], [a, b, c])</code>
with <c>{K2, true}</c>, thus changing the name of the option and
simultaneously negating the value specified by
<seealso marker="#get_bool/2">
- <c>get_bool(Key, <anno>ListIn</anno></c></seealso>.
+ <c>get_bool(Key, <anno>ListIn</anno>)</c></seealso>.
If the same <c>K1</c> occurs more than once in
<c><anno>Negations</anno></c>, only the first occurrence is used.</p>
<p>For example, <c>substitute_negations([{no_foo, foo}], L)</c>
diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml
index efc8b75075..a8ef8ff5c5 100644
--- a/lib/stdlib/doc/src/unicode_usage.xml
+++ b/lib/stdlib/doc/src/unicode_usage.xml
@@ -62,6 +62,10 @@
<item><p>In Erlang/OTP 17.0, the encoding default for Erlang
source files was switched to UTF-8.</p></item>
+
+ <item><p>In Erlang/OTP 20.0, atoms and function can contain
+ Unicode characters. Module names are still restricted to
+ the ISO-Latin-1 range.</p></item>
</list>
<p>This section outlines the current Unicode support and gives some
@@ -339,9 +343,10 @@
<tag>The language</tag>
<item>
<p>Having the source code in UTF-8 also allows you to write string
- literals containing Unicode characters with code points &gt; 255,
- although atoms, module names, and function names are restricted to
- the ISO Latin-1 range. Binary literals, where you use type
+ literals, function names, and atoms containing Unicode
+ characters with code points &gt; 255.
+ Module names are still restricted to the ISO Latin-1 range.
+ Binary literals, where you use type
<c>/utf8</c>, can also be expressed using Unicode characters &gt; 255.
Having module names using characters other than 7-bit ASCII can cause
trouble on operating systems with inconsistent file naming schemes,
@@ -432,15 +437,17 @@ external_charlist() = maybe_improper_list(char() | external_unicode_binary() |
<section>
<title>Basic Language Support</title>
- <p><marker id="unicode_in_erlang"/>As from Erlang/OTP R16, Erlang source
- files can be written in UTF-8 or bytewise (<c>latin1</c>) encoding. For
- information about how to state the encoding of an Erlang source file, see
- the <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso> module.
- Strings and comments can be written using Unicode, but functions must
- still be named using characters from the ISO Latin-1 character set, and
- atoms are restricted to the same ISO Latin-1 range. These restrictions in
- the language are of course independent of the encoding of the source
- file.</p>
+ <p><marker id="unicode_in_erlang"/>As from Erlang/OTP R16, Erlang
+ source files can be written in UTF-8 or bytewise (<c>latin1</c>)
+ encoding. For information about how to state the encoding of an
+ Erlang source file, see the <seealso
+ marker="stdlib:epp#encoding"><c>epp(3)</c></seealso> module. As
+ from Erlang/OTP R16, strings and comments can be written using
+ Unicode. As from Erlang/OTP 20, also atoms and functions can be
+ written using Unicode. Modules names must still be named using
+ characters from the ISO Latin-1 character set. (These
+ restrictions in the language are independent of the encoding of
+ the source file.)</p>
<section>
<title>Bit Syntax</title>
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 0ffca0886f..0789f5dfb7 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -156,6 +156,8 @@ format_error(pmod_unsupported) ->
"parameterized modules are no longer supported";
%% format_error({redefine_mod_import, M, P}) ->
%% io_lib:format("module '~s' already imported from package '~s'", [M, P]);
+format_error(non_latin1_module_unsupported) ->
+ "module names with non-latin1 characters are not supported";
format_error(invalid_call) ->
"invalid function call";
@@ -733,9 +735,15 @@ form(Form, #lint{state=State}=St) ->
start_state({attribute,Line,module,{_,_}}=Form, St0) ->
St1 = add_error(Line, pmod_unsupported, St0),
attribute_state(Form, St1#lint{state=attribute});
-start_state({attribute,_,module,M}, St0) ->
+start_state({attribute,Line,module,M}, St0) ->
St1 = St0#lint{module=M},
- St1#lint{state=attribute};
+ St2 = St1#lint{state=attribute},
+ case is_non_latin1_name(M) of
+ true ->
+ add_error(Line, non_latin1_module_unsupported, St2);
+ false ->
+ St2
+ end;
start_state(Form, St) ->
Anno = case Form of
{eof, L} -> erl_anno:new(L);
@@ -745,6 +753,9 @@ start_state(Form, St) ->
St1 = add_error(Anno, undefined_module, St),
attribute_state(Form, St1#lint{state=attribute}).
+is_non_latin1_name(Name) ->
+ lists:any(fun(C) -> C > 255 end, atom_to_list(Name)).
+
%% attribute_state(Form, State) ->
%% State'
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index d6fd1e3ea1..90e19e6b9f 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -75,10 +75,28 @@
take/2,
update_counter/3, update_counter/4, update_element/3]).
+%% internal exports
+-export([internal_request_all/0]).
+
-spec all() -> [Tab] when
Tab :: tab().
all() ->
+ receive_all(ets:internal_request_all(),
+ erlang:system_info(schedulers),
+ []).
+
+receive_all(_Ref, 0, All) ->
+ All;
+receive_all(Ref, N, All) ->
+ receive
+ {Ref, SchedAll} ->
+ receive_all(Ref, N-1, SchedAll ++ All)
+ end.
+
+-spec internal_request_all() -> reference().
+
+internal_request_all() ->
erlang:nif_error(undef).
-spec delete(Tab) -> true when
diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl
index fda7a2cd8a..d89ff4a624 100644
--- a/lib/stdlib/src/otp_internal.erl
+++ b/lib/stdlib/src/otp_internal.erl
@@ -55,6 +55,11 @@ obsolete_1(erlang, now, 0) ->
obsolete_1(calendar, local_time_to_universal_time, 1) ->
{deprecated, {calendar, local_time_to_universal_time_dst, 1}};
+%% *** CRYPTO added in OTP 20 ***
+
+obsolete_1(crypto, rand_uniform, 2) ->
+ {deprecated, {rand, uniform, 1}};
+
%% *** CRYPTO added in OTP 19 ***
obsolete_1(crypto, rand_bytes, 1) ->
@@ -63,178 +68,178 @@ obsolete_1(crypto, rand_bytes, 1) ->
%% *** CRYPTO added in R16B01 ***
obsolete_1(crypto, md4, 1) ->
- {deprecated, {crypto, hash, 2}};
+ {removed, {crypto, hash, 2}, "20.0"};
obsolete_1(crypto, md5, 1) ->
- {deprecated, {crypto, hash, 2}};
+ {removed, {crypto, hash, 2}, "20.0"};
obsolete_1(crypto, sha, 1) ->
- {deprecated, {crypto, hash, 2}};
+ {removed, {crypto, hash, 2}, "20.0"};
obsolete_1(crypto, md4_init, 0) ->
- {deprecated, {crypto, hash_init, 1}};
+ {removed, {crypto, hash_init, 1}, "20.0"};
obsolete_1(crypto, md5_init, 0) ->
- {deprecated, {crypto, hash_init, 1}};
+ {removed, {crypto, hash_init, 1}, "20.0"};
obsolete_1(crypto, sha_init, 0) ->
- {deprecated, {crypto, hash_init, 1}};
+ {removed, {crypto, hash_init, 1}, "20.0"};
obsolete_1(crypto, md4_update, 2) ->
- {deprecated, {crypto, hash_update, 2}};
+ {removed, {crypto, hash_update, 2}, "20.0"};
obsolete_1(crypto, md5_update, 2) ->
- {deprecated, {crypto, hash_update, 2}};
+ {removed, {crypto, hash_update, 2}, "20.0"};
obsolete_1(crypto, sha_update, 2) ->
- {deprecated, {crypto, hash_update, 2}};
+ {removed, {crypto, hash_update, 2}, "20.0"};
obsolete_1(crypto, md4_final, 1) ->
- {deprecated, {crypto, hash_final, 1}};
+ {removed, {crypto, hash_final, 1}, "20.0"};
obsolete_1(crypto, md5_final, 1) ->
- {deprecated, {crypto, hash_final, 1}};
+ {removed, {crypto, hash_final, 1}, "20.0"};
obsolete_1(crypto, sha_final, 1) ->
- {deprecated, {crypto, hash_final, 1}};
+ {removed, {crypto, hash_final, 1}, "20.0"};
obsolete_1(crypto, md5_mac, 2) ->
- {deprecated, {crypto, hmac, 3}};
+ {removed, {crypto, hmac, 3}, "20.0"};
obsolete_1(crypto, sha_mac, 2) ->
- {deprecated, {crypto, hmac, 3}};
+ {removed, {crypto, hmac, 3}, "20.0"};
obsolete_1(crypto, sha_mac, 3) ->
- {deprecated, {crypto, hmac, 4}};
+ {removed, {crypto, hmac, 4}, "20.0"};
obsolete_1(crypto, sha_mac_96, 2) ->
- {deprecated, {crypto, hmac, 4}};
+ {removed, {crypto, hmac, 4}, "20.0"};
obsolete_1(crypto, md5_mac_96, 2) ->
- {deprecated, {crypto, hmac, 4}};
+ {removed, {crypto, hmac, 4}, "20.0"};
obsolete_1(crypto, rsa_sign, 2) ->
- {deprecated, {crypto, sign, 4}};
+ {removed, {crypto, sign, 4}, "20.0"};
obsolete_1(crypto, rsa_sign, 3) ->
- {deprecated, {crypto, sign, 4}};
+ {removed, {crypto, sign, 4}, "20.0"};
obsolete_1(crypto, rsa_verify, 3) ->
- {deprecated, {crypto, verify, 5}};
+ {removed, {crypto, verify, 5}, "20.0"};
obsolete_1(crypto, rsa_verify, 4) ->
- {deprecated, {crypto, verify, 5}};
+ {removed, {crypto, verify, 5}, "20.0"};
obsolete_1(crypto, dss_sign, 2) ->
- {deprecated, {crypto, sign, 4}};
+ {removed, {crypto, sign, 4}, "20.0"};
obsolete_1(crypto, dss_sign, 3) ->
- {deprecated, {crypto, sign, 4}};
+ {removed, {crypto, sign, 4}, "20.0"};
obsolete_1(crypto, dss_verify, 3) ->
- {deprecated, {crypto, verify, 5}};
+ {removed, {crypto, verify, 5}, "20.0"};
obsolete_1(crypto, dss_verify, 4) ->
- {deprecated, {crypto, verify, 5}};
+ {removed, {crypto, verify, 5}, "20.0"};
obsolete_1(crypto, mod_exp, 3) ->
- {deprecated, {crypto, mod_pow, 3}};
+ {removed, {crypto, mod_pow, 3}, "20.0"};
obsolete_1(crypto, dh_compute_key, 3) ->
- {deprecated, {crypto, compute_key, 4}};
+ {removed, {crypto, compute_key, 4}, "20.0"};
obsolete_1(crypto, dh_generate_key, 1) ->
- {deprecated, {crypto, generate_key, 2}};
+ {removed, {crypto, generate_key, 2}, "20.0"};
obsolete_1(crypto, dh_generate_key, 2) ->
- {deprecated, {crypto, generate_key, 3}};
+ {removed, {crypto, generate_key, 3}, "20.0"};
obsolete_1(crypto, des_cbc_encrypt, 3) ->
- {deprecated, {crypto, block_encrypt, 4}};
+ {removed, {crypto, block_encrypt, 4}, "20.0"};
obsolete_1(crypto, des3_cbc_encrypt, 5) ->
- {deprecated, {crypto, block_encrypt, 4}};
+ {removed, {crypto, block_encrypt, 4}, "20.0"};
obsolete_1(crypto, des_ecb_encrypt, 2) ->
- {deprecated, {crypto, block_encrypt, 3}};
+ {removed, {crypto, block_encrypt, 3}, "20.0"};
obsolete_1(crypto, des_ede3_cbc_encrypt, 5) ->
- {deprecated, {crypto, block_encrypt, 4}};
+ {removed, {crypto, block_encrypt, 4}, "20.0"};
obsolete_1(crypto, des_cfb_encrypt, 3) ->
- {deprecated, {crypto, block_encrypt, 4}};
+ {removed, {crypto, block_encrypt, 4}, "20.0"};
obsolete_1(crypto, des3_cfb_encrypt, 5) ->
- {deprecated, {crypto, block_encrypt, 4}};
+ {removed, {crypto, block_encrypt, 4}, "20.0"};
obsolete_1(crypto, blowfish_ecb_encrypt, 2) ->
- {deprecated, {crypto, block_encrypt, 3}};
+ {removed, {crypto, block_encrypt, 3}, "20.0"};
obsolete_1(crypto, blowfish_cbc_encrypt, 3) ->
- {deprecated, {crypto, block_encrypt, 4}};
+ {removed, {crypto, block_encrypt, 4}, "20.0"};
obsolete_1(crypto, blowfish_cfb64_encrypt, 3) ->
- {deprecated, {crypto, block_encrypt, 4}};
+ {removed, {crypto, block_encrypt, 4}, "20.0"};
obsolete_1(crypto, blowfish_ofb64_encrypt, 3) ->
- {deprecated, {crypto, block_encrypt, 4}};
+ {removed, {crypto, block_encrypt, 4}, "20.0"};
obsolete_1(crypto, aes_cfb_128_encrypt, 3) ->
- {deprecated, {crypto, block_encrypt, 4}};
+ {removed, {crypto, block_encrypt, 4}, "20.0"};
obsolete_1(crypto, aes_cbc_128_encrypt, 3) ->
- {deprecated, {crypto, block_encrypt, 4}};
+ {removed, {crypto, block_encrypt, 4}, "20.0"};
obsolete_1(crypto, aes_cbc_256_encrypt, 3) ->
- {deprecated, {crypto, block_encrypt, 4}};
+ {removed, {crypto, block_encrypt, 4}, "20.0"};
obsolete_1(crypto,rc2_cbc_encrypt, 3) ->
- {deprecated, {crypto, block_encrypt, 4}};
+ {removed, {crypto, block_encrypt, 4}, "20.0"};
obsolete_1(crypto,rc2_40_cbc_encrypt, 3) ->
- {deprecated, {crypto, block_encrypt, 4}};
+ {removed, {crypto, block_encrypt, 4}, "20.0"};
obsolete_1(crypto, des_cbc_decrypt, 3) ->
- {deprecated, {crypto, block_decrypt, 4}};
+ {removed, {crypto, block_decrypt, 4}, "20.0"};
obsolete_1(crypto, des3_cbc_decrypt, 5) ->
- {deprecated, {crypto, block_decrypt, 4}};
+ {removed, {crypto, block_decrypt, 4}, "20.0"};
obsolete_1(crypto, des_ecb_decrypt, 2) ->
- {deprecated, {crypto, block_decrypt, 3}};
+ {removed, {crypto, block_decrypt, 3}, "20.0"};
obsolete_1(crypto, des_ede3_cbc_decrypt, 5) ->
- {deprecated, {crypto, block_decrypt, 4}};
+ {removed, {crypto, block_decrypt, 4}, "20.0"};
obsolete_1(crypto, des_cfb_decrypt, 3) ->
- {deprecated, {crypto, block_decrypt, 4}};
+ {removed, {crypto, block_decrypt, 4}, "20.0"};
obsolete_1(crypto, des3_cfb_decrypt, 5) ->
- {deprecated, {crypto, block_decrypt, 4}};
+ {removed, {crypto, block_decrypt, 4}, "20.0"};
obsolete_1(crypto, blowfish_ecb_decrypt, 2) ->
- {deprecated, {crypto, block_decrypt, 3}};
+ {removed, {crypto, block_decrypt, 3}, "20.0"};
obsolete_1(crypto, blowfish_cbc_decrypt, 3) ->
- {deprecated, {crypto, block_decrypt, 4}};
+ {removed, {crypto, block_decrypt, 4}, "20.0"};
obsolete_1(crypto, blowfish_cfb64_decrypt, 3) ->
- {deprecated, {crypto, block_decrypt, 4}};
+ {removed, {crypto, block_decrypt, 4}, "20.0"};
obsolete_1(crypto, blowfish_ofb64_decrypt, 3) ->
- {deprecated, {crypto, block_decrypt, 4}};
+ {removed, {crypto, block_decrypt, 4}, "20.0"};
obsolete_1(crypto, aes_cfb_128_decrypt, 3) ->
- {deprecated, {crypto, block_decrypt, 4}};
+ {removed, {crypto, block_decrypt, 4}, "20.0"};
obsolete_1(crypto, aes_cbc_128_decrypt, 3) ->
- {deprecated, {crypto, block_decrypt, 4}};
+ {removed, {crypto, block_decrypt, 4}, "20.0"};
obsolete_1(crypto, aes_cbc_256_decrypt, 3) ->
- {deprecated, {crypto, block_decrypt, 4}};
+ {removed, {crypto, block_decrypt, 4}, "20.0"};
obsolete_1(crypto,rc2_cbc_decrypt, 3) ->
- {deprecated, {crypto, block_decrypt, 4}};
+ {removed, {crypto, block_decrypt, 4}, "20.0"};
obsolete_1(crypto,rc2_40_cbc_decrypt, 3) ->
- {deprecated, {crypto, block_decrypt, 4}};
+ {removed, {crypto, block_decrypt, 4}, "20.0"};
obsolete_1(crypto, aes_ctr_stream_decrypt, 2) ->
- {deprecated, {crypto, stream_decrypt, 2}};
+ {removed, {crypto, stream_decrypt, 2}, "20.0"};
obsolete_1(crypto, aes_ctr_stream_encrypt, 2) ->
- {deprecated, {crypto, stream_encrypt, 2}};
+ {removed, {crypto, stream_encrypt, 2}, "20.0"};
obsolete_1(crypto, aes_ctr_decrypt, 3) ->
- {deprecated, {crypto, stream_decrypt, 2}};
+ {removed, {crypto, stream_decrypt, 2}, "20.0"};
obsolete_1(crypto, aes_ctr_encrypt, 3) ->
- {deprecated, {crypto, stream_encrypt, 2}};
+ {removed, {crypto, stream_encrypt, 2}, "20.0"};
obsolete_1(crypto, rc4_encrypt, 2) ->
- {deprecated, {crypto, stream_encrypt, 2}};
+ {removed, {crypto, stream_encrypt, 2}, "20.0"};
obsolete_1(crypto, rc4_encrypt_with_state, 2) ->
- {deprecated, {crypto, stream_encrypt, 2}};
+ {removed, {crypto, stream_encrypt, 2}, "20.0"};
obsolete_1(crypto, aes_ctr_stream_init, 2) ->
- {deprecated, {crypto, stream_init, 3}};
+ {removed, {crypto, stream_init, 3}, "20.0"};
obsolete_1(crypto, rc4_set_key, 1) ->
- {deprecated, {crypto, stream_init, 2}};
+ {removed, {crypto, stream_init, 2}, "20.0"};
obsolete_1(crypto, rsa_private_decrypt, 3) ->
- {deprecated, {crypto, private_decrypt, 4}};
+ {removed, {crypto, private_decrypt, 4}, "20.0"};
obsolete_1(crypto, rsa_public_decrypt, 3) ->
- {deprecated, {crypto, public_decrypt, 4}};
+ {removed, {crypto, public_decrypt, 4}, "20.0"};
obsolete_1(crypto, rsa_private_encrypt, 3) ->
- {deprecated, {crypto, private_encrypt, 4}};
+ {removed, {crypto, private_encrypt, 4}, "20.0"};
obsolete_1(crypto, rsa_public_encrypt, 3) ->
- {deprecated, {crypto, public_encrypt, 4}};
+ {removed, {crypto, public_encrypt, 4}, "20.0"};
obsolete_1(crypto, des_cfb_ivec, 2) ->
- {deprecated, {crypto, next_iv, 3}};
+ {removed, {crypto, next_iv, 3}, "20.0"};
obsolete_1(crypto,des_cbc_ivec, 1) ->
- {deprecated, {crypto, next_iv, 2}};
+ {removed, {crypto, next_iv, 2}, "20.0"};
obsolete_1(crypto, aes_cbc_ivec, 1) ->
- {deprecated, {crypto, next_iv, 2}};
+ {removed, {crypto, next_iv, 2}, "20.0"};
obsolete_1(crypto,info, 0) ->
- {deprecated, {crypto, module_info, 0}};
+ {removed, {crypto, module_info, 0}, "20.0"};
obsolete_1(crypto, strong_rand_mpint, 3) ->
- {deprecated, "needed only by deprecated functions"};
+ {removed, "removed in 20.0; only needed by removed functions"};
obsolete_1(crypto, erlint, 1) ->
- {deprecated, "needed only by deprecated functions"};
+ {removed, "removed in 20.0; only needed by removed functions"};
obsolete_1(crypto, mpint, 1) ->
- {deprecated, "needed only by deprecated functions"};
+ {removed, "removed in 20.0; only needed by removed functions"};
%% *** SNMP ***
@@ -387,13 +392,13 @@ obsolete_1(erlang, concat_binary, 1) ->
%% Added in R14A.
obsolete_1(ssl, peercert, 2) ->
- {deprecated,"deprecated (will be removed in R15A); use ssl:peercert/1 and public_key:pkix_decode_cert/2 instead"};
+ {removed ,"removed in R15A; use ssl:peercert/1 and public_key:pkix_decode_cert/2 instead"};
%% Added in R14B.
obsolete_1(public_key, pem_to_der, 1) ->
- {deprecated,"deprecated (will be removed in R15A); use file:read_file/1 and public_key:pem_decode/1"};
+ {removed,"removed in R15A; use file:read_file/1 and public_key:pem_decode/1"};
obsolete_1(public_key, decode_private_key, A) when A =:= 1; A =:= 2 ->
- {deprecated,{public_key,pem_entry_decode,1},"R15A"};
+ {removed, "removed in R15A; use public_key:pem_entry_decode/1"};
%% Added in R14B03.
obsolete_1(docb_gen, _, _) ->
@@ -415,10 +420,10 @@ obsolete_1(inviso, _, _) ->
obsolete_1(gs, _, _) ->
{removed,"the gs application has been removed; use the wx application instead"};
obsolete_1(ssh, sign_data, 2) ->
- {deprecated,"deprecated (will be removed in R16A); use public_key:pem_decode/1, public_key:pem_entry_decode/1 "
+ {removed,"removed in R16A; use public_key:pem_decode/1, public_key:pem_entry_decode/1 "
"and public_key:sign/3 instead"};
obsolete_1(ssh, verify_data, 3) ->
- {deprecated,"deprecated (will be removed in R16A); use public_key:ssh_decode/1, and public_key:verify/4 instead"};
+ {removed,"removed in R16A; use public_key:ssh_decode/1, and public_key:verify/4 instead"};
%% Added in R16
obsolete_1(wxCalendarCtrl, enableYearChange, _) -> %% wx bug documented?
@@ -515,10 +520,9 @@ obsolete_1(erl_parse, get_attribute, 2) ->
obsolete_1(erl_lint, modify_line, 2) ->
{removed,{erl_parse,map_anno,2},"19.0"};
obsolete_1(ssl, negotiated_next_protocol, 1) ->
- {deprecated,{ssl,negotiated_protocol,1}};
-
+ {removed,"removed in 20.0; use ssl:negotiated_protocol/1 instead"};
obsolete_1(ssl, connection_info, 1) ->
- {deprecated, "deprecated; use connection_information/[1,2] instead"};
+ {removed, "removed in 20.0; use ssl:connection_information/[1,2] instead"};
obsolete_1(httpd_conf, check_enum, 2) ->
{deprecated, "deprecated; use lists:member/2 instead"};
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index df38edf393..fd7de65302 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -64,7 +64,8 @@
predef/1,
maps/1,maps_type/1,maps_parallel_match/1,
otp_11851/1,otp_11879/1,otp_13230/1,
- record_errors/1, otp_xxxxx/1]).
+ record_errors/1, otp_xxxxx/1,
+ non_latin1_module/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -84,7 +85,7 @@ all() ->
too_many_arguments, basic_errors, bin_syntax_errors, predef,
maps, maps_type, maps_parallel_match,
otp_11851, otp_11879, otp_13230,
- record_errors, otp_xxxxx].
+ record_errors, otp_xxxxx, non_latin1_module].
groups() ->
[{unused_vars_warn, [],
@@ -2098,11 +2099,11 @@ otp_5362(Config) when is_list(Config) ->
[{2,erl_lint,disallowed_nowarn_bif_clash}],[]}},
{call_deprecated_function,
- <<"t(X) -> crypto:md5(X).">>,
+ <<"t(X) -> calendar:local_time_to_universal_time(X).">>,
[],
{warnings,
- [{1,erl_lint,{deprecated,{crypto,md5,1},
- {crypto,hash,2}, "a future release"}}]}},
+ [{1,erl_lint,{deprecated,{calendar,local_time_to_universal_time,1},
+ {calendar,local_time_to_universal_time_dst,1}, "a future release"}}]}},
{call_removed_function,
<<"t(X) -> regexp:match(X).">>,
@@ -3923,6 +3924,24 @@ otp_xxxxx(Config) ->
[]}],
run(Config, Ts).
+%% OTP-14285: We currently don't support non-latin1 module names.
+
+non_latin1_module(_Config) ->
+ do_non_latin1_module('юникод'),
+ do_non_latin1_module(list_to_atom([256,$a,$b,$c])),
+ do_non_latin1_module(list_to_atom([$a,$b,256,$c])),
+ ok.
+
+do_non_latin1_module(Mod) ->
+ File = atom_to_list(Mod) ++ ".erl",
+ Forms = [{attribute,1,file,{File,1}},
+ {attribute,1,module,Mod},
+ {eof,2}],
+ error = compile:forms(Forms),
+ {error,_,[]} = compile:forms(Forms, [return]),
+ ok.
+
+
run(Config, Tests) ->
F = fun({N,P,Ws,E}, BadL) ->
case catch run_test(Config, P, Ws) of
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 8581440d58..ebf7dbff62 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -75,7 +75,7 @@
-export([otp_9423/1]).
-export([otp_10182/1]).
-export([ets_all/1]).
--export([memory_check_summary/1]).
+-export([massive_ets_all/1]).
-export([take/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -93,7 +93,6 @@ init_per_testcase(Case, Config) ->
io:format("*** SEED: ~p ***\n", [rand:export_seed()]),
start_spawn_logger(),
wait_for_test_procs(), %% Ensure previous case cleaned up
- put('__ETS_TEST_CASE__', Case),
[{test_case, Case} | Config].
end_per_testcase(_Func, _Config) ->
@@ -134,9 +133,8 @@ all() ->
otp_9932,
otp_9423,
ets_all,
- take,
-
- memory_check_summary]. % MUST BE LAST
+ massive_ets_all,
+ take].
groups() ->
[{new, [],
@@ -181,27 +179,6 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-%% Test that we did not have "too many" failed verify_etsmem()'s
-%% in the test suite.
-%% verify_etsmem() may give a low number of false positives
-%% as concurrent activities, such as lingering processes
-%% from earlier test suites, may do unrelated ets (de)allocations.
-memory_check_summary(_Config) ->
- case whereis(ets_test_spawn_logger) of
- undefined ->
- ct:fail("No spawn logger exist");
- _ ->
- ets_test_spawn_logger ! {self(), get_failed_memchecks},
- receive {get_failed_memchecks, FailedMemchecks} -> ok end,
- io:format("Failed memchecks: ~p\n",[FailedMemchecks]),
- NoFailedMemchecks = length(FailedMemchecks),
- if NoFailedMemchecks > 1 ->
- ct:fail("Too many failed (~p) memchecks", [NoFailedMemchecks]);
- true ->
- ok
- end
- end.
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -5545,6 +5522,68 @@ ets_all_run() ->
false = lists:member(Table, ets:all()),
ets_all_run().
+create_tables(N) ->
+ create_tables(N, []).
+
+create_tables(0, Ts) ->
+ Ts;
+create_tables(N, Ts) ->
+ create_tables(N-1, [ets:new(tjo, [])|Ts]).
+
+massive_ets_all(Config) when is_list(Config) ->
+ Me = self(),
+ InitTables = lists:sort(ets:all()),
+ io:format("InitTables=~p~n", [InitTables]),
+ PMs0 = lists:map(fun (Sid) ->
+ my_spawn_opt(fun () ->
+ Ts = create_tables(250),
+ Me ! {self(), up, Ts},
+ receive {Me, die} -> ok end
+ end,
+ [link, monitor, {scheduler, Sid}])
+ end,
+ lists:seq(1, erlang:system_info(schedulers_online))),
+ AllRes = lists:sort(lists:foldl(fun ({P, _M}, Ts) ->
+ receive
+ {P, up, PTs} ->
+ PTs ++ Ts
+ end
+ end,
+ InitTables,
+ PMs0)),
+ AllRes = lists:sort(ets:all()),
+ PMs1 = lists:map(fun (_) ->
+ my_spawn_opt(fun () ->
+ AllRes = lists:sort(ets:all())
+ end,
+ [link, monitor])
+ end, lists:seq(1, 50)),
+ lists:foreach(fun ({P, M}) ->
+ receive
+ {'DOWN', M, process, P, _} ->
+ ok
+ end
+ end, PMs1),
+ PMs2 = lists:map(fun (_) ->
+ my_spawn_opt(fun () ->
+ _ = ets:all()
+ end,
+ [link, monitor])
+ end, lists:seq(1, 50)),
+ lists:foreach(fun ({P, _M}) ->
+ P ! {Me, die}
+ end, PMs0),
+ lists:foreach(fun ({P, M}) ->
+ receive
+ {'DOWN', M, process, P, _} ->
+ ok
+ end
+ end, PMs0 ++ PMs2),
+ EndTables = lists:sort(ets:all()),
+ io:format("EndTables=~p~n", [EndTables]),
+ InitTables = EndTables,
+ ok.
+
take(Config) when is_list(Config) ->
%% Simple test for set tables.
@@ -5712,45 +5751,27 @@ etsmem() ->
{Bl0+Bl,BlSz0+BlSz}
end, {0,0}, CS)
end},
- {Mem,AllTabs, erts_debug:get_internal_state('DbTable_meta')}.
+ {Mem,AllTabs}.
-verify_etsmem(EtsMem) ->
+verify_etsmem({MemInfo,AllTabs}) ->
wait_for_test_procs(),
- verify_etsmem(EtsMem, false).
-
-verify_etsmem({MemInfo,AllTabs,MetaState}=EtsMem, Adjusted) ->
case etsmem() of
- {MemInfo,_,_} ->
+ {MemInfo,_} ->
io:format("Ets mem info: ~p", [MemInfo]),
case MemInfo of
{ErlMem,EtsAlloc} when ErlMem == notsup; EtsAlloc == undefined ->
%% Use 'erl +Mea max' to do more complete memory leak testing.
{comment,"Incomplete or no mem leak testing"};
_ ->
- case Adjusted of
- true ->
- {comment, "Meta state adjusted"};
- false ->
- ok
- end
+ ok
end;
- {MemInfo2, AllTabs2, MetaState2} ->
+ {MemInfo2, AllTabs2} ->
io:format("Expected: ~p", [MemInfo]),
io:format("Actual: ~p", [MemInfo2]),
io:format("Changed tables before: ~p\n",[AllTabs -- AllTabs2]),
io:format("Changed tables after: ~p\n", [AllTabs2 -- AllTabs]),
- io:format("Meta state before: ~p\n", [MetaState]),
- io:format("Meta state after: ~p\n", [MetaState2]),
- case {MetaState =:= MetaState2, Adjusted} of
- {false, false} ->
- io:format("Adjust meta state and retry...\n\n",[]),
- {ok,ok} = erts_debug:set_internal_state('DbTable_meta', MetaState),
- verify_etsmem(EtsMem, true);
- _ ->
- ets_test_spawn_logger ! {failed_memcheck, get('__ETS_TEST_CASE__')},
- {comment, "Failed memory check"}
- end
+ ct:fail("Failed memory check")
end.
@@ -5772,10 +5793,10 @@ stop_loopers(Loopers) ->
looper(Fun, State) ->
looper(Fun, Fun(State)).
-spawn_logger(Procs, FailedMemchecks) ->
+spawn_logger(Procs) ->
receive
{new_test_proc, Proc} ->
- spawn_logger([Proc|Procs], FailedMemchecks);
+ spawn_logger([Proc|Procs]);
{sync_test_procs, Kill, From} ->
lists:foreach(fun (Proc) when From == Proc ->
ok;
@@ -5799,14 +5820,7 @@ spawn_logger(Procs, FailedMemchecks) ->
end
end, Procs),
From ! test_procs_synced,
- spawn_logger([From], FailedMemchecks);
-
- {failed_memcheck, TestCase} ->
- spawn_logger(Procs, [TestCase|FailedMemchecks]);
-
- {Pid, get_failed_memchecks} ->
- Pid ! {get_failed_memchecks, FailedMemchecks},
- spawn_logger(Procs, FailedMemchecks)
+ spawn_logger([From])
end.
pid_status(Pid) ->
@@ -5822,7 +5836,7 @@ start_spawn_logger() ->
case whereis(ets_test_spawn_logger) of
Pid when is_pid(Pid) -> true;
_ -> register(ets_test_spawn_logger,
- spawn_opt(fun () -> spawn_logger([], []) end,
+ spawn_opt(fun () -> spawn_logger([]) end,
[{priority, max}]))
end.
diff --git a/system/doc/reference_manual/character_set.xml b/system/doc/reference_manual/character_set.xml
index f0f4c23608..1129ad63d8 100644
--- a/system/doc/reference_manual/character_set.xml
+++ b/system/doc/reference_manual/character_set.xml
@@ -102,13 +102,15 @@
<tcaption>Character Classes</tcaption>
</table>
<p>In Erlang/OTP R16B the syntax of Erlang tokens was extended to
- handle Unicode. The support is limited to
- string literals and comments. Atoms, module names, and
- function names are restricted to the ISO-Latin-1 range.
+ handle Unicode. The support was limited to
+ string literals and comments.
More about the usage of Unicode in Erlang source files
can be found in <seealso
marker="stdlib:unicode_usage#unicode_in_erlang">STDLIB's User's
Guide</seealso>.</p>
+ <p>From Erlang/OTP 20, atoms and function names are also allowed
+ to contain Unicode characters outside the ISO-Latin-1 range.
+ Module names are still restricted to the ISO-Latin-1 range.</p>
</section>
<section>
<title>Source File Encoding</title>