aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator')
-rw-r--r--erts/emulator/beam/bif.c2
-rw-r--r--erts/emulator/beam/erl_alloc_util.c2
-rw-r--r--erts/emulator/beam/erl_async.c14
-rw-r--r--erts/emulator/beam/erl_db_util.c5
-rw-r--r--erts/emulator/beam/erl_driver.h4
-rw-r--r--erts/emulator/beam/erl_init.c173
-rw-r--r--erts/emulator/beam/erl_port_task.c23
-rw-r--r--erts/emulator/beam/erl_ptab.c139
-rw-r--r--erts/emulator/beam/erl_ptab.h2
-rwxr-xr-xerts/emulator/beam/global.h7
-rw-r--r--erts/emulator/drivers/common/efile_drv.c131
-rw-r--r--erts/emulator/drivers/common/inet_drv.c167
-rw-r--r--erts/emulator/drivers/win32/win_efile.c1
-rw-r--r--erts/emulator/sys/win32/erl_win_dyn_driver.h4
-rw-r--r--erts/emulator/test/Makefile3
-rw-r--r--erts/emulator/test/efile_SUITE.erl88
-rw-r--r--erts/emulator/test/emulator_smoke.spec3
-rw-r--r--erts/emulator/test/match_spec_SUITE.erl12
-rw-r--r--erts/emulator/test/scheduler_SUITE.erl64
19 files changed, 665 insertions, 179 deletions
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index ff237b6a78..755c5e6882 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -3934,7 +3934,7 @@ BIF_RETTYPE halt_2(BIF_ALIST_2)
{
Sint code;
Eterm optlist = BIF_ARG_2;
- int flush = 0;
+ int flush = 1;
for (optlist = BIF_ARG_2;
is_list(optlist);
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index bf8a37c71b..e6d9f83aed 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -4369,7 +4369,7 @@ info_options(Allctr_t *allctr,
#endif
"option lmbcs: %beu\n"
"option smbcs: %beu\n"
- "option mbcgs: %beu\n",
+ "option mbcgs: %beu\n"
"option acul: %d\n",
topt,
allctr->ramv ? "true" : "false",
diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c
index 054d1a48f6..e6d72f569b 100644
--- a/erts/emulator/beam/erl_async.c
+++ b/erts/emulator/beam/erl_async.c
@@ -583,6 +583,20 @@ int erts_async_ready_clean(void *varq, void *val)
#endif
/*
+** Generate a fair async key prom an ErlDrvPort
+** The port data gives a fair distribution grom port pointer
+** to unsigned integer - to be used in key for driver_async below.
+*/
+unsigned int driver_async_port_key(ErlDrvPort port)
+{
+ ErlDrvTermData td = driver_mk_port(port);
+ if (td == (ErlDrvTermData) NIL) {
+ return 0;
+ }
+ return (unsigned int) (UWord) internal_port_data(td);
+}
+
+/*
** Schedule async_invoke on a worker thread
** NOTE will be syncrounous when threads are unsupported
** return values:
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 713ac0ba18..ef3749a2c4 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -2319,6 +2319,8 @@ restart:
break;
case matchSilent:
--esp;
+ if (in_flags & ERTS_PAM_IGNORE_TRACE_SILENT)
+ break;
if (*esp == am_true) {
erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
ERTS_TRACE_FLAGS(c_p) |= F_TRACE_SILENT;
@@ -4971,7 +4973,8 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace)
save_cp = p->cp;
p->cp = NULL;
res = erts_match_set_run(p, mps, arr, n,
- ERTS_PAM_COPY_RESULT, &ret_flags);
+ ERTS_PAM_COPY_RESULT|ERTS_PAM_IGNORE_TRACE_SILENT,
+ &ret_flags);
p->cp = save_cp;
} else {
n = 0;
diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h
index 42b14cd7bc..b68fd46fcc 100644
--- a/erts/emulator/beam/erl_driver.h
+++ b/erts/emulator/beam/erl_driver.h
@@ -133,7 +133,7 @@ typedef struct {
#define ERL_DRV_EXTENDED_MARKER (0xfeeeeeed)
#define ERL_DRV_EXTENDED_MAJOR_VERSION 2
-#define ERL_DRV_EXTENDED_MINOR_VERSION 1
+#define ERL_DRV_EXTENDED_MINOR_VERSION 2
/*
* The emulator will refuse to load a driver with different major
@@ -643,6 +643,8 @@ EXTERN int erl_drv_send_term(ErlDrvTermData port,
int len);
/* Async IO functions */
+EXTERN unsigned int driver_async_port_key(ErlDrvPort port);
+
EXTERN long driver_async(ErlDrvPort ix,
unsigned int* key,
void (*async_invoke)(void*),
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 8d137df7ae..8c4fffa75b 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -549,9 +549,12 @@ void erts_usage(void)
ERTS_SCHED_THREAD_MAX_STACK_SIZE);
erts_fprintf(stderr, "-spp Bool set port parallelism scheduling hint\n");
erts_fprintf(stderr, "-S n1:n2 set number of schedulers (n1), and number of\n");
- erts_fprintf(stderr, " schedulers online (n2), valid range for both\n");
- erts_fprintf(stderr, " numbers are [1-%d]\n",
+ erts_fprintf(stderr, " schedulers online (n2), maximum for both\n");
+ erts_fprintf(stderr, " numbers is %d\n",
ERTS_MAX_NO_OF_SCHEDULERS);
+ erts_fprintf(stderr, "-SP p1:p2 specify schedulers (p1) and schedulers online (p2)\n");
+ erts_fprintf(stderr, " as percentages of logical processors configured and logical\n");
+ erts_fprintf(stderr, " processors available, respectively\n");
erts_fprintf(stderr, "-t size set the maximum number of atoms the "
"emulator can handle\n");
erts_fprintf(stderr, " valid range is [%d-%d]\n",
@@ -631,6 +634,8 @@ early_init(int *argc, char **argv) /*
int ncpuavail;
int schdlrs;
int schdlrs_onln;
+ int schdlrs_percentage = 100;
+ int schdlrs_onln_percentage = 100;
int max_main_threads;
int max_reader_groups;
int reader_groups;
@@ -758,63 +763,132 @@ early_init(int *argc, char **argv) /*
}
break;
}
- case 'S' : {
- int tot, onln;
- char *arg = get_arg(argv[i]+2, argv[i+1], &i);
- switch (sscanf(arg, "%d:%d", &tot, &onln)) {
- case 0:
- switch (sscanf(arg, ":%d", &onln)) {
+ case 'S' :
+ if (argv[i][2] == 'P') {
+ int ptot, ponln;
+ char *arg = get_arg(argv[i]+3, argv[i+1], &i);
+ switch (sscanf(arg, "%d:%d", &ptot, &ponln)) {
+ case 0:
+ switch (sscanf(arg, ":%d", &ponln)) {
+ case 1:
+ if (ponln < 0)
+ goto bad_SP;
+ ptot = 100;
+ goto chk_SP;
+ default:
+ goto bad_SP;
+ }
case 1:
- tot = no_schedulers;
- goto chk_S;
+ if (ptot < 0)
+ goto bad_SP;
+ ponln = ptot < 100 ? ptot : 100;
+ goto chk_SP;
+ case 2:
+ if (ptot < 0 || ponln < 0)
+ goto bad_SP;
+ chk_SP:
+ schdlrs_percentage = ptot;
+ schdlrs_onln_percentage = ponln;
+ break;
default:
- goto bad_S;
- }
- case 1:
- onln = tot < schdlrs_onln ? tot : schdlrs_onln;
- case 2:
- chk_S:
- if (tot > 0)
- schdlrs = tot;
- else
- schdlrs = no_schedulers + tot;
- if (onln > 0)
- schdlrs_onln = onln;
- else
- schdlrs_onln = no_schedulers_online + onln;
- if (schdlrs < 1 || ERTS_MAX_NO_OF_SCHEDULERS < schdlrs) {
- erts_fprintf(stderr,
- "bad amount of schedulers %d\n",
- tot);
- erts_usage();
- }
- if (schdlrs_onln < 1 || schdlrs < schdlrs_onln) {
+ bad_SP:
+ erts_fprintf(stderr,
+ "bad schedulers percentage specifier %s\n",
+ arg);
+ erts_usage();
+ break;
+ }
+
+ VERBOSE(DEBUG_SYSTEM,
+ ("using %d:%d scheduler percentages\n",
+ schdlrs_percentage, schdlrs_onln_percentage));
+ } else {
+ int tot, onln;
+ char *arg = get_arg(argv[i]+2, argv[i+1], &i);
+ switch (sscanf(arg, "%d:%d", &tot, &onln)) {
+ case 0:
+ switch (sscanf(arg, ":%d", &onln)) {
+ case 1:
+ tot = no_schedulers;
+ goto chk_S;
+ default:
+ goto bad_S;
+ }
+ case 1:
+ onln = tot < schdlrs_onln ? tot : schdlrs_onln;
+ case 2:
+ chk_S:
+ if (tot > 0)
+ schdlrs = tot;
+ else
+ schdlrs = no_schedulers + tot;
+ if (onln > 0)
+ schdlrs_onln = onln;
+ else
+ schdlrs_onln = no_schedulers_online + onln;
+ if (schdlrs < 1 || ERTS_MAX_NO_OF_SCHEDULERS < schdlrs) {
+ erts_fprintf(stderr,
+ "bad amount of schedulers %d\n",
+ tot);
+ erts_usage();
+ }
+ if (schdlrs_onln < 1 || schdlrs < schdlrs_onln) {
+ erts_fprintf(stderr,
+ "bad amount of schedulers online %d "
+ "(total amount of schedulers %d)\n",
+ schdlrs_onln, schdlrs);
+ erts_usage();
+ }
+ break;
+ default:
+ bad_S:
erts_fprintf(stderr,
- "bad amount of schedulers online %d "
- "(total amount of schedulers %d)\n",
- schdlrs_onln, schdlrs);
+ "bad amount of schedulers %s\n",
+ arg);
erts_usage();
+ break;
}
- break;
- default:
- bad_S:
- erts_fprintf(stderr,
- "bad amount of schedulers %s\n",
- arg);
- erts_usage();
- break;
- }
- VERBOSE(DEBUG_SYSTEM,
- ("using %d:%d scheduler(s)\n", tot, onln));
- break;
- }
+ VERBOSE(DEBUG_SYSTEM,
+ ("using %d:%d scheduler(s)\n", tot, onln));
+ }
+ break;
default:
break;
}
}
i++;
}
+
+#ifdef ERTS_SMP
+ /* apply any scheduler percentages */
+ if (schdlrs_percentage != 100 || schdlrs_onln_percentage != 100) {
+ schdlrs = schdlrs * schdlrs_percentage / 100;
+ schdlrs_onln = schdlrs_onln * schdlrs_onln_percentage / 100;
+ if (schdlrs < 1)
+ schdlrs = 1;
+ if (ERTS_MAX_NO_OF_SCHEDULERS < schdlrs) {
+ erts_fprintf(stderr,
+ "bad schedulers percentage %d "
+ "(total amount of schedulers %d)\n",
+ schdlrs_percentage, schdlrs);
+ erts_usage();
+ }
+ if (schdlrs_onln < 1)
+ schdlrs_onln = 1;
+ if (schdlrs < schdlrs_onln) {
+ erts_fprintf(stderr,
+ "bad schedulers online percentage %d "
+ "(total amount of schedulers %d, online %d)\n",
+ schdlrs_onln_percentage, schdlrs, schdlrs_onln);
+ erts_usage();
+ }
+ }
+#else
+ /* Silence gcc warnings */
+ (void)schdlrs_percentage;
+ (void)schdlrs_onln_percentage;
+#endif
}
#ifndef USE_THREADS
@@ -1312,7 +1386,10 @@ erl_start(int argc, char **argv)
break;
case 'S' : /* Was handled in early_init() just read past it */
- (void) get_arg(argv[i]+2, argv[i+1], &i);
+ if (argv[i][2] == 'P')
+ (void) get_arg(argv[i]+3, argv[i+1], &i);
+ else
+ (void) get_arg(argv[i]+2, argv[i+1], &i);
break;
case 's' : {
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index 7d53ce7152..547a42beb2 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -1838,6 +1838,16 @@ release_port(void *vport)
{
erts_port_dec_refc((Port *) vport);
}
+
+static void
+schedule_release_port(void *vport) {
+ Port *pp = (Port*)vport;
+ /* This is only used when a port release was ordered from a non-scheduler */
+ erts_schedule_thr_prgr_later_op(release_port,
+ (void *) pp,
+ &pp->common.u.release);
+}
+
#endif
static void
@@ -2033,10 +2043,15 @@ begin_port_cleanup(Port *pp, ErtsPortTask **execqp, int *processing_busy_q_p)
* Schedule cleanup of port structure...
*/
#ifdef ERTS_SMP
- /* Has to be more or less immediate to release any driver */
- erts_schedule_thr_prgr_later_op(release_port,
- (void *) pp,
- &pp->common.u.release);
+ /* We might not be a scheduler, eg. traceing to port we are sys_msg_dispatcher */
+ if (!erts_get_scheduler_data()) {
+ erts_schedule_misc_aux_work(1, schedule_release_port, (void*)pp);
+ } else {
+ /* Has to be more or less immediate to release any driver */
+ erts_schedule_thr_prgr_later_op(release_port,
+ (void *) pp,
+ &pp->common.u.release);
+ }
#else
pp->cleanup = 1;
#endif
diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c
index d69619dd44..fa5482b841 100644
--- a/erts/emulator/beam/erl_ptab.c
+++ b/erts/emulator/beam/erl_ptab.c
@@ -433,7 +433,7 @@ erts_ptab_mem_size(ErtsPTab *ptab)
{
UWord size = ptab->r.o.max*sizeof(erts_smp_atomic_t);
if (ptab->r.o.free_id_data)
- size += ptab->r.o.max*sizeof(Uint32);
+ size += ptab->r.o.max*sizeof(erts_smp_atomic32_t);
return size;
}
@@ -474,7 +474,7 @@ erts_ptab_init_table(ErtsPTab *ptab,
tab_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(erts_smp_atomic_t));
alloc_sz = tab_sz;
if (!legacy)
- alloc_sz += ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(Uint32));
+ alloc_sz += ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(erts_smp_atomic32_t));
ptab->r.o.tab = erts_alloc_permanent_cache_aligned(atype, alloc_sz);
tab_end = ((char *) ptab->r.o.tab) + tab_sz;
tab_entry = ptab->r.o.tab;
@@ -497,6 +497,10 @@ erts_ptab_init_table(ErtsPTab *ptab,
ASSERT(ptab->r.o.pix_cl_shift + ptab->r.o.pix_cli_shift == bits);
+ ptab->r.o.invalid_element = invalid_element;
+ ptab->r.o.invalid_data = erts_ptab_id2data(ptab, invalid_element->id);
+ ptab->r.o.release_element = release_element;
+
if (legacy) {
ptab->r.o.free_id_data = NULL;
ptab->r.o.dix_cl_mask = 0;
@@ -506,11 +510,11 @@ erts_ptab_init_table(ErtsPTab *ptab,
}
else {
- tab_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(Uint32));
- ptab->r.o.free_id_data = (Uint32 *) tab_end;
+ tab_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(erts_smp_atomic32_t));
+ ptab->r.o.free_id_data = (erts_smp_atomic32_t *) tab_end;
tab_cache_lines = tab_sz/ERTS_CACHE_LINE_SIZE;
- ix_per_cache_line = (ERTS_CACHE_LINE_SIZE/sizeof(Uint32));
+ ix_per_cache_line = (ERTS_CACHE_LINE_SIZE/sizeof(erts_smp_atomic32_t));
ptab->r.o.dix_cl_mask = tab_cache_lines-1;
ptab->r.o.dix_cl_shift = erts_fit_in_bits_int32(ix_per_cache_line-1);
@@ -525,7 +529,9 @@ erts_ptab_init_table(ErtsPTab *ptab,
ix = 0;
for (cl = 0; cl < tab_cache_lines; cl++) {
for (cli = 0; cli < ix_per_cache_line; cli++) {
- ptab->r.o.free_id_data[ix] = cli*tab_cache_lines+cl;
+ erts_smp_atomic32_init_nob(&ptab->r.o.free_id_data[ix],
+ cli*tab_cache_lines+cl);
+ ASSERT(erts_smp_atomic32_read_nob(&ptab->r.o.free_id_data[ix]) != ptab->r.o.invalid_data);
ix++;
}
}
@@ -534,9 +540,6 @@ erts_ptab_init_table(ErtsPTab *ptab,
erts_smp_atomic32_init_nob(&ptab->vola.tile.fid_ix, -1);
}
- ptab->r.o.invalid_element = invalid_element;
- ptab->r.o.invalid_data = erts_ptab_id2data(ptab, invalid_element->id);
- ptab->r.o.release_element = release_element;
erts_smp_interval_init(&ptab->list.data.interval);
ptab->list.data.deleted.start = NULL;
@@ -606,11 +609,13 @@ erts_ptab_new_element(ErtsPTab *ptab,
= erts_smp_current_interval_nob(erts_ptab_interval(ptab));
if (ptab->r.o.free_id_data) {
+ do {
+ ix = (Uint32) erts_smp_atomic32_inc_read_acqb(&ptab->vola.tile.aid_ix);
+ ix = ix_to_free_id_data_ix(ptab, ix);
- ix = (Uint32) erts_smp_atomic32_inc_read_acqb(&ptab->vola.tile.aid_ix);
- ix = ix_to_free_id_data_ix(ptab, ix);
-
- data = ptab->r.o.free_id_data[ix];
+ data = erts_smp_atomic32_xchg_nob(&ptab->r.o.free_id_data[ix],
+ (erts_aint32_t)ptab->r.o.invalid_data);
+ }while ((Eterm)data == ptab->r.o.invalid_data);
init_ptab_el(init_arg, (Eterm) data);
@@ -763,7 +768,7 @@ erts_ptab_delete_element(ErtsPTab *ptab,
erts_smp_atomic_set_relb(&ptab->r.o.tab[pix], ERTS_AINT_NULL);
if (ptab->r.o.free_id_data) {
-
+ Uint32 prev_data;
/* Next data for this slot... */
data = (Uint32) erts_ptab_id2data(ptab, ptab_el->id);
data += ptab->r.o.max;
@@ -772,14 +777,17 @@ erts_ptab_delete_element(ErtsPTab *ptab,
data += ptab->r.o.max;
data &= ~(~((Uint32) 0) << ERTS_PTAB_ID_DATA_SIZE);
}
-
ASSERT(data != ptab->r.o.invalid_data);
ASSERT(pix == erts_ptab_data2pix(ptab, data));
- ix = (Uint32) erts_smp_atomic32_inc_read_relb(&ptab->vola.tile.fid_ix);
- ix = ix_to_free_id_data_ix(ptab, ix);
-
- ptab->r.o.free_id_data[ix] = data;
+ do {
+ ix = (Uint32) erts_smp_atomic32_inc_read_relb(&ptab->vola.tile.fid_ix);
+ ix = ix_to_free_id_data_ix(ptab, ix);
+
+ prev_data = erts_smp_atomic32_cmpxchg_nob(&ptab->r.o.free_id_data[ix],
+ data,
+ ptab->r.o.invalid_data);
+ }while ((Eterm)prev_data != ptab->r.o.invalid_data);
}
ASSERT(erts_smp_atomic32_read_nob(&ptab->vola.tile.count) > 0);
@@ -1392,6 +1400,31 @@ erts_ptab_init(void)
* Debug stuff
*/
+static void assert_ptab_consistency(ErtsPTab *ptab)
+{
+#ifdef DEBUG
+ if (ptab->r.o.free_id_data) {
+ Uint32 ix, pix, data;
+ int free_pids = 0;
+ int null_slots = 0;
+
+ for (ix=0; ix < ptab->r.o.max; ix++) {
+ if (erts_smp_atomic32_read_nob(&ptab->r.o.free_id_data[ix]) != ptab->r.o.invalid_data) {
+ ++free_pids;
+ data = erts_smp_atomic32_read_nob(&ptab->r.o.free_id_data[ix]);
+ pix = erts_ptab_data2pix(ptab, (Eterm) data);
+ ASSERT(erts_ptab_pix2intptr_nob(ptab, pix) == ERTS_AINT_NULL);
+ }
+ if (erts_smp_atomic_read_nob(&ptab->r.o.tab[ix]) == ERTS_AINT_NULL) {
+ ++null_slots;
+ }
+ }
+ ASSERT(free_pids == null_slots);
+ ASSERT(free_pids == ptab->r.o.max - erts_smp_atomic32_read_nob(&ptab->vola.tile.count));
+ }
+#endif
+}
+
Sint
erts_ptab_test_next_id(ErtsPTab *ptab, int set, Uint next)
{
@@ -1402,46 +1435,49 @@ erts_ptab_test_next_id(ErtsPTab *ptab, int set, Uint next)
erts_ptab_rwlock(ptab);
+ assert_ptab_consistency(ptab);
+
if (ptab->r.o.free_id_data) {
- Uint32 aid_ix, dix;
+ Uint32 id_ix, dix;
if (set) {
- Uint32 max_ix, ser, num, start;
+ Uint32 i, max_ix, num, stop_id_ix;
max_ix = ptab->r.o.max - 1;
- ser = next & ~max_ix;
- start = num = next & max_ix;
-
- aid_ix = (Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.aid_ix) + 1;
-
- do {
- Uint32 pix = erts_ptab_data2pix(ptab, num);
+ num = next;
+ id_ix = (Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.aid_ix);
+
+ for (i=0; i <= max_ix; ++i) {
+ Uint32 pix;
+ ++num;
+ num &= ~(~((Uint32) 0) << ERTS_PTAB_ID_DATA_SIZE);
+ if (num == ptab->r.o.invalid_data) {
+ num += ptab->r.o.max;
+ num &= ~(~((Uint32) 0) << ERTS_PTAB_ID_DATA_SIZE);
+ }
+ pix = erts_ptab_data2pix(ptab, num);
if (ERTS_AINT_NULL == erts_ptab_pix2intptr_nob(ptab, pix)) {
- dix = ix_to_free_id_data_ix(ptab, aid_ix);
- ptab->r.o.free_id_data[dix] = ser + num;
- ASSERT(pix == erts_ptab_data2pix(ptab, ser+num));
- if (aid_ix == max_ix)
- aid_ix = 0;
- else
- aid_ix++;
+ ++id_ix;
+ dix = ix_to_free_id_data_ix(ptab, id_ix);
+ erts_smp_atomic32_set_nob(&ptab->r.o.free_id_data[dix], num);
+ ASSERT(pix == erts_ptab_data2pix(ptab, num));
}
- if (num == max_ix)
- num = 0;
- else
- num++;
- } while (num != start);
+ }
+ erts_smp_atomic32_set_nob(&ptab->vola.tile.fid_ix, id_ix);
-#ifdef DEBUG
- if (aid_ix == 0)
- aid_ix = max_ix;
- else
- aid_ix--;
- ASSERT((aid_ix & max_ix) == (((Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.fid_ix)) & max_ix));
-#endif
+ /* Write invalid_data in rest of free_id_data[]: */
+ stop_id_ix = (1 + erts_smp_atomic32_read_nob(&ptab->vola.tile.aid_ix)) & max_ix;
+ while (1) {
+ id_ix = (id_ix+1) & max_ix;
+ if (id_ix == stop_id_ix)
+ break;
+ dix = ix_to_free_id_data_ix(ptab, id_ix);
+ erts_smp_atomic32_set_nob(&ptab->r.o.free_id_data[dix],
+ ptab->r.o.invalid_data);
+ }
}
-
- aid_ix = (Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.aid_ix) + 1;
- dix = ix_to_free_id_data_ix(ptab, aid_ix);
- res = (Sint) ptab->r.o.free_id_data[dix];
+ id_ix = (Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.aid_ix) + 1;
+ dix = ix_to_free_id_data_ix(ptab, id_ix);
+ res = (Sint) erts_smp_atomic32_read_nob(&ptab->r.o.free_id_data[dix]);
}
else {
/* Deprecated legacy algorithm... */
@@ -1485,6 +1521,7 @@ erts_ptab_test_next_id(ErtsPTab *ptab, int set, Uint next)
}
}
+ assert_ptab_consistency(ptab);
erts_ptab_rwunlock(ptab);
return res;
diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h
index c2d8bd9cad..e3e05f14af 100644
--- a/erts/emulator/beam/erl_ptab.h
+++ b/erts/emulator/beam/erl_ptab.h
@@ -100,7 +100,7 @@ typedef struct {
typedef struct {
erts_smp_atomic_t *tab;
- Uint32 *free_id_data;
+ erts_smp_atomic32_t *free_id_data;
Uint32 max;
Uint32 pix_mask;
Uint32 pix_cl_mask;
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index cecfa8a0fd..bacd5a5752 100755
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1018,9 +1018,10 @@ Eterm erts_match_set_lint(Process *p, Eterm matchexpr);
extern void erts_match_set_release_result(Process* p);
enum erts_pam_run_flags {
- ERTS_PAM_TMP_RESULT=0,
- ERTS_PAM_COPY_RESULT=1,
- ERTS_PAM_CONTIGUOUS_TUPLE=2
+ ERTS_PAM_TMP_RESULT=1,
+ ERTS_PAM_COPY_RESULT=2,
+ ERTS_PAM_CONTIGUOUS_TUPLE=4,
+ ERTS_PAM_IGNORE_TRACE_SILENT=8
};
extern Eterm erts_match_set_run(Process *p, Binary *mpsp,
Eterm *args, int num_args,
diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c
index 595b0488a8..c997fe1bf9 100644
--- a/erts/emulator/drivers/common/efile_drv.c
+++ b/erts/emulator/drivers/common/efile_drv.c
@@ -546,53 +546,81 @@ static void *ef_safe_realloc(void *op, Uint s)
(((char *)(ev)->iov[(q)].iov_base) + (p))
/* int EV_GET_CHAR(ErlIOVec *ev, char *p, int *pp, int *qp) */
-#define EV_GET_CHAR(ev, p, pp, qp) \
- (*(pp)+1 <= (ev)->iov[*(qp)].iov_len \
- ? (*(p) = *EV_CHAR_P(ev, *(pp), *(qp)), \
- *(pp) = ( *(pp)+1 < (ev)->iov[*(qp)].iov_len \
- ? *(pp)+1 \
- : ((*(qp))++, 0)), \
- !0) \
- : 0)
+#define EV_GET_CHAR(ev, p, pp, qp) efile_ev_get_char(ev, p ,pp, qp)
+static int
+efile_ev_get_char(ErlIOVec *ev, char *p, int *pp, int *qp) {
+ if (*(pp)+1 <= (ev)->iov[*(qp)].iov_len) {
+ *(p) = *EV_CHAR_P(ev, *(pp), *(qp));
+ if (*(pp)+1 < (ev)->iov[*(qp)].iov_len)
+ *(pp) = *(pp)+1;
+ else {
+ (*(qp))++;
+ *pp = 0;
+ }
+ return !0;
+ }
+ return 0;
+}
/* Uint32 EV_UINT32(ErlIOVec *ev, int p, int q)*/
#define EV_UINT32(ev, p, q) \
((Uint32) *(((unsigned char *)(ev)->iov[(q)].iov_base) + (p)))
/* int EV_GET_UINT32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */
-#define EV_GET_UINT32(ev, p, pp, qp) \
- (*(pp)+4 <= (ev)->iov[*(qp)].iov_len \
- ? (*(p) = (EV_UINT32(ev, *(pp), *(qp)) << 24) \
- | (EV_UINT32(ev, *(pp)+1, *(qp)) << 16) \
- | (EV_UINT32(ev, *(pp)+2, *(qp)) << 8) \
- | (EV_UINT32(ev, *(pp)+3, *(qp))), \
- *(pp) = ( *(pp)+4 < (ev)->iov[*(qp)].iov_len \
- ? *(pp)+4 \
- : ((*(qp))++, 0)), \
- !0) \
- : 0)
+#define EV_GET_UINT32(ev, p, pp, qp) efile_ev_get_uint32(ev,p,pp,qp)
+static int
+efile_ev_get_uint32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) {
+ if (*(pp)+4 <= (ev)->iov[*(qp)].iov_len) {
+ *(p) = (EV_UINT32(ev, *(pp), *(qp)) << 24)
+ | (EV_UINT32(ev, *(pp)+1, *(qp)) << 16)
+ | (EV_UINT32(ev, *(pp)+2, *(qp)) << 8)
+ | (EV_UINT32(ev, *(pp)+3, *(qp)));
+ if (*(pp)+4 < (ev)->iov[*(qp)].iov_len)
+ *(pp) = *(pp)+4;
+ else {
+ (*(qp))++;
+ *pp = 0;
+ }
+ return !0;
+ }
+ return 0;
+}
/* Uint64 EV_UINT64(ErlIOVec *ev, int p, int q)*/
#define EV_UINT64(ev, p, q) \
((Uint64) *(((unsigned char *)(ev)->iov[(q)].iov_base) + (p)))
-/* int EV_GET_UINT64(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */
-#define EV_GET_UINT64(ev, p, pp, qp) \
- (*(pp)+8 <= (ev)->iov[*(qp)].iov_len \
- ? (*(p) = (EV_UINT64(ev, *(pp), *(qp)) << 56) \
- | (EV_UINT64(ev, *(pp)+1, *(qp)) << 48) \
- | (EV_UINT64(ev, *(pp)+2, *(qp)) << 40) \
- | (EV_UINT64(ev, *(pp)+3, *(qp)) << 32) \
- | (EV_UINT64(ev, *(pp)+4, *(qp)) << 24) \
- | (EV_UINT64(ev, *(pp)+5, *(qp)) << 16) \
- | (EV_UINT64(ev, *(pp)+6, *(qp)) << 8) \
- | (EV_UINT64(ev, *(pp)+7, *(qp))), \
- *(pp) = ( *(pp)+8 < (ev)->iov[*(qp)].iov_len \
- ? *(pp)+8 \
- : ((*(qp))++, 0)), \
- !0) \
- : 0)
+/* int EV_GET_UINT64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) */
+#define EV_GET_UINT64(ev, p, pp, qp) efile_ev_get_uint64(ev,p,pp,qp)
+static int
+efile_ev_get_uint64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) {
+ if (*(pp)+8 <= (ev)->iov[*(qp)].iov_len) {
+ *(p) = (EV_UINT64(ev, *(pp), *(qp)) << 56)
+ | (EV_UINT64(ev, *(pp)+1, *(qp)) << 48)
+ | (EV_UINT64(ev, *(pp)+2, *(qp)) << 40)
+ | (EV_UINT64(ev, *(pp)+3, *(qp)) << 32)
+ | (EV_UINT64(ev, *(pp)+4, *(qp)) << 24)
+ | (EV_UINT64(ev, *(pp)+5, *(qp)) << 16)
+ | (EV_UINT64(ev, *(pp)+6, *(qp)) << 8)
+ | (EV_UINT64(ev, *(pp)+7, *(qp)));
+ if (*(pp)+8 < (ev)->iov[*(qp)].iov_len)
+ *(pp) = *(pp)+8;
+ else {
+ (*(qp))++;
+ *pp = 0;
+ }
+ return !0;
+ }
+ return 0;
+}
+/* int EV_GET_SINT64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) */
+#define EV_GET_SINT64(ev, p, pp, qp) efile_ev_get_sint64(ev,p,pp,qp)
+static int
+efile_ev_get_sint64(ErlIOVec *ev, Sint64 *p, int *pp, int *qp) {
+ Uint64 *tmp = (Uint64*)p;
+ return EV_GET_UINT64(ev,tmp,pp,qp);
+}
#if 0
@@ -744,6 +772,7 @@ file_init(void)
return 0;
}
+
/*********************************************************************
* Driver entry point -> start
*/
@@ -760,7 +789,7 @@ file_start(ErlDrvPort port, char* command)
}
desc->fd = FILE_FD_INVALID;
desc->port = port;
- desc->key = (unsigned int) (UWord) port;
+ desc->key = driver_async_port_key(port);
desc->flags = 0;
desc->invoke = NULL;
desc->d = NULL;
@@ -3105,25 +3134,25 @@ file_flush(ErlDrvData e) {
/*********************************************************************
* Driver entry point -> control
+ * Only debug functionality...
*/
static ErlDrvSSizeT
file_control(ErlDrvData e, unsigned int command,
char* buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) {
- /*
- * warning: variable ‘desc’ set but not used
- * [-Wunused-but-set-variable]
- * ... no kidding ...
- *
- *
file_descriptor *desc = (file_descriptor *)e;
switch (command) {
+ case 'K' :
+ if (rlen < 4) {
+ *rbuf = EF_ALLOC(4);
+ }
+ (*rbuf)[0] = ((desc->key) >> 24) & 0xFF;
+ (*rbuf)[1] = ((desc->key) >> 16) & 0xFF;
+ (*rbuf)[2] = ((desc->key) >> 8) & 0xFF;
+ (*rbuf)[3] = (desc->key) & 0xFF;
+ return 4;
default:
return 0;
- }
- ASSERT(0);
- desc = NULL;
- */
- return 0;
+ }
}
/*********************************************************************
@@ -3604,7 +3633,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
for(i = 0; i < n; i++) {
Uint32 sizeH, sizeL;
size_t size;
- if ( !EV_GET_UINT64(ev, &d->c.pwritev.specs[i].offset, &p, &q)
+ if ( !EV_GET_SINT64(ev, &d->c.pwritev.specs[i].offset, &p, &q)
|| !EV_GET_UINT32(ev, &sizeH, &p, &q)
|| !EV_GET_UINT32(ev, &sizeL, &p, &q)) {
/* Misalignment in buffer */
@@ -3746,7 +3775,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
for (i = 1; i < 1+n; i++) {
Uint32 sizeH, sizeL;
size_t size;
- if ( !EV_GET_UINT64(ev, &d->c.preadv.offsets[i-1], &p, &q)
+ if ( !EV_GET_SINT64(ev, &d->c.preadv.offsets[i-1], &p, &q)
|| !EV_GET_UINT32(ev, &sizeH, &p, &q)
|| !EV_GET_UINT32(ev, &sizeL, &p, &q)) {
reply_posix_error(desc, EINVAL);
@@ -3814,7 +3843,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
Uint32 origin; /* Origin of seek. */
if (ev->size < 1+8+4
- || !EV_GET_UINT64(ev, &offset, &p, &q)
+ || !EV_GET_SINT64(ev, &offset, &p, &q)
|| !EV_GET_UINT32(ev, &origin, &p, &q)) {
/* Wrong length of buffer to contain offset and origin */
reply_posix_error(desc, EINVAL);
@@ -3927,7 +3956,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
goto done;
}
if (ev->size < 1+1+8+4
- || !EV_GET_UINT64(ev, &hdr_offset, &p, &q)
+ || !EV_GET_SINT64(ev, &hdr_offset, &p, &q)
|| !EV_GET_UINT32(ev, &max_size, &p, &q)) {
/* Buffer too short to contain
* the header offset and max size spec */
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 301ce2d0e2..60db50e80a 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -282,7 +282,7 @@ static BOOL (WINAPI *fpSetHandleInformation)(HANDLE,DWORD,DWORD);
static unsigned long zero_value = 0;
static unsigned long one_value = 1;
-#else
+#else /* #ifdef __WIN32__ */
#include <sys/time.h>
#ifdef NETDB_H_NEEDS_IN_H
@@ -315,9 +315,17 @@ static unsigned long one_value = 1;
#include <net/if.h>
+#ifdef HAVE_SCHED_H
+#include <sched.h>
+#endif
+
+#ifdef HAVE_SETNS_H
+#include <setns.h>
+#endif
+
/* SCTP support -- currently for UNIX platforms only: */
#undef HAVE_SCTP
-#if (!defined(__WIN32__) && defined(HAVE_SCTP_H))
+#if defined(HAVE_SCTP_H)
#include <netinet/sctp.h>
@@ -418,7 +426,7 @@ static int (*p_sctp_bindx)(int sd, struct sockaddr *addrs,
static int (*p_sctp_peeloff)(int sd, sctp_assoc_t assoc_id) = NULL;
#endif
-#endif /* SCTP supported */
+#endif /* #if defined(HAVE_SCTP_H) */
#ifndef WANT_NONBLOCKING
#define WANT_NONBLOCKING
@@ -512,7 +520,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
} while(0)
-#endif /* __WIN32__ */
+#endif /* #ifdef __WIN32__ #else */
#ifdef HAVE_SOCKLEN_T
# define SOCKLEN_T socklen_t
@@ -680,6 +688,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define INET_LOPT_TCP_SEND_TIMEOUT_CLOSE 35 /* auto-close on send timeout or not */
#define INET_LOPT_MSGQ_HIWTRMRK 36 /* set local msgq high watermark */
#define INET_LOPT_MSGQ_LOWTRMRK 37 /* set local msgq low watermark */
+#define INET_LOPT_NETNS 38 /* Network namespace pathname */
/* SCTP options: a separate range, from 100: */
#define SCTP_OPT_RTOINFO 100
#define SCTP_OPT_ASSOCINFO 101
@@ -955,6 +964,10 @@ typedef struct {
int is_ignored; /* if a fd is ignored by the inet_drv.
This flag should be set to true when
the fd is used outside of inet_drv. */
+#ifdef HAVE_SETNS
+ char *netns; /* Socket network namespace name
+ as full file path */
+#endif
} inet_descriptor;
@@ -1181,6 +1194,7 @@ static ErlDrvTermData am_dontroute;
static ErlDrvTermData am_priority;
static ErlDrvTermData am_tos;
static ErlDrvTermData am_ipv6_v6only;
+static ErlDrvTermData am_netns;
#endif
/* speical errors for bad ports and sequences */
@@ -3498,6 +3512,7 @@ static void inet_init_sctp(void) {
INIT_ATOM(priority);
INIT_ATOM(tos);
INIT_ATOM(ipv6_v6only);
+ INIT_ATOM(netns);
/* Option names */
INIT_ATOM(sctp_rtoinfo);
@@ -3908,12 +3923,81 @@ static int erl_inet_close(inet_descriptor* desc)
static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type,
char** rbuf, ErlDrvSizeT rsize)
{
+ int save_errno;
+#ifdef HAVE_SETNS
+ int current_ns, new_ns;
+ current_ns = new_ns = 0;
+#endif
+ save_errno = 0;
+
if (desc->state != INET_STATE_CLOSED)
return ctl_xerror(EXBADSEQ, rbuf, rsize);
+
+#ifdef HAVE_SETNS
+ if (desc->netns != NULL) {
+ /* Temporarily change network namespace for this thread
+ * while creating the socket
+ */
+ current_ns = open("/proc/self/ns/net", O_RDONLY);
+ if (current_ns == INVALID_SOCKET)
+ return ctl_error(sock_errno(), rbuf, rsize);
+ new_ns = open(desc->netns, O_RDONLY);
+ if (new_ns == INVALID_SOCKET) {
+ save_errno = sock_errno();
+ while (close(current_ns) == INVALID_SOCKET &&
+ sock_errno() == EINTR);
+ return ctl_error(save_errno, rbuf, rsize);
+ }
+ if (setns(new_ns, CLONE_NEWNET) != 0) {
+ save_errno = sock_errno();
+ while (close(new_ns) == INVALID_SOCKET &&
+ sock_errno() == EINTR);
+ while (close(current_ns) == INVALID_SOCKET &&
+ sock_errno() == EINTR);
+ return ctl_error(save_errno, rbuf, rsize);
+ }
+ else {
+ while (close(new_ns) == INVALID_SOCKET &&
+ sock_errno() == EINTR);
+ }
+ }
+#endif
if ((desc->s = sock_open(domain, type, desc->sprotocol)) == INVALID_SOCKET)
- return ctl_error(sock_errno(), rbuf, rsize);
- if ((desc->event = sock_create_event(desc)) == INVALID_EVENT)
- return ctl_error(sock_errno(), rbuf, rsize);
+ save_errno = sock_errno();
+#ifdef HAVE_SETNS
+ if (desc->netns != NULL) {
+ /* Restore network namespace */
+ if (setns(current_ns, CLONE_NEWNET) != 0) {
+ /* XXX Failed to restore network namespace.
+ * What to do? Tidy up and return an error...
+ * Note that the thread now might still be in the namespace.
+ * Can this even happen? Should the emulator be aborted?
+ */
+ if (desc->s != INVALID_SOCKET)
+ save_errno = sock_errno();
+ while (close(desc->s) == INVALID_SOCKET &&
+ sock_errno() == EINTR);
+ desc->s = INVALID_SOCKET;
+ while (close(current_ns) == INVALID_SOCKET &&
+ sock_errno() == EINTR);
+ return ctl_error(save_errno, rbuf, rsize);
+ }
+ else {
+ while (close(current_ns) == INVALID_SOCKET &&
+ sock_errno() == EINTR);
+ }
+ }
+#endif
+ if (desc->s == INVALID_SOCKET)
+ return ctl_error(save_errno, rbuf, rsize);
+
+ if ((desc->event = sock_create_event(desc)) == INVALID_EVENT) {
+ save_errno = sock_errno();
+ while (close(desc->s) == INVALID_SOCKET &&
+ sock_errno() == EINTR);
+ desc->s = INVALID_SOCKET;
+ return ctl_error(save_errno, rbuf, rsize);
+ }
SET_NONBLOCKING(desc->s);
#ifdef __WIN32__
driver_select(desc->port, desc->event, ERL_DRV_READ, 1);
@@ -5529,6 +5613,20 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len)
}
continue;
+#ifdef HAVE_SETNS
+ case INET_LOPT_NETNS:
+ /* It is annoying that ival and len are both (signed) int */
+ if (ival < 0) return -1;
+ if (len < ival) return -1;
+ if (desc->netns != NULL) FREE(desc->netns);
+ desc->netns = ALLOC(((unsigned int) ival) + 1);
+ memcpy(desc->netns, ptr, ival);
+ desc->netns[ival] = '\0';
+ ptr += ival;
+ len -= ival;
+ continue;
+#endif
+
case INET_OPT_REUSEADDR:
#ifdef __WIN32__
continue; /* Bjorn says */
@@ -5858,6 +5956,21 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len)
res = 0;
continue;
+#ifdef HAVE_SETNS
+ case INET_LOPT_NETNS:
+ {
+ size_t ns_len;
+ ns_len = get_int32(curr); curr += 4;
+ CHKLEN(curr, ns_len);
+ if (desc->netns != NULL) FREE(desc->netns);
+ desc->netns = ALLOC(ns_len + 1);
+ memcpy(desc->netns, curr, ns_len);
+ desc->netns[ns_len] = '\0';
+ curr += ns_len;
+ }
+ continue;
+#endif
+
/* SCTP options and applicable generic INET options: */
case SCTP_OPT_RTOINFO:
@@ -6454,6 +6567,22 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc,
}
continue;
+#ifdef HAVE_SETNS
+ case INET_LOPT_NETNS:
+ if (desc->netns != NULL) {
+ size_t netns_len;
+ netns_len = strlen(desc->netns);
+ *ptr++ = opt;
+ put_int32(netns_len, ptr);
+ PLACE_FOR(netns_len, ptr);
+ memcpy(ptr, desc->netns, netns_len);
+ ptr += netns_len;
+ } else {
+ TRUNCATE_TO(0,ptr);
+ }
+ continue;
+#endif
+
case INET_OPT_PRIORITY:
#ifdef SO_PRIORITY
type = SO_PRIORITY;
@@ -6737,6 +6866,22 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc,
break;
}
+#ifdef HAVE_SETNS
+ case INET_LOPT_NETNS:
+ if (desc->netns != NULL) {
+ PLACE_FOR
+ (spec, i,
+ LOAD_ATOM_CNT + LOAD_BUF2BINARY_CNT + LOAD_TUPLE_CNT);
+ i = LOAD_ATOM (spec, i, am_netns);
+ i = LOAD_BUF2BINARY
+ (spec, i, desc->netns, strlen(desc->netns));
+ i = LOAD_TUPLE (spec, i, 2);
+ break;
+ }
+ else
+ continue; /* Ignore */
+#endif
+
/* SCTP and generic INET options: */
case SCTP_OPT_RTOINFO:
@@ -7458,6 +7603,10 @@ static ErlDrvSSizeT inet_subscribe(inet_descriptor* desc,
static void inet_stop(inet_descriptor* desc)
{
erl_inet_close(desc);
+#ifdef HAVE_SETNS
+ if (desc->netns != NULL)
+ FREE(desc->netns);
+#endif
FREE(desc);
}
@@ -7537,6 +7686,10 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol)
desc->is_ignored = 0;
+#ifdef HAVE_SETNS
+ desc->netns = NULL;
+#endif
+
return (ErlDrvData)desc;
}
diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c
index be3d86a1d2..b36a103f8e 100644
--- a/erts/emulator/drivers/win32/win_efile.c
+++ b/erts/emulator/drivers/win32/win_efile.c
@@ -772,6 +772,7 @@ efile_may_openfile(Efile_error* errInfo, char *name) {
DWORD attr;
if ((attr = GetFileAttributesW(wname)) == INVALID_FILE_ATTRIBUTES) {
+ errno = ENOENT;
return check_error(-1, errInfo);
}
diff --git a/erts/emulator/sys/win32/erl_win_dyn_driver.h b/erts/emulator/sys/win32/erl_win_dyn_driver.h
index a7c53c904d..b9a9838a36 100644
--- a/erts/emulator/sys/win32/erl_win_dyn_driver.h
+++ b/erts/emulator/sys/win32/erl_win_dyn_driver.h
@@ -80,6 +80,7 @@ WDD_TYPEDEF(int, erl_drv_output_term, (ErlDrvTermData, ErlDrvTermData*, int));
WDD_TYPEDEF(int, driver_output_term, (ErlDrvPort, ErlDrvTermData*, int));
WDD_TYPEDEF(int, erl_drv_send_term, (ErlDrvTermData, ErlDrvTermData, ErlDrvTermData*, int));
WDD_TYPEDEF(int, driver_send_term, (ErlDrvPort, ErlDrvTermData, ErlDrvTermData*, int));
+WDD_TYPEDEF(unsigned int, driver_async_port_key, (ErlDrvPort));
WDD_TYPEDEF(long, driver_async, (ErlDrvPort,unsigned int*,void (*)(void*),void*,void (*)(void*)));
WDD_TYPEDEF(int, driver_async_cancel, (unsigned int));
WDD_TYPEDEF(int, driver_lock_driver, (ErlDrvPort));
@@ -197,6 +198,7 @@ typedef struct {
WDD_FTYPE(driver_output_term) *driver_output_term;
WDD_FTYPE(erl_drv_send_term) *erl_drv_send_term;
WDD_FTYPE(driver_send_term) *driver_send_term;
+ WDD_FTYPE(driver_async_port_key) *driver_async_port_key;
WDD_FTYPE(driver_async) *driver_async;
WDD_FTYPE(driver_async_cancel) *driver_async_cancel;
WDD_FTYPE(driver_lock_driver) *driver_lock_driver;
@@ -308,6 +310,7 @@ extern TWinDynDriverCallbacks WinDynDriverCallbacks;
#define driver_output_term (WinDynDriverCallbacks.driver_output_term)
#define erl_drv_send_term (WinDynDriverCallbacks.erl_drv_send_term)
#define driver_send_term (WinDynDriverCallbacks.driver_send_term)
+#define driver_async_port_key (WinDynDriverCallbacks.driver_async_port_key)
#define driver_async (WinDynDriverCallbacks.driver_async)
#define driver_async_cancel (WinDynDriverCallbacks.driver_async_cancel)
#define driver_lock_driver (WinDynDriverCallbacks.driver_lock_driver)
@@ -443,6 +446,7 @@ do { \
((W).driver_output_term) = driver_output_term; \
((W).erl_drv_send_term) = erl_drv_send_term; \
((W).driver_send_term) = driver_send_term; \
+((W).driver_async_port_key) = driver_async_port_key; \
((W).driver_async) = driver_async; \
((W).driver_async_cancel) = driver_async_cancel; \
((W).driver_lock_driver) = driver_lock_driver; \
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index 9594ab48b1..f02ca3cb98 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -140,7 +140,8 @@ EMAKEFILE=Emakefile
TEST_SPEC_FILES= emulator.spec \
emulator.spec.win \
- emulator_bench.spec
+ emulator_bench.spec \
+ emulator_smoke.spec
# ----------------------------------------------------
# Release directory specification
diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl
index 65367eab98..f79bb761d1 100644
--- a/erts/emulator/test/efile_SUITE.erl
+++ b/erts/emulator/test/efile_SUITE.erl
@@ -19,16 +19,16 @@
-module(efile_SUITE).
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
--export([iter_max_files/1]).
+-export([iter_max_files/1, async_dist/1]).
--export([do_iter_max_files/2]).
+-export([do_iter_max_files/2, do_async_dist/1]).
-include_lib("test_server/include/test_server.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [iter_max_files].
+ [iter_max_files, async_dist].
groups() ->
[].
@@ -45,6 +45,84 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+do_async_dist(Dir) ->
+ X = 100,
+ AT = erlang:system_info(thread_pool_size),
+ Keys = file_keys(Dir,AT*X,[],[]),
+ Tab = ets:new(x,[ordered_set]),
+ [ ets:insert(Tab,{N,0}) || N <- lists:seq(0,AT-1) ],
+ [ ets:update_counter(Tab,(N rem AT),1) || N <- Keys ],
+ Res = [ V || {_,V} <- ets:tab2list(Tab) ],
+ ets:delete(Tab),
+ {Res, sdev(Res)/X}.
+
+sdev(List) ->
+ Len = length(List),
+ Mean = lists:sum(List)/Len,
+ math:sqrt(lists:sum([ (X - Mean) * (X - Mean) || X <- List ]) / Len).
+
+file_keys(_,0,FdList,FnList) ->
+ [ file:close(FD) || FD <- FdList ],
+ [ file:delete(FN) || FN <- FnList ],
+ [];
+file_keys(Dir,Num,FdList,FnList) ->
+ Name = "dummy"++integer_to_list(Num),
+ FN = filename:join([Dir,Name]),
+ case file:open(FN,[write,raw]) of
+ {ok,FD} ->
+ {file_descriptor,prim_file,{Port,_}} = FD,
+ <<X:32/integer-big>> =
+ iolist_to_binary(erlang:port_control(Port,$K,[])),
+ [X | file_keys(Dir,Num-1,[FD|FdList],[FN|FnList])];
+ {error,_} ->
+ % Try freeing up FD's if there are any
+ case FdList of
+ [] ->
+ exit({cannot_open_file,FN});
+ _ ->
+ [ file:close(FD) || FD <- FdList ],
+ [ file:delete(F) || F <- FnList ],
+ file_keys(Dir,Num,[],[])
+ end
+ end.
+
+async_dist(doc) ->
+ "Check that the distribution of files over async threads is fair";
+async_dist(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir,Config),
+ TestFile = filename:join(DataDir, "existing_file"),
+ Dir = filename:dirname(code:which(?MODULE)),
+ AsyncSizes = [7,10,100,255,256,64,63,65],
+ Max = 0.5,
+
+ lists:foreach(fun(Size) ->
+ {ok,Node} =
+ test_server:start_node
+ (test_iter_max_files,slave,
+ [{args,
+ "+A "++integer_to_list(Size)++
+ " -pa " ++ Dir}]),
+ {Distr,SD} = rpc:call(Node,?MODULE,do_async_dist,
+ [DataDir]),
+ test_server:stop_node(Node),
+ if
+ SD > Max ->
+ io:format("Bad async queue distribution for "
+ "~p async threads:~n"
+ " Standard deviation is ~p~n"
+ " Key distribution:~n ~lp~n",
+ [Size,SD,Distr]),
+ exit({bad_async_dist,Size,SD,Distr});
+ true ->
+ io:format("OK async queue distribution for "
+ "~p async threads:~n"
+ " Standard deviation is ~p~n"
+ " Key distribution:~n ~lp~n",
+ [Size,SD,Distr]),
+ ok
+ end
+ end, AsyncSizes),
+ ok.
%%
%% Open as many files as possible. Do this several times and check
@@ -98,7 +176,7 @@ open_files(Name) ->
?line case file:open(Name, [read,raw]) of
{ok, Fd} ->
[Fd| open_files(Name)];
- {error, Reason} ->
-% io:format("Error reason: ~p", [Reason]),
+ {error, _Reason} ->
+% io:format("Error reason: ~p", [_Reason]),
[]
end.
diff --git a/erts/emulator/test/emulator_smoke.spec b/erts/emulator/test/emulator_smoke.spec
new file mode 100644
index 0000000000..3219aeb823
--- /dev/null
+++ b/erts/emulator/test/emulator_smoke.spec
@@ -0,0 +1,3 @@
+{suites,"../emulator_test",[smoke_test_SUITE,time_SUITE]}.
+{cases,"../emulator_test",crypto_SUITE,[t_md5]}.
+{cases,"../emulator_test",float_SUITE,[fpe,cmp_integer]}. \ No newline at end of file
diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl
index 8dbc6b6538..b56b7ce525 100644
--- a/erts/emulator/test/match_spec_SUITE.erl
+++ b/erts/emulator/test/match_spec_SUITE.erl
@@ -22,7 +22,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2, not_run/1]).
-export([test_1/1, test_2/1, test_3/1, bad_match_spec_bin/1,
- trace_control_word/1, silent/1, silent_no_ms/1,
+ trace_control_word/1, silent/1, silent_no_ms/1, silent_test/1,
ms_trace2/1, ms_trace3/1, boxed_and_small/1,
destructive_in_test_bif/1, guard_exceptions/1,
unary_plus/1, unary_minus/1, moving_labels/1]).
@@ -55,7 +55,7 @@ all() ->
case test_server:is_native(match_spec_SUITE) of
false ->
[test_1, test_2, test_3, bad_match_spec_bin,
- trace_control_word, silent, silent_no_ms, ms_trace2,
+ trace_control_word, silent, silent_no_ms, silent_test, ms_trace2,
ms_trace3, boxed_and_small, destructive_in_test_bif,
guard_exceptions, unary_plus, unary_minus, fpe,
moving_labels,
@@ -501,6 +501,14 @@ silent_no_ms(Config) when is_list(Config) ->
{trace,Tracee,return_to,{?MODULE,f3,2}}]
end).
+silent_test(doc) ->
+ ["Test that match_spec_test does not activate silent"];
+silent_test(_Config) ->
+ {flags,[]} = erlang:trace_info(self(),flags),
+ erlang:match_spec_test([],[{'_',[],[{silent,true}]}],trace),
+ {flags,[]} = erlang:trace_info(self(),flags).
+
+
ms_trace2(doc) ->
["Test the match spec functions {trace/2}"];
ms_trace2(suite) -> [];
diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl
index 8931562828..81539faa09 100644
--- a/erts/emulator/test/scheduler_SUITE.erl
+++ b/erts/emulator/test/scheduler_SUITE.erl
@@ -52,6 +52,7 @@
update_cpu_info/1,
sct_cmd/1,
sbt_cmd/1,
+ scheduler_threads/1,
scheduler_suspend/1,
reader_groups/1]).
@@ -66,7 +67,7 @@ all() ->
equal_with_part_time_max,
equal_and_high_with_part_time_max, equal_with_high,
equal_with_high_max, bound_process,
- {group, scheduler_bind}, scheduler_suspend,
+ {group, scheduler_bind}, scheduler_threads, scheduler_suspend,
reader_groups].
groups() ->
@@ -1039,7 +1040,66 @@ sbt_test(Config, CpuTCmd, ClBt, Bt, LP) ->
tuple_to_list(SB)),
?line stop_node(Node),
?line ok.
-
+
+scheduler_threads(Config) when is_list(Config) ->
+ SmpSupport = erlang:system_info(smp_support),
+ {Sched, SchedOnln, _} = get_sstate(Config, ""),
+ %% Configure half the number of both the scheduler threads and
+ %% the scheduler threads online.
+ {HalfSched, HalfSchedOnln} = case SmpSupport of
+ false -> {1,1};
+ true ->
+ {Sched div 2,
+ SchedOnln div 2}
+ end,
+ {HalfSched, HalfSchedOnln, _} = get_sstate(Config, "+SP 50:50"),
+ %% Use +S to configure 4x the number of scheduler threads and
+ %% 4x the number of scheduler threads online, but alter that
+ %% setting using +SP to 50% scheduler threads and 25% scheduler
+ %% threads online. The result should be 2x scheduler threads and
+ %% 1x scheduler threads online.
+ TwiceSched = case SmpSupport of
+ false -> 1;
+ true -> Sched*2
+ end,
+ FourSched = integer_to_list(Sched*4),
+ FourSchedOnln = integer_to_list(SchedOnln*4),
+ CombinedCmd1 = "+S "++FourSched++":"++FourSchedOnln++" +SP50:25",
+ {TwiceSched, SchedOnln, _} = get_sstate(Config, CombinedCmd1),
+ %% Now do the same test but with the +S and +SP options in the
+ %% opposite order, since order shouldn't matter.
+ CombinedCmd2 = "+SP50:25 +S "++FourSched++":"++FourSchedOnln,
+ {TwiceSched, SchedOnln, _} = get_sstate(Config, CombinedCmd2),
+ %% Apply two +SP options to make sure the second overrides the first
+ TwoCmd = "+SP 25:25 +SP 100:100",
+ {Sched, SchedOnln, _} = get_sstate(Config, TwoCmd),
+ %% Configure 50% of scheduler threads online only
+ {Sched, HalfSchedOnln, _} = get_sstate(Config, "+SP:50"),
+ %% Configure 2x scheduler threads only
+ {TwiceSched, SchedOnln, _} = get_sstate(Config, "+SP 200"),
+ %% Test resetting the scheduler counts
+ ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0",
+ {Sched, SchedOnln, _} = get_sstate(Config, ResetCmd),
+ %% Test negative +S settings, but only for SMP-enabled emulators
+ case SmpSupport of
+ false -> ok;
+ true ->
+ SchedMinus1 = Sched-1,
+ SchedOnlnMinus1 = SchedOnln-1,
+ {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"),
+ {Sched, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"),
+ {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1")
+ end,
+ ok.
+
+get_sstate(Config, Cmd) ->
+ {ok, Node} = start_node(Config, Cmd),
+ [SState] = mcall(Node, [fun () ->
+ erlang:system_info(schedulers_state)
+ end]),
+ stop_node(Node),
+ SState.
+
scheduler_suspend(Config) when is_list(Config) ->
?line Dog = ?t:timetrap(?t:minutes(5)),
?line lists:foreach(fun (S) -> scheduler_suspend_test(Config, S) end,