aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/doc/src/erts_alloc.xml52
-rw-r--r--erts/emulator/beam/erl_afit_alloc.c15
-rw-r--r--erts/emulator/beam/erl_alloc.c153
-rw-r--r--erts/emulator/beam/erl_alloc.types15
-rw-r--r--erts/emulator/beam/erl_alloc_util.c701
-rw-r--r--erts/emulator/beam/erl_alloc_util.h43
-rw-r--r--erts/emulator/beam/erl_bestfit_alloc.c167
-rw-r--r--erts/emulator/beam/erl_bestfit_alloc.h3
-rw-r--r--erts/emulator/beam/erl_db.c47
-rw-r--r--erts/emulator/beam/erl_db_util.c4
-rw-r--r--erts/emulator/beam/erl_goodfit_alloc.c22
-rw-r--r--erts/emulator/beam/erl_instrument.c14
-rw-r--r--erts/emulator/beam/erl_lock_check.c1
-rw-r--r--erts/emulator/beam/erl_nif.c10
-rw-r--r--erts/emulator/test/nif_SUITE.erl45
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c46
-rw-r--r--erts/emulator/test/send_term_SUITE.erl4
-rw-r--r--erts/etc/common/erlexec.c3
-rw-r--r--lib/erl_interface/src/connect/ei_resolve.c7
-rw-r--r--lib/inets/Makefile4
-rw-r--r--lib/inets/doc/src/httpc.xml180
-rw-r--r--lib/inets/doc/src/notes.xml57
-rw-r--r--lib/inets/src/ftp/ftp.erl4
-rw-r--r--lib/inets/src/http_client/httpc.erl37
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl13
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl6
-rw-r--r--lib/inets/src/http_lib/http_transport.erl211
-rw-r--r--lib/inets/src/inets_app/inets.appup.src18
-rw-r--r--lib/inets/test/ftp_suite_lib.erl35
-rw-r--r--lib/inets/test/httpc_SUITE.erl560
-rw-r--r--lib/inets/test/httpd_SUITE.erl304
-rw-r--r--lib/inets/test/httpd_test_lib.erl91
-rw-r--r--lib/inets/test/inets_test_lib.erl151
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/odbc/c_src/odbcserver.c22
-rw-r--r--lib/odbc/c_src/odbcserver.h1
-rw-r--r--lib/odbc/doc/src/databases.xml4
-rw-r--r--lib/odbc/doc/src/odbc.xml2
-rw-r--r--lib/odbc/src/odbc.appup.src6
-rw-r--r--lib/odbc/src/odbc.erl3
-rw-r--r--lib/odbc/src/odbc_internal.hrl1
-rw-r--r--lib/odbc/test/mysql.erl4
-rw-r--r--lib/odbc/test/odbc_data_type_SUITE.erl152
-rw-r--r--lib/odbc/test/oracle.erl4
-rw-r--r--lib/odbc/test/postgres.erl4
-rw-r--r--lib/odbc/test/sqlserver.erl4
-rw-r--r--lib/odbc/vsn.mk2
-rw-r--r--lib/reltool/src/reltool_sys_win.erl133
-rw-r--r--lib/sasl/src/release_handler.erl40
-rw-r--r--lib/sasl/src/release_handler_1.erl91
-rw-r--r--lib/sasl/src/systools_lib.erl33
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl135
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/Makefile.src32
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/README16
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.app (renamed from lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app)4
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.appup3
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/priv/file0
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/src/a.erl54
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/src/a_sup.erl37
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app7
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/src/b_lib.erl3
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/src/b_server.erl37
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app7
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.appup3
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/src/b_lib.erl3
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/src/b_server.erl37
-rw-r--r--lib/ssl/doc/src/ssl.xml4
-rw-r--r--lib/ssl/src/ssl.erl54
-rw-r--r--lib/ssl/src/ssl_certificate.erl54
-rw-r--r--lib/ssl/src/ssl_certificate_db.erl49
-rw-r--r--lib/ssl/src/ssl_connection.erl142
-rw-r--r--lib/ssl/src/ssl_handshake.erl44
-rw-r--r--lib/ssl/src/ssl_internal.hrl4
-rw-r--r--lib/ssl/src/ssl_manager.erl50
-rw-r--r--lib/ssl/src/ssl_session.erl6
-rw-r--r--lib/ssl/src/ssl_session_cache.erl14
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl268
-rw-r--r--lib/ssl/test/ssl_session_cache_SUITE.erl38
-rw-r--r--lib/stdlib/src/erl_scan.erl7
-rw-r--r--lib/stdlib/src/io_lib.erl4
-rw-r--r--lib/stdlib/src/queue.erl127
-rw-r--r--lib/stdlib/test/erl_scan_SUITE.erl4
-rw-r--r--lib/stdlib/test/ets_SUITE.erl19
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl18
-rw-r--r--lib/tools/doc/src/xref.xml8
85 files changed, 3411 insertions, 1412 deletions
diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml
index 51a4a2bca0..452e5d990e 100644
--- a/erts/doc/src/erts_alloc.xml
+++ b/erts/doc/src/erts_alloc.xml
@@ -78,14 +78,20 @@
segments are allocated, cached segments are used if possible
instead of creating new segments. This in order to reduce
the number of system calls made.</item>
+ <tag><c>sbmbc_alloc</c></tag>
+ <item>Allocator used by other allocators for allocation of carriers
+ where only small blocks are placed. Currently this allocator is
+ disabled by default.</item>
</taglist>
<p><c>sys_alloc</c> and <c>fix_alloc</c> are always enabled and
cannot be disabled. <c>mseg_alloc</c> is always enabled if it is
available and an allocator that uses it is enabled. All other
allocators can be <seealso marker="#M_e">enabled or disabled</seealso>.
By default all allocators are enabled.
- When an allocator is disabled, <c>sys_alloc</c>
- is used instead of the disabled allocator.</p>
+ When an allocator is disabled, <c>sys_alloc</c> is used instead of
+ the disabled allocator. <c>sbmbc_alloc</c> is an exception. If
+ <c>sbmbc_alloc</c> is disabled, other allocators will not handle
+ small blocks in separate carriers.</p>
<p>The main idea with the <c>erts_alloc</c> library is to separate
memory blocks that are used differently into different memory
areas, and by this achieving less memory fragmentation. By
@@ -103,15 +109,20 @@
following does <em>not</em> apply to them.</p>
<p>An allocator manages multiple areas, called carriers, in which
memory blocks are placed. A carrier is either placed in a
- separate memory segment (allocated via <c>mseg_alloc</c>) or in
- the heap segment (allocated via <c>sys_alloc</c>). Multiblock
+ separate memory segment (allocated via <c>mseg_alloc</c>), in
+ the heap segment (allocated via <c>sys_alloc</c>), or inside
+ another carrier (in case it is a carrier created by
+ <c>sbmbc_alloc</c>). Multiblock
carriers are used for storage of several blocks. Singleblock
carriers are used for storage of one block. Blocks that are
larger than the value of the singleblock carrier threshold
(<seealso marker="#M_sbct">sbct</seealso>) parameter are placed
- in singleblock carriers. Blocks smaller than the value of the
- <c>sbct</c> parameter are placed in multiblock
- carriers. Normally an allocator creates a "main multiblock
+ in singleblock carriers. Blocks that are smaller than the value
+ of the <c>sbct</c> parameter are placed in multiblock
+ carriers. Blocks that are smaller than the small block multiblock
+ carrier threshold (<seealso marker="#M_sbmbct">sbmbct</seealso>)
+ will be placed in multiblock carriers only used for small blocks.
+ Normally an allocator creates a "main multiblock
carrier". Main multiblock carriers are never deallocated. The
size of the main multiblock carrier is determined by the value
of the <seealso marker="#M_mmbcs">mmbcs</seealso> parameter.</p>
@@ -133,8 +144,11 @@
<c>sbct</c> parameter should be larger than the value of the
<c>lmbcs</c> parameter, the allocator may have to create
multiblock carriers that are larger than the value of the
- <c>lmbcs</c> parameter, though. Singleblock carriers allocated
- via <c>mseg_alloc</c> are sized to whole pages.</p>
+ <c>lmbcs</c> parameter, though. The size of multiblock carriers
+ for small blocks is determined by the small block multiblock
+ carrier size (<seealso marker="#M_sbmbcs">sbmbcs</seealso>).
+ Singleblock carriers allocated via <c>mseg_alloc</c> are sized
+ to whole pages.</p>
<p>Sizes of carriers allocated via <c>sys_alloc</c> are
decided based on the value of the <c>sys_alloc</c> carrier size
(<seealso marker="#Muycs">ycs</seealso>) parameter. The size of
@@ -194,6 +208,11 @@
</taglist>
</section>
+ <note><p>
+ Currently only allocators using the best fit and the address order
+ best fit strategies are able to use "small block multi block carriers".
+ </p></note>
+
<section>
<marker id="flags"></marker>
<title>System Flags Effecting erts_alloc</title>
@@ -215,6 +234,7 @@
the currently present allocators:</p>
<list type="bulleted">
<item><c>B: binary_alloc</c></item>
+ <item><c>C: sbmbc_alloc</c></item>
<item><c>D: std_alloc</c></item>
<item><c>E: ets_alloc</c></item>
<item><c>F: fix_alloc</c></item>
@@ -395,6 +415,20 @@
threshold will be placed in singleblock carriers. Blocks
smaller than this threshold will be placed in multiblock
carriers.</item>
+ <tag><marker id="M_sbmbcs"><c><![CDATA[+M<S>sbmbcs <size>]]></c></marker></tag>
+ <item>
+ Small block multiblock carrier size (in bytes). Memory blocks smaller
+ than the small block multiblock carrier threshold
+ (<seealso marker="#M_sbmbct">sbmbct</seealso>) will be placed in
+ multiblock carriers used for small blocks only. This parameter
+ determines the size of such carriers.
+ </item>
+ <tag><marker id="M_sbmbct"><c><![CDATA[+M<S>sbmbct <size>]]></c></marker></tag>
+ <item>
+ Small block multiblock carrier threshold (in bytes). Memory blocks
+ smaller than this threshold will be placed in multiblock carriers
+ used for small blocks only.
+ </item>
<tag><marker id="M_smbcs"><c><![CDATA[+M<S>smbcs <size>]]></c></marker></tag>
<item>
Smallest (<c>mseg_alloc</c>) multiblock carrier size (in
diff --git a/erts/emulator/beam/erl_afit_alloc.c b/erts/emulator/beam/erl_afit_alloc.c
index e8b594bb47..d397cd8848 100644
--- a/erts/emulator/beam/erl_afit_alloc.c
+++ b/erts/emulator/beam/erl_afit_alloc.c
@@ -43,9 +43,9 @@
/* Prototypes of callback functions */
static Block_t * get_free_block (Allctr_t *, Uint,
- Block_t *, Uint);
-static void link_free_block (Allctr_t *, Block_t *);
-static void unlink_free_block (Allctr_t *, Block_t *);
+ Block_t *, Uint, Uint32);
+static void link_free_block (Allctr_t *, Block_t *, Uint32);
+static void unlink_free_block (Allctr_t *, Block_t *, Uint32);
static Eterm info_options (Allctr_t *, char *, int *,
@@ -72,6 +72,8 @@ erts_afalc_start(AFAllctr_t *afallctr,
is a struct). */
Allctr_t *allctr = (Allctr_t *) afallctr;
+ init->sbmbct = 0; /* Small mbc not supported by afit */
+
sys_memcpy((void *) afallctr, (void *) &nulled_state, sizeof(AFAllctr_t));
allctr->mbc_header_size = sizeof(Carrier_t);
@@ -105,7 +107,8 @@ erts_afalc_start(AFAllctr_t *afallctr,
}
static Block_t *
-get_free_block(Allctr_t *allctr, Uint size, Block_t *cand_blk, Uint cand_size)
+get_free_block(Allctr_t *allctr, Uint size, Block_t *cand_blk, Uint cand_size,
+ Uint32 flags)
{
AFAllctr_t *afallctr = (AFAllctr_t *) allctr;
@@ -123,7 +126,7 @@ get_free_block(Allctr_t *allctr, Uint size, Block_t *cand_blk, Uint cand_size)
}
static void
-link_free_block(Allctr_t *allctr, Block_t *block)
+link_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
{
AFFreeBlock_t *blk = (AFFreeBlock_t *) block;
AFAllctr_t *afallctr = (AFAllctr_t *) allctr;
@@ -144,7 +147,7 @@ link_free_block(Allctr_t *allctr, Block_t *block)
}
static void
-unlink_free_block(Allctr_t *allctr, Block_t *block)
+unlink_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
{
AFFreeBlock_t *blk = (AFFreeBlock_t *) block;
AFAllctr_t *afallctr = (AFAllctr_t *) allctr;
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index cda404af5e..840534ec5e 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -87,13 +87,15 @@ typedef union {
char align_afa[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(AFAllctr_t))];
} ErtsAllocatorState_t;
-static ErtsAllocatorState_t sl_alloc_state;
+static ErtsAllocatorState_t sbmbc_alloc_state;
static ErtsAllocatorState_t std_alloc_state;
static ErtsAllocatorState_t ll_alloc_state;
#if HALFWORD_HEAP
-static ErtsAllocatorState_t std_alloc_low_state;
-static ErtsAllocatorState_t ll_alloc_low_state;
+static ErtsAllocatorState_t sbmbc_low_alloc_state;
+static ErtsAllocatorState_t std_low_alloc_state;
+static ErtsAllocatorState_t ll_low_alloc_state;
#endif
+static ErtsAllocatorState_t sl_alloc_state;
static ErtsAllocatorState_t temp_alloc_state;
static ErtsAllocatorState_t eheap_alloc_state;
static ErtsAllocatorState_t binary_alloc_state;
@@ -162,6 +164,7 @@ typedef struct {
char *mtrace;
char *nodename;
} instr;
+ struct au_init sbmbc_alloc;
struct au_init sl_alloc;
struct au_init std_alloc;
struct au_init ll_alloc;
@@ -171,8 +174,9 @@ typedef struct {
struct au_init ets_alloc;
struct au_init driver_alloc;
#if HALFWORD_HEAP
- struct au_init std_alloc_low;
- struct au_init ll_alloc_low;
+ struct au_init sbmbc_low_alloc;
+ struct au_init std_low_alloc;
+ struct au_init ll_low_alloc;
#endif
} erts_alc_hndl_args_init_t;
@@ -185,6 +189,34 @@ do { \
} while (0)
static void
+set_default_sbmbc_alloc_opts(struct au_init *ip)
+{
+ SET_DEFAULT_ALLOC_OPTS(ip);
+ ip->enable = 0;
+ ip->thr_spec = 0;
+ ip->atype = BESTFIT;
+ ip->init.bf.ao = 1;
+ ip->init.util.ramv = 0;
+ ip->init.util.mmsbc = 0;
+ ip->init.util.mmmbc = 500;
+ ip->init.util.sbct = ~((UWord) 0);
+ ip->init.util.name_prefix = "sbmbc_";
+ ip->init.util.alloc_no = ERTS_ALC_A_SBMBC;
+#ifndef SMALL_MEMORY
+ ip->init.util.mmbcs = 2*1024*1024; /* Main carrier size */
+#else
+ ip->init.util.mmbcs = 1*1024*1024; /* Main carrier size */
+#endif
+ ip->init.util.ts = ERTS_ALC_MTA_SBMBC;
+ ip->init.util.asbcst = 0;
+ ip->init.util.rsbcst = 0;
+ ip->init.util.rsbcmt = 0;
+ ip->init.util.rmbcmt = 0;
+ ip->init.util.sbmbct = 0;
+ ip->init.util.sbmbcs = 0;
+}
+
+static void
set_default_sl_alloc_opts(struct au_init *ip)
{
SET_DEFAULT_ALLOC_OPTS(ip);
@@ -202,6 +234,7 @@ set_default_sl_alloc_opts(struct au_init *ip)
ip->init.util.ts = ERTS_ALC_MTA_SHORT_LIVED;
ip->init.util.rsbcst = 80;
#if HALFWORD_HEAP
+ ip->init.util.force = 1;
ip->init.util.low_mem = 1;
#endif
@@ -249,6 +282,8 @@ set_default_ll_alloc_opts(struct au_init *ip)
ip->init.util.rsbcst = 0;
ip->init.util.rsbcmt = 0;
ip->init.util.rmbcmt = 0;
+ ip->init.util.sbmbct = 0;
+ ip->init.util.sbmbcs = 0;
}
static void
@@ -269,6 +304,7 @@ set_default_temp_alloc_opts(struct au_init *ip)
ip->init.util.rsbcst = 90;
ip->init.util.rmbcmt = 100;
#if HALFWORD_HEAP
+ ip->init.util.force = 1;
ip->init.util.low_mem = 1;
#endif
}
@@ -291,6 +327,7 @@ set_default_eheap_alloc_opts(struct au_init *ip)
ip->init.util.ts = ERTS_ALC_MTA_EHEAP;
ip->init.util.rsbcst = 50;
#if HALFWORD_HEAP
+ ip->init.util.force = 1;
ip->init.util.low_mem = 1;
#endif
}
@@ -436,10 +473,13 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
hdbg_init();
#endif
+ erts_have_sbmbc_alloc = 0;
+
erts_sys_alloc_init();
init_thr_ix(erts_no_schedulers);
erts_init_utils_mem();
+ set_default_sbmbc_alloc_opts(&init.sbmbc_alloc);
set_default_sl_alloc_opts(&init.sl_alloc);
set_default_std_alloc_opts(&init.std_alloc);
set_default_ll_alloc_opts(&init.ll_alloc);
@@ -453,6 +493,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
handle_args(argc, argv, &init);
if (erts_no_schedulers <= 1) {
+ init.sbmbc_alloc.thr_spec = 0;
init.sl_alloc.thr_spec = 0;
init.std_alloc.thr_spec = 0;
init.ll_alloc.thr_spec = 0;
@@ -464,6 +505,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
if (init.erts_alloc_config) {
/* Adjust flags that erts_alloc_config won't like */
+ init.sbmbc_alloc.thr_spec = 0;
init.temp_alloc.thr_spec = 0;
init.sl_alloc.thr_spec = 0;
init.std_alloc.thr_spec = 0;
@@ -480,6 +522,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
init.temp_alloc.thr_spec = erts_no_schedulers;
/* Others must use thread preferred interface */
+ adjust_tpref(&init.sbmbc_alloc, erts_no_schedulers);
adjust_tpref(&init.sl_alloc, erts_no_schedulers);
adjust_tpref(&init.std_alloc, erts_no_schedulers);
adjust_tpref(&init.ll_alloc, erts_no_schedulers);
@@ -497,6 +540,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
* The following allocators cannot be run with afit strategy.
* Make sure they don't...
*/
+ refuse_af_strategy(&init.sbmbc_alloc);
refuse_af_strategy(&init.sl_alloc);
refuse_af_strategy(&init.std_alloc);
refuse_af_strategy(&init.ll_alloc);
@@ -551,19 +595,30 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
#if HALFWORD_HEAP
/* Init low memory variants by cloning */
- init.std_alloc_low = init.std_alloc;
- init.std_alloc_low.init.util.alloc_no = ERTS_ALC_A_STANDARD_LOW;
- init.std_alloc_low.init.util.low_mem = 1;
-
- init.ll_alloc_low = init.ll_alloc;
- init.ll_alloc_low.init.util.alloc_no = ERTS_ALC_A_LONG_LIVED_LOW;
- init.ll_alloc_low.init.util.low_mem = 1;
-
- set_au_allocator(ERTS_ALC_A_STANDARD_LOW, &init.std_alloc_low);
- set_au_allocator(ERTS_ALC_A_LONG_LIVED_LOW, &init.ll_alloc_low);
+ init.sbmbc_low_alloc = init.sbmbc_alloc;
+ init.sbmbc_low_alloc.init.util.name_prefix = "sbmbc_low_";
+ init.sbmbc_low_alloc.init.util.alloc_no = ERTS_ALC_A_STANDARD_LOW;
+ init.sbmbc_low_alloc.init.util.low_mem = 1;
+
+ init.std_low_alloc = init.std_alloc;
+ init.std_low_alloc.init.util.name_prefix = "std_low_";
+ init.std_low_alloc.init.util.alloc_no = ERTS_ALC_A_STANDARD_LOW;
+ init.std_low_alloc.init.util.force = 1;
+ init.std_low_alloc.init.util.low_mem = 1;
+
+ init.ll_low_alloc = init.ll_alloc;
+ init.ll_low_alloc.init.util.name_prefix = "ll_low_";
+ init.ll_low_alloc.init.util.alloc_no = ERTS_ALC_A_LONG_LIVED_LOW;
+ init.ll_low_alloc.init.util.force = 1;
+ init.ll_low_alloc.init.util.low_mem = 1;
+
+ set_au_allocator(ERTS_ALC_A_SBMBC_LOW, &init.sbmbc_low_alloc);
+ set_au_allocator(ERTS_ALC_A_STANDARD_LOW, &init.std_low_alloc);
+ set_au_allocator(ERTS_ALC_A_LONG_LIVED_LOW, &init.ll_low_alloc);
#endif /* HALFWORD */
set_au_allocator(ERTS_ALC_A_TEMPORARY, &init.temp_alloc);
+ set_au_allocator(ERTS_ALC_A_SBMBC, &init.sbmbc_alloc);
set_au_allocator(ERTS_ALC_A_SHORT_LIVED, &init.sl_alloc);
set_au_allocator(ERTS_ALC_A_STANDARD, &init.std_alloc);
set_au_allocator(ERTS_ALC_A_LONG_LIVED, &init.ll_alloc);
@@ -593,6 +648,20 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
erts_mtrace_init(init.instr.mtrace, init.instr.nodename);
+ /* sbmbc_alloc() needs to be started first */
+ start_au_allocator(ERTS_ALC_A_SBMBC,
+ &init.sbmbc_alloc,
+ &sbmbc_alloc_state);
+#if HALFWORD_HEAP
+ start_au_allocator(ERTS_ALC_A_SBMBC_LOW,
+ &init.sbmbc_low_alloc,
+ &sbmbc_low_alloc_state);
+ erts_have_sbmbc_alloc = (init.sbmbc_alloc.enable
+ && init.sbmbc_low_alloc.enable);
+#else
+ erts_have_sbmbc_alloc = init.sbmbc_alloc.enable;
+#endif
+
start_au_allocator(ERTS_ALC_A_TEMPORARY,
&init.temp_alloc,
&temp_alloc_state);
@@ -610,11 +679,11 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
&ll_alloc_state);
#if HALFWORD_HEAP
start_au_allocator(ERTS_ALC_A_LONG_LIVED_LOW,
- &init.ll_alloc_low,
- &ll_alloc_low_state);
+ &init.ll_low_alloc,
+ &ll_low_alloc_state);
start_au_allocator(ERTS_ALC_A_STANDARD_LOW,
- &init.std_alloc_low,
- &std_alloc_low_state);
+ &init.std_low_alloc,
+ &std_low_alloc_state);
#endif
start_au_allocator(ERTS_ALC_A_EHEAP,
&init.eheap_alloc,
@@ -680,14 +749,11 @@ set_au_allocator(ErtsAlcType_t alctr_n, struct au_init *init)
ErtsAllocatorInfo_t *ai = &erts_allctrs_info[alctr_n];
ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[alctr_n];
-#if HALFWORD_HEAP
- /* If halfword heap, silently ignore any disabling of internal
- * allocators for low memory
+ /*
+ * Some allocators are forced on if halfword heap is used.
*/
- if (init->init.util.low_mem) {
+ if (init->init.util.force)
init->enable = 1;
- }
-#endif
if (!init->enable) {
af->alloc = erts_sys_alloc;
@@ -947,6 +1013,20 @@ get_kb_value(char *param_end, char** argv, int* ip)
}
static Uint
+get_byte_value(char *param_end, char** argv, int* ip)
+{
+ Sint tmp;
+ char *rest;
+ char *param = argv[*ip]+1;
+ char *value = get_value(param_end, argv, ip);
+ errno = 0;
+ tmp = (Sint) strtol(value, &rest, 10);
+ if (errno != 0 || rest == value || tmp < 0)
+ bad_value(param, param_end, value);
+ return (Uint) tmp;
+}
+
+static Uint
get_amount_value(char *param_end, char** argv, int* ip)
{
Sint tmp;
@@ -1085,6 +1165,12 @@ handle_au_arg(struct au_init *auip,
if(has_prefix("sbct", sub_param)) {
auip->init.util.sbct = get_kb_value(sub_param + 4, argv, ip);
}
+ else if (has_prefix("sbmbcs", sub_param)) {
+ auip->init.util.sbmbcs = get_byte_value(sub_param + 6, argv, ip);
+ }
+ else if (has_prefix("sbmbct", sub_param)) {
+ auip->init.util.sbmbct = get_byte_value(sub_param + 6, argv, ip);
+ }
else if (has_prefix("smbcs", sub_param)) {
auip->default_.smbcs = 0;
auip->init.util.smbcs = get_kb_value(sub_param + 5, argv, ip);
@@ -1123,6 +1209,7 @@ static void
handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
{
struct au_init *aui[] = {
+ &init->sbmbc_alloc,
&init->binary_alloc,
&init->std_alloc,
&init->ets_alloc,
@@ -1150,6 +1237,9 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
case 'B':
handle_au_arg(&init->binary_alloc, &argv[i][3], argv, &i);
break;
+ case 'C':
+ handle_au_arg(&init->sbmbc_alloc, &argv[i][3], argv, &i);
+ break;
case 'D':
handle_au_arg(&init->std_alloc, &argv[i][3], argv, &i);
break;
@@ -1856,12 +1946,16 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
return am_badarg;
}
- /* All alloc_util allocators *have* to be enabled */
+ /* All alloc_util allocators except sbmbc_alloc *have* to be enabled */
for (ai = ERTS_ALC_A_MIN; ai <= ERTS_ALC_A_MAX; ai++) {
switch (ai) {
case ERTS_ALC_A_SYSTEM:
case ERTS_ALC_A_FIXED_SIZE:
+ case ERTS_ALC_A_SBMBC:
+#if HALFWORD_HEAP
+ case ERTS_ALC_A_SBMBC_LOW:
+#endif
break;
default:
if (!erts_allctrs_info[ai].enabled
@@ -1901,6 +1995,12 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
* Often not thread safe and usually never
* contain any allocated memory.
*/
+ case ERTS_ALC_A_SBMBC:
+ /* Included in other allocators */
+#if HALFWORD_HEAP
+ case ERTS_ALC_A_SBMBC_LOW:
+ /* Included in other allocators */
+#endif
continue;
case ERTS_ALC_A_EHEAP:
save = &size.processes;
@@ -2925,6 +3025,7 @@ unsigned long erts_alc_test(unsigned long op,
init.atype = GOODFIT;
init.init.util.name_prefix = (char *) a1;
init.init.util.ts = a2 ? 1 : 0;
+ init.init.util.sbmbct = 0;
if ((char **) a3) {
char **argv = (char **) a3;
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index c6cc0e1fac..eda0831441 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -65,6 +65,11 @@
allocator SYSTEM true sys_alloc
+allocator SBMBC true sbmbc_alloc
++if halfword
+allocator SBMBC_LOW true sbmbc_low_alloc
++endif
+
+if smp
allocator TEMPORARY true temp_alloc
@@ -76,8 +81,8 @@ allocator ETS true ets_alloc
allocator FIXED_SIZE true fix_alloc
+if halfword
-allocator LONG_LIVED_LOW true ll_alloc_low
-allocator STANDARD_LOW true std_alloc_low
+allocator LONG_LIVED_LOW true ll_low_alloc
+allocator STANDARD_LOW true std_low_alloc
+endif
+else # Non smp build
@@ -91,8 +96,8 @@ allocator ETS false ets_alloc
allocator FIXED_SIZE false fix_alloc
+if halfword
-allocator LONG_LIVED_LOW false ll_alloc_low
-allocator STANDARD_LOW false std_alloc_low
+allocator LONG_LIVED_LOW false ll_low_alloc
+allocator STANDARD_LOW false std_low_alloc
+endif
+endif
@@ -134,6 +139,7 @@ class SYSTEM system_data
#
# <TYPE> <ALLOCATOR> <CLASS> <DESCRIPTION>
+type SBMBC SBMBC SYSTEM small_block_mbc
type PROC FIXED_SIZE PROCESSES proc
type ATOM FIXED_SIZE ATOM atom_entry
type MODULE FIXED_SIZE CODE module_entry
@@ -330,6 +336,7 @@ type SSB SHORT_LIVED PROCESSES ssb
+if halfword
+type SBMBC_LOW SBMBC_LOW SYSTEM small_block_mbc_low
type DDLL_PROCESS STANDARD_LOW SYSTEM ddll_processes
type MONITOR_LH STANDARD_LOW PROCESSES monitor_lh
type NLINK_LH STANDARD_LOW PROCESSES nlink_lh
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index cc04ef65bf..19c552d8cd 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -66,6 +66,7 @@
static int atoms_initialized = 0;
static int initialized = 0;
+int erts_have_sbmbc_alloc;
#if HAVE_ERTS_MSEG
@@ -85,8 +86,6 @@ static int initialized = 0;
#undef ASSERT
#define ASSERT ASSERT_EXPR
-#define ERTS_ALCU_FLG_FAIL_REALLOC_MOVE ((UWord) 1)
-
#if 0
/* Can be useful for debugging */
#define MBC_REALLOC_ALWAYS_MOVES
@@ -275,14 +274,26 @@ static void check_blk_carrier(Allctr_t *, Block_t *);
#ifdef DEBUG
#define DEBUG_CHECK_CARRIER_NO_SZ(AP) \
- ASSERT(((AP)->sbcs.curr_mseg.no && (AP)->sbcs.curr_mseg.size) \
- || (!(AP)->sbcs.curr_mseg.no && !(AP)->sbcs.curr_mseg.size));\
- ASSERT(((AP)->sbcs.curr_sys_alloc.no && (AP)->sbcs.curr_sys_alloc.size)\
- || (!(AP)->sbcs.curr_sys_alloc.no && !(AP)->sbcs.curr_sys_alloc.size));\
- ASSERT(((AP)->mbcs.curr_mseg.no && (AP)->mbcs.curr_mseg.size) \
- || (!(AP)->mbcs.curr_mseg.no && !(AP)->mbcs.curr_mseg.size));\
- ASSERT(((AP)->mbcs.curr_sys_alloc.no && (AP)->mbcs.curr_sys_alloc.size)\
- || (!(AP)->mbcs.curr_sys_alloc.no && !(AP)->mbcs.curr_sys_alloc.size))
+ ASSERT(((AP)->sbcs.curr.norm.mseg.no \
+ && (AP)->sbcs.curr.norm.mseg.size) \
+ || (!(AP)->sbcs.curr.norm.mseg.no \
+ && !(AP)->sbcs.curr.norm.mseg.size)); \
+ ASSERT(((AP)->sbcs.curr.norm.sys_alloc.no \
+ && (AP)->sbcs.curr.norm.sys_alloc.size) \
+ || (!(AP)->sbcs.curr.norm.sys_alloc.no \
+ && !(AP)->sbcs.curr.norm.sys_alloc.size)); \
+ ASSERT(((AP)->mbcs.curr.norm.mseg.no \
+ && (AP)->mbcs.curr.norm.mseg.size) \
+ || (!(AP)->mbcs.curr.norm.mseg.no \
+ && !(AP)->mbcs.curr.norm.mseg.size)); \
+ ASSERT(((AP)->mbcs.curr.norm.sys_alloc.no \
+ && (AP)->mbcs.curr.norm.sys_alloc.size) \
+ || (!(AP)->mbcs.curr.norm.sys_alloc.no \
+ && !(AP)->mbcs.curr.norm.sys_alloc.size)); \
+ ASSERT(((AP)->sbmbcs.curr.small_block.no \
+ && (AP)->sbmbcs.curr.small_block.size) \
+ || (!(AP)->sbmbcs.curr.small_block.no \
+ && !(AP)->sbmbcs.curr.small_block.size))
#else
#define DEBUG_CHECK_CARRIER_NO_SZ(AP)
@@ -292,27 +303,27 @@ static void check_blk_carrier(Allctr_t *, Block_t *);
(AP)->sbcs.blocks.curr.size += (BSZ); \
if ((AP)->sbcs.blocks.max.size < (AP)->sbcs.blocks.curr.size) \
(AP)->sbcs.blocks.max.size = (AP)->sbcs.blocks.curr.size; \
- if ((AP)->sbcs.max.no < ((AP)->sbcs.curr_mseg.no \
- + (AP)->sbcs.curr_sys_alloc.no)) \
- (AP)->sbcs.max.no = ((AP)->sbcs.curr_mseg.no \
- + (AP)->sbcs.curr_sys_alloc.no); \
- if ((AP)->sbcs.max.size < ((AP)->sbcs.curr_mseg.size \
- + (AP)->sbcs.curr_sys_alloc.size)) \
- (AP)->sbcs.max.size = ((AP)->sbcs.curr_mseg.size \
- + (AP)->sbcs.curr_sys_alloc.size)
+ if ((AP)->sbcs.max.no < ((AP)->sbcs.curr.norm.mseg.no \
+ + (AP)->sbcs.curr.norm.sys_alloc.no)) \
+ (AP)->sbcs.max.no = ((AP)->sbcs.curr.norm.mseg.no \
+ + (AP)->sbcs.curr.norm.sys_alloc.no); \
+ if ((AP)->sbcs.max.size < ((AP)->sbcs.curr.norm.mseg.size \
+ + (AP)->sbcs.curr.norm.sys_alloc.size)) \
+ (AP)->sbcs.max.size = ((AP)->sbcs.curr.norm.mseg.size \
+ + (AP)->sbcs.curr.norm.sys_alloc.size)
#define STAT_MSEG_SBC_ALLOC(AP, CSZ, BSZ) \
do { \
- (AP)->sbcs.curr_mseg.no++; \
- (AP)->sbcs.curr_mseg.size += (CSZ); \
+ (AP)->sbcs.curr.norm.mseg.no++; \
+ (AP)->sbcs.curr.norm.mseg.size += (CSZ); \
STAT_SBC_ALLOC((AP), (BSZ)); \
DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
} while (0)
#define STAT_SYS_ALLOC_SBC_ALLOC(AP, CSZ, BSZ) \
do { \
- (AP)->sbcs.curr_sys_alloc.no++; \
- (AP)->sbcs.curr_sys_alloc.size += (CSZ); \
+ (AP)->sbcs.curr.norm.sys_alloc.no++; \
+ (AP)->sbcs.curr.norm.sys_alloc.size += (CSZ); \
STAT_SBC_ALLOC((AP), (BSZ)); \
DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
} while (0)
@@ -324,85 +335,111 @@ do { \
#define STAT_MSEG_SBC_FREE(AP, CSZ, BSZ) \
do { \
- ASSERT((AP)->sbcs.curr_mseg.no > 0); \
- (AP)->sbcs.curr_mseg.no--; \
- ASSERT((AP)->sbcs.curr_mseg.size >= (CSZ)); \
- (AP)->sbcs.curr_mseg.size -= (CSZ); \
+ ASSERT((AP)->sbcs.curr.norm.mseg.no > 0); \
+ (AP)->sbcs.curr.norm.mseg.no--; \
+ ASSERT((AP)->sbcs.curr.norm.mseg.size >= (CSZ)); \
+ (AP)->sbcs.curr.norm.mseg.size -= (CSZ); \
STAT_SBC_FREE((AP), (BSZ)); \
DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
} while (0)
#define STAT_SYS_ALLOC_SBC_FREE(AP, CSZ, BSZ) \
do { \
- ASSERT((AP)->sbcs.curr_sys_alloc.no > 0); \
- (AP)->sbcs.curr_sys_alloc.no--; \
- ASSERT((AP)->sbcs.curr_sys_alloc.size >= (CSZ)); \
- (AP)->sbcs.curr_sys_alloc.size -= (CSZ); \
+ ASSERT((AP)->sbcs.curr.norm.sys_alloc.no > 0); \
+ (AP)->sbcs.curr.norm.sys_alloc.no--; \
+ ASSERT((AP)->sbcs.curr.norm.sys_alloc.size >= (CSZ)); \
+ (AP)->sbcs.curr.norm.sys_alloc.size -= (CSZ); \
STAT_SBC_FREE((AP), (BSZ)); \
DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
} while (0)
#define STAT_MBC_ALLOC(AP) \
- if ((AP)->mbcs.max.no < ((AP)->mbcs.curr_mseg.no \
- + (AP)->mbcs.curr_sys_alloc.no)) \
- (AP)->mbcs.max.no = ((AP)->mbcs.curr_mseg.no \
- + (AP)->mbcs.curr_sys_alloc.no); \
- if ((AP)->mbcs.max.size < ((AP)->mbcs.curr_mseg.size \
- + (AP)->mbcs.curr_sys_alloc.size)) \
- (AP)->mbcs.max.size = ((AP)->mbcs.curr_mseg.size \
- + (AP)->mbcs.curr_sys_alloc.size)
+ if ((AP)->mbcs.max.no < ((AP)->mbcs.curr.norm.mseg.no \
+ + (AP)->mbcs.curr.norm.sys_alloc.no)) \
+ (AP)->mbcs.max.no = ((AP)->mbcs.curr.norm.mseg.no \
+ + (AP)->mbcs.curr.norm.sys_alloc.no); \
+ if ((AP)->mbcs.max.size < ((AP)->mbcs.curr.norm.mseg.size \
+ + (AP)->mbcs.curr.norm.sys_alloc.size)) \
+ (AP)->mbcs.max.size = ((AP)->mbcs.curr.norm.mseg.size \
+ + (AP)->mbcs.curr.norm.sys_alloc.size)
+
+#define STAT_SBMBC_ALLOC(AP, CSZ) \
+do { \
+ (AP)->sbmbcs.curr.small_block.no++; \
+ (AP)->sbmbcs.curr.small_block.size += (CSZ); \
+ if ((AP)->sbmbcs.max.no < (AP)->sbmbcs.curr.small_block.no) \
+ (AP)->sbmbcs.max.no = (AP)->sbmbcs.curr.small_block.no; \
+ if ((AP)->sbmbcs.max.size < (AP)->sbmbcs.curr.small_block.size) \
+ (AP)->sbmbcs.max.size = (AP)->sbmbcs.curr.small_block.size; \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+} while (0)
#define STAT_MSEG_MBC_ALLOC(AP, CSZ) \
do { \
- (AP)->mbcs.curr_mseg.no++; \
- (AP)->mbcs.curr_mseg.size += (CSZ); \
+ (AP)->mbcs.curr.norm.mseg.no++; \
+ (AP)->mbcs.curr.norm.mseg.size += (CSZ); \
STAT_MBC_ALLOC((AP)); \
DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
} while (0)
#define STAT_SYS_ALLOC_MBC_ALLOC(AP, CSZ) \
do { \
- (AP)->mbcs.curr_sys_alloc.no++; \
- (AP)->mbcs.curr_sys_alloc.size += (CSZ); \
+ (AP)->mbcs.curr.norm.sys_alloc.no++; \
+ (AP)->mbcs.curr.norm.sys_alloc.size += (CSZ); \
STAT_MBC_ALLOC((AP)); \
DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
} while (0)
+#define STAT_SBMBC_FREE(AP, CSZ) \
+do { \
+ ASSERT((AP)->sbmbcs.curr.small_block.no > 0); \
+ (AP)->sbmbcs.curr.small_block.no--; \
+ ASSERT((AP)->sbmbcs.curr.small_block.size >= (CSZ)); \
+ (AP)->sbmbcs.curr.small_block.size -= (CSZ); \
+ DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
+} while (0)
+
#define STAT_MSEG_MBC_FREE(AP, CSZ) \
do { \
- ASSERT((AP)->mbcs.curr_mseg.no > 0); \
- (AP)->mbcs.curr_mseg.no--; \
- ASSERT((AP)->mbcs.curr_mseg.size >= (CSZ)); \
- (AP)->mbcs.curr_mseg.size -= (CSZ); \
+ ASSERT((AP)->mbcs.curr.norm.mseg.no > 0); \
+ (AP)->mbcs.curr.norm.mseg.no--; \
+ ASSERT((AP)->mbcs.curr.norm.mseg.size >= (CSZ)); \
+ (AP)->mbcs.curr.norm.mseg.size -= (CSZ); \
DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
} while (0)
#define STAT_SYS_ALLOC_MBC_FREE(AP, CSZ) \
do { \
- ASSERT((AP)->mbcs.curr_sys_alloc.no > 0); \
- (AP)->mbcs.curr_sys_alloc.no--; \
- ASSERT((AP)->mbcs.curr_sys_alloc.size >= (CSZ)); \
- (AP)->mbcs.curr_sys_alloc.size -= (CSZ); \
+ ASSERT((AP)->mbcs.curr.norm.sys_alloc.no > 0); \
+ (AP)->mbcs.curr.norm.sys_alloc.no--; \
+ ASSERT((AP)->mbcs.curr.norm.sys_alloc.size >= (CSZ)); \
+ (AP)->mbcs.curr.norm.sys_alloc.size -= (CSZ); \
DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
} while (0)
-#define STAT_MBC_BLK_ALLOC(AP, BSZ) \
+#define STAT_MBC_BLK_ALLOC(AP, BSZ, FLGS) \
do { \
- (AP)->mbcs.blocks.curr.no++; \
- if ((AP)->mbcs.blocks.max.no < (AP)->mbcs.blocks.curr.no) \
- (AP)->mbcs.blocks.max.no = (AP)->mbcs.blocks.curr.no; \
- (AP)->mbcs.blocks.curr.size += (BSZ); \
- if ((AP)->mbcs.blocks.max.size < (AP)->mbcs.blocks.curr.size) \
- (AP)->mbcs.blocks.max.size = (AP)->mbcs.blocks.curr.size; \
+ CarriersStats_t *cstats__ = (((FLGS) & ERTS_ALCU_FLG_SBMBC) \
+ ? &(AP)->sbmbcs \
+ : &(AP)->mbcs); \
+ cstats__->blocks.curr.no++; \
+ if (cstats__->blocks.max.no < cstats__->blocks.curr.no) \
+ cstats__->blocks.max.no = cstats__->blocks.curr.no; \
+ cstats__->blocks.curr.size += (BSZ); \
+ if (cstats__->blocks.max.size < cstats__->blocks.curr.size) \
+ cstats__->blocks.max.size = cstats__->blocks.curr.size; \
} while (0)
-#define STAT_MBC_BLK_FREE(AP, BSZ) \
+#define STAT_MBC_BLK_FREE(AP, BSZ, FLGS) \
do { \
- ASSERT((AP)->mbcs.blocks.curr.no > 0); \
- (AP)->mbcs.blocks.curr.no--; \
- ASSERT((AP)->mbcs.blocks.curr.size >= (BSZ)); \
- (AP)->mbcs.blocks.curr.size -= (BSZ); \
+ CarriersStats_t *cstats__ = (((FLGS) & ERTS_ALCU_FLG_SBMBC) \
+ ? &(AP)->sbmbcs \
+ : &(AP)->mbcs); \
+ ASSERT(cstats__->blocks.curr.no > 0); \
+ cstats__->blocks.curr.no--; \
+ ASSERT(cstats__->blocks.curr.size >= (BSZ)); \
+ cstats__->blocks.curr.size -= (BSZ); \
} while (0)
/* Debug stuff... */
@@ -410,7 +447,7 @@ do { \
static UWord carrier_alignment;
#define DEBUG_SAVE_ALIGNMENT(C) \
do { \
- UWord algnmnt__ = sizeof(Unit_t) - (((UWord) (C)) % sizeof(Unit_t)); \
+ UWord algnmnt__ = sizeof(Unit_t) - (((UWord) (C)) % sizeof(Unit_t));\
carrier_alignment = MIN(carrier_alignment, algnmnt__); \
ASSERT(((UWord) (C)) % sizeof(UWord) == 0); \
} while (0)
@@ -524,8 +561,8 @@ static Uint
get_next_mbc_size(Allctr_t *allctr)
{
Uint size;
- int cs = (allctr->mbcs.curr_mseg.no
- + allctr->mbcs.curr_sys_alloc.no
+ int cs = (allctr->mbcs.curr.norm.mseg.no
+ + allctr->mbcs.curr.norm.sys_alloc.no
- (allctr->main_carrier ? 1 : 0));
ASSERT(cs >= 0);
@@ -609,7 +646,8 @@ unlink_carrier(CarrierList_t *cl, Carrier_t *crr)
}
}
-
+static Block_t *create_sbmbc(Allctr_t *allctr, Uint umem_sz);
+static void destroy_sbmbc(Allctr_t *allctr, Block_t *blk);
static Block_t *create_carrier(Allctr_t *, Uint, UWord);
static void destroy_carrier(Allctr_t *, Block_t *);
@@ -619,39 +657,57 @@ static void destroy_carrier(Allctr_t *, Block_t *);
* block in a sbc.
*/
static ERTS_INLINE void *
-mbc_alloc_block(Allctr_t *allctr, Uint size, Uint *blk_szp)
+mbc_alloc_block(Allctr_t *allctr, Uint size, Uint *blk_szp, Uint32 *alcu_flgsp)
{
Block_t *blk;
+ Uint get_blk_sz;
+ Uint sbmbct;
ASSERT(size);
ASSERT(size < allctr->sbc_threshold);
- *blk_szp = UMEMSZ2BLKSZ(allctr, size);
+ *blk_szp = get_blk_sz = UMEMSZ2BLKSZ(allctr, size);
+
+ sbmbct = allctr->sbmbc_threshold;
+ if (sbmbct) {
+ if (get_blk_sz < sbmbct) {
+ *alcu_flgsp |= ERTS_ALCU_FLG_SBMBC;
+ if (get_blk_sz + allctr->min_block_size > sbmbct) {
+ /* Since we use block size to determine if blocks are
+ located in sbmbc or not... */
+ get_blk_sz += allctr->min_block_size;
+ }
+ }
+ }
- blk = (*allctr->get_free_block)(allctr, *blk_szp, NULL, 0);
+ blk = (*allctr->get_free_block)(allctr, get_blk_sz, NULL, 0, *alcu_flgsp);
-#if HALFWORD_HEAP
if (!blk) {
- blk = create_carrier(allctr, *blk_szp, CFLG_MBC|CFLG_FORCE_MSEG);
- }
+ if ((*alcu_flgsp) & ERTS_ALCU_FLG_SBMBC)
+ blk = create_sbmbc(allctr, get_blk_sz);
+ else {
+#if HALFWORD_HEAP
+ blk = create_carrier(allctr, get_blk_sz, CFLG_MBC|CFLG_FORCE_MSEG);
#else
- if (!blk) {
- blk = create_carrier(allctr, *blk_szp, CFLG_MBC);
- if (!blk) {
- /* Emergency! We couldn't create the carrier as we wanted.
- Try to place it in a sys_alloced sbc. */
- blk = create_carrier(allctr,
- size,
- CFLG_SBC|CFLG_FORCE_SIZE|CFLG_FORCE_SYS_ALLOC);
+ blk = create_carrier(allctr, get_blk_sz, CFLG_MBC);
+ if (!blk) {
+ /* Emergency! We couldn't create the carrier as we wanted.
+ Try to place it in a sys_alloced sbc. */
+ blk = create_carrier(allctr,
+ size,
+ (CFLG_SBC
+ | CFLG_FORCE_SIZE
+ | CFLG_FORCE_SYS_ALLOC));
+ }
+#endif
}
}
-#endif
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
if (IS_MBC_BLK(blk)) {
- (*allctr->link_free_block)(allctr, blk);
+ (*allctr->link_free_block)(allctr, blk, *alcu_flgsp);
HARD_CHECK_BLK_CARRIER(allctr, blk);
- (*allctr->unlink_free_block)(allctr, blk);
+ (*allctr->unlink_free_block)(allctr, blk, *alcu_flgsp);
}
#endif
@@ -664,7 +720,8 @@ mbc_alloc_finalize(Allctr_t *allctr,
Uint org_blk_sz,
UWord flags,
Uint want_blk_sz,
- int valid_blk_info)
+ int valid_blk_info,
+ Uint32 alcu_flgs)
{
Uint blk_sz;
Uint nxt_blk_sz;
@@ -700,7 +757,7 @@ mbc_alloc_finalize(Allctr_t *allctr,
SET_PREV_BLK_FREE(nxt_nxt_blk);
}
}
- (*allctr->link_free_block)(allctr, nxt_blk);
+ (*allctr->link_free_block)(allctr, nxt_blk, alcu_flgs);
ASSERT(IS_NOT_LAST_BLK(blk));
ASSERT(IS_FREE_BLK(nxt_blk));
@@ -741,7 +798,7 @@ mbc_alloc_finalize(Allctr_t *allctr,
: IS_NOT_LAST_BLK(blk));
}
- STAT_MBC_BLK_ALLOC(allctr, blk_sz);
+ STAT_MBC_BLK_ALLOC(allctr, blk_sz, alcu_flgs);
ASSERT(IS_ALLOCED_BLK(blk));
ASSERT(blk_sz == BLK_SZ(blk));
@@ -761,7 +818,8 @@ mbc_alloc(Allctr_t *allctr, Uint size)
{
Block_t *blk;
Uint blk_sz;
- blk = mbc_alloc_block(allctr, size, &blk_sz);
+ Uint32 alcu_flgs = 0;
+ blk = mbc_alloc_block(allctr, size, &blk_sz, &alcu_flgs);
if (!blk)
return NULL;
if (IS_MBC_BLK(blk))
@@ -770,7 +828,8 @@ mbc_alloc(Allctr_t *allctr, Uint size)
BLK_SZ(blk),
GET_BLK_HDR_FLGS(blk),
blk_sz,
- 1);
+ 1,
+ alcu_flgs);
return BLK2UMEM(blk);
}
@@ -779,6 +838,7 @@ mbc_free(Allctr_t *allctr, void *p)
{
Uint is_first_blk;
Uint is_last_blk;
+ Uint32 alcu_flgs = 0;
Uint blk_sz;
Block_t *blk;
Block_t *nxt_blk;
@@ -788,13 +848,15 @@ mbc_free(Allctr_t *allctr, void *p)
blk = UMEM2BLK(p);
blk_sz = BLK_SZ(blk);
+ if (blk_sz < allctr->sbmbc_threshold)
+ alcu_flgs |= ERTS_ALCU_FLG_SBMBC;
ASSERT(IS_MBC_BLK(blk));
ASSERT(blk_sz >= allctr->min_block_size);
HARD_CHECK_BLK_CARRIER(allctr, blk);
- STAT_MBC_BLK_FREE(allctr, blk_sz);
+ STAT_MBC_BLK_FREE(allctr, blk_sz, alcu_flgs);
is_first_blk = IS_FIRST_BLK(blk);
is_last_blk = IS_LAST_BLK(blk);
@@ -802,7 +864,7 @@ mbc_free(Allctr_t *allctr, void *p)
if (!is_first_blk && IS_PREV_BLK_FREE(blk)) {
/* Coalesce with previous block... */
blk = PREV_BLK(blk);
- (*allctr->unlink_free_block)(allctr, blk);
+ (*allctr->unlink_free_block)(allctr, blk, alcu_flgs);
blk_sz += BLK_SZ(blk);
is_first_blk = IS_FIRST_BLK(blk);
@@ -818,7 +880,7 @@ mbc_free(Allctr_t *allctr, void *p)
nxt_blk = NXT_BLK(blk);
if (IS_FREE_BLK(nxt_blk)) {
/* Coalesce with next block... */
- (*allctr->unlink_free_block)(allctr, nxt_blk);
+ (*allctr->unlink_free_block)(allctr, nxt_blk, alcu_flgs);
blk_sz += BLK_SZ(nxt_blk);
SET_BLK_SZ(blk, blk_sz);
@@ -850,16 +912,20 @@ mbc_free(Allctr_t *allctr, void *p)
if (is_first_blk
&& is_last_blk
- && allctr->main_carrier != FBLK2MBC(allctr, blk))
- destroy_carrier(allctr, blk);
+ && allctr->main_carrier != FBLK2MBC(allctr, blk)) {
+ if (alcu_flgs & ERTS_ALCU_FLG_SBMBC)
+ destroy_sbmbc(allctr, blk);
+ else
+ destroy_carrier(allctr, blk);
+ }
else {
- (*allctr->link_free_block)(allctr, blk);
+ (*allctr->link_free_block)(allctr, blk, alcu_flgs);
HARD_CHECK_BLK_CARRIER(allctr, blk);
}
}
static void *
-mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
+mbc_realloc(Allctr_t *allctr, void *p, Uint size, Uint32 alcu_flgs)
{
void *new_p;
Uint old_blk_sz;
@@ -867,7 +933,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
#ifndef MBC_REALLOC_ALWAYS_MOVES
Block_t *new_blk, *cand_blk;
Uint cand_blk_sz;
- Uint blk_sz;
+ Uint blk_sz, get_blk_sz;
Block_t *nxt_blk;
Uint nxt_blk_sz;
Uint is_last_blk;
@@ -883,10 +949,16 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
ASSERT(old_blk_sz >= allctr->min_block_size);
#ifdef MBC_REALLOC_ALWAYS_MOVES
- if (flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
+ if (alcu_flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
return NULL;
#else /* !MBC_REALLOC_ALWAYS_MOVES */
- blk_sz = UMEMSZ2BLKSZ(allctr, size);
+ get_blk_sz = blk_sz = UMEMSZ2BLKSZ(allctr, size);
+ if ((alcu_flgs & ERTS_ALCU_FLG_SBMBC)
+ && (blk_sz + allctr->min_block_size > allctr->sbmbc_threshold)) {
+ /* Since we use block size to determine if blocks are
+ located in sbmbc or not... */
+ get_blk_sz = blk_sz + allctr->min_block_size;
+ }
ASSERT(IS_ALLOCED_BLK(blk));
ASSERT(IS_MBC_BLK(blk));
@@ -901,6 +973,9 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
Uint diff_sz_val = old_blk_sz - blk_sz;
Uint old_blk_sz_val = old_blk_sz;
+ if (get_blk_sz >= old_blk_sz)
+ return p;
+
if (diff_sz_val >= (~((Uint) 0) / 100)) {
/* div both by 128 */
old_blk_sz_val >>= 7;
@@ -909,7 +984,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
/* Avoid fragmentation by moving the block if it is shrunk much */
if (100*diff_sz_val > allctr->mbc_move_threshold*old_blk_sz_val) {
- if (flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
+ if (alcu_flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
return NULL;
cand_blk_sz = old_blk_sz;
@@ -926,9 +1001,10 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
}
new_blk = (*allctr->get_free_block)(allctr,
- blk_sz,
+ get_blk_sz,
cand_blk,
- cand_blk_sz);
+ cand_blk_sz,
+ alcu_flgs);
if (new_blk || cand_blk != blk)
goto move_into_new_blk;
@@ -952,8 +1028,8 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
nxt_blk_sz,
SBH_THIS_FREE|SBH_PREV_ALLOCED|SBH_NOT_LAST_BLK);
- STAT_MBC_BLK_FREE(allctr, old_blk_sz);
- STAT_MBC_BLK_ALLOC(allctr, blk_sz);
+ STAT_MBC_BLK_FREE(allctr, old_blk_sz, alcu_flgs);
+ STAT_MBC_BLK_ALLOC(allctr, blk_sz, alcu_flgs);
ASSERT(BLK_SZ(blk) >= allctr->min_block_size);
@@ -964,7 +1040,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
if (IS_FREE_BLK(nxt_nxt_blk)) {
/* Coalesce with next free block... */
nxt_blk_sz += BLK_SZ(nxt_nxt_blk);
- (*allctr->unlink_free_block)(allctr, nxt_nxt_blk);
+ (*allctr->unlink_free_block)(allctr, nxt_nxt_blk, alcu_flgs);
SET_BLK_SZ(nxt_blk, nxt_blk_sz);
is_last_blk = IS_LAST_BLK(nxt_nxt_blk);
@@ -979,7 +1055,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
}
}
- (*allctr->link_free_block)(allctr, nxt_blk);
+ (*allctr->link_free_block)(allctr, nxt_blk, alcu_flgs);
ASSERT(IS_ALLOCED_BLK(blk));
@@ -1009,12 +1085,12 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
if (!is_last_blk) {
nxt_blk = NXT_BLK(blk);
nxt_blk_sz = BLK_SZ(nxt_blk);
- if (IS_FREE_BLK(nxt_blk) && blk_sz <= old_blk_sz + nxt_blk_sz) {
+ if (IS_FREE_BLK(nxt_blk) && get_blk_sz <= old_blk_sz + nxt_blk_sz) {
/* Grow into next block... */
HARD_CHECK_BLK_CARRIER(allctr, blk);
- (*allctr->unlink_free_block)(allctr, nxt_blk);
+ (*allctr->unlink_free_block)(allctr, nxt_blk, alcu_flgs);
nxt_blk_sz -= blk_sz - old_blk_sz;
is_last_blk = IS_LAST_BLK(nxt_blk);
@@ -1051,13 +1127,13 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
else
SET_BLK_SZ_FTR(nxt_blk, nxt_blk_sz);
- (*allctr->link_free_block)(allctr, nxt_blk);
+ (*allctr->link_free_block)(allctr, nxt_blk, alcu_flgs);
ASSERT(IS_FREE_BLK(nxt_blk));
}
- STAT_MBC_BLK_FREE(allctr, old_blk_sz);
- STAT_MBC_BLK_ALLOC(allctr, blk_sz);
+ STAT_MBC_BLK_FREE(allctr, old_blk_sz, alcu_flgs);
+ STAT_MBC_BLK_ALLOC(allctr, blk_sz, alcu_flgs);
ASSERT(IS_ALLOCED_BLK(blk));
@@ -1088,7 +1164,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
}
}
- if (flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
+ if (alcu_flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
return NULL;
/* Need to grow in another block */
@@ -1108,7 +1184,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
}
}
- if (cand_blk_sz < blk_sz) {
+ if (cand_blk_sz < get_blk_sz) {
/* We wont fit in cand_blk get a new one */
#endif /* !MBC_REALLOC_ALWAYS_MOVES */
@@ -1127,9 +1203,10 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
/* We will at least fit in cand_blk */
new_blk = (*allctr->get_free_block)(allctr,
- blk_sz,
+ get_blk_sz,
cand_blk,
- cand_blk_sz);
+ cand_blk_sz,
+ alcu_flgs);
move_into_new_blk:
/*
* new_blk, and cand_blk have to be correctly set
@@ -1142,7 +1219,8 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
BLK_SZ(new_blk),
GET_BLK_HDR_FLGS(new_blk),
blk_sz,
- 1);
+ 1,
+ alcu_flgs);
new_p = BLK2UMEM(new_blk);
sys_memcpy(new_p, p, MIN(size, old_blk_sz - ABLK_HDR_SZ));
mbc_free(allctr, p);
@@ -1164,7 +1242,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
HARD_CHECK_BLK_CARRIER(allctr, blk);
- (*allctr->unlink_free_block)(allctr, new_blk); /* prev */
+ (*allctr->unlink_free_block)(allctr, new_blk, alcu_flgs); /* prev */
if (is_last_blk)
new_blk_flgs |= LAST_BLK_HDR_FLG;
@@ -1173,7 +1251,7 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
if (IS_FREE_BLK(nxt_blk)) {
new_blk_flgs |= GET_LAST_BLK_HDR_FLG(nxt_blk);
new_blk_sz += BLK_SZ(nxt_blk);
- (*allctr->unlink_free_block)(allctr, nxt_blk);
+ (*allctr->unlink_free_block)(allctr, nxt_blk, alcu_flgs);
}
}
@@ -1196,9 +1274,10 @@ mbc_realloc(Allctr_t *allctr, void *p, Uint size, UWord flgs)
new_blk_sz,
new_blk_flgs,
blk_sz,
- 0);
+ 0,
+ alcu_flgs);
- STAT_MBC_BLK_FREE(allctr, old_blk_sz);
+ STAT_MBC_BLK_FREE(allctr, old_blk_sz, alcu_flgs);
return new_p;
}
@@ -1243,6 +1322,100 @@ do { \
#define CHECK_1BLK_CARRIER(A, SBC, MSEGED, C, CSZ, B, BSZ)
#endif
+static Block_t *
+create_sbmbc(Allctr_t *allctr, Uint umem_sz)
+{
+ Block_t *blk;
+ Uint blk_sz;
+ Uint crr_sz = allctr->sbmbc_size;
+ Carrier_t *crr;
+
+#if HALFWORD_HEAP
+ if (allctr->mseg_opt.low_mem)
+ crr = erts_alloc(ERTS_ALC_T_SBMBC_LOW, crr_sz);
+ else
+#endif
+ crr = erts_alloc(ERTS_ALC_T_SBMBC, crr_sz);
+
+ INC_CC(allctr->calls.sbmbc_alloc);
+ SET_CARRIER_HDR(crr, crr_sz, SCH_SYS_ALLOC|SCH_MBC);
+
+ blk = MBC2FBLK(allctr, crr);
+
+#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
+ if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
+ crr_sz -= sizeof(UWord);
+#endif
+
+ blk_sz = UNIT_FLOOR(crr_sz - allctr->mbc_header_size);
+
+ SET_MBC_BLK_FTR(((UWord *) blk)[-1]);
+ SET_BLK_HDR(blk, blk_sz, SBH_THIS_FREE|SBH_PREV_FREE|SBH_LAST_BLK);
+
+#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
+ *((Carrier_t **) NXT_BLK(blk)) = crr;
+#endif
+
+ link_carrier(&allctr->sbmbc_list, crr);
+
+#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
+ if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
+ crr_sz += sizeof(UWord);
+#endif
+
+ STAT_SBMBC_ALLOC(allctr, crr_sz);
+ CHECK_1BLK_CARRIER(allctr, 0, 0, crr, crr_sz, blk, blk_sz);
+#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
+ if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
+ crr_sz -= sizeof(UWord);
+#endif
+ if (allctr->creating_mbc)
+ (*allctr->creating_mbc)(allctr, crr, ERTS_ALCU_FLG_SBMBC);
+
+ DEBUG_SAVE_ALIGNMENT(crr);
+ return blk;
+}
+
+static void
+destroy_sbmbc(Allctr_t *allctr, Block_t *blk)
+{
+ Uint crr_sz;
+ Carrier_t *crr;
+
+ ASSERT(IS_FIRST_BLK(blk));
+
+ ASSERT(IS_MBC_BLK(blk));
+
+ crr = FBLK2MBC(allctr, blk);
+ crr_sz = CARRIER_SZ(crr);
+
+#ifdef DEBUG
+ if (!allctr->stopped) {
+ ASSERT(IS_LAST_BLK(blk));
+
+#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
+ (*allctr->link_free_block)(allctr, blk, ERTS_ALCU_FLG_SBMBC);
+ HARD_CHECK_BLK_CARRIER(allctr, blk);
+ (*allctr->unlink_free_block)(allctr, blk, ERTS_ALCU_FLG_SBMBC);
+#endif
+ }
+#endif
+
+ STAT_SBMBC_FREE(allctr, crr_sz);
+
+ unlink_carrier(&allctr->sbmbc_list, crr);
+ if (allctr->destroying_mbc)
+ (*allctr->destroying_mbc)(allctr, crr, ERTS_ALCU_FLG_SBMBC);
+
+ INC_CC(allctr->calls.sbmbc_free);
+
+#if HALFWORD_HEAP
+ if (allctr->mseg_opt.low_mem)
+ erts_free(ERTS_ALC_T_SBMBC_LOW, crr);
+ else
+#endif
+ erts_free(ERTS_ALC_T_SBMBC, crr);
+}
static Block_t *
create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
@@ -1271,11 +1444,11 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
if (erts_mseg_no() >= max_mseg_carriers)
goto try_sys_alloc;
if (flags & CFLG_SBC) {
- if (allctr->sbcs.curr_mseg.no >= allctr->max_mseg_sbcs)
+ if (allctr->sbcs.curr.norm.mseg.no >= allctr->max_mseg_sbcs)
goto try_sys_alloc;
}
else {
- if (allctr->mbcs.curr_mseg.no >= allctr->max_mseg_mbcs)
+ if (allctr->mbcs.curr.norm.mseg.no >= allctr->max_mseg_mbcs)
goto try_sys_alloc;
}
@@ -1289,7 +1462,7 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
if (crr_sz < allctr->mbc_header_size + blk_sz)
crr_sz = allctr->mbc_header_size + blk_sz;
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- if (sizeof(Unit_t) == sizeof(UWord))
+ if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
crr_sz += sizeof(UWord);
#endif
}
@@ -1330,7 +1503,7 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
&& bcrr_sz < allctr->smallest_mbc_size)
bcrr_sz = allctr->smallest_mbc_size;
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- if (sizeof(Unit_t) == sizeof(UWord))
+ if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
bcrr_sz += sizeof(UWord);
#endif
@@ -1385,7 +1558,7 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
blk = MBC2FBLK(allctr, crr);
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- if (sizeof(Unit_t) == sizeof(UWord))
+ if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
crr_sz -= sizeof(UWord);
#endif
@@ -1406,16 +1579,16 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
link_carrier(&allctr->mbc_list, crr);
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- if (sizeof(Unit_t) == sizeof(UWord))
+ if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
crr_sz += sizeof(UWord);
#endif
CHECK_1BLK_CARRIER(allctr, 0, is_mseg, crr, crr_sz, blk, blk_sz);
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- if (sizeof(Unit_t) == sizeof(UWord))
+ if (allctr->mbc_header_size % sizeof(Unit_t) == 0)
crr_sz -= sizeof(UWord);
#endif
if (allctr->creating_mbc)
- (*allctr->creating_mbc)(allctr, crr);
+ (*allctr->creating_mbc)(allctr, crr, 0);
}
@@ -1595,9 +1768,9 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk)
ASSERT(IS_LAST_BLK(blk));
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
- (*allctr->link_free_block)(allctr, blk);
+ (*allctr->link_free_block)(allctr, blk, 0);
HARD_CHECK_BLK_CARRIER(allctr, blk);
- (*allctr->unlink_free_block)(allctr, blk);
+ (*allctr->unlink_free_block)(allctr, blk, 0);
#endif
}
#endif
@@ -1614,7 +1787,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk)
unlink_carrier(&allctr->mbc_list, crr);
if (allctr->destroying_mbc)
- (*allctr->destroying_mbc)(allctr, crr);
+ (*allctr->destroying_mbc)(allctr, crr, 0);
}
@@ -1658,12 +1831,15 @@ static struct {
Eterm lmbcs;
Eterm smbcs;
Eterm mbcgs;
+ Eterm sbmbcs;
+ Eterm sbmbct;
#if HAVE_ERTS_MSEG
Eterm mmc;
#endif
Eterm ycs;
+ /* Eterm sbmbcs; */
Eterm mbcs;
Eterm sbcs;
Eterm sys_alloc_carriers_size;
@@ -1688,6 +1864,8 @@ static struct {
Eterm mseg_dealloc;
Eterm mseg_realloc;
#endif
+ Eterm sbmbc_alloc;
+ Eterm sbmbc_free;
#ifdef DEBUG
Eterm end_of_atoms;
#endif
@@ -1746,12 +1924,15 @@ init_atoms(Allctr_t *allctr)
AM_INIT(lmbcs);
AM_INIT(smbcs);
AM_INIT(mbcgs);
+ AM_INIT(sbmbcs);
+ AM_INIT(sbmbct);
#if HAVE_ERTS_MSEG
AM_INIT(mmc);
#endif
AM_INIT(ycs);
+ /*AM_INIT(sbmbcs);*/
AM_INIT(mbcs);
AM_INIT(sbcs);
AM_INIT(sys_alloc_carriers_size);
@@ -1776,6 +1957,8 @@ init_atoms(Allctr_t *allctr)
AM_INIT(mseg_dealloc);
AM_INIT(mseg_realloc);
#endif
+ AM_INIT(sbmbc_free);
+ AM_INIT(sbmbc_alloc);
#ifdef DEBUG
for (atom = (Eterm *) &am; atom < &am.end_of_atoms; atom++) {
@@ -1869,7 +2052,9 @@ sz_info_carriers(Allctr_t *allctr,
Uint *szp)
{
Eterm res = THE_NON_VALUE;
- Uint curr_size = cs->curr_mseg.size + cs->curr_sys_alloc.size;
+ Uint curr_size = (cs == &allctr->sbmbcs
+ ? cs->curr.small_block.size
+ : cs->curr.norm.mseg.size + cs->curr.norm.sys_alloc.size);
if (print_to_p) {
int to = *print_to_p;
@@ -1917,8 +2102,17 @@ info_carriers(Allctr_t *allctr,
Uint *szp)
{
Eterm res = THE_NON_VALUE;
- Uint curr_no = cs->curr_mseg.no + cs->curr_sys_alloc.no;
- Uint curr_size = cs->curr_mseg.size + cs->curr_sys_alloc.size;
+ Uint curr_no, curr_size;
+ int small_block = cs == &allctr->sbmbcs;
+
+ if (small_block) {
+ curr_no = cs->curr.small_block.no;
+ curr_size = cs->curr.small_block.size;
+ }
+ else {
+ curr_no = cs->curr.norm.mseg.no + cs->curr.norm.sys_alloc.no;
+ curr_size = cs->curr.norm.mseg.size + cs->curr.norm.sys_alloc.size;
+ }
if (print_to_p) {
int to = *print_to_p;
@@ -1944,18 +2138,20 @@ info_carriers(Allctr_t *allctr,
curr_no,
cs->max.no,
cs->max_ever.no);
+ if (!small_block) {
#if HAVE_ERTS_MSEG
- erts_print(to,
- arg,
- "%smseg carriers: %bpu\n",
- prefix,
- cs->curr_mseg.no);
-#endif
- erts_print(to,
- arg,
- "%ssys_alloc carriers: %bpu\n",
- prefix,
- cs->curr_sys_alloc.no);
+ erts_print(to,
+ arg,
+ "%smseg carriers: %bpu\n",
+ prefix,
+ cs->curr.norm.mseg.no);
+#endif
+ erts_print(to,
+ arg,
+ "%ssys_alloc carriers: %bpu\n",
+ prefix,
+ cs->curr.norm.sys_alloc.no);
+ }
erts_print(to,
arg,
"%scarriers size: %beu %bpu %bpu\n",
@@ -1963,43 +2159,49 @@ info_carriers(Allctr_t *allctr,
curr_size,
cs->max.size,
cs->max_ever.size);
+ if (!small_block) {
#if HAVE_ERTS_MSEG
- erts_print(to,
- arg,
- "%smseg carriers size: %bpu\n",
- prefix,
- cs->curr_mseg.size);
-#endif
- erts_print(to,
- arg,
- "%ssys_alloc carriers size: %bpu\n",
- prefix,
- cs->curr_sys_alloc.size);
+ erts_print(to,
+ arg,
+ "%smseg carriers size: %bpu\n",
+ prefix,
+ cs->curr.norm.mseg.size);
+#endif
+ erts_print(to,
+ arg,
+ "%ssys_alloc carriers size: %bpu\n",
+ prefix,
+ cs->curr.norm.sys_alloc.size);
+ }
}
if (hpp || szp) {
res = NIL;
- add_2tup(hpp, szp, &res,
- am.sys_alloc_carriers_size,
- bld_unstable_uint(hpp, szp, cs->curr_sys_alloc.size));
+ if (!small_block) {
+ add_2tup(hpp, szp, &res,
+ am.sys_alloc_carriers_size,
+ bld_unstable_uint(hpp, szp, cs->curr.norm.sys_alloc.size));
#if HAVE_ERTS_MSEG
- add_2tup(hpp, szp, &res,
- am.mseg_alloc_carriers_size,
- bld_unstable_uint(hpp, szp, cs->curr_mseg.size));
+ add_2tup(hpp, szp, &res,
+ am.mseg_alloc_carriers_size,
+ bld_unstable_uint(hpp, szp, cs->curr.norm.mseg.size));
#endif
+ }
add_4tup(hpp, szp, &res,
am.carriers_size,
bld_unstable_uint(hpp, szp, curr_size),
bld_unstable_uint(hpp, szp, cs->max.size),
bld_unstable_uint(hpp, szp, cs->max_ever.size));
- add_2tup(hpp, szp, &res,
- am.sys_alloc_carriers,
- bld_unstable_uint(hpp, szp, cs->curr_sys_alloc.no));
+ if (!small_block) {
+ add_2tup(hpp, szp, &res,
+ am.sys_alloc_carriers,
+ bld_unstable_uint(hpp, szp, cs->curr.norm.sys_alloc.no));
#if HAVE_ERTS_MSEG
- add_2tup(hpp, szp, &res,
- am.mseg_alloc_carriers,
- bld_unstable_uint(hpp, szp, cs->curr_mseg.no));
+ add_2tup(hpp, szp, &res,
+ am.mseg_alloc_carriers,
+ bld_unstable_uint(hpp, szp, cs->curr.norm.mseg.no));
#endif
+ }
add_4tup(hpp, szp, &res,
am.carriers,
bld_unstable_uint(hpp, szp, curr_no),
@@ -2077,6 +2279,9 @@ info_calls(Allctr_t *allctr,
PRINT_CC_5(to, arg, prefix, "free", allctr->calls.this_free);
PRINT_CC_5(to, arg, prefix, "realloc", allctr->calls.this_realloc);
+ PRINT_CC_4(to, arg, "sbmbc_alloc", allctr->calls.sbmbc_alloc);
+ PRINT_CC_4(to, arg, "sbmbc_free", allctr->calls.sbmbc_free);
+
#if HAVE_ERTS_MSEG
PRINT_CC_4(to, arg, "mseg_alloc", allctr->calls.mseg_alloc);
PRINT_CC_4(to, arg, "mseg_dealloc", allctr->calls.mseg_dealloc);
@@ -2128,6 +2333,14 @@ info_calls(Allctr_t *allctr,
bld_unstable_uint(hpp, szp, allctr->calls.mseg_alloc.no));
#endif
add_3tup(hpp, szp, &res,
+ am.sbmbc_free,
+ bld_unstable_uint(hpp, szp, allctr->calls.sbmbc_free.giga_no),
+ bld_unstable_uint(hpp, szp, allctr->calls.sbmbc_free.no));
+ add_3tup(hpp, szp, &res,
+ am.sbmbc_alloc,
+ bld_unstable_uint(hpp, szp, allctr->calls.sbmbc_alloc.giga_no),
+ bld_unstable_uint(hpp, szp, allctr->calls.sbmbc_alloc.no));
+ add_3tup(hpp, szp, &res,
allctr->name.realloc,
bld_unstable_uint(hpp, szp, allctr->calls.this_realloc.giga_no),
bld_unstable_uint(hpp, szp, allctr->calls.this_realloc.no));
@@ -2191,7 +2404,9 @@ info_options(Allctr_t *allctr,
#endif
"option lmbcs: %beu\n"
"option smbcs: %beu\n"
- "option mbcgs: %beu\n",
+ "option mbcgs: %beu\n"
+ "option sbmbcs: %beu\n"
+ "option sbmbct: %beu\n",
topt,
allctr->ramv ? "true" : "false",
#if HALFWORD_HEAP
@@ -2211,7 +2426,9 @@ info_options(Allctr_t *allctr,
#endif
allctr->largest_mbc_size,
allctr->smallest_mbc_size,
- allctr->mbc_growth_stages);
+ allctr->mbc_growth_stages,
+ allctr->sbmbc_size,
+ allctr->sbmbc_threshold);
}
res = (*allctr->info_options)(allctr, "option ", print_to_p, print_to_arg,
@@ -2219,6 +2436,12 @@ info_options(Allctr_t *allctr,
if (hpp || szp) {
add_2tup(hpp, szp, &res,
+ am.sbmbct,
+ bld_uint(hpp, szp, allctr->sbmbc_threshold));
+ add_2tup(hpp, szp, &res,
+ am.sbmbcs,
+ bld_uint(hpp, szp, allctr->sbmbc_size));
+ add_2tup(hpp, szp, &res,
am.mbcgs,
bld_uint(hpp, szp, allctr->mbc_growth_stages));
add_2tup(hpp, szp, &res,
@@ -2285,10 +2508,10 @@ update_max_ever_values(CarriersStats_t *cs)
static ERTS_INLINE void
reset_max_values(CarriersStats_t *cs)
{
- cs->max.no = cs->curr_mseg.no + cs->curr_sys_alloc.no;
- cs->max.size = cs->curr_mseg.size + cs->curr_sys_alloc.size;
- cs->blocks.max.no = cs->blocks.curr.no;
- cs->blocks.max.size = cs->blocks.curr.size;
+ cs->max.no = cs->curr.norm.mseg.no + cs->curr.norm.sys_alloc.no;
+ cs->max.size = cs->curr.norm.mseg.size + cs->curr.norm.sys_alloc.size;
+ cs->blocks.max.no = cs->blocks.curr.no;
+ cs->blocks.max.size = cs->blocks.curr.size;
}
@@ -2367,7 +2590,7 @@ erts_alcu_sz_info(Allctr_t *allctr,
Uint **hpp,
Uint *szp)
{
- Eterm res, mbcs, sbcs;
+ Eterm res, sbmbcs, mbcs, sbcs;
res = THE_NON_VALUE;
@@ -2389,24 +2612,29 @@ erts_alcu_sz_info(Allctr_t *allctr,
/* Update sbc values not continously updated */
allctr->sbcs.blocks.curr.no
- = allctr->sbcs.curr_mseg.no + allctr->sbcs.curr_sys_alloc.no;
+ = allctr->sbcs.curr.norm.mseg.no + allctr->sbcs.curr.norm.sys_alloc.no;
allctr->sbcs.blocks.max.no = allctr->sbcs.max.no;
+ update_max_ever_values(&allctr->sbmbcs);
update_max_ever_values(&allctr->mbcs);
update_max_ever_values(&allctr->sbcs);
- mbcs = sz_info_carriers(allctr, &allctr->mbcs, "mbcs ", print_to_p,
- print_to_arg, hpp, szp);
- sbcs = sz_info_carriers(allctr, &allctr->sbcs, "sbcs ", print_to_p,
- print_to_arg, hpp, szp);
+ sbmbcs = sz_info_carriers(allctr, &allctr->sbmbcs, "sbmbcs ", print_to_p,
+ print_to_arg, hpp, szp);
+ mbcs = sz_info_carriers(allctr, &allctr->mbcs, "mbcs ", print_to_p,
+ print_to_arg, hpp, szp);
+ sbcs = sz_info_carriers(allctr, &allctr->sbcs, "sbcs ", print_to_p,
+ print_to_arg, hpp, szp);
if (hpp || szp) {
res = NIL;
add_2tup(hpp, szp, &res, am.sbcs, sbcs);
add_2tup(hpp, szp, &res, am.mbcs, mbcs);
+ add_2tup(hpp, szp, &res, am.sbmbcs, sbmbcs);
}
if (begin_max_period) {
+ reset_max_values(&allctr->sbmbcs);
reset_max_values(&allctr->mbcs);
reset_max_values(&allctr->sbcs);
}
@@ -2428,7 +2656,7 @@ erts_alcu_info(Allctr_t *allctr,
Uint **hpp,
Uint *szp)
{
- Eterm res, sett, mbcs, sbcs, calls;
+ Eterm res, sett, sbmbcs, mbcs, sbcs, calls;
res = THE_NON_VALUE;
@@ -2450,9 +2678,10 @@ erts_alcu_info(Allctr_t *allctr,
/* Update sbc values not continously updated */
allctr->sbcs.blocks.curr.no
- = allctr->sbcs.curr_mseg.no + allctr->sbcs.curr_sys_alloc.no;
+ = allctr->sbcs.curr.norm.mseg.no + allctr->sbcs.curr.norm.sys_alloc.no;
allctr->sbcs.blocks.max.no = allctr->sbcs.max.no;
+ update_max_ever_values(&allctr->sbmbcs);
update_max_ever_values(&allctr->mbcs);
update_max_ever_values(&allctr->sbcs);
@@ -2464,11 +2693,13 @@ erts_alcu_info(Allctr_t *allctr,
ERTS_ALCU_VSN_STR);
}
- sett = info_options(allctr, print_to_p, print_to_arg, hpp, szp);
- mbcs = info_carriers(allctr, &allctr->mbcs, "mbcs ", print_to_p,
- print_to_arg, hpp, szp);
- sbcs = info_carriers(allctr, &allctr->sbcs, "sbcs ", print_to_p,
- print_to_arg, hpp, szp);
+ sett = info_options(allctr, print_to_p, print_to_arg, hpp, szp);
+ sbmbcs = info_carriers(allctr, &allctr->sbmbcs, "sbmbcs ", print_to_p,
+ print_to_arg, hpp, szp);
+ mbcs = info_carriers(allctr, &allctr->mbcs, "mbcs ", print_to_p,
+ print_to_arg, hpp, szp);
+ sbcs = info_carriers(allctr, &allctr->sbcs, "sbcs ", print_to_p,
+ print_to_arg, hpp, szp);
calls = info_calls(allctr, print_to_p, print_to_arg, hpp, szp);
if (hpp || szp) {
@@ -2477,6 +2708,7 @@ erts_alcu_info(Allctr_t *allctr,
add_2tup(hpp, szp, &res, am.calls, calls);
add_2tup(hpp, szp, &res, am.sbcs, sbcs);
add_2tup(hpp, szp, &res, am.mbcs, mbcs);
+ add_2tup(hpp, szp, &res, am.sbmbcs, sbmbcs);
add_2tup(hpp, szp, &res, am.options, sett);
add_3tup(hpp, szp, &res,
am.versions,
@@ -2485,6 +2717,7 @@ erts_alcu_info(Allctr_t *allctr,
}
if (begin_max_period) {
+ reset_max_values(&allctr->sbmbcs);
reset_max_values(&allctr->mbcs);
reset_max_values(&allctr->sbcs);
}
@@ -2508,12 +2741,14 @@ erts_alcu_current_size(Allctr_t *allctr, AllctrSize_t *size)
erts_mtx_lock(&allctr->mutex);
#endif
- size->carriers = allctr->mbcs.curr_mseg.size;
- size->carriers += allctr->mbcs.curr_sys_alloc.size;
- size->carriers += allctr->sbcs.curr_mseg.size;
- size->carriers += allctr->sbcs.curr_sys_alloc.size;
+ size->carriers = allctr->mbcs.curr.norm.mseg.size;
+ size->carriers += allctr->mbcs.curr.norm.sys_alloc.size;
+ size->carriers += allctr->sbmbcs.curr.small_block.size;
+ size->carriers += allctr->sbcs.curr.norm.mseg.size;
+ size->carriers += allctr->sbcs.curr.norm.sys_alloc.size;
size->blocks = allctr->mbcs.blocks.curr.size;
+ size->blocks += allctr->sbmbcs.blocks.curr.size;
size->blocks += allctr->sbcs.blocks.curr.size;
#ifdef USE_THREADS
@@ -2725,7 +2960,7 @@ do_erts_alcu_realloc(ErtsAlcType_t type,
void *extra,
void *p,
Uint size,
- UWord flgs)
+ Uint32 alcu_flgs)
{
Allctr_t *allctr = (Allctr_t *) extra;
Block_t *blk;
@@ -2758,9 +2993,32 @@ do_erts_alcu_realloc(ErtsAlcType_t type,
blk = UMEM2BLK(p);
+ if (allctr->sbmbc_threshold > 0) {
+ Uint old_sz, new_sz, lim;
+ lim = allctr->sbmbc_threshold;
+ old_sz = BLK_SZ(blk);
+ new_sz = UMEMSZ2BLKSZ(allctr, size);
+ if ((old_sz < lim && lim <= new_sz)
+ || (new_sz < lim && lim <= old_sz)) {
+ /* *Need* to move it... */
+
+ INC_CC(allctr->calls.this_realloc);
+ res = do_erts_alcu_alloc(type, extra, size);
+ DEC_CC(allctr->calls.this_alloc);
+
+ sys_memcpy(res, p, MIN(size, old_sz - ABLK_HDR_SZ));
+
+ do_erts_alcu_free(type, extra, p);
+ DEC_CC(allctr->calls.this_free);
+ return res;
+ }
+ if (old_sz < lim)
+ alcu_flgs |= ERTS_ALCU_FLG_SBMBC;
+ }
+
if (size < allctr->sbc_threshold) {
if (IS_MBC_BLK(blk))
- res = mbc_realloc(allctr, p, size, flgs);
+ res = mbc_realloc(allctr, p, size, alcu_flgs);
else {
Uint used_sz = allctr->sbc_header_size + ABLK_HDR_SZ + size;
Uint crr_sz;
@@ -2791,7 +3049,7 @@ do_erts_alcu_realloc(ErtsAlcType_t type,
if (100*diff_sz_val < allctr->sbc_move_threshold*crr_sz_val)
/* Data won't be copied into a new carrier... */
goto do_carrier_resize;
- else if (flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
+ else if (alcu_flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
return NULL;
res = mbc_alloc(allctr, size);
@@ -2814,7 +3072,7 @@ do_erts_alcu_realloc(ErtsAlcType_t type,
#endif
res = new_blk ? BLK2UMEM(new_blk) : NULL;
}
- else if (flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
+ else if (alcu_flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
return NULL;
else {
#if HALFWORD_HEAP
@@ -3174,6 +3432,29 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
allctr->min_block_size = UNIT_CEILING(allctr->min_block_size
+ sizeof(UWord));
+
+ allctr->sbmbc_threshold = init->sbmbct;
+
+ if (!erts_have_sbmbc_alloc
+#if HALFWORD_HEAP
+ || allctr->alloc_no == ERTS_ALC_A_SBMBC_LOW
+#endif
+ || allctr->alloc_no == ERTS_ALC_A_SBMBC)
+ allctr->sbmbc_threshold = 0;
+
+ if (!allctr->sbmbc_threshold)
+ allctr->sbmbc_size = 0;
+ else {
+ Uint min_size;
+ allctr->sbmbc_size = init->sbmbcs;
+ min_size = allctr->sbmbc_threshold;
+ min_size += allctr->min_block_size;
+ min_size += allctr->mbc_header_size;
+ if (allctr->sbmbc_size < min_size)
+ allctr->sbmbc_size = min_size;
+ }
+
+
#if HAVE_ERTS_MSEG
if (allctr->mseg_opt.abs_shrink_th > ~((UWord) 0) / 100)
allctr->mseg_opt.abs_shrink_th = ~((UWord) 0) / 100;
@@ -3185,12 +3466,16 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_mtx_init_x_opt(&allctr->mutex,
- "alcu_allocator",
- make_small(allctr->alloc_no),
- ERTS_LCNT_LT_ALLOC);
+ allctr->alloc_no == ERTS_ALC_A_SBMBC
+ ? "sbmbc_alloc"
+ : "alcu_allocator",
+ make_small(allctr->alloc_no),
+ ERTS_LCNT_LT_ALLOC);
#else
erts_mtx_init_x(&allctr->mutex,
- "alcu_allocator",
+ allctr->alloc_no == ERTS_ALC_A_SBMBC
+ ? "sbmbc_alloc"
+ : "alcu_allocator",
make_small(allctr->alloc_no));
#endif /*ERTS_ENABLE_LOCK_COUNT*/
@@ -3260,7 +3545,7 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
if (!blk)
goto error;
- (*allctr->link_free_block)(allctr, blk);
+ (*allctr->link_free_block)(allctr, blk, 0);
HARD_CHECK_BLK_CARRIER(allctr, blk);
@@ -3290,6 +3575,8 @@ erts_alcu_stop(Allctr_t *allctr)
destroy_carrier(allctr, SBC2BLK(allctr, allctr->sbc_list.first));
while (allctr->mbc_list.first)
destroy_carrier(allctr, MBC2FBLK(allctr, allctr->mbc_list.first));
+ while (allctr->sbmbc_list.first)
+ destroy_sbmbc(allctr, MBC2FBLK(allctr, allctr->sbmbc_list.first));
#ifdef USE_THREADS
if (allctr->thread_safe)
@@ -3387,13 +3674,15 @@ erts_alcu_verify_unused(Allctr_t *allctr)
{
UWord no;
- no = allctr->sbcs.curr_mseg.no;
- no += allctr->sbcs.curr_sys_alloc.no;
+ no = allctr->sbcs.curr.norm.mseg.no;
+ no += allctr->sbcs.curr.norm.sys_alloc.no;
no += allctr->mbcs.blocks.curr.no;
+ no += allctr->sbmbcs.blocks.curr.no;
if (no) {
UWord sz = allctr->sbcs.blocks.curr.size;
sz += allctr->mbcs.blocks.curr.size;
+ sz += allctr->sbmbcs.blocks.curr.size;
erl_exit(ERTS_ABORT_EXIT,
"%salloc() used when expected to be unused!\n"
"Total amount of blocks allocated: %bpu\n"
@@ -3492,7 +3781,7 @@ check_blk_carrier(Allctr_t *allctr, Block_t *iblk)
(*allctr->check_block)(allctr, blk, (int) is_free_blk);
if (IS_LAST_BLK(blk)) {
- carrier_end = ((char *) NXT_BLK(blk)) + sizeof(UWord);
+ carrier_end = ((char *) NXT_BLK(blk));
mbc = *((Carrier_t **) NXT_BLK(blk));
prev_blk = NULL;
blk = MBC2FBLK(allctr, mbc);
@@ -3507,9 +3796,9 @@ check_blk_carrier(Allctr_t *allctr, Block_t *iblk)
ASSERT(IS_MB_CARRIER(mbc));
ASSERT((((char *) mbc)
+ allctr->mbc_header_size
- + tot_blk_sz
- + sizeof(UWord)) == carrier_end);
- ASSERT(((char *) mbc) + CARRIER_SZ(mbc) == carrier_end);
+ + tot_blk_sz) == carrier_end);
+ ASSERT(((char *) mbc) + CARRIER_SZ(mbc) - sizeof(Unit_t) <= carrier_end
+ && carrier_end <= ((char *) mbc) + CARRIER_SZ(mbc));
if (allctr->check_mbc)
(*allctr->check_mbc)(allctr, mbc);
@@ -3523,6 +3812,7 @@ check_blk_carrier(Allctr_t *allctr, Block_t *iblk)
cl = &allctr->mbc_list;
}
+#if 0 /* FIXIT sbmbc */
if (cl->first == crr) {
ASSERT(!crr->prev);
}
@@ -3537,6 +3827,7 @@ check_blk_carrier(Allctr_t *allctr, Block_t *iblk)
ASSERT(crr->next);
ASSERT(crr->next->prev == crr);
}
+#endif
}
#endif
diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h
index ddf84c086c..fed4d3dbe6 100644
--- a/erts/emulator/beam/erl_alloc_util.h
+++ b/erts/emulator/beam/erl_alloc_util.h
@@ -34,6 +34,7 @@ typedef struct {
typedef struct {
char *name_prefix;
ErtsAlcType_t alloc_no;
+ int force;
int ts;
int tspec;
int tpref;
@@ -50,6 +51,8 @@ typedef struct {
UWord lmbcs;
UWord smbcs;
UWord mbcgs;
+ UWord sbmbct;
+ UWord sbmbcs;
} AllctrInit_t;
typedef struct {
@@ -67,6 +70,7 @@ typedef struct {
#define ERTS_DEFAULT_ALLCTR_INIT { \
NULL, \
ERTS_ALC_A_INVALID, /* (number) alloc_no: allocator number */\
+ 0, /* (bool) force: force enabled */\
1, /* (bool) ts: thread safe */\
0, /* (bool) tspec: thread specific */\
0, /* (bool) tpref: thread preferred */\
@@ -82,7 +86,9 @@ typedef struct {
10, /* (amount) mmmbc: max mseg mbcs */\
10*1024*1024, /* (bytes) lmbcs: largest mbc size */\
1024*1024, /* (bytes) smbcs: smallest mbc size */\
- 10 /* (amount) mbcgs: mbc growth stages */\
+ 10, /* (amount) mbcgs: mbc growth stages */\
+ 256, /* (bytes) sbmbct: small block mbc threshold */\
+ 8*1024 /* (bytes) sbmbcs: small block mbc size */\
}
#else /* if SMALL_MEMORY */
@@ -95,6 +101,7 @@ typedef struct {
#define ERTS_DEFAULT_ALLCTR_INIT { \
NULL, \
ERTS_ALC_A_INVALID, /* (number) alloc_no: allocator number */\
+ 0, /* (bool) force: force enabled */\
1, /* (bool) ts: thread safe */\
0, /* (bool) tspec: thread specific */\
0, /* (bool) tpref: thread preferred */\
@@ -109,7 +116,9 @@ typedef struct {
10, /* (amount) mmmbc: max mseg mbcs */\
1024*1024, /* (bytes) lmbcs: largest mbc size */\
128*1024, /* (bytes) smbcs: smallest mbc size */\
- 10 /* (amount) mbcgs: mbc growth stages */\
+ 10, /* (amount) mbcgs: mbc growth stages */\
+ 256, /* (bytes) sbmbct: small block mbc threshold */\
+ 8*1024 /* (bytes) sbmbcs: small block mbc size */\
}
#endif
@@ -144,6 +153,9 @@ void erts_alcu_current_size(Allctr_t *, AllctrSize_t *);
#if defined(GET_ERL_ALLOC_UTIL_IMPL) && !defined(ERL_ALLOC_UTIL_IMPL__)
#define ERL_ALLOC_UTIL_IMPL__
+#define ERTS_ALCU_FLG_FAIL_REALLOC_MOVE (((Uint32) 1) << 0)
+#define ERTS_ALCU_FLG_SBMBC (((Uint32) 1) << 1)
+
#ifdef USE_THREADS
#define ERL_THREADS_EMU_INTERNAL__
#include "erl_threads.h"
@@ -188,6 +200,8 @@ void erts_alcu_current_size(Allctr_t *, AllctrSize_t *);
#define CARRIER_SZ(C) \
((C)->chdr & SZ_MASK)
+extern int erts_have_sbmbc_alloc;
+
typedef union {char c[8]; long l; double d;} Unit_t;
typedef struct Carrier_t_ Carrier_t;
@@ -216,8 +230,13 @@ typedef struct {
} StatValues_t;
typedef struct {
- StatValues_t curr_mseg;
- StatValues_t curr_sys_alloc;
+ union {
+ struct {
+ StatValues_t mseg;
+ StatValues_t sys_alloc;
+ } norm;
+ StatValues_t small_block;
+ } curr;
StatValues_t max;
StatValues_t max_ever;
struct {
@@ -257,6 +276,8 @@ struct Allctr_t_ {
Uint largest_mbc_size;
Uint smallest_mbc_size;
Uint mbc_growth_stages;
+ Uint sbmbc_threshold;
+ Uint sbmbc_size;
#if HAVE_ERTS_MSEG
ErtsMsegOpt_t mseg_opt;
#endif
@@ -269,6 +290,7 @@ struct Allctr_t_ {
Uint min_block_size;
/* Carriers */
+ CarrierList_t sbmbc_list;
CarrierList_t mbc_list;
CarrierList_t sbc_list;
@@ -277,15 +299,15 @@ struct Allctr_t_ {
/* Callback functions (first 4 are mandatory) */
Block_t * (*get_free_block) (Allctr_t *, Uint,
- Block_t *, Uint);
- void (*link_free_block) (Allctr_t *, Block_t *);
- void (*unlink_free_block) (Allctr_t *, Block_t *);
+ Block_t *, Uint, Uint32);
+ void (*link_free_block) (Allctr_t *, Block_t *, Uint32);
+ void (*unlink_free_block) (Allctr_t *, Block_t *, Uint32);
Eterm (*info_options) (Allctr_t *, char *, int *,
void *, Uint **, Uint *);
Uint (*get_next_mbc_size) (Allctr_t *);
- void (*creating_mbc) (Allctr_t *, Carrier_t *);
- void (*destroying_mbc) (Allctr_t *, Carrier_t *);
+ void (*creating_mbc) (Allctr_t *, Carrier_t *, Uint32);
+ void (*destroying_mbc) (Allctr_t *, Carrier_t *, Uint32);
void (*init_atoms) (void);
#ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
@@ -312,6 +334,8 @@ struct Allctr_t_ {
CallCounter_t this_alloc;
CallCounter_t this_free;
CallCounter_t this_realloc;
+ CallCounter_t sbmbc_alloc;
+ CallCounter_t sbmbc_free;
CallCounter_t mseg_alloc;
CallCounter_t mseg_dealloc;
CallCounter_t mseg_realloc;
@@ -322,6 +346,7 @@ struct Allctr_t_ {
CarriersStats_t sbcs;
CarriersStats_t mbcs;
+ CarriersStats_t sbmbcs;
#ifdef DEBUG
#ifdef USE_THREADS
diff --git a/erts/emulator/beam/erl_bestfit_alloc.c b/erts/emulator/beam/erl_bestfit_alloc.c
index 3035e5df16..d9b1170a3d 100644
--- a/erts/emulator/beam/erl_bestfit_alloc.c
+++ b/erts/emulator/beam/erl_bestfit_alloc.c
@@ -84,24 +84,24 @@
#ifdef HARD_DEBUG
-static RBTree_t * check_tree(BFAllctr_t *, Uint);
+static RBTree_t * check_tree(RBTree_t, int, Uint);
#endif
-static void tree_delete(Allctr_t *allctr, Block_t *del);
+static void tree_delete(Allctr_t *allctr, Block_t *del, Uint32 flags);
/* Prototypes of callback functions */
/* "address order best fit" specific callback functions */
static Block_t * aobf_get_free_block (Allctr_t *, Uint,
- Block_t *, Uint);
-static void aobf_link_free_block (Allctr_t *, Block_t *);
+ Block_t *, Uint, Uint32);
+static void aobf_link_free_block (Allctr_t *, Block_t *, Uint32);
#define aobf_unlink_free_block tree_delete
/* "best fit" specific callback functions */
static Block_t * bf_get_free_block (Allctr_t *, Uint,
- Block_t *, Uint);
-static void bf_link_free_block (Allctr_t *, Block_t *);
-static ERTS_INLINE void bf_unlink_free_block (Allctr_t *, Block_t *);
+ Block_t *, Uint, Uint32);
+static void bf_link_free_block (Allctr_t *, Block_t *, Uint32);
+static ERTS_INLINE void bf_unlink_free_block (Allctr_t *, Block_t *, Uint32);
static Eterm info_options (Allctr_t *, char *, int *,
@@ -303,7 +303,7 @@ replace(RBTree_t **root, RBTree_t *x, RBTree_t *y)
}
static void
-tree_insert_fixup(BFAllctr_t *bfallctr, RBTree_t *blk)
+tree_insert_fixup(RBTree_t **root, RBTree_t *blk)
{
RBTree_t *x = blk, *y;
@@ -311,7 +311,7 @@ tree_insert_fixup(BFAllctr_t *bfallctr, RBTree_t *blk)
* Rearrange the tree so that it satisfies the Red-Black Tree properties
*/
- RBT_ASSERT(x != bfallctr->root && IS_RED(x->parent));
+ RBT_ASSERT(x != *root && IS_RED(x->parent));
do {
/*
@@ -336,7 +336,7 @@ tree_insert_fixup(BFAllctr_t *bfallctr, RBTree_t *blk)
if (x == x->parent->right) {
x = x->parent;
- left_rotate(&bfallctr->root, x);
+ left_rotate(root, x);
}
RBT_ASSERT(x == x->parent->parent->left->left);
@@ -347,7 +347,7 @@ tree_insert_fixup(BFAllctr_t *bfallctr, RBTree_t *blk)
SET_BLACK(x->parent);
SET_RED(x->parent->parent);
- right_rotate(&bfallctr->root, x->parent->parent);
+ right_rotate(root, x->parent->parent);
RBT_ASSERT(x == x->parent->left);
RBT_ASSERT(IS_RED(x));
@@ -370,7 +370,7 @@ tree_insert_fixup(BFAllctr_t *bfallctr, RBTree_t *blk)
if (x == x->parent->left) {
x = x->parent;
- right_rotate(&bfallctr->root, x);
+ right_rotate(root, x);
}
RBT_ASSERT(x == x->parent->parent->right->right);
@@ -381,7 +381,7 @@ tree_insert_fixup(BFAllctr_t *bfallctr, RBTree_t *blk)
SET_BLACK(x->parent);
SET_RED(x->parent->parent);
- left_rotate(&bfallctr->root, x->parent->parent);
+ left_rotate(root, x->parent->parent);
RBT_ASSERT(x == x->parent->right);
RBT_ASSERT(IS_RED(x));
@@ -390,9 +390,9 @@ tree_insert_fixup(BFAllctr_t *bfallctr, RBTree_t *blk)
break;
}
}
- } while (x != bfallctr->root && IS_RED(x->parent));
+ } while (x != *root && IS_RED(x->parent));
- SET_BLACK(bfallctr->root);
+ SET_BLACK(*root);
}
@@ -402,18 +402,22 @@ tree_insert_fixup(BFAllctr_t *bfallctr, RBTree_t *blk)
* callback function in the address order case.
*/
static void
-tree_delete(Allctr_t *allctr, Block_t *del)
+tree_delete(Allctr_t *allctr, Block_t *del, Uint32 flags)
{
BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
Uint spliced_is_black;
+ RBTree_t **root = ((flags & ERTS_ALCU_FLG_SBMBC)
+ ? &bfallctr->sbmbc_root
+ : &bfallctr->mbc_root);
RBTree_t *x, *y, *z = (RBTree_t *) del;
RBTree_t null_x; /* null_x is used to get the fixup started when we
splice out a node without children. */
null_x.parent = NULL;
+
#ifdef HARD_DEBUG
- check_tree(bfallctr, 0);
+ check_tree(*root, bfallctr->address_order, 0);
#endif
/* Remove node from tree... */
@@ -440,8 +444,8 @@ tree_delete(Allctr_t *allctr, Block_t *del)
}
if (!y->parent) {
- RBT_ASSERT(bfallctr->root == y);
- bfallctr->root = x;
+ RBT_ASSERT(*root == y);
+ *root = x;
}
else if (y == y->parent->left)
y->parent->left = x;
@@ -451,7 +455,7 @@ tree_delete(Allctr_t *allctr, Block_t *del)
}
if (y != z) {
/* We spliced out the successor of z; replace z by the successor */
- replace(&bfallctr->root, z, y);
+ replace(root, z, y);
}
if (spliced_is_black) {
@@ -476,7 +480,7 @@ tree_delete(Allctr_t *allctr, Block_t *del)
SET_BLACK(y);
RBT_ASSERT(IS_BLACK(x->parent));
SET_RED(x->parent);
- left_rotate(&bfallctr->root, x->parent);
+ left_rotate(root, x->parent);
y = x->parent->right;
}
RBT_ASSERT(y);
@@ -489,7 +493,7 @@ tree_delete(Allctr_t *allctr, Block_t *del)
if (IS_BLACK(y->right)) {
SET_BLACK(y->left);
SET_RED(y);
- right_rotate(&bfallctr->root, y);
+ right_rotate(root, y);
y = x->parent->right;
}
RBT_ASSERT(y);
@@ -500,8 +504,8 @@ tree_delete(Allctr_t *allctr, Block_t *del)
}
RBT_ASSERT(y->right);
SET_BLACK(y->right);
- left_rotate(&bfallctr->root, x->parent);
- x = bfallctr->root;
+ left_rotate(root, x->parent);
+ x = *root;
break;
}
}
@@ -515,7 +519,7 @@ tree_delete(Allctr_t *allctr, Block_t *del)
SET_BLACK(y);
RBT_ASSERT(IS_BLACK(x->parent));
SET_RED(x->parent);
- right_rotate(&bfallctr->root, x->parent);
+ right_rotate(root, x->parent);
y = x->parent->left;
}
RBT_ASSERT(y);
@@ -528,7 +532,7 @@ tree_delete(Allctr_t *allctr, Block_t *del)
if (IS_BLACK(y->left)) {
SET_BLACK(y->right);
SET_RED(y);
- left_rotate(&bfallctr->root, y);
+ left_rotate(root, y);
y = x->parent->left;
}
RBT_ASSERT(y);
@@ -538,8 +542,8 @@ tree_delete(Allctr_t *allctr, Block_t *del)
}
RBT_ASSERT(y->left);
SET_BLACK(y->left);
- right_rotate(&bfallctr->root, x->parent);
- x = bfallctr->root;
+ right_rotate(root, x->parent);
+ x = *root;
break;
}
}
@@ -556,8 +560,8 @@ tree_delete(Allctr_t *allctr, Block_t *del)
RBT_ASSERT(!null_x.left);
RBT_ASSERT(!null_x.right);
}
- else if (bfallctr->root == &null_x) {
- bfallctr->root = NULL;
+ else if (*root == &null_x) {
+ *root = NULL;
RBT_ASSERT(!null_x.left);
RBT_ASSERT(!null_x.right);
}
@@ -567,7 +571,7 @@ tree_delete(Allctr_t *allctr, Block_t *del)
DESTROY_TREE_NODE(del);
#ifdef HARD_DEBUG
- check_tree(bfallctr, 0);
+ check_tree(root, bfallctr->address_order, 0);
#endif
}
@@ -577,23 +581,28 @@ tree_delete(Allctr_t *allctr, Block_t *del)
\* */
static void
-aobf_link_free_block(Allctr_t *allctr, Block_t *block)
+aobf_link_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
{
BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
+ RBTree_t **root = ((flags & ERTS_ALCU_FLG_SBMBC)
+ ? &bfallctr->sbmbc_root
+ : &bfallctr->mbc_root);
RBTree_t *blk = (RBTree_t *) block;
Uint blk_sz = BLK_SZ(blk);
+
+
blk->flags = 0;
blk->left = NULL;
blk->right = NULL;
- if (!bfallctr->root) {
+ if (!*root) {
blk->parent = NULL;
SET_BLACK(blk);
- bfallctr->root = blk;
+ *root = blk;
}
else {
- RBTree_t *x = bfallctr->root;
+ RBTree_t *x = *root;
while (1) {
Uint size;
@@ -623,28 +632,32 @@ aobf_link_free_block(Allctr_t *allctr, Block_t *block)
SET_RED(blk);
if (IS_RED(blk->parent))
- tree_insert_fixup(bfallctr, blk);
+ tree_insert_fixup(root, blk);
}
#ifdef HARD_DEBUG
- check_tree(bfallctr, 0);
+ check_tree(root, 1, 0);
#endif
}
#if 0 /* tree_delete() is directly used instead */
static void
-aobf_unlink_free_block(Allctr_t *allctr, Block_t *block)
+aobf_unlink_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
{
- tree_delete(allctr, block);
+ tree_delete(allctr, block, flags);
}
#endif
static Block_t *
aobf_get_free_block(Allctr_t *allctr, Uint size,
- Block_t *cand_blk, Uint cand_size)
+ Block_t *cand_blk, Uint cand_size,
+ Uint32 flags)
{
BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
- RBTree_t *x = bfallctr->root;
+ RBTree_t **root = ((flags & ERTS_ALCU_FLG_SBMBC)
+ ? &bfallctr->sbmbc_root
+ : &bfallctr->mbc_root);
+ RBTree_t *x = *root;
RBTree_t *blk = NULL;
Uint blk_sz;
@@ -665,7 +678,7 @@ aobf_get_free_block(Allctr_t *allctr, Uint size,
return NULL;
#ifdef HARD_DEBUG
- ASSERT(blk == check_tree(bfallctr, size));
+ ASSERT(blk == check_tree(root, 1, size));
#endif
if (cand_blk) {
@@ -676,7 +689,7 @@ aobf_get_free_block(Allctr_t *allctr, Uint size,
return NULL; /* cand_blk was better */
}
- aobf_unlink_free_block(allctr, (Block_t *) blk);
+ aobf_unlink_free_block(allctr, (Block_t *) blk, flags);
return (Block_t *) blk;
}
@@ -687,9 +700,12 @@ aobf_get_free_block(Allctr_t *allctr, Uint size,
\* */
static void
-bf_link_free_block(Allctr_t *allctr, Block_t *block)
+bf_link_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
{
BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
+ RBTree_t **root = ((flags & ERTS_ALCU_FLG_SBMBC)
+ ? &bfallctr->sbmbc_root
+ : &bfallctr->mbc_root);
RBTree_t *blk = (RBTree_t *) block;
Uint blk_sz = BLK_SZ(blk);
@@ -700,13 +716,13 @@ bf_link_free_block(Allctr_t *allctr, Block_t *block)
blk->left = NULL;
blk->right = NULL;
- if (!bfallctr->root) {
+ if (!*root) {
blk->parent = NULL;
SET_BLACK(blk);
- bfallctr->root = blk;
+ *root = blk;
}
else {
- RBTree_t *x = bfallctr->root;
+ RBTree_t *x = *root;
while (1) {
Uint size;
@@ -745,7 +761,7 @@ bf_link_free_block(Allctr_t *allctr, Block_t *block)
SET_RED(blk);
if (IS_RED(blk->parent))
- tree_insert_fixup(bfallctr, blk);
+ tree_insert_fixup(root, blk);
}
@@ -753,14 +769,17 @@ bf_link_free_block(Allctr_t *allctr, Block_t *block)
LIST_NEXT(blk) = NULL;
#ifdef HARD_DEBUG
- check_tree(bfallctr, 0);
+ check_tree(root, 0, 0);
#endif
}
static ERTS_INLINE void
-bf_unlink_free_block(Allctr_t *allctr, Block_t *block)
+bf_unlink_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
{
BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
+ RBTree_t **root = ((flags & ERTS_ALCU_FLG_SBMBC)
+ ? &bfallctr->sbmbc_root
+ : &bfallctr->mbc_root);
RBTree_t *x = (RBTree_t *) block;
if (IS_LIST_ELEM(x)) {
@@ -778,9 +797,9 @@ bf_unlink_free_block(Allctr_t *allctr, Block_t *block)
ASSERT(IS_LIST_ELEM(LIST_NEXT(x)));
#ifdef HARD_DEBUG
- check_tree(bfallctr, 0);
+ check_tree(root, 0, 0);
#endif
- replace(&bfallctr->root, x, LIST_NEXT(x));
+ replace(root, x, LIST_NEXT(x));
#ifdef HARD_DEBUG
check_tree(bfallctr, 0);
@@ -788,7 +807,7 @@ bf_unlink_free_block(Allctr_t *allctr, Block_t *block)
}
else {
/* Remove from tree */
- tree_delete(allctr, block);
+ tree_delete(allctr, block, flags);
}
DESTROY_LIST_ELEM(x);
@@ -797,10 +816,14 @@ bf_unlink_free_block(Allctr_t *allctr, Block_t *block)
static Block_t *
bf_get_free_block(Allctr_t *allctr, Uint size,
- Block_t *cand_blk, Uint cand_size)
+ Block_t *cand_blk, Uint cand_size,
+ Uint32 flags)
{
BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
- RBTree_t *x = bfallctr->root;
+ RBTree_t **root = ((flags & ERTS_ALCU_FLG_SBMBC)
+ ? &bfallctr->sbmbc_root
+ : &bfallctr->mbc_root);
+ RBTree_t *x = *root;
RBTree_t *blk = NULL;
Uint blk_sz;
@@ -827,7 +850,7 @@ bf_get_free_block(Allctr_t *allctr, Uint size,
#ifdef HARD_DEBUG
{
- RBTree_t *ct_blk = check_tree(bfallctr, size);
+ RBTree_t *ct_blk = check_tree(root, 0, size);
ASSERT(BLK_SZ(ct_blk) == BLK_SZ(blk));
}
#endif
@@ -839,7 +862,7 @@ bf_get_free_block(Allctr_t *allctr, Uint size,
the tree node */
blk = LIST_NEXT(blk) ? LIST_NEXT(blk) : blk;
- bf_unlink_free_block(allctr, (Block_t *) blk);
+ bf_unlink_free_block(allctr, (Block_t *) blk, flags);
return (Block_t *) blk;
}
@@ -949,7 +972,7 @@ erts_bfalc_test(unsigned long op, unsigned long a1, unsigned long a2)
{
switch (op) {
case 0x200: return (unsigned long) ((BFAllctr_t *) a1)->address_order;
- case 0x201: return (unsigned long) ((BFAllctr_t *) a1)->root;
+ case 0x201: return (unsigned long) ((BFAllctr_t *) a1)->mbc_root;
case 0x202: return (unsigned long) ((RBTree_t *) a1)->parent;
case 0x203: return (unsigned long) ((RBTree_t *) a1)->left;
case 0x204: return (unsigned long) ((RBTree_t *) a1)->right;
@@ -985,7 +1008,7 @@ erts_bfalc_test(unsigned long op, unsigned long a1, unsigned long a2)
#endif
#ifdef PRINT_TREE
-static void print_tree(BFAllctr_t *);
+static void print_tree(RBTree_t *, int);
#endif
/*
@@ -1003,7 +1026,7 @@ static void print_tree(BFAllctr_t *);
*/
static RBTree_t *
-check_tree(BFAllctr_t *bfallctr, Uint size)
+check_tree(RBTree_t *root, int ao, Uint size)
{
RBTree_t *res = NULL;
Sint blacks;
@@ -1011,13 +1034,13 @@ check_tree(BFAllctr_t *bfallctr, Uint size)
RBTree_t *x;
#ifdef PRINT_TREE
- print_tree(bfallctr);
+ print_tree(root, ao);
#endif
- if (!bfallctr->root)
+ if (!root)
return res;
- x = bfallctr->root;
+ x = root;
ASSERT(IS_BLACK(x));
ASSERT(!x->parent);
curr_blacks = 1;
@@ -1060,11 +1083,11 @@ check_tree(BFAllctr_t *bfallctr, Uint size)
ASSERT(IS_BLACK(x->left));
}
- ASSERT(x->parent || x == bfallctr->root);
+ ASSERT(x->parent || x == root);
if (x->left) {
ASSERT(x->left->parent == x);
- if (bfallctr->address_order) {
+ if (ao) {
ASSERT(BLK_SZ(x->left) < BLK_SZ(x)
|| (BLK_SZ(x->left) == BLK_SZ(x) && x->left < x));
}
@@ -1076,7 +1099,7 @@ check_tree(BFAllctr_t *bfallctr, Uint size)
if (x->right) {
ASSERT(x->right->parent == x);
- if (bfallctr->address_order) {
+ if (ao) {
ASSERT(BLK_SZ(x->right) > BLK_SZ(x)
|| (BLK_SZ(x->right) == BLK_SZ(x) && x->right > x));
}
@@ -1087,7 +1110,7 @@ check_tree(BFAllctr_t *bfallctr, Uint size)
}
if (size && BLK_SZ(x) >= size) {
- if (bfallctr->address_order) {
+ if (ao) {
if (!res
|| BLK_SZ(x) < BLK_SZ(res)
|| (BLK_SZ(x) == BLK_SZ(res) && x < res))
@@ -1109,8 +1132,8 @@ check_tree(BFAllctr_t *bfallctr, Uint size)
ASSERT(curr_blacks == 0);
- UNSET_LEFT_VISITED(bfallctr->root);
- UNSET_RIGHT_VISITED(bfallctr->root);
+ UNSET_LEFT_VISITED(root);
+ UNSET_RIGHT_VISITED(root);
return res;
@@ -1148,11 +1171,11 @@ print_tree_aux(RBTree_t *x, int indent)
static void
-print_tree(BFAllctr_t *bfallctr)
+print_tree(RBTree_t *root, int ao)
{
- char *type = bfallctr->address_order ? "Size-Adress" : "Size";
+ char *type = ao ? "Size-Adress" : "Size";
fprintf(stderr, " --- %s tree begin ---\r\n", type);
- print_tree_aux(bfallctr->root, 0);
+ print_tree_aux(root, 0);
fprintf(stderr, " --- %s tree end ---\r\n", type);
}
diff --git a/erts/emulator/beam/erl_bestfit_alloc.h b/erts/emulator/beam/erl_bestfit_alloc.h
index cb35e21e57..faa2d9742e 100644
--- a/erts/emulator/beam/erl_bestfit_alloc.h
+++ b/erts/emulator/beam/erl_bestfit_alloc.h
@@ -54,7 +54,8 @@ typedef struct RBTree_t_ RBTree_t;
struct BFAllctr_t_ {
Allctr_t allctr; /* Has to be first! */
- RBTree_t * root;
+ RBTree_t * mbc_root;
+ RBTree_t * sbmbc_root;
int address_order;
};
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index e0a6aa05c6..eb50f56502 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -338,13 +338,13 @@ static ERTS_INLINE void db_unlock(DbTable* tb, db_lock_kind_t kind)
ASSERT(tb != meta_pid_to_tab && tb != meta_pid_to_fixed_tab);
if (tb->common.type & DB_FINE_LOCKED) {
- if (tb->common.is_thread_safe) {
- ASSERT(kind == LCK_WRITE);
+ if (kind == LCK_WRITE) {
+ ASSERT(tb->common.is_thread_safe);
tb->common.is_thread_safe = 0;
erts_smp_rwmtx_rwunlock(&tb->common.rwlock);
}
else {
- ASSERT(kind != LCK_WRITE);
+ ASSERT(!tb->common.is_thread_safe);
erts_smp_rwmtx_runlock(&tb->common.rwlock);
}
}
@@ -543,9 +543,9 @@ static int remove_named_tab(DbTable *tb, int have_lock)
* We keep our increased refc over this op in order to
* prevent the table from disapearing.
*/
- erts_smp_rwmtx_rwunlock(&tb->common.rwlock);
+ db_unlock(tb, LCK_WRITE);
erts_smp_rwmtx_rwlock(rwlock);
- erts_smp_rwmtx_rwlock(&tb->common.rwlock);
+ db_lock(tb, LCK_WRITE);
}
#endif
@@ -1650,24 +1650,6 @@ BIF_RETTYPE ets_delete_1(BIF_ALIST_1)
tb->common.status &= ~(DB_PROTECTED|DB_PUBLIC|DB_PRIVATE);
tb->common.status |= DB_DELETE;
- 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.
- */
- erts_smp_rwmtx_rwunlock(&tb->common.rwlock);
- erts_smp_rwmtx_rwlock(mmtl);
- erts_smp_rwmtx_rwlock(&tb->common.rwlock);
- }
-#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))
- remove_named_tab(tb, 0);
-
if (tb->common.owner != BIF_P->id) {
DeclareTmpHeap(meta_tuple,3,BIF_P);
@@ -1691,6 +1673,25 @@ BIF_RETTYPE ets_delete_1(BIF_ALIST_1)
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);
+ }
+#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))
+ remove_named_tab(tb, 0);
+
/* disable inheritance */
free_heir_data(tb);
tb->common.heir = am_none;
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index c3b074f782..e5be1f253a 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -1731,6 +1731,7 @@ Eterm db_prog_match(Process *c_p, Binary *bprog,
#define BEGIN_ATOMIC_TRACE(p) \
do { \
if (! atomic_trace) { \
+ erts_refc_inc(&bprog->refc, 2); \
erts_smp_proc_unlock((p), ERTS_PROC_LOCK_MAIN); \
erts_smp_block_system(0); \
atomic_trace = !0; \
@@ -1741,6 +1742,9 @@ Eterm db_prog_match(Process *c_p, Binary *bprog,
if (atomic_trace) { \
erts_smp_release_system(); \
erts_smp_proc_lock((p), ERTS_PROC_LOCK_MAIN); \
+ if (erts_refc_dectest(&bprog->refc, 0) == 0) {\
+ erts_bin_free(bprog); \
+ } \
atomic_trace = 0; \
} \
} while (0)
diff --git a/erts/emulator/beam/erl_goodfit_alloc.c b/erts/emulator/beam/erl_goodfit_alloc.c
index 76b206d76f..1cc508ac5a 100644
--- a/erts/emulator/beam/erl_goodfit_alloc.c
+++ b/erts/emulator/beam/erl_goodfit_alloc.c
@@ -163,10 +163,10 @@ BKT_MIN_SZ(GFAllctr_t *gfallctr, int ix)
/* Prototypes of callback functions */
static Block_t * get_free_block (Allctr_t *, Uint,
- Block_t *, Uint);
-static void link_free_block (Allctr_t *, Block_t *);
-static void unlink_free_block (Allctr_t *, Block_t *);
-static void update_last_aux_mbc (Allctr_t *, Carrier_t *);
+ Block_t *, Uint, Uint32);
+static void link_free_block (Allctr_t *, Block_t *, Uint32);
+static void unlink_free_block (Allctr_t *, Block_t *, Uint32);
+static void update_last_aux_mbc (Allctr_t *, Carrier_t *, Uint32);
static Eterm info_options (Allctr_t *, char *, int *,
void *, Uint **, Uint *);
static void init_atoms (void);
@@ -197,6 +197,8 @@ erts_gfalc_start(GFAllctr_t *gfallctr,
is a struct). */
Allctr_t *allctr = (Allctr_t *) gfallctr;
+ init->sbmbct = 0; /* Small mbc not yet supported by goodfit */
+
sys_memcpy((void *) gfallctr, (void *) &nulled_state, sizeof(GFAllctr_t));
allctr->mbc_header_size = sizeof(Carrier_t);
@@ -379,7 +381,7 @@ search_bucket(Allctr_t *allctr, int ix, Uint size)
static Block_t *
get_free_block(Allctr_t *allctr, Uint size,
- Block_t *cand_blk, Uint cand_size)
+ Block_t *cand_blk, Uint cand_size, Uint32 flags)
{
GFAllctr_t *gfallctr = (GFAllctr_t *) allctr;
int unsafe_bi, min_bi;
@@ -398,7 +400,7 @@ get_free_block(Allctr_t *allctr, Uint size,
if (blk) {
if (cand_blk && cand_size <= BLK_SZ(blk))
return NULL; /* cand_blk was better */
- unlink_free_block(allctr, blk);
+ unlink_free_block(allctr, blk, flags);
return blk;
}
if (min_bi < NO_OF_BKTS - 1) {
@@ -418,14 +420,14 @@ get_free_block(Allctr_t *allctr, Uint size,
ASSERT(blk);
if (cand_blk && cand_size <= BLK_SZ(blk))
return NULL; /* cand_blk was better */
- unlink_free_block(allctr, blk);
+ unlink_free_block(allctr, blk, flags);
return blk;
}
static void
-link_free_block(Allctr_t *allctr, Block_t *block)
+link_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
{
GFAllctr_t *gfallctr = (GFAllctr_t *) allctr;
GFFreeBlock_t *blk = (GFFreeBlock_t *) block;
@@ -446,7 +448,7 @@ link_free_block(Allctr_t *allctr, Block_t *block)
}
static void
-unlink_free_block(Allctr_t *allctr, Block_t *block)
+unlink_free_block(Allctr_t *allctr, Block_t *block, Uint32 flags)
{
GFAllctr_t *gfallctr = (GFAllctr_t *) allctr;
GFFreeBlock_t *blk = (GFFreeBlock_t *) block;
@@ -467,7 +469,7 @@ unlink_free_block(Allctr_t *allctr, Block_t *block)
}
static void
-update_last_aux_mbc(Allctr_t *allctr, Carrier_t *mbc)
+update_last_aux_mbc(Allctr_t *allctr, Carrier_t *mbc, Uint32 flags)
{
GFAllctr_t *gfallctr = (GFAllctr_t *) allctr;
diff --git a/erts/emulator/beam/erl_instrument.c b/erts/emulator/beam/erl_instrument.c
index f3f3c22933..c5615818f2 100644
--- a/erts/emulator/beam/erl_instrument.c
+++ b/erts/emulator/beam/erl_instrument.c
@@ -1152,6 +1152,14 @@ erts_instr_get_type_info(Process *proc)
return res;
}
+#if HALFWORD_HEAP
+#define ERTS_IS_SBMBC_ALLOCATOR_NO__(NO) \
+ ((NO) == ERTS_ALC_A_SBMBC || (NO) == ERTS_ALC_A_SBMBC_LOW)
+#else
+#define ERTS_IS_SBMBC_ALLOCATOR_NO__(NO) \
+ ((NO) == ERTS_ALC_A_SBMBC)
+#endif
+
Uint
erts_instr_init(int stat, int map_stat)
{
@@ -1186,6 +1194,8 @@ erts_instr_init(int stat, int map_stat)
sys_memzero((void *) stats->n, sizeof(Stat_t)*(ERTS_ALC_N_MAX+1));
for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
+ if (ERTS_IS_SBMBC_ALLOCATOR_NO__(i))
+ continue;
if (erts_allctrs_info[i].enabled)
stats->ap[i] = &stats->a[i];
else
@@ -1199,6 +1209,8 @@ erts_instr_init(int stat, int map_stat)
erts_instr_memory_map = 1;
erts_instr_stat = 1;
for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
+ if (ERTS_IS_SBMBC_ALLOCATOR_NO__(i))
+ continue;
erts_allctrs[i].alloc = map_stat_alloc;
erts_allctrs[i].realloc = map_stat_realloc;
erts_allctrs[i].free = map_stat_free;
@@ -1209,6 +1221,8 @@ erts_instr_init(int stat, int map_stat)
else {
erts_instr_stat = 1;
for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
+ if (ERTS_IS_SBMBC_ALLOCATOR_NO__(i))
+ continue;
erts_allctrs[i].alloc = stat_alloc;
erts_allctrs[i].realloc = stat_realloc;
erts_allctrs[i].free = stat_free;
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index 9180508a49..587d82f2bb 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -153,6 +153,7 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "instr", NULL },
{ "fix_alloc", "index" },
{ "alcu_allocator", "index" },
+ { "sbmbc_alloc", "index" },
{ "alcu_delayed_free", "index" },
{ "mseg", NULL },
#if HALFWORD_HEAP
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 68421b4387..d9b1a8e89d 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -833,8 +833,11 @@ ERL_NIF_TERM enif_make_uint(ErlNifEnv* env, unsigned i)
ERL_NIF_TERM enif_make_long(ErlNifEnv* env, long i)
{
+ if (IS_SSMALL(i)) {
+ return make_small(i);
+ }
#if SIZEOF_LONG == ERTS_SIZEOF_ETERM
- return IS_SSMALL(i) ? make_small(i) : small_to_big(i, alloc_heap(env,2));
+ return small_to_big(i, alloc_heap(env,2));
#elif SIZEOF_LONG == 8
ensure_heap(env,3);
return erts_sint64_to_big(i, &env->hp);
@@ -843,8 +846,11 @@ ERL_NIF_TERM enif_make_long(ErlNifEnv* env, long i)
ERL_NIF_TERM enif_make_ulong(ErlNifEnv* env, unsigned long i)
{
+ if (IS_USMALL(0,i)) {
+ return make_small(i);
+ }
#if SIZEOF_LONG == ERTS_SIZEOF_ETERM
- return IS_USMALL(0,i) ? make_small(i) : uint_to_big(i,alloc_heap(env,2));
+ return uint_to_big(i,alloc_heap(env,2));
#elif SIZEOF_LONG == 8
ensure_heap(env,3);
return erts_uint64_to_big(i, &env->hp);
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index 91d695d979..2867e8e2e4 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -257,10 +257,48 @@ types(Config) when is_list(Config) ->
end,
[{},{ok},{{}},{[],{}},{1,2,3,4,5}]),
Stuff = [[],{},0,0.0,(1 bsl 100),(fun()-> ok end),make_ref(),self()],
- [eq_cmp(A,clone(B)) || A<-Stuff, B<-Stuff],
+ [eq_cmp(A,clone(B)) || A<-Stuff, B<-Stuff],
+
+ {IntSz, LongSz} = type_sizes(),
+ UintMax = (1 bsl (IntSz*8)) - 1,
+ IntMax = UintMax bsr 1,
+ IntMin = -(IntMax+1),
+ UlongMax = (1 bsl (LongSz*8)) - 1,
+ LongMax = UlongMax bsr 1,
+ LongMin = -(LongMax+1),
+ Uint64Max = (1 bsl 64) - 1,
+ Int64Max = Uint64Max bsr 1,
+ Int64Min = -(Int64Max+1),
+ Limits = [{IntMin,IntMax},{0,UintMax},{LongMin,LongMax},{0,UlongMax},{Int64Min,Int64Max},{0,Uint64Max}],
+ io:format("Limits = ~p\n", [Limits]),
+ lists:foreach(fun(I) ->
+ R1 = echo_int(I),
+ %%io:format("echo_int(~p) -> ~p\n", [I, R1]),
+ R2 = my_echo_int(I, Limits),
+ ?line R1 = R2,
+ ?line true = (R1 =:= R2),
+ ?line true = (R1 == R2)
+ end, int_list()),
+
?line verify_tmpmem(TmpMem),
ok.
+int_list() ->
+ Start = 1 bsl 200,
+ int_list([Start], -Start).
+int_list([N | _]=List, End) when N<End ->
+ List;
+int_list([N | _]=List, End) ->
+ int_list([N - (1 + (abs(N) div 3)) | List], End).
+
+my_echo_int(I, Limits) ->
+ lists:map(fun({Min,Max}) ->
+ if I < Min -> false;
+ I > Max -> false;
+ true -> I
+ end
+ end, Limits).
+
clone(X) ->
binary_to_term(term_to_binary(X)).
@@ -1170,9 +1208,10 @@ tmpmem() ->
MemInfo ->
MSBCS = lists:foldl(
fun ({instance, _, L}, Acc) ->
+ {value,{_,SBMBCS}} = lists:keysearch(sbmbcs, 1, L),
{value,{_,MBCS}} = lists:keysearch(mbcs, 1, L),
{value,{_,SBCS}} = lists:keysearch(sbcs, 1, L),
- [MBCS,SBCS | Acc]
+ [SBMBCS,MBCS,SBCS | Acc]
end,
[],
MemInfo),
@@ -1269,6 +1308,8 @@ send_blob_thread(_,_,_) -> ?nif_stub.
join_send_thread(_) -> ?nif_stub.
copy_blob(_) -> ?nif_stub.
send_term(_,_) -> ?nif_stub.
+echo_int(_) -> ?nif_stub.
+type_sizes() -> ?nif_stub.
nif_stub_error(Line) ->
exit({nif_not_loaded,module,?MODULE,line,Line}).
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 0bb93daa33..00a1365bc3 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -28,6 +28,7 @@
static int static_cntA; /* zero by default */
static int static_cntB = NIF_SUITE_LIB_VER * 100;
+static ERL_NIF_TERM atom_false;
static ERL_NIF_TERM atom_self;
static ERL_NIF_TERM atom_ok;
static ERL_NIF_TERM atom_join;
@@ -103,7 +104,7 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
msgenv_resource_type = enif_open_resource_type(env,NULL,"nif_SUITE.msgenv",
msgenv_dtor,
ERL_NIF_RT_CREATE, NULL);
-
+ atom_false = enif_make_atom(env,"false");
atom_self = enif_make_atom(env,"self");
atom_ok = enif_make_atom(env,"ok");
atom_join = enif_make_atom(env,"join");
@@ -481,6 +482,45 @@ error:
return enif_make_atom(env,"error");
}
+static ERL_NIF_TERM echo_int(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int sint;
+ unsigned uint;
+ long slong;
+ unsigned long ulong;
+ ErlNifSInt64 sint64;
+ ErlNifUInt64 uint64;
+ ERL_NIF_TERM sint_term = atom_false, uint_term = atom_false;
+ ERL_NIF_TERM slong_term = atom_false, ulong_term = atom_false;
+ ERL_NIF_TERM sint64_term = atom_false, uint64_term = atom_false;
+
+ if (enif_get_int(env, argv[0], &sint)) {
+ sint_term = enif_make_int(env, sint);
+ }
+ if (enif_get_uint(env, argv[0], &uint)) {
+ uint_term = enif_make_uint(env, uint);
+ }
+ if (enif_get_long(env, argv[0], &slong)) {
+ slong_term = enif_make_long(env, slong);
+ }
+ if (enif_get_ulong(env, argv[0], &ulong)) {
+ ulong_term = enif_make_ulong(env, ulong);
+ }
+ if (enif_get_int64(env, argv[0], &sint64)) {
+ sint64_term = enif_make_int64(env, sint64);
+ }
+ if (enif_get_uint64(env, argv[0], &uint64)) {
+ uint64_term = enif_make_uint64(env, uint64);
+ }
+ return enif_make_list6(env, sint_term, uint_term, slong_term, ulong_term, sint64_term, uint64_term);
+}
+
+static ERL_NIF_TERM type_sizes(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ return enif_make_tuple2(env, enif_make_int(env, sizeof(int)),
+ enif_make_int(env, sizeof(long)));
+}
+
static ERL_NIF_TERM tuple_2_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int arity = -1;
@@ -1417,7 +1457,9 @@ static ErlNifFunc nif_funcs[] =
{"send_blob_thread", 3, send_blob_thread},
{"join_send_thread", 1, join_send_thread},
{"copy_blob", 1, copy_blob},
- {"send_term", 2, send_term}
+ {"send_term", 2, send_term},
+ {"echo_int", 1, echo_int},
+ {"type_sizes", 0, type_sizes}
};
ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload)
diff --git a/erts/emulator/test/send_term_SUITE.erl b/erts/emulator/test/send_term_SUITE.erl
index 6615873392..ba0ba804ca 100644
--- a/erts/emulator/test/send_term_SUITE.erl
+++ b/erts/emulator/test/send_term_SUITE.erl
@@ -175,6 +175,10 @@ chk_temp_alloc() ->
%% Verify that we havn't got anything allocated by temp_alloc
lists:foreach(
fun ({instance, _, TI}) ->
+ ?line {value, {sbmbcs, SBMBCInfo}}
+ = lists:keysearch(sbmbcs, 1, TI),
+ ?line {value, {blocks, 0, _, _}}
+ = lists:keysearch(blocks, 1, SBMBCInfo),
?line {value, {mbcs, MBCInfo}}
= lists:keysearch(mbcs, 1, TI),
?line {value, {blocks, 0, _, _}}
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index 7e04724f5a..2bd576d8e8 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -64,6 +64,7 @@
static const char plusM_au_allocs[]= {
'u', /* all alloc_util allocators */
'B', /* binary_alloc */
+ 'C', /* sbmbc_alloc */
'D', /* std_alloc */
'E', /* ets_alloc */
'H', /* eheap_alloc */
@@ -93,6 +94,8 @@ static char *plusM_au_alloc_switches[] = {
"rsbcst",
"sbct",
"smbcs",
+ "sbmbcs",
+ "sbmbct",
NULL
};
diff --git a/lib/erl_interface/src/connect/ei_resolve.c b/lib/erl_interface/src/connect/ei_resolve.c
index 50c5a4161d..ba8f8fbce3 100644
--- a/lib/erl_interface/src/connect/ei_resolve.c
+++ b/lib/erl_interface/src/connect/ei_resolve.c
@@ -185,7 +185,12 @@ static int verify_dns_configuration(void)
* align: increment buf until it is dword-aligned, reduce len by same amount.
* advance: increment buf by n bytes, reduce len by same amount .
*/
-#define align_buf(buf,len) for (;(((unsigned)buf)&0x3); (buf)++, len--)
+#if defined SIZEOF_VOID_P
+#define ALIGNBYTES (SIZEOF_VOID_P - 1)
+#else
+#define ALIGNBYTES (sizeof(void*) - 1)
+#endif
+#define align_buf(buf,len) for (;(((unsigned)buf) & ALIGNBYTES); (buf)++, len--)
#define advance_buf(buf,len,n) ((buf)+=(n),(len)-=(n))
/* "and now the tricky part..." */
diff --git a/lib/inets/Makefile b/lib/inets/Makefile
index ec05efa461..f4c2746b0a 100644
--- a/lib/inets/Makefile
+++ b/lib/inets/Makefile
@@ -36,6 +36,8 @@ SPECIAL_TARGETS =
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+.PHONY: info gclean
+
info:
@echo "OS: $(OS)"
@echo "DOCB: $(DOCB)"
@@ -44,3 +46,5 @@ info:
@echo "APP_VSN: $(APP_VSN)"
@echo ""
+gclean:
+ git clean -fXd
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index f6b6827e93..d1671ac9bd 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -144,7 +144,7 @@ filename() = string()
<v>Result = {status_line(), headers(), Body} |
{status_code(), Body} | request_id() </v>
<v>Body = string() | binary()</v>
- <v>Profile = profile()</v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
<v>Reason = term() </v>
</type>
<desc>
@@ -194,16 +194,16 @@ filename() = string()
<v>Result = {status_line(), headers(), Body} |
{status_code(), Body} | request_id() </v>
<v>Body = string() | binary()</v>
- <v>Profile = profile() </v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
<v>Reason = {connect_failed, term()} |
{send_failed, term()} | term() </v>
</type>
<desc>
<p>Sends a HTTP-request. The function can be both synchronous
- and asynchronous. In the later case the function will return
- <c>{ok, RequestId}</c> and later on the information will be delivered
- to the <c>receiver</c> depending on that value. </p>
+ and asynchronous. In the later case the function will return
+ <c>{ok, RequestId}</c> and later on the information will be delivered
+ to the <c>receiver</c> depending on that value. </p>
<p>Http option (<c>http_option()</c>) details: </p>
<taglist>
@@ -211,7 +211,7 @@ filename() = string()
<item>
<p>Timeout time for the request. </p>
<p>The clock starts ticking as soon as the request has been
- sent. </p>
+ sent. </p>
<p>Time is in milliseconds. </p>
<p>Defaults to <c>infinity</c>. </p>
</item>
@@ -219,7 +219,7 @@ filename() = string()
<tag><c><![CDATA[connect_timeout]]></c></tag>
<item>
<p>Connection timeout time, used during the initial request,
- when the client is <em>connecting</em> to the server. </p>
+ when the client is <em>connecting</em> to the server. </p>
<p>Time is in milliseconds. </p>
<p>Defaults to the value of the <c>timeout</c> option. </p>
</item>
@@ -227,60 +227,61 @@ filename() = string()
<tag><c><![CDATA[ssl]]></c></tag>
<item>
<p>This is the default ssl config option, currently defaults to
- <c>essl</c>, see below. </p>
+ <c>essl</c>, see below. </p>
<p>Defaults to <c>[]</c>. </p>
</item>
<tag><c><![CDATA[ossl]]></c></tag>
<item>
<p>If using the OpenSSL based (old) implementation of SSL,
- these SSL-specific options are used. </p>
+ these SSL-specific options are used. </p>
<p>Defaults to <c>[]</c>. </p>
</item>
<tag><c><![CDATA[essl]]></c></tag>
<item>
<p>If using the Erlang based (new) implementation of SSL,
- these SSL-specific options are used. </p>
+ these SSL-specific options are used. </p>
<p>Defaults to <c>[]</c>. </p>
</item>
<tag><c><![CDATA[autoredirect]]></c></tag>
<item>
- <p>Should the client automatically retrieve the information
- from the new URI and return that as the result instead
- of a 30X-result code. </p>
- <p>Note that for some 30X-result codes automatic redirect
- is not allowed. In these cases the 30X-result will always
- be returned. </p>
- <p>Defaults to <c>true</c>. </p>
+ <p>Should the client automatically retrieve the information
+ from the new URI and return that as the result instead
+ of a 30X-result code. </p>
+ <p>Note that for some 30X-result codes automatic redirect
+ is not allowed. In these cases the 30X-result will always
+ be returned. </p>
+ <p>Defaults to <c>true</c>. </p>
</item>
<tag><c><![CDATA[proxy_auth]]></c></tag>
<item>
<p>A proxy-authorization header using the provided user name and
- password will be added to the request. </p>
+ password will be added to the request. </p>
</item>
<tag><c><![CDATA[version]]></c></tag>
<item>
<p>Can be used to make the client act as an <c>HTTP/1.0</c> or
- <c>HTTP/0.9</c> client. By default this is an <c>HTTP/1.1</c>
- client. When using <c>HTTP/1.0</c> persistent connections will
- not be used. </p>
- <p>Defaults to the string <c>"HTTP/1.1"</c>. </p>
+ <c>HTTP/0.9</c> client. By default this is an <c>HTTP/1.1</c>
+ client. When using <c>HTTP/1.0</c> persistent connections will
+ not be used. </p>
+ <p>Defaults to the string <c>"HTTP/1.1"</c>. </p>
</item>
<tag><c><![CDATA[relaxed]]></c></tag>
<item>
- <p>If set to <c>true</c> workarounds for known server deviations from
- the HTTP-standard are enabled. </p>
+ <p>If set to <c>true</c> workarounds for known server deviations
+ from the HTTP-standard are enabled. </p>
<p>Defaults to <c>false</c>. </p>
</item>
<tag><c><![CDATA[url_encode]]></c></tag>
<item>
- <p>Will apply Percent-encoding, also known as URL encoding on the URL.</p>
+ <p>Will apply Percent-encoding, also known as URL encoding on the
+ URL.</p>
<p>Defaults to <c>false</c>. </p>
</item>
</taglist>
@@ -296,77 +297,77 @@ filename() = string()
<tag><c><![CDATA[stream]]></c></tag>
<item>
<p>Streams the body of a 200 or 206 response to the calling
- process or to a file. When streaming to the calling process
- using the option <c>self</c> the following stream messages
- will be sent to that process: <c>{http, {RequestId,
- stream_start, Headers}, {http, {RequestId, stream,
- BinBodyPart}, {http, {RequestId, stream_end, Headers}</c>. When
- streaming to to the calling processes using the option
- <c>{self, once}</c> the first message will have an additional
- element e.i. <c>{http, {RequestId, stream_start, Headers, Pid}</c>,
- this is the process id that should be used as an argument to
- <c>http:stream_next/1</c> to trigger the next message to be sent to
- the calling process. </p>
+ process or to a file. When streaming to the calling process
+ using the option <c>self</c> the following stream messages
+ will be sent to that process: <c>{http, {RequestId,
+ stream_start, Headers}, {http, {RequestId, stream,
+ BinBodyPart}, {http, {RequestId, stream_end, Headers}</c>. When
+ streaming to to the calling processes using the option
+ <c>{self, once}</c> the first message will have an additional
+ element e.i. <c>{http, {RequestId, stream_start, Headers, Pid}</c>,
+ this is the process id that should be used as an argument to
+ <c>http:stream_next/1</c> to trigger the next message to be sent to
+ the calling process. </p>
<p>Note that it is possible that chunked encoding will add
- headers so that there are more headers in the <c>stream_end</c>
- message than in the <c>stream_start</c>.
- When streaming to a file and the request is asynchronous the
- message <c>{http, {RequestId, saved_to_file}}</c> will be sent. </p>
+ headers so that there are more headers in the <c>stream_end</c>
+ message than in the <c>stream_start</c>.
+ When streaming to a file and the request is asynchronous the
+ message <c>{http, {RequestId, saved_to_file}}</c> will be sent. </p>
<p>Defaults to <c>none</c>. </p>
</item>
<tag><c><![CDATA[body_format]]></c></tag>
<item>
<p>Defines if the body shall be delivered as a string or as a
- binary. This option is only valid for the synchronous
- request. </p>
+ binary. This option is only valid for the synchronous
+ request. </p>
<p>Defaults to <c>string</c>. </p>
</item>
<tag><c><![CDATA[full_result]]></c></tag>
<item>
<p>Should a "full result" be returned to the caller (that is,
- the body, the headers and the entire status-line) or not
- (the body and the status code). </p>
+ the body, the headers and the entire status-line) or not
+ (the body and the status code). </p>
<p>Defaults to <c>true</c>. </p>
</item>
<tag><c><![CDATA[header_as_is]]></c></tag>
<item>
<p>Shall the headers provided by the user be made
- lower case or be regarded as case sensitive. </p>
+ lower case or be regarded as case sensitive. </p>
<p>Note that the http standard requires them to be
- case insenstive. This feature should only be used if there is
- no other way to communicate with the server or for testing
- purpose. Also note that when this option is used no headers
- will be automatically added, all necessary headers have to be
- provided by the user. </p>
- <p>Defaults to <c>false</c>. </p>
+ case insenstive. This feature should only be used if there is
+ no other way to communicate with the server or for testing
+ purpose. Also note that when this option is used no headers
+ will be automatically added, all necessary headers have to be
+ provided by the user. </p>
+ <p>Defaults to <c>false</c>. </p>
</item>
<tag><c><![CDATA[socket_opts]]></c></tag>
<item>
<p>Socket options to be used for this and subsequent
- request(s). </p>
- <p>Overrides any value set by the
- <seealso marker="#set_options">set_options</seealso>
- function. </p>
+ request(s). </p>
+ <p>Overrides any value set by the
+ <seealso marker="#set_options">set_options</seealso>
+ function. </p>
<p>Note that the validity of the options are <em>not</em>
- checked in any way. </p>
+ checked in any way. </p>
<p>Note that this may change the socket behaviour
- (see <seealso marker="kernel:inet#setopts/2">inet:setopts/2</seealso>)
- for an already existing one, and therefore an already connected
- request handler. </p>
+ (see <seealso marker="kernel:inet#setopts/2">inet:setopts/2</seealso>)
+ for an already existing one, and therefore an already connected
+ request handler. </p>
<p>By default the socket options set by the
- <seealso marker="#set_options">set_options/1,2</seealso>
- function are used when establishing a connection. </p>
+ <seealso marker="#set_options">set_options/1,2</seealso>
+ function are used when establishing a connection. </p>
</item>
<tag><c><![CDATA[receiver]]></c></tag>
<item>
<p>Defines how the client will deliver the result of an
- asynchroneous request (<c>sync</c> has the value
- <c>false</c>). </p>
+ asynchroneous request (<c>sync</c> has the value
+ <c>false</c>). </p>
<taglist>
<tag><c><![CDATA[pid()]]></c></tag>
@@ -380,7 +381,7 @@ filename() = string()
<tag><c><![CDATA[function/1]]></c></tag>
<item>
<p>Information will be delivered to the receiver via calls
- to the provided fun: </p>
+ to the provided fun: </p>
<pre>
Receiver(ReplyInfo)
</pre>
@@ -389,7 +390,7 @@ Receiver(ReplyInfo)
<tag><c><![CDATA[{Module, Funcion, Args}]]></c></tag>
<item>
<p>Information will be delivered to the receiver via calls
- to the callback function: </p>
+ to the callback function: </p>
<pre>
apply(Module, Function, [ReplyInfo | Args])
</pre>
@@ -410,7 +411,7 @@ apply(Module, Function, [ReplyInfo | Args])
</pre>
<p>Defaults to the <c>pid()</c> of the process calling the request
- function (<c>self()</c>). </p>
+ function (<c>self()</c>). </p>
</item>
</taglist>
@@ -425,7 +426,7 @@ apply(Module, Function, [ReplyInfo | Args])
<type>
<v>RequestId = request_id() - A unique identifier as returned
by request/4</v>
- <v>Profile = profile()</v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
</type>
<desc>
<p>Cancels an asynchronous HTTP-request. </p>
@@ -514,11 +515,10 @@ apply(Module, Function, [ReplyInfo | Args])
This option is used to switch on (or off)
different levels of erlang trace on the client.
It is a debug feature.</d>
- <v>Profile = profile()</v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
</type>
<desc>
- <p>Sets options to be used for subsequent
- requests.</p>
+ <p>Sets options to be used for subsequent requests.</p>
<note>
<p>If possible the client will keep its connections
alive and use persistent connections
@@ -548,7 +548,7 @@ apply(Module, Function, [ReplyInfo | Args])
</type>
<desc>
<p>Triggers the next message to be streamed, e.i.
- same behavior as active once for sockets.</p>
+ same behavior as active once for sockets. </p>
<marker id="verify_cookies"></marker>
<marker id="store_cookies"></marker>
@@ -562,14 +562,14 @@ apply(Module, Function, [ReplyInfo | Args])
<type>
<v>SetCookieHeaders = headers() - where field = "set-cookie"</v>
<v>Url = url()</v>
- <v>Profile = profile()</v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
</type>
<desc>
<p>Saves the cookies defined in SetCookieHeaders
- in the client profile's cookie database. You need to
- call this function if you have set the option <c>cookies</c> to <c>verify</c>.
- If no profile is specified the default profile will be used.
- </p>
+ in the client profile's cookie database. You need to
+ call this function if you have set the option <c>cookies</c>
+ to <c>verify</c>.
+ If no profile is specified the default profile will be used. </p>
<marker id="cookie_header"></marker>
</desc>
@@ -582,13 +582,12 @@ apply(Module, Function, [ReplyInfo | Args])
making a request to Url using the profile <c>Profile</c>.</fsummary>
<type>
<v>Url = url()</v>
- <v>Profile = profile()</v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
</type>
<desc>
<p>Returns the cookie header that would be sent
- when making a request to <c>Url</c> using the profile <c>Profile</c>.
- If no profile is specified the default profile will be used.
- </p>
+ when making a request to <c>Url</c> using the profile <c>Profile</c>.
+ If no profile is specified the default profile will be used. </p>
<marker id="reset_cookies"></marker>
</desc>
@@ -600,12 +599,12 @@ apply(Module, Function, [ReplyInfo | Args])
<name>reset_cookies(Profile) -> void()</name>
<fsummary>Reset the cookie database.</fsummary>
<type>
- <v>Profile = profile()</v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
</type>
<desc>
- <p>Resets (clears) the cookie database for the specified <c>Profile</c>.
- If no profile is specified the default profile will be used.
- </p>
+ <p>Resets (clears) the cookie database for the specified
+ <c>Profile</c>. If no profile is specified the default profile
+ will be used. </p>
</desc>
</func>
@@ -615,17 +614,16 @@ apply(Module, Function, [ReplyInfo | Args])
<name>which_cookies(Profile) -> cookies()</name>
<fsummary>Dumps out the entire cookie database.</fsummary>
<type>
- <v>Profile = profile()</v>
- <v>cookies() = [cooie_stores()]</v>
- <v>cookie_stores() = {cookies, icookies()} | {session_cookies, icookies()}</v>
- <v>icookies() = [icookie()]</v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
+ <v>cookies() = [cookie_stores()]</v>
+ <v>cookie_stores() = {cookies, cookies()} | {session_cookies, cookies()}</v>
+ <v>cookies() = [cookie()]</v>
<v>cookie() = term()</v>
</type>
<desc>
<p>This function produces a list of the entire cookie database.
- It is intended for debugging/testing purposes.
- If no profile is specified the default profile will be used.
- </p>
+ It is intended for debugging/testing purposes.
+ If no profile is specified the default profile will be used. </p>
</desc>
</func>
</funcs>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 0926df8581..34f26bf45b 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -32,6 +32,63 @@
<file>notes.xml</file>
</header>
+ <section><title>Inets 5.7</title>
+
+ <section><title>Improvements and New Features</title>
+<!--
+ <p>-</p>
+-->
+
+ <list>
+ <item>
+ <p>[httpc|httpd] Added support for IPv6 with ssl. </p>
+ <p>Own Id: OTP-5566</p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+<!--
+ <p>-</p>
+-->
+
+ <list>
+ <item>
+ <p>[httpc] Remove unnecessary usage of iolist_to_binary when
+ processing body (for PUT and POST). </p>
+ <p>Filipe David Manana</p>
+ <p>Own Id: OTP-9317</p>
+ </item>
+
+ <item>
+ <p>[ftp] FTP client doesn't work with IPv6 host.</p>
+ <p>Attila Rajmund Nohl</p>
+ <p>Own Id: OTP-9342 Aux Id: seq11853</p>
+ </item>
+
+ <item>
+ <p>[httpd] Peer/sockname resolv doesn't work with IPv6 addrs
+ in HTTP. </p>
+ <p>Attila Rajmund Nohl.</p>
+ <p>Own Id: OTP-9343</p>
+ </item>
+
+ <item>
+ <p>[httpc] Clients started stand-alone not properly handled.
+ Also it was not documented how to use them, that is that
+ once started, they are represented by a <c>pid()</c> and not by
+ their <c>profile()</c>. </p>
+ <p>Own Id: OTP-9365</p>
+ </item>
+
+ </list>
+ </section>
+
+ </section> <!-- 5.7 -->
+
+
<section><title>Inets 5.6</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl
index fe6cb0c191..ac72963347 100644
--- a/lib/inets/src/ftp/ftp.erl
+++ b/lib/inets/src/ftp/ftp.erl
@@ -1038,10 +1038,12 @@ handle_call({_, {open, ip_comm, Opts}}, From, State) ->
Port = key_search(port, Opts, ?FTP_PORT),
Timeout = key_search(timeout, Opts, ?CONNECTION_TIMEOUT),
Progress = key_search(progress, Opts, ignore),
+ IpFamily = key_search(ipfamily, Opts, inet),
State2 = State#state{client = From,
mode = Mode,
- progress = progress(Progress)},
+ progress = progress(Progress),
+ ipfamily = IpFamily},
?fcrd("handle_call(open) -> setup ctrl connection with",
[{host, Host}, {port, Port}, {timeout, Timeout}]),
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index 6ffa5e8ba5..fe8e93af1f 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -64,17 +64,16 @@ default_profile() ->
profile_name(?DEFAULT_PROFILE) ->
httpc_manager;
+profile_name(Profile) when is_pid(Profile) ->
+ Profile;
profile_name(Profile) ->
- profile_name("httpc_manager_", Profile).
+ Prefix = lists:flatten(io_lib:format("~w_", [?MODULE])),
+ profile_name(Prefix, Profile).
profile_name(Prefix, Profile) when is_atom(Profile) ->
list_to_atom(Prefix ++ atom_to_list(Profile));
-profile_name(Prefix, Profile) when is_pid(Profile) ->
- ProfileStr0 =
- string:strip(string:strip(erlang:pid_to_list(Profile), left, $<), right, $>),
- F = fun($.) -> $_; (X) -> X end,
- ProfileStr = [F(C) || C <- ProfileStr0],
- list_to_atom(Prefix ++ "pid_" ++ ProfileStr).
+profile_name(_Prefix, Profile) when is_pid(Profile) ->
+ Profile.
%%--------------------------------------------------------------------------
@@ -115,9 +114,11 @@ request(Url, Profile) ->
%% {keyfile, path()} | {password, string()} | {cacertfile, path()} |
%% {ciphers, string()}
%% Options - [Option]
-%% Option - {sync, Boolean} | {body_format, BodyFormat} |
-%% {full_result, Boolean} | {stream, To} |
-%% {headers_as_is, Boolean}
+%% Option - {sync, Boolean} |
+%% {body_format, BodyFormat} |
+%% {full_result, Boolean} |
+%% {stream, To} |
+%% {headers_as_is, Boolean}
%% StatusLine = {HTTPVersion, StatusCode, ReasonPhrase}</v>
%% HTTPVersion = string()
%% StatusCode = integer()
@@ -518,17 +519,15 @@ mk_chunkify_fun(ProcessBody) ->
eof ->
{ok, <<"0\r\n\r\n">>, eof_body};
{ok, Data, NewAcc} ->
- {ok, mk_chunk_bin(Data), NewAcc}
+ Chunk = [
+ integer_to_list(iolist_size(Data), 16),
+ "\r\n",
+ Data,
+ "\r\n"],
+ {ok, Chunk, NewAcc}
end
end.
-mk_chunk_bin(Data) ->
- Bin = iolist_to_binary(Data),
- iolist_to_binary([hex_size(Bin), "\r\n", Bin, "\r\n"]).
-
-hex_size(Bin) ->
- hd(io_lib:format("~.16B", [size(Bin)])).
-
handle_answer(RequestId, false, _) ->
{ok, RequestId};
@@ -552,9 +551,7 @@ return_answer(Options, {{"HTTP/0.9",_,_}, _, BinBody}) ->
{ok, Body};
return_answer(Options, {StatusLine, Headers, BinBody}) ->
-
Body = maybe_format_body(BinBody, Options),
-
case proplists:get_value(full_result, Options, true) of
true ->
{ok, {StatusLine, Headers, Body}};
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 1f0e012e7e..9ac9ee6f7b 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -515,7 +515,7 @@ handle_info({Proto, _Socket, Data},
{stop, normal, NewState}
end,
- ?hcri("data processed", []),
+ ?hcri("data processed", [{final_result, FinalResult}]),
FinalResult;
@@ -629,8 +629,9 @@ handle_info(timeout_queue, #state{timers = Timers} = State) ->
Timers#timers{queue_timer = undefined}}};
%% Setting up the connection to the server somehow failed.
-handle_info({init_error, _, ClientErrMsg},
+handle_info({init_error, Tag, ClientErrMsg},
State = #state{request = Request}) ->
+ ?hcrv("init error", [{tag, Tag}, {client_error, ClientErrMsg}]),
NewState = answer_request(Request, ClientErrMsg, State),
{stop, normal, NewState};
@@ -707,9 +708,9 @@ terminate(normal,
%% And, just in case, close our side (**really** overkill)
http_transport:close(SocketType, Socket);
-terminate(Reason, #state{session = #session{id = Id,
- socket = Socket,
- socket_type = SocketType},
+terminate(Reason, #state{session = #session{id = Id,
+ socket = Socket,
+ socket_type = SocketType},
request = undefined,
profile_name = ProfileName,
timers = Timers,
@@ -1403,7 +1404,7 @@ try_to_enable_pipeline_or_keep_alive(
answer_request(#request{id = RequestId, from = From} = Request, Msg,
#state{timers = Timers, profile_name = ProfileName} = State) ->
- ?hcrt("answer request", [{request, Request}]),
+ ?hcrt("answer request", [{request, Request}, {msg, Msg}]),
httpc_response:send(From, Msg),
RequestTimers = Timers#timers.request_timers,
TimerRef =
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index 7f66b477eb..9015bf1ce2 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -52,7 +52,7 @@
cancel = [], % [{RequestId, HandlerPid, ClientPid}]
handler_db, % ets() - Entry: #handler_info{}
cookie_db, % cookie_db()
- session_db, % ets() - Entry: #tcp_session{}
+ session_db, % ets() - Entry: #session{}
profile_name, % atom()
options = #options{}
}).
@@ -178,7 +178,7 @@ request_done(RequestId, ProfileName) ->
%%--------------------------------------------------------------------
%% Function: insert_session(Session, ProfileName) -> _
-%% Session - #tcp_session{}
+%% Session - #session{}
%% ProfileName - atom()
%%
%% Description: Inserts session information into the httpc manager
@@ -669,7 +669,7 @@ select_session(Method, HostPort, Scheme, SessionType,
(SessionType =:= keep_alive) of
true ->
%% Look for handlers connecting to this host (HostPort)
- %% tcp_session with record name field (tcp_session) and
+ %% session with record name field (session) and
%% socket fields ignored. The fields id (part of: HostPort),
%% client_close, scheme and type specified.
%% The fields id (part of: HandlerPid) and queue_length
diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl
index 01b51d531a..9b8190ebed 100644
--- a/lib/inets/src/http_lib/http_transport.erl
+++ b/lib/inets/src/http_lib/http_transport.erl
@@ -33,8 +33,8 @@
peername/2, sockname/2,
resolve/0
]).
-
-export([negotiate/3]).
+-export([ipv4_name/1, ipv6_name/1]).
-include_lib("inets/src/inets_app/inets_internal.hrl").
-include("http_internal.hrl").
@@ -142,8 +142,8 @@ connect({ossl, SslConfig}, {Host, Port}, _, Timeout) ->
ERROR
end;
-connect({essl, SslConfig}, {Host, Port}, _, Timeout) ->
- Opts = [binary, {active, false}, {ssl_imp, new}] ++ SslConfig,
+connect({essl, SslConfig}, {Host, Port}, Opts0, Timeout) ->
+ Opts = [binary, {active, false}, {ssl_imp, new} | Opts0] ++ SslConfig,
?hlrt("connect using essl",
[{host, Host},
{port, Port},
@@ -176,8 +176,8 @@ connect({essl, SslConfig}, {Host, Port}, _, Timeout) ->
listen(SocketType, Port) ->
listen(SocketType, undefined, Port).
-listen(ip_comm = SocketType, Addr, Port) ->
- listen(SocketType, Addr, Port, undefined);
+listen(ip_comm = _SocketType, Addr, Port) ->
+ listen_ip_comm(Addr, Port, undefined);
%% Wrapper for backaward compatibillity
listen({ssl, SSLConfig}, Addr, Port) ->
@@ -187,35 +187,33 @@ listen({ssl, SSLConfig}, Addr, Port) ->
{ssl_config, SSLConfig}]),
listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port);
-listen({ossl, SSLConfig} = Ssl, Addr, Port) ->
+listen({ossl, SSLConfig}, Addr, Port) ->
?hlrt("listen (ossl)",
[{addr, Addr},
{port, Port},
{ssl_config, SSLConfig}]),
- Opt = sock_opt(Ssl, Addr, SSLConfig),
- ?hlrt("listen options", [{opt, Opt}]),
- ssl:listen(Port, [{ssl_imp, old} | Opt]);
+ listen_ssl(Addr, Port, [{ssl_imp, old} | SSLConfig]);
-listen({essl, SSLConfig} = Ssl, Addr, Port) ->
+listen({essl, SSLConfig}, Addr, Port) ->
?hlrt("listen (essl)",
[{addr, Addr},
{port, Port},
{ssl_config, SSLConfig}]),
- Opt = sock_opt(Ssl, Addr, SSLConfig),
- ?hlrt("listen options", [{opt, Opt}]),
- Opt2 = [{ssl_imp, new}, {reuseaddr, true} | Opt],
- ssl:listen(Port, Opt2).
+ listen_ssl(Addr, Port, [{ssl_imp, new}, {reuseaddr, true} | SSLConfig]).
+
listen(ip_comm, Addr, Port, Fd) ->
- case (catch listen_ip_comm(Addr, Port, Fd)) of
+ listen_ip_comm(Addr, Port, Fd).
+
+listen_ip_comm(Addr, Port, Fd) ->
+ case (catch do_listen_ip_comm(Addr, Port, Fd)) of
{'EXIT', Reason} ->
{error, {exit, Reason}};
Else ->
Else
end.
-
-listen_ip_comm(Addr, Port, Fd) ->
+do_listen_ip_comm(Addr, Port, Fd) ->
{NewPort, Opts, IpFamily} = get_socket_info(Addr, Port, Fd),
case IpFamily of
inet6fb4 ->
@@ -248,6 +246,41 @@ listen_ip_comm(Addr, Port, Fd) ->
gen_tcp:listen(NewPort, Opts2)
end.
+
+listen_ssl(Addr, Port, Opts0) ->
+ IpFamily = ipfamily_default(Addr, Port),
+ BaseOpts = [{backlog, 128}, {reuseaddr, true} | Opts0],
+ Opts = sock_opts(Addr, BaseOpts),
+ case IpFamily of
+ inet6fb4 ->
+ Opts2 = [inet6 | Opts],
+ ?hlrt("try ipv6 listen", [{opts, Opts2}]),
+ case (catch ssl:listen(Port, Opts2)) of
+ {error, Reason} when ((Reason =:= nxdomain) orelse
+ (Reason =:= eafnosupport)) ->
+ Opts3 = [inet | Opts],
+ ?hlrt("ipv6 listen failed - try ipv4 instead",
+ [{reason, Reason}, {opts, Opts3}]),
+ ssl:listen(Port, Opts3);
+
+ {'EXIT', Reason} ->
+ Opts3 = [inet | Opts],
+ ?hlrt("ipv6 listen exit - try ipv4 instead",
+ [{reason, Reason}, {opts, Opts3}]),
+ ssl:listen(Port, Opts3);
+
+ Other ->
+ ?hlrt("ipv6 listen done", [{other, Other}]),
+ Other
+ end;
+
+ _ ->
+ Opts2 = [IpFamily | Opts],
+ ?hlrt("listen", [{opts, Opts2}]),
+ ssl:listen(Port, Opts2)
+ end.
+
+
ipfamily_default(Addr, Port) ->
httpd_conf:lookup(Addr, Port, ipfamily, inet6fb4).
@@ -257,9 +290,9 @@ get_socket_info(Addr, Port, Fd0) ->
%% The presence of a file descriptor takes precedence
case get_fd(Port, Fd0, IpFamilyDefault) of
{Fd, IpFamily} ->
- {0, sock_opt(ip_comm, Addr, [{fd, Fd} | BaseOpts]), IpFamily};
+ {0, sock_opts(Addr, [{fd, Fd} | BaseOpts]), IpFamily};
undefined ->
- {Port, sock_opt(ip_comm, Addr, BaseOpts), IpFamilyDefault}
+ {Port, sock_opts(Addr, BaseOpts), IpFamilyDefault}
end.
get_fd(Port, undefined = _Fd, IpFamilyDefault) ->
@@ -499,44 +532,28 @@ close({essl, _}, Socket) ->
%% connection, usning either gen_tcp or ssl.
%%-------------------------------------------------------------------------
peername(ip_comm, Socket) ->
- case inet:peername(Socket) of
- {ok,{{A, B, C, D}, Port}} ->
- PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++
- integer_to_list(C)++"."++integer_to_list(D),
- {Port, PeerName};
- {ok,{{A, B, C, D, E, F, G, H}, Port}} ->
- PeerName = http_util:integer_to_hexlist(A) ++ ":"++
- http_util:integer_to_hexlist(B) ++ ":" ++
- http_util:integer_to_hexlist(C) ++ ":" ++
- http_util:integer_to_hexlist(D) ++ ":" ++
- http_util:integer_to_hexlist(E) ++ ":" ++
- http_util:integer_to_hexlist(F) ++ ":" ++
- http_util:integer_to_hexlist(G) ++":"++
- http_util:integer_to_hexlist(H),
- {Port, PeerName};
- {error, _} ->
- {-1, "unknown"}
- end;
+ do_peername(inet:peername(Socket));
%% Wrapper for backaward compatibillity
peername({ssl, SSLConfig}, Socket) ->
peername({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
peername({ossl, _}, Socket) ->
- peername_ssl(Socket);
+ do_peername(ssl:peername(Socket));
peername({essl, _}, Socket) ->
- peername_ssl(Socket).
-
-peername_ssl(Socket) ->
- case ssl:peername(Socket) of
- {ok,{{A, B, C, D}, Port}} ->
- PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++
- integer_to_list(C)++"."++integer_to_list(D),
- {Port, PeerName};
- {error, _} ->
- {-1, "unknown"}
- end.
+ do_peername(ssl:peername(Socket)).
+
+do_peername({ok, {Addr, Port}})
+ when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
+ PeerName = ipv4_name(Addr),
+ {Port, PeerName};
+do_peername({ok, {Addr, Port}})
+ when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
+ PeerName = ipv6_name(Addr),
+ {Port, PeerName};
+do_peername({error, _}) ->
+ {-1, "unknown"}.
%%-------------------------------------------------------------------------
@@ -550,44 +567,28 @@ peername_ssl(Socket) ->
%% other end of connection, using either gen_tcp or ssl.
%%-------------------------------------------------------------------------
sockname(ip_comm, Socket) ->
- case inet:sockname(Socket) of
- {ok,{{A, B, C, D}, Port}} ->
- SockName = integer_to_list(A)++"."++integer_to_list(B)++"."++
- integer_to_list(C)++"."++integer_to_list(D),
- {Port, SockName};
- {ok,{{A, B, C, D, E, F, G, H}, Port}} ->
- SockName = http_util:integer_to_hexlist(A) ++ ":"++
- http_util:integer_to_hexlist(B) ++ ":" ++
- http_util:integer_to_hexlist(C) ++ ":" ++
- http_util:integer_to_hexlist(D) ++ ":" ++
- http_util:integer_to_hexlist(E) ++ ":" ++
- http_util:integer_to_hexlist(F) ++ ":" ++
- http_util:integer_to_hexlist(G) ++":"++
- http_util:integer_to_hexlist(H),
- {Port, SockName};
- {error, _} ->
- {-1, "unknown"}
- end;
+ do_sockname(inet:sockname(Socket));
%% Wrapper for backaward compatibillity
sockname({ssl, SSLConfig}, Socket) ->
sockname({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
sockname({ossl, _}, Socket) ->
- sockname_ssl(Socket);
+ do_sockname(ssl:sockname(Socket));
sockname({essl, _}, Socket) ->
- sockname_ssl(Socket).
-
-sockname_ssl(Socket) ->
- case ssl:sockname(Socket) of
- {ok,{{A, B, C, D}, Port}} ->
- SockName = integer_to_list(A)++"."++integer_to_list(B)++"."++
- integer_to_list(C)++"."++integer_to_list(D),
- {Port, SockName};
- {error, _} ->
- {-1, "unknown"}
- end.
+ do_sockname(ssl:sockname(Socket)).
+
+do_sockname({ok, {Addr, Port}})
+ when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
+ SockName = ipv4_name(Addr),
+ {Port, SockName};
+do_sockname({ok, {Addr, Port}})
+ when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
+ SockName = ipv6_name(Addr),
+ {Port, SockName};
+do_sockname({error, _}) ->
+ {-1, "unknown"}.
%%-------------------------------------------------------------------------
@@ -601,29 +602,49 @@ resolve() ->
Name.
+%%-------------------------------------------------------------------------
+%% ipv4_name(Ipv4Addr) -> string()
+%% ipv6_name(Ipv6Addr) -> string()
+%% Ipv4Addr = ip4_address()
+%% Ipv6Addr = ip6_address()
+%%
+%% Description: Returns the local hostname.
+%%-------------------------------------------------------------------------
+ipv4_name({A, B, C, D}) ->
+ integer_to_list(A) ++ "." ++
+ integer_to_list(B) ++ "." ++
+ integer_to_list(C) ++ "." ++
+ integer_to_list(D).
+
+ipv6_name({A, B, C, D, E, F, G, H}) ->
+ http_util:integer_to_hexlist(A) ++ ":"++
+ http_util:integer_to_hexlist(B) ++ ":" ++
+ http_util:integer_to_hexlist(C) ++ ":" ++
+ http_util:integer_to_hexlist(D) ++ ":" ++
+ http_util:integer_to_hexlist(E) ++ ":" ++
+ http_util:integer_to_hexlist(F) ++ ":" ++
+ http_util:integer_to_hexlist(G) ++ ":" ++
+ http_util:integer_to_hexlist(H).
+
+
%%%========================================================================
%%% Internal functions
%%%========================================================================
+%% -- sock_opts --
%% Address any comes from directive: BindAddress "*"
-sock_opt(ip_comm, any = Addr, Opts) ->
- sock_opt2([{ip, Addr} | Opts]);
-sock_opt(ip_comm, undefined, Opts) ->
- sock_opt2(Opts);
-sock_opt(_, any = _Addr, Opts) ->
- sock_opt2(Opts);
-sock_opt(_, undefined = _Addr, Opts) ->
- sock_opt2(Opts);
-sock_opt(_, {_,_,_,_} = Addr, Opts) ->
- sock_opt2([{ip, Addr} | Opts]);
-sock_opt(ip_comm, Addr, Opts) ->
- sock_opt2([{ip, Addr} | Opts]);
-sock_opt(_, Addr, Opts) ->
- sock_opt2([{ip, Addr} | Opts]).
-
-sock_opt2(Opts) ->
+sock_opts(undefined, Opts) ->
+ sock_opts(Opts);
+sock_opts(any = Addr, Opts) ->
+ sock_opts([{ip, Addr} | Opts]);
+sock_opts(Addr, Opts) ->
+ sock_opts([{ip, Addr} | Opts]).
+
+sock_opts(Opts) ->
[{packet, 0}, {active, false} | Opts].
+
+%% -- negotiate --
negotiate(ip_comm,_,_) ->
?hlrt("negotiate(ip_comm)", []),
ok;
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index 47f3fbba58..8b0fcb185d 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -18,6 +18,15 @@
{"%VSN%",
[
+ {"5.6",
+ [
+ {load_module, httpc, soft_purge, soft_purge, [httpc_manager]},
+ {load_module, http_transport, soft_purge, soft_purge, [http_transport]},
+ {update, httpc_handler, soft, soft_purge, soft_purge, []},
+ {update, httpc_manager, soft, soft_purge, soft_purge, [httpc_handler]},
+ {update, ftp, soft, soft_purge, soft_purge, []}
+ ]
+ },
{"5.5.2",
[
{restart_application, inets}
@@ -40,6 +49,15 @@
}
],
[
+ {"5.6",
+ [
+ {load_module, httpc, soft_purge, soft_purge, [httpc_manager]},
+ {load_module, http_transport, soft_purge, soft_purge, [http_transport]},
+ {update, httpc_handler, soft, soft_purge, soft_purge, []},
+ {update, httpc_manager, soft, soft_purge, soft_purge, [httpc_handler]},
+ {update, ftp, soft, soft_purge, soft_purge, []}
+ ]
+ },
{"5.5.2",
[
{restart_application, inets}
diff --git a/lib/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl
index d0d07a8358..3ebd02229e 100644
--- a/lib/inets/test/ftp_suite_lib.erl
+++ b/lib/inets/test/ftp_suite_lib.erl
@@ -1129,10 +1129,16 @@ ticket_6035(Config) ->
LogFile = filename:join([PrivDir,"ticket_6035.log"]),
try
begin
+ p("ticket_6035 -> select ftpd host"),
Host = dirty_select_ftpd_host(Config),
+ p("ticket_6035 -> ftpd host selected (~p) => now spawn ftp owner", [Host]),
Pid = spawn(?MODULE, open_wait_6035, [Host, self()]),
+ p("ticket_6035 -> waiter spawned: ~p => now open error logfile (~p)",
+ [Pid, LogFile]),
error_logger:logfile({open, LogFile}),
- ok = kill_ftp_proc_6035(Pid,LogFile),
+ p("ticket_6035 -> error logfile open => now kill waiter process"),
+ true = kill_ftp_proc_6035(Pid, LogFile),
+ p("ticket_6035 -> waiter process killed => now close error logfile"),
error_logger:logfile(close),
p("ticket_6035 -> done", []),
ok
@@ -1146,7 +1152,7 @@ kill_ftp_proc_6035(Pid, LogFile) ->
p("kill_ftp_proc_6035 -> entry"),
receive
open ->
- p("kill_ftp_proc_6035 -> received open: send shutdown"),
+ p("kill_ftp_proc_6035 -> received open => now issue shutdown"),
exit(Pid, shutdown),
kill_ftp_proc_6035(Pid, LogFile);
{open_failed, Reason} ->
@@ -1159,11 +1165,11 @@ kill_ftp_proc_6035(Pid, LogFile) ->
is_error_report_6035(LogFile)
end.
-open_wait_6035(FtpServer, From) ->
- p("open_wait_6035 -> try connect to ~s", [FtpServer]),
+open_wait_6035({Tag, FtpServer}, From) ->
+ p("open_wait_6035 -> try connect to [~p] ~s for ~p", [Tag, FtpServer, From]),
case ftp:open(FtpServer, [{timeout, timer:seconds(15)}]) of
{ok, Pid} ->
- p("open_wait_6035 -> connected, now login"),
+ p("open_wait_6035 -> connected (~p), now login", [Pid]),
LoginResult = ftp:user(Pid,"anonymous","kldjf"),
p("open_wait_6035 -> login result: ~p", [LoginResult]),
From ! open,
@@ -1191,22 +1197,27 @@ is_error_report_6035(LogFile) ->
Res =
case file:read_file(LogFile) of
{ok, Bin} ->
- p("is_error_report_6035 -> logfile read"),
- read_log_6035(binary_to_list(Bin));
+ Txt = binary_to_list(Bin),
+ p("is_error_report_6035 -> logfile read: ~n~p", [Txt]),
+ read_log_6035(Txt);
_ ->
- ok
+ false
end,
p("is_error_report_6035 -> logfile read result: "
"~n ~p", [Res]),
- file:delete(LogFile),
+ %% file:delete(LogFile),
Res.
read_log_6035("=ERROR REPORT===="++_Rest) ->
- error_report;
-read_log_6035([_H|T]) ->
+ p("read_log_6035 -> ERROR REPORT detected"),
+ true;
+read_log_6035([H|T]) ->
+ p("read_log_6035 -> OTHER: "
+ "~p", [H]),
read_log_6035(T);
read_log_6035([]) ->
- ok.
+ p("read_log_6035 -> done"),
+ false.
%%--------------------------------------------------------------------
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index 1998bd3950..6edd5371af 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -64,16 +64,6 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[
- proxy_options,
- proxy_head,
- proxy_get,
- proxy_trace,
- proxy_post,
- proxy_put,
- proxy_delete,
- proxy_auth,
- proxy_headers,
- proxy_emulate_lower_versions,
http_options,
http_head,
http_get,
@@ -88,15 +78,6 @@ all() ->
http_headers,
http_headers_dummy,
http_bad_response,
- ssl_head,
- ossl_head,
- essl_head,
- ssl_get,
- ossl_get,
- essl_get,
- ssl_trace,
- ossl_trace,
- essl_trace,
http_redirect,
http_redirect_loop,
http_internal_server_error,
@@ -106,21 +87,44 @@ all() ->
http_emulate_lower_versions,
http_relaxed,
page_does_not_exist,
- proxy_page_does_not_exist,
- proxy_https_not_supported,
- http_stream,
- http_stream_once,
- proxy_stream,
parse_url,
options,
- ipv6,
headers_as_is,
+ {group, proxy},
+ {group, ssl},
+ {group, stream},
+ {group, ipv6},
{group, tickets},
initial_server_connect
].
groups() ->
- [{tickets, [], [hexed_query_otp_6191,
+ [
+ {proxy, [], [proxy_options,
+ proxy_head,
+ proxy_get,
+ proxy_trace,
+ proxy_post,
+ proxy_put,
+ proxy_delete,
+ proxy_auth,
+ proxy_headers,
+ proxy_emulate_lower_versions,
+ proxy_page_does_not_exist,
+ proxy_https_not_supported]},
+ {ssl, [], [ssl_head,
+ ossl_head,
+ essl_head,
+ ssl_get,
+ ossl_get,
+ essl_get,
+ ssl_trace,
+ ossl_trace,
+ essl_trace]},
+ {stream, [], [http_stream,
+ http_stream_once,
+ proxy_stream]},
+ {tickets, [], [hexed_query_otp_6191,
empty_body_otp_6243,
empty_response_header_otp_6830,
transfer_encoding_otp_6807,
@@ -139,7 +143,10 @@ groups() ->
{otp_8154, [], [otp_8154_1]},
{otp_8106, [], [otp_8106_pid,
otp_8106_fun,
- otp_8106_mfa]}].
+ otp_8106_mfa]},
+ {ipv6, [], [ipv6_ipcomm, ipv6_essl]}
+ ].
+
init_per_group(_GroupName, Config) ->
@@ -213,36 +220,38 @@ end_per_suite(Config) ->
%% Note: This function is free to add any key/value pairs to the Config
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
+
init_per_testcase(otp_8154_1 = Case, Config) ->
init_per_testcase(Case, 5, Config);
-init_per_testcase(initial_server_connect, Config) ->
+init_per_testcase(initial_server_connect = Case, Config) ->
%% Try to check if crypto actually exist or not,
%% this test case does not work unless it does
- case (catch crypto:start()) of
- ok ->
- application:start(public_key),
- application:start(ssl),
+ try
+ begin
+ ensure_started(crypto),
+ ensure_started(public_key),
+ ensure_started(ssl),
inets:start(),
- Config;
- _ ->
- {skip,"Could not start crypto"}
+ Config
+ end
+ catch
+ throw:{error, {failed_starting, App, ActualError}} ->
+ tsp("init_per_testcase(~w) -> failed starting ~w: "
+ "~n ~p", [Case, App, ActualError]),
+ SkipString =
+ "Could not start " ++ atom_to_list(App),
+ {skip, SkipString};
+ _:X ->
+ SkipString =
+ lists:flatten(
+ io_lib:format("Failed starting apps: ~p", [X])),
+ {skip, SkipString}
end;
init_per_testcase(Case, Config) ->
init_per_testcase(Case, 2, Config).
-init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) ->
- tsp("init_per_testcase_ssl -> stop ssl"),
- application:stop(ssl),
- Config2 = lists:keydelete(local_ssl_server, 1, Config),
- %% Will start inets
- tsp("init_per_testcase_ssl -> try start http server (including inets)"),
- Server = inets_test_lib:start_http_server(
- filename:join(PrivDir, SslConfFile), Tag),
- tsp("init_per_testcase -> Server: ~p", [Server]),
- [{local_ssl_server, Server} | Config2].
-
init_per_testcase(Case, Timeout, Config) ->
io:format(user, "~n~n*** INIT ~w:~w[~w] ***~n~n",
[?MODULE, Case, Timeout]),
@@ -261,13 +270,16 @@ init_per_testcase(Case, Timeout, Config) ->
NewConfig =
case atom_to_list(Case) of
[$s, $s, $l | _] ->
- init_per_testcase_ssl(ssl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]);
+ init_per_testcase_ssl(ssl, PrivDir, SslConfFile,
+ [{watchdog, Dog} | TmpConfig]);
[$o, $s, $s, $l | _] ->
- init_per_testcase_ssl(ossl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]);
+ init_per_testcase_ssl(ossl, PrivDir, SslConfFile,
+ [{watchdog, Dog} | TmpConfig]);
[$e, $s, $s, $l | _] ->
- init_per_testcase_ssl(essl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]);
+ init_per_testcase_ssl(essl, PrivDir, SslConfFile,
+ [{watchdog, Dog} | TmpConfig]);
"proxy_" ++ Rest ->
io:format("init_per_testcase -> Rest: ~p~n", [Rest]),
@@ -275,16 +287,23 @@ init_per_testcase(Case, Timeout, Config) ->
"https_not_supported" ->
tsp("init_per_testcase -> [proxy case] start inets"),
inets:start(),
- tsp("init_per_testcase -> [proxy case] start ssl"),
- application:start(crypto),
- application:start(public_key),
- case (catch application:start(ssl)) of
+ tsp("init_per_testcase -> "
+ "[proxy case] start crypto, public_key and ssl"),
+ try ensure_started([crypto, public_key, ssl]) of
ok ->
- [{watchdog, Dog} | TmpConfig];
- _ ->
- [{skip, "SSL does not seem to be supported"}
- | TmpConfig]
+ [{watchdog, Dog} | TmpConfig]
+ catch
+ throw:{error, {failed_starting, App, _}} ->
+ SkipString =
+ "Could not start " ++ atom_to_list(App),
+ {skip, SkipString};
+ _:X ->
+ SkipString =
+ lists:flatten(
+ io_lib:format("Failed starting apps: ~p", [X])),
+ {skip, SkipString}
end;
+
_ ->
%% We use erlang.org for the proxy tests
%% and after the switch to erlang-web, many
@@ -321,6 +340,33 @@ init_per_testcase(Case, Timeout, Config) ->
[{skip, "proxy not responding"} | TmpConfig]
end
end;
+
+ "ipv6_" ++ _Rest ->
+ %% Ensure needed apps (crypto, public_key and ssl) started
+ try ensure_started([crypto, public_key, ssl]) of
+ ok ->
+ Profile = ipv6,
+ %% A stand-alone profile is represented by a pid()
+ {ok, ProfilePid} =
+ inets:start(httpc,
+ [{profile, Profile},
+ {data_dir, PrivDir}], stand_alone),
+ httpc:set_options([{ipfamily, inet6}], ProfilePid),
+ tsp("httpc profile pid: ~p", [ProfilePid]),
+ [{watchdog, Dog}, {profile, ProfilePid}| TmpConfig]
+ catch
+ throw:{error, {failed_starting, App, ActualError}} ->
+ tsp("init_per_testcase(~w) -> failed starting ~w: "
+ "~n ~p", [Case, App, ActualError]),
+ SkipString =
+ "Could not start " ++ atom_to_list(App),
+ {skip, SkipString};
+ _:X ->
+ SkipString =
+ lists:flatten(
+ io_lib:format("Failed starting apps: ~p", [X])),
+ {skip, SkipString}
+ end;
_ ->
TmpConfig2 = lists:keydelete(local_server, 1, TmpConfig),
Server =
@@ -330,9 +376,7 @@ init_per_testcase(Case, Timeout, Config) ->
[{watchdog, Dog}, {local_server, Server} | TmpConfig2]
end,
- %% httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT},
- %% ["localhost", ?IPV6_LOCAL_HOST]}}]),
-
+ %% This will fail for the ipv6_ - cases (but that is ok)
httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT},
["localhost", ?IPV6_LOCAL_HOST]}},
{ipfamily, inet6fb4}]),
@@ -341,6 +385,19 @@ init_per_testcase(Case, Timeout, Config) ->
NewConfig.
+init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) ->
+ tsp("init_per_testcase_ssl(~w) -> stop ssl", [Tag]),
+ application:stop(ssl),
+ Config2 = lists:keydelete(local_ssl_server, 1, Config),
+ %% Will start inets
+ tsp("init_per_testcase_ssl(~w) -> try start http server (including inets)",
+ [Tag]),
+ Server = inets_test_lib:start_http_server(
+ filename:join(PrivDir, SslConfFile), Tag),
+ tsp("init_per_testcase(~w) -> Server: ~p", [Tag, Server]),
+ [{local_ssl_server, Server} | Config2].
+
+
%%--------------------------------------------------------------------
%% Function: end_per_testcase(Case, Config) -> _
%% Case - atom()
@@ -349,13 +406,36 @@ init_per_testcase(Case, Timeout, Config) ->
%% A list of key/value pairs, holding the test case configuration.
%% Description: Cleanup after each test case
%%--------------------------------------------------------------------
-end_per_testcase(http_save_to_file, Config) ->
- PrivDir = ?config(priv_dir, Config),
+end_per_testcase(http_save_to_file = Case, Config) ->
+ io:format(user, "~n~n*** END ~w:~w ***~n~n",
+ [?MODULE, Case]),
+ PrivDir = ?config(priv_dir, Config),
FullPath = filename:join(PrivDir, "dummy.html"),
file:delete(FullPath),
finish(Config);
-end_per_testcase(_, Config) ->
+end_per_testcase(Case, Config) ->
+ io:format(user, "~n~n*** END ~w:~w ***~n~n",
+ [?MODULE, Case]),
+ case atom_to_list(Case) of
+ "ipv6_" ++ _Rest ->
+ tsp("end_per_testcase(~w) -> stop ssl", [Case]),
+ application:stop(ssl),
+ tsp("end_per_testcase(~w) -> stop public_key", [Case]),
+ application:stop(public_key),
+ tsp("end_per_testcase(~w) -> stop crypto", [Case]),
+ application:stop(crypto),
+ ProfilePid = ?config(profile, Config),
+ tsp("end_per_testcase(~w) -> stop httpc profile (~p)",
+ [Case, ProfilePid]),
+ unlink(ProfilePid),
+ inets:stop(stand_alone, ProfilePid),
+ tsp("end_per_testcase(~w) -> httpc profile (~p) stopped",
+ [Case, ProfilePid]),
+ ok;
+ _ ->
+ ok
+ end,
finish(Config).
finish(Config) ->
@@ -364,6 +444,7 @@ finish(Config) ->
undefined ->
ok;
_ ->
+ tsp("finish -> stop watchdog (~p)", [Dog]),
test_server:timetrap_cancel(Dog)
end.
@@ -565,7 +646,7 @@ http_relaxed(suite) ->
http_relaxed(Config) when is_list(Config) ->
ok = httpc:set_options([{ipv6, disabled}]), % also test the old option
%% ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++
"/missing_reason_phrase.html",
@@ -591,7 +672,7 @@ http_dummy_pipe(suite) ->
[];
http_dummy_pipe(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/foobar.html",
@@ -905,7 +986,7 @@ http_headers_dummy(suite) ->
[];
http_headers_dummy(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy_headers.html",
@@ -970,7 +1051,7 @@ http_bad_response(suite) ->
[];
http_bad_response(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_crlf.html",
@@ -1089,9 +1170,9 @@ ssl_get(SslTag, Config) when is_list(Config) ->
httpc:request(get, {URL, []}, [{ssl, SSLConfig}], []),
inets_test_lib:check_body(Body);
{ok, _} ->
- {skip, "Failed to start local http-server"};
+ {skip, "local http-server not started"};
_ ->
- {skip, "Failed to start SSL"}
+ {skip, "SSL not started"}
end.
@@ -1149,9 +1230,9 @@ ssl_trace(SslTag, Config) when is_list(Config) ->
tsf({failed, Error})
end;
{ok, _} ->
- {skip, "Failed to start local http-server"};
+ {skip, "local http-server not started"};
_ ->
- {skip, "Failed to start SSL"}
+ {skip, "SSL not started"}
end.
@@ -1170,7 +1251,7 @@ http_redirect(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
tsp("http_redirect -> start dummy server inet"),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
tsp("http_redirect -> server port = ~p", [Port]),
URL300 = ?URL_START ++ integer_to_list(Port) ++ "/300.html",
@@ -1282,7 +1363,7 @@ http_redirect_loop(suite) ->
[];
http_redirect_loop(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/redirectloop.html",
@@ -1299,7 +1380,7 @@ http_internal_server_error(suite) ->
[];
http_internal_server_error(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL500 = ?URL_START ++ integer_to_list(Port) ++ "/500.html",
@@ -1335,7 +1416,7 @@ http_userinfo(suite) ->
http_userinfo(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URLAuth = "http://alladin:sesame@localhost:"
++ integer_to_list(Port) ++ "/userinfo.html",
@@ -1361,7 +1442,7 @@ http_cookie(suite) ->
[];
http_cookie(Config) when is_list(Config) ->
ok = httpc:set_options([{cookies, enabled}, {ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URLStart = ?URL_START
++ integer_to_list(Port),
@@ -1735,7 +1816,7 @@ http_stream_once(Config) when is_list(Config) ->
p("http_stream_once -> set ipfamily to inet", []),
ok = httpc:set_options([{ipfamily, inet}]),
p("http_stream_once -> start dummy server", []),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
PortStr = integer_to_list(Port),
p("http_stream_once -> once", []),
@@ -1871,28 +1952,79 @@ parse_url(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-ipv6() ->
- [{require,ipv6_hosts}].
-ipv6(doc) ->
- ["Test ipv6."];
-ipv6(suite) ->
- [];
-ipv6(Config) when is_list(Config) ->
- {ok, Hostname} = inet:gethostname(),
-
- case lists:member(list_to_atom(Hostname),
- ct:get_config(ipv6_hosts)) of
- true ->
- {DummyServerPid, Port} = dummy_server(self(), ipv6),
-
- URL = "http://[" ++ ?IPV6_LOCAL_HOST ++ "]:" ++
+
+ipv6_ipcomm() ->
+ [{require, ipv6_hosts}].
+ipv6_ipcomm(doc) ->
+ ["Test ip_comm ipv6."];
+ipv6_ipcomm(suite) ->
+ [];
+ipv6_ipcomm(Config) when is_list(Config) ->
+ HTTPOptions = [],
+ SocketType = ip_comm,
+ Scheme = "http",
+ Extra = [],
+ ipv6(SocketType, Scheme, HTTPOptions, Extra, Config).
+
+
+%%-------------------------------------------------------------------------
+
+ipv6_essl() ->
+ [{require, ipv6_hosts}].
+ipv6_essl(doc) ->
+ ["Test essl ipv6."];
+ipv6_essl(suite) ->
+ [];
+ipv6_essl(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ CertFile = filename:join(DataDir, "ssl_client_cert.pem"),
+ SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}],
+ SSLConfig = {essl, SSLOptions},
+ tsp("ssl_ipv6 -> make request using: "
+ "~n SSLOptions: ~p", [SSLOptions]),
+ HTTPOptions = [{ssl, SSLConfig}],
+ SocketType = essl,
+ Scheme = "https",
+ Extra = SSLOptions,
+ ipv6(SocketType, Scheme, HTTPOptions, Extra, Config).
+
+
+%%-------------------------------------------------------------------------
+
+ipv6(SocketType, Scheme, HTTPOptions, Extra, Config) ->
+ %% Check if we are a IPv6 host
+ tsp("ipv6 -> verify ipv6 support", []),
+ case inets_test_lib:has_ipv6_support(Config) of
+ {ok, Addr} ->
+ tsp("ipv6 -> ipv6 supported: ~p", [Addr]),
+ {DummyServerPid, Port} = dummy_server(SocketType, ipv6, Extra),
+ Profile = ?config(profile, Config),
+ URL =
+ Scheme ++
+ "://[" ++ http_transport:ipv6_name(Addr) ++ "]:" ++
integer_to_list(Port) ++ "/foobar.html",
- {ok, {{_,200,_}, [_ | _], [_|_]}} =
- httpc:request(get, {URL, []}, [], []),
-
- DummyServerPid ! stop,
+ tsp("ipv6 -> issue request with: "
+ "~n URL: ~p"
+ "~n HTTPOptions: ~p", [URL, HTTPOptions]),
+ case httpc:request(get, {URL, []}, HTTPOptions, [], Profile) of
+ {ok, {{_,200,_}, [_ | _], [_|_]}} ->
+ tsp("ipv6 -> expected result"),
+ DummyServerPid ! stop,
+ ok;
+ {ok, Unexpected} ->
+ tsp("ipv6 -> unexpected result: "
+ "~n ~p", [Unexpected]),
+ DummyServerPid ! stop,
+ tsf({unexpected_result, Unexpected});
+ {error, Reason} ->
+ tsp("ipv6 -> error: "
+ "~n Reason: ~p", [Reason]),
+ DummyServerPid ! stop,
+ tsf(Reason)
+ end,
ok;
- false ->
+ _ ->
+ tsp("ipv6 -> ipv6 not supported", []),
{skip, "Host does not support IPv6"}
end.
@@ -1945,7 +2077,7 @@ http_invalid_http(suite) ->
[];
http_invalid_http(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/invalid_http.html",
@@ -2002,7 +2134,7 @@ transfer_encoding_otp_6807(suite) ->
[];
transfer_encoding_otp_6807(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++
"/capital_transfer_encoding.html",
@@ -2035,7 +2167,7 @@ empty_response_header_otp_6830(suite) ->
[];
empty_response_header_otp_6830(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/no_headers.html",
{ok, {{_,200,_}, [], [_ | _]}} = httpc:request(URL),
@@ -2052,7 +2184,7 @@ no_content_204_otp_6982(suite) ->
[];
no_content_204_otp_6982(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/no_content.html",
{ok, {{_,204,_}, [], []}} = httpc:request(URL),
@@ -2070,7 +2202,7 @@ missing_CR_otp_7304(suite) ->
[];
missing_CR_otp_7304(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_CR.html",
{ok, {{_,200,_}, _, [_ | _]}} = httpc:request(URL),
@@ -2089,7 +2221,7 @@ otp_7883_1(suite) ->
otp_7883_1(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html",
{error, socket_closed_remotely} = httpc:request(URL),
@@ -2105,7 +2237,7 @@ otp_7883_2(suite) ->
otp_7883_2(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html",
Method = get,
@@ -2626,7 +2758,7 @@ otp_8371(suite) ->
[];
otp_8371(Config) when is_list(Config) ->
ok = httpc:set_options([{ipv6, disabled}]), % also test the old option
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++
"/ensure_host_header_with_port.html",
@@ -2864,75 +2996,179 @@ receive_streamed_body(RequestId, Body, Pid) ->
test_server:fail(Msg)
end.
+%% Perform a synchronous stop
+dummy_server_stop(Pid) ->
+ Pid ! {stop, self()},
+ receive
+ {stopped, Pid} ->
+ ok
+ end.
+
+dummy_server(IpV) ->
+ dummy_server(self(), ip_comm, IpV, []).
+dummy_server(SocketType, IpV, Extra) ->
+ dummy_server(self(), SocketType, IpV, Extra).
-dummy_server(Caller, IpV) ->
- Pid = spawn(httpc_SUITE, dummy_server_init, [Caller, IpV]),
+dummy_server(Caller, SocketType, IpV, Extra) ->
+ Args = [Caller, SocketType, IpV, Extra],
+ Pid = spawn(httpc_SUITE, dummy_server_init, Args),
receive
{port, Port} ->
{Pid, Port}
end.
-dummy_server_init(Caller, IpV) ->
+dummy_server_init(Caller, ip_comm, IpV, _) ->
+ BaseOpts = [binary, {packet, 0}, {reuseaddr,true}, {active, false}],
{ok, ListenSocket} =
case IpV of
ipv4 ->
- gen_tcp:listen(0, [binary, inet, {packet, 0},
- {reuseaddr,true},
- {active, false}]);
+ tsp("ip_comm ipv4 listen", []),
+ gen_tcp:listen(0, [inet | BaseOpts]);
ipv6 ->
- gen_tcp:listen(0, [binary, inet6, {packet, 0},
- {reuseaddr,true},
- {active, false}])
+ tsp("ip_comm ipv6 listen", []),
+ gen_tcp:listen(0, [inet6 | BaseOpts])
end,
{ok, Port} = inet:port(ListenSocket),
- tsp("dummy_server_init -> Port: ~p", [Port]),
+ tsp("dummy_server_init(ip_comm) -> Port: ~p", [Port]),
Caller ! {port, Port},
- dummy_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]},
- [], ListenSocket).
+ dummy_ipcomm_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]},
+ [], ListenSocket);
+dummy_server_init(Caller, essl, IpV, SSLOptions) ->
+ BaseOpts = [{ssl_imp, new},
+ {backlog, 128}, binary, {reuseaddr,true}, {active, false} |
+ SSLOptions],
+ dummy_ssl_server_init(Caller, BaseOpts, IpV);
+dummy_server_init(Caller, ossl, IpV, SSLOptions) ->
+ BaseOpts = [{ssl_imp, old},
+ {backlog, 128}, binary, {active, false} | SSLOptions],
+ dummy_ssl_server_init(Caller, BaseOpts, IpV).
+
+dummy_ssl_server_init(Caller, BaseOpts, IpV) ->
+ {ok, ListenSocket} =
+ case IpV of
+ ipv4 ->
+ tsp("dummy_ssl_server_init -> ssl ipv4 listen", []),
+ ssl:listen(0, [inet | BaseOpts]);
+ ipv6 ->
+ tsp("dummy_ssl_server_init -> ssl ipv6 listen", []),
+ ssl:listen(0, [inet6 | BaseOpts])
+ end,
+ tsp("dummy_ssl_server_init -> ListenSocket: ~p", [ListenSocket]),
+ {ok, {_, Port}} = ssl:sockname(ListenSocket),
+ tsp("dummy_ssl_server_init -> Port: ~p", [Port]),
+ Caller ! {port, Port},
+ dummy_ssl_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]},
+ [], ListenSocket).
-dummy_server_loop(MFA, Handlers, ListenSocket) ->
+dummy_ipcomm_server_loop(MFA, Handlers, ListenSocket) ->
receive
stop ->
- lists:foreach(fun(Handler) -> Handler ! stop end, Handlers)
+ tsp("dummy_ipcomm_server_loop -> stop handlers", []),
+ lists:foreach(fun(Handler) -> Handler ! stop end, Handlers);
+ {stop, From} ->
+ tsp("dummy_ipcomm_server_loop -> "
+ "stop command from ~p for handlers (~p)", [From, Handlers]),
+ Stopper = fun(Handler) -> Handler ! stop end,
+ lists:foreach(Stopper, Handlers),
+ From ! {stopped, self()}
after 0 ->
+ tsp("dummy_ipcomm_server_loop -> await accept", []),
{ok, Socket} = gen_tcp:accept(ListenSocket),
+ tsp("dummy_ipcomm_server_loop -> accepted: ~p", [Socket]),
HandlerPid = dummy_request_handler(MFA, Socket),
+ tsp("dummy_icomm_server_loop -> handler created: ~p", [HandlerPid]),
gen_tcp:controlling_process(Socket, HandlerPid),
- HandlerPid ! controller,
- dummy_server_loop(MFA, [HandlerPid | Handlers],
+ tsp("dummy_ipcomm_server_loop -> "
+ "control transfered to handler", []),
+ HandlerPid ! ipcomm_controller,
+ tsp("dummy_ipcomm_server_loop -> "
+ "handler informed about control transfer", []),
+ dummy_ipcomm_server_loop(MFA, [HandlerPid | Handlers],
ListenSocket)
end.
+dummy_ssl_server_loop(MFA, Handlers, ListenSocket) ->
+ receive
+ stop ->
+ tsp("dummy_ssl_server_loop -> stop handlers", []),
+ lists:foreach(fun(Handler) -> Handler ! stop end, Handlers);
+ {stop, From} ->
+ tsp("dummy_ssl_server_loop -> "
+ "stop command from ~p for handlers (~p)", [From, Handlers]),
+ Stopper = fun(Handler) -> Handler ! stop end,
+ lists:foreach(Stopper, Handlers),
+ From ! {stopped, self()}
+ after 0 ->
+ tsp("dummy_ssl_server_loop -> await accept", []),
+ {ok, Socket} = ssl:transport_accept(ListenSocket),
+ tsp("dummy_ssl_server_loop -> accepted: ~p", [Socket]),
+ HandlerPid = dummy_request_handler(MFA, Socket),
+ tsp("dummy_ssl_server_loop -> handler created: ~p", [HandlerPid]),
+ ssl:controlling_process(Socket, HandlerPid),
+ tsp("dummy_ssl_server_loop -> control transfered to handler", []),
+ HandlerPid ! ssl_controller,
+ tsp("dummy_ssl_server_loop -> "
+ "handler informed about control transfer", []),
+ dummy_ssl_server_loop(MFA, [HandlerPid | Handlers],
+ ListenSocket)
+ end.
+
dummy_request_handler(MFA, Socket) ->
+ tsp("spawn request handler", []),
spawn(httpc_SUITE, dummy_request_handler_init, [MFA, Socket]).
dummy_request_handler_init(MFA, Socket) ->
- receive
- controller ->
- inet:setopts(Socket, [{active, true}])
- end,
- dummy_request_handler_loop(MFA, Socket).
+ SockType =
+ receive
+ ipcomm_controller ->
+ tsp("dummy_request_handler_init -> "
+ "received ip_comm controller - activate", []),
+ inet:setopts(Socket, [{active, true}]),
+ ip_comm;
+ ssl_controller ->
+ tsp("dummy_request_handler_init -> "
+ "received ssl controller - activate", []),
+ ssl:setopts(Socket, [{active, true}]),
+ ssl
+ end,
+ dummy_request_handler_loop(MFA, SockType, Socket).
-dummy_request_handler_loop({Module, Function, Args}, Socket) ->
+dummy_request_handler_loop({Module, Function, Args}, SockType, Socket) ->
tsp("dummy_request_handler_loop -> entry with"
"~n Module: ~p"
"~n Function: ~p"
"~n Args: ~p", [Module, Function, Args]),
receive
- {tcp, _, Data} ->
- tsp("dummy_request_handler_loop -> Data ~p", [Data]),
- case handle_request(Module, Function, [Data | Args], Socket) of
- stop ->
+ {Proto, _, Data} when (Proto =:= tcp) orelse (Proto =:= ssl) ->
+ tsp("dummy_request_handler_loop -> [~w] Data ~p", [Proto, Data]),
+ case handle_request(Module, Function, [Data | Args], Socket, Proto) of
+ stop when Proto =:= tcp ->
gen_tcp:close(Socket);
+ stop when Proto =:= ssl ->
+ ssl:close(Socket);
NewMFA ->
- dummy_request_handler_loop(NewMFA, Socket)
+ dummy_request_handler_loop(NewMFA, SockType, Socket)
end;
- stop ->
- gen_tcp:close(Socket)
+ stop when SockType =:= ip_comm ->
+ gen_tcp:close(Socket);
+ stop when SockType =:= ssl ->
+ ssl:close(Socket)
end.
-handle_request(Module, Function, Args, Socket) ->
+
+mk_close(tcp) -> fun(Sock) -> gen_tcp:close(Sock) end;
+mk_close(ssl) -> fun(Sock) -> ssl:close(Sock) end.
+
+mk_send(tcp) -> fun(Sock, Data) -> gen_tcp:send(Sock, Data) end;
+mk_send(ssl) -> fun(Sock, Data) -> ssl:send(Sock, Data) end.
+
+handle_request(Module, Function, Args, Socket, Proto) ->
+ Close = mk_close(Proto),
+ Send = mk_send(Proto),
+ handle_request(Module, Function, Args, Socket, Close, Send).
+
+handle_request(Module, Function, Args, Socket, Close, Send) ->
tsp("handle_request -> entry with"
"~n Module: ~p"
"~n Function: ~p"
@@ -2941,7 +3177,7 @@ handle_request(Module, Function, Args, Socket) ->
{ok, Result} ->
tsp("handle_request -> ok"
"~n Result: ~p", [Result]),
- case (catch handle_http_msg(Result, Socket)) of
+ case (catch handle_http_msg(Result, Socket, Close, Send)) of
stop ->
stop;
<<>> ->
@@ -2949,7 +3185,8 @@ handle_request(Module, Function, Args, Socket) ->
{httpd_request, parse, [[<<>>, ?HTTP_MAX_HEADER_SIZE]]};
Data ->
handle_request(httpd_request, parse,
- [Data |[?HTTP_MAX_HEADER_SIZE]], Socket)
+ [Data |[?HTTP_MAX_HEADER_SIZE]], Socket,
+ Close, Send)
end;
NewMFA ->
tsp("handle_request -> "
@@ -2957,7 +3194,7 @@ handle_request(Module, Function, Args, Socket) ->
NewMFA
end.
-handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) ->
+handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket, Close, Send) ->
tsp("handle_http_msg -> entry with: "
"~n RelUri: ~p"
"~n Headers: ~p"
@@ -3114,16 +3351,16 @@ handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) ->
"Expires:Sat, 29 Oct 1994 19:43:31 GMT\r\n" ++
"Proxy-Authenticate:#1Basic" ++
"\r\n\r\n",
- gen_tcp:send(Socket, Head),
- gen_tcp:send(Socket, http_chunk:encode("<HTML><BODY>fo")),
- gen_tcp:send(Socket, http_chunk:encode("obar</BODY></HTML>")),
+ Send(Socket, Head),
+ Send(Socket, http_chunk:encode("<HTML><BODY>fo")),
+ Send(Socket, http_chunk:encode("obar</BODY></HTML>")),
http_chunk:encode_last();
"/capital_transfer_encoding.html" ->
Head = "HTTP/1.1 200 ok\r\n" ++
"Transfer-Encoding:Chunked\r\n\r\n",
- gen_tcp:send(Socket, Head),
- gen_tcp:send(Socket, http_chunk:encode("<HTML><BODY>fo")),
- gen_tcp:send(Socket, http_chunk:encode("obar</BODY></HTML>")),
+ Send(Socket, Head),
+ Send(Socket, http_chunk:encode("<HTML><BODY>fo")),
+ Send(Socket, http_chunk:encode("obar</BODY></HTML>")),
http_chunk:encode_last();
"/cookie.html" ->
"HTTP/1.1 200 ok\r\n" ++
@@ -3142,20 +3379,20 @@ handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) ->
"/once_chunked.html" ->
Head = "HTTP/1.1 200 ok\r\n" ++
"Transfer-Encoding:Chunked\r\n\r\n",
- gen_tcp:send(Socket, Head),
- gen_tcp:send(Socket, http_chunk:encode("<HTML><BODY>fo")),
- gen_tcp:send(Socket,
+ Send(Socket, Head),
+ Send(Socket, http_chunk:encode("<HTML><BODY>fo")),
+ Send(Socket,
http_chunk:encode("obar</BODY></HTML>")),
http_chunk:encode_last();
"/once.html" ->
Head = "HTTP/1.1 200 ok\r\n" ++
"Content-Length:32\r\n\r\n",
- gen_tcp:send(Socket, Head),
- gen_tcp:send(Socket, "<HTML><BODY>fo"),
+ Send(Socket, Head),
+ Send(Socket, "<HTML><BODY>fo"),
test_server:sleep(1000),
- gen_tcp:send(Socket, "ob"),
+ Send(Socket, "ob"),
test_server:sleep(1000),
- gen_tcp:send(Socket, "ar</BODY></HTML>");
+ Send(Socket, "ar</BODY></HTML>");
"/invalid_http.html" ->
"HTTP/1.1 301\r\nDate:Sun, 09 Dec 2007 13:04:18 GMT\r\n" ++
"Transfer-Encoding:chunked\r\n\r\n";
@@ -3178,9 +3415,9 @@ handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) ->
ok;
close ->
%% Nothing to send, just close
- gen_tcp:close(Socket);
+ Close(Socket);
_ when is_list(Msg) orelse is_binary(Msg) ->
- gen_tcp:send(Socket, Msg)
+ Send(Socket, Msg)
end,
tsp("handle_http_msg -> done"),
NextRequest.
@@ -3316,3 +3553,20 @@ dummy_ssl_server_hang_loop(_) ->
stop ->
ok
end.
+
+
+ensure_started([]) ->
+ ok;
+ensure_started([App|Apps]) ->
+ ensure_started(App),
+ ensure_started(Apps);
+ensure_started(App) when is_atom(App) ->
+ case (catch application:start(App)) of
+ ok ->
+ ok;
+ {error, {already_started, _}} ->
+ ok;
+ Error ->
+ throw({error, {failed_starting, App, Error}})
+ end.
+
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index fde5178879..c4d4bf969b 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -207,8 +207,11 @@
-export([ticket_5775/1,ticket_5865/1,ticket_5913/1,ticket_6003/1,
ticket_7304/1]).
-%%% Misc
--export([ipv6_hostname/1, ipv6_address/1]).
+%%% IPv6 tests
+-export([ipv6_hostname_ipcomm/0, ipv6_hostname_ipcomm/1,
+ ipv6_address_ipcomm/0, ipv6_address_ipcomm/1,
+ ipv6_hostname_essl/0, ipv6_hostname_essl/1,
+ ipv6_address_essl/0, ipv6_address_essl/1]).
%% Help functions
-export([cleanup_mnesia/0, setup_mnesia/0, setup_mnesia/1]).
@@ -241,9 +244,15 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [{group, ip}, {group, ssl}, {group, http_1_1_ip},
- {group, http_1_0_ip}, {group, http_0_9_ip},
- {group, tickets}].
+ [
+ {group, ip},
+ {group, ssl},
+ {group, http_1_1_ip},
+ {group, http_1_0_ip},
+ {group, http_0_9_ip},
+ {group, ipv6},
+ {group, tickets}
+ ].
groups() ->
[{ip, [],
@@ -329,7 +338,8 @@ groups() ->
{http_1_0_ip, [],
[ip_head_1_0, ip_get_1_0, ip_post_1_0]},
{http_0_9_ip, [], [ip_get_0_9]},
- {ipv6, [], [ipv6_hostname, ipv6_address]},
+ {ipv6, [], [ipv6_hostname_ipcomm, ipv6_address_ipcomm,
+ ipv6_hostname_essl, ipv6_address_essl]},
{tickets, [],
[ticket_5775, ticket_5865, ticket_5913, ticket_6003,
ticket_7304]}].
@@ -408,10 +418,10 @@ init_per_testcase2(Case, Config) ->
"~n Config: ~p"
"~n", [?MODULE, Case, Config]),
- IpNormal = integer_to_list(?IP_PORT) ++ ".conf",
- IpHtacess = integer_to_list(?IP_PORT) ++ "htacess.conf",
- SslNormal = integer_to_list(?SSL_PORT) ++ ".conf",
- SslHtacess = integer_to_list(?SSL_PORT) ++ "htacess.conf",
+ IpNormal = integer_to_list(?IP_PORT) ++ ".conf",
+ IpHtaccess = integer_to_list(?IP_PORT) ++ "htaccess.conf",
+ SslNormal = integer_to_list(?SSL_PORT) ++ ".conf",
+ SslHtaccess = integer_to_list(?SSL_PORT) ++ "htaccess.conf",
DataDir = ?config(data_dir, Config),
SuiteTopDir = ?config(suite_top_dir, Config),
@@ -471,9 +481,9 @@ init_per_testcase2(Case, Config) ->
io:format(user, "~w:init_per_testcase2(~w) -> ip testcase setups~n",
[?MODULE, Case]),
create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig],
- normal_acess, IpNormal),
+ normal_access, IpNormal),
create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig],
- mod_htaccess, IpHtacess),
+ mod_htaccess, IpHtaccess),
%% To be used by SSL test cases
io:format(user, "~w:init_per_testcase2(~w) -> ssl testcase setups~n",
@@ -491,9 +501,9 @@ init_per_testcase2(Case, Config) ->
end,
create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig],
- normal_acess, SslNormal),
+ normal_access, SslNormal),
create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig],
- mod_htaccess, SslHtacess),
+ mod_htaccess, SslHtaccess),
%% To be used by IPv6 test cases. Case-clause is so that
%% you can do ts:run(inets, httpd_SUITE, <test case>)
@@ -501,22 +511,52 @@ init_per_testcase2(Case, Config) ->
%% on 'test_host_ipv6_only' that will only be present
%% when you run the whole test suite due to shortcomings
%% of the test server.
- %% case (catch ?config(test_host_ipv6_only, Config)) of
- %% {_,IPv6Host,IPv6Adress,_,_} ->
- %% create_ipv6_config([{port, ?IP_PORT},
- %% {sock_type, ip_comm} | NewConfig],
- %% "ipv6_hostname.conf", IPv6Host),
- %% create_ipv6_config([{port, ?IP_PORT},
- %% {sock_type, ip_comm} | NewConfig],
- %% "ipv6_address.conf", IPv6Adress);
- %% _ ->
- %% ok
- %% end,
-
+
+ io:format(user, "~w:init_per_testcase2(~w) -> "
+ "maybe generate IPv6 config file(s)", [?MODULE, Case]),
+ NewConfig2 =
+ case atom_to_list(Case) of
+ "ipv6_" ++ _ ->
+ case (catch inets_test_lib:has_ipv6_support(NewConfig)) of
+ {ok, IPv6Address0} ->
+ {ok, Hostname} = inet:gethostname(),
+ IPv6Address = http_transport:ipv6_name(IPv6Address0),
+ create_ipv6_config([{port, ?IP_PORT},
+ {sock_type, ip_comm},
+ {ipv6_host, IPv6Address} |
+ NewConfig],
+ "ipv6_hostname_ipcomm.conf",
+ Hostname),
+ create_ipv6_config([{port, ?IP_PORT},
+ {sock_type, ip_comm},
+ {ipv6_host, IPv6Address} |
+ NewConfig],
+ "ipv6_address_ipcomm.conf",
+ IPv6Address),
+ create_ipv6_config([{port, ?SSL_PORT},
+ {sock_type, essl},
+ {ipv6_host, IPv6Address} |
+ NewConfig],
+ "ipv6_hostname_essl.conf",
+ Hostname),
+ create_ipv6_config([{port, ?SSL_PORT},
+ {sock_type, essl},
+ {ipv6_host, IPv6Address} |
+ NewConfig],
+ "ipv6_address_essl.conf",
+ IPv6Address),
+ [{ipv6_host, IPv6Address} | NewConfig];
+ _ ->
+ NewConfig
+ end;
+ _ ->
+ NewConfig
+ end,
+
io:format(user, "~w:init_per_testcase2(~w) -> done~n",
[?MODULE, Case]),
- NewConfig.
+ NewConfig2.
init_per_testcase3(Case, Config) ->
@@ -547,10 +587,10 @@ init_per_testcase3(Case, Config) ->
[?MODULE, Case]),
inets:disable_trace();
_ ->
- %% TraceLevel = max,
io:format(user, "~w:init_per_testcase3(~w) -> enabling trace",
[?MODULE, Case]),
- TraceLevel = 70,
+ %% TraceLevel = 70,
+ TraceLevel = max,
TraceDest = io,
inets:enable_trace(TraceLevel, TraceDest, httpd)
end,
@@ -569,7 +609,7 @@ init_per_testcase3(Case, Config) ->
inets_test_lib:start_http_server(
filename:join(TcTopDir,
integer_to_list(?IP_PORT) ++
- "htacess.conf")),
+ "htaccess.conf")),
"mod_htaccess";
"ip_" ++ Rest ->
inets_test_lib:start_http_server(
@@ -602,7 +642,7 @@ init_per_testcase3(Case, Config) ->
case inets_test_lib:start_http_server_ssl(
filename:join(TcTopDir,
integer_to_list(?SSL_PORT) ++
- "htacess.conf"), SslTag) of
+ "htaccess.conf"), SslTag) of
ok ->
"mod_htaccess";
Other ->
@@ -627,16 +667,13 @@ init_per_testcase3(Case, Config) ->
{skip, "SSL does not seem to be supported"}
end;
"ipv6_" ++ _ = TestCaseStr ->
- {ok, Hostname} = inet:gethostname(),
-
- case lists:member(list_to_atom(Hostname),
- ?config(ipv6_hosts, Config)) of
- true ->
+ case inets_test_lib:has_ipv6_support() of
+ {ok, _} ->
inets_test_lib:start_http_server(
filename:join(TcTopDir,
TestCaseStr ++ ".conf"));
- false ->
+ _ ->
{skip, "Host does not support IPv6"}
end
end,
@@ -650,8 +687,8 @@ init_per_testcase3(Case, Config) ->
"mod_htaccess" ->
ServerRoot = ?config(server_root, Config),
Path = filename:join([ServerRoot, "htdocs"]),
- catch remove_htacess(Path),
- create_htacess_data(Path, ?config(address, Config)),
+ catch remove_htaccess(Path),
+ create_htaccess_data(Path, ?config(address, Config)),
[{watchdog, Dog} | NewConfig];
"range" ->
ServerRoot = ?config(server_root, Config),
@@ -2409,30 +2446,76 @@ ip_mod_cgi_chunked_encoding_test(Config) when is_list(Config) ->
ok.
%-------------------------------------------------------------------------
-ipv6_hostname(doc) ->
+
+ipv6_hostname_ipcomm() ->
+ [{require, ipv6_hosts}].
+ipv6_hostname_ipcomm(X) ->
+ SocketType = ip_comm,
+ Port = ?IP_PORT,
+ ipv6_hostname(SocketType, Port, X).
+
+ipv6_hostname_essl() ->
+ [{require, ipv6_hosts}].
+ipv6_hostname_essl(X) ->
+ SocketType = essl,
+ Port = ?SSL_PORT,
+ ipv6_hostname(SocketType, Port, X).
+
+ipv6_hostname(_SocketType, _Port, doc) ->
["Test standard ipv6 address"];
-ipv6_hostname(suite)->
+ipv6_hostname(_SocketType, _Port, suite)->
[];
-ipv6_hostname(Config) when is_list(Config) ->
+ipv6_hostname(SocketType, Port, Config) when is_list(Config) ->
+ tsp("ipv6_hostname -> entry with"
+ "~n SocketType: ~p"
+ "~n Port: ~p"
+ "~n Config: ~p", [SocketType, Port, Config]),
Host = ?config(host, Config),
- httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, node(),
- "GET / HTTP/1.1\r\n\r\n",
- [{statuscode, 200},
- {version, "HTTP/1.1"}]),
+ URI = "GET HTTP://" ++
+ Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n",
+ tsp("ipv6_hostname -> Host: ~p", [Host]),
+ httpd_test_lib:verify_request(SocketType, Host, Port, [inet6],
+ node(),
+ URI,
+ [{statuscode, 200}, {version, "HTTP/1.1"}]),
ok.
%%-------------------------------------------------------------------------
-ipv6_address(doc) ->
+
+ipv6_address_ipcomm() ->
+ [{require, ipv6_hosts}].
+ipv6_address_ipcomm(X) ->
+ SocketType = ip_comm,
+ Port = ?IP_PORT,
+ ipv6_address(SocketType, Port, X).
+
+ipv6_address_essl() ->
+ [{require, ipv6_hosts}].
+ipv6_address_essl(X) ->
+ SocketType = essl,
+ Port = ?SSL_PORT,
+ ipv6_address(SocketType, Port, X).
+
+ipv6_address(_SocketType, _Port, doc) ->
["Test standard ipv6 address"];
-ipv6_address(suite)->
+ipv6_address(_SocketType, _Port, suite)->
[];
-ipv6_address(Config) when is_list(Config) ->
- httpd_test_lib:verify_request(ip_comm, ?IPV6_LOCAL_HOST, ?IP_PORT,
- node(), "GET / HTTP/1.1\r\n\r\n",
- [{statuscode, 200},
- {version, "HTTP/1.1"}]),
+ipv6_address(SocketType, Port, Config) when is_list(Config) ->
+ tsp("ipv6_address -> entry with"
+ "~n SocketType: ~p"
+ "~n Port: ~p"
+ "~n Config: ~p", [SocketType, Port, Config]),
+ Host = ?config(host, Config),
+ tsp("ipv6_address -> Host: ~p", [Host]),
+ URI = "GET HTTP://" ++
+ Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n",
+ httpd_test_lib:verify_request(SocketType, Host, Port, [inet6],
+ node(),
+ URI,
+ [{statuscode, 200}, {version, "HTTP/1.1"}]),
ok.
+
%%--------------------------------------------------------------------
ticket_5775(doc) ->
["Tests that content-length is correct"];
@@ -2805,22 +2888,22 @@ cleanup_mnesia() ->
mnesia:delete_schema([node()]),
ok.
-create_htacess_data(Path, IpAddress)->
- create_htacess_dirs(Path),
+create_htaccess_data(Path, IpAddress)->
+ create_htaccess_dirs(Path),
create_html_file(filename:join([Path,"ht/open/dummy.html"])),
create_html_file(filename:join([Path,"ht/blocknet/dummy.html"])),
create_html_file(filename:join([Path,"ht/secret/dummy.html"])),
create_html_file(filename:join([Path,"ht/secret/top_secret/dummy.html"])),
- create_htacess_file(filename:join([Path,"ht/open/.htaccess"]),
+ create_htaccess_file(filename:join([Path,"ht/open/.htaccess"]),
Path, "user one Aladdin"),
- create_htacess_file(filename:join([Path,"ht/secret/.htaccess"]),
+ create_htaccess_file(filename:join([Path,"ht/secret/.htaccess"]),
Path, "group group1 group2"),
- create_htacess_file(filename:join([Path,
+ create_htaccess_file(filename:join([Path,
"ht/secret/top_secret/.htaccess"]),
Path, "user four"),
- create_htacess_file(filename:join([Path,"ht/blocknet/.htaccess"]),
+ create_htaccess_file(filename:join([Path,"ht/blocknet/.htaccess"]),
Path, nouser, IpAddress),
create_user_group_file(filename:join([Path,"ht","users.file"]),
@@ -2835,7 +2918,7 @@ create_html_file(PathAndFileName)->
"<html><head><title>test</title></head>
<body>testar</body></html>")).
-create_htacess_file(PathAndFileName, BaseDir, RequireData)->
+create_htaccess_file(PathAndFileName, BaseDir, RequireData)->
file:write_file(PathAndFileName,
list_to_binary(
"AuthUserFile "++ BaseDir ++
@@ -2844,7 +2927,7 @@ create_htacess_file(PathAndFileName, BaseDir, RequireData)->
" Basic\n<Limit>\nrequire " ++ RequireData ++
"\n</Limit>")).
-create_htacess_file(PathAndFileName, BaseDir, nouser, IpAddress)->
+create_htaccess_file(PathAndFileName, BaseDir, nouser, IpAddress)->
file:write_file(PathAndFileName,list_to_binary(
"AuthUserFile "++ BaseDir ++
"/ht/users.file\nAuthGroupFile " ++
@@ -2858,14 +2941,14 @@ create_htacess_file(PathAndFileName, BaseDir, nouser, IpAddress)->
create_user_group_file(PathAndFileName, Data)->
file:write_file(PathAndFileName, list_to_binary(Data)).
-create_htacess_dirs(Path)->
+create_htaccess_dirs(Path)->
ok = file:make_dir(filename:join([Path,"ht"])),
ok = file:make_dir(filename:join([Path,"ht/open"])),
ok = file:make_dir(filename:join([Path,"ht/blocknet"])),
ok = file:make_dir(filename:join([Path,"ht/secret"])),
ok = file:make_dir(filename:join([Path,"ht/secret/top_secret"])).
-remove_htacess_dirs(Path)->
+remove_htaccess_dirs(Path)->
file:del_dir(filename:join([Path,"ht/secret/top_secret"])),
file:del_dir(filename:join([Path,"ht/secret"])),
file:del_dir(filename:join([Path,"ht/blocknet"])),
@@ -2888,7 +2971,7 @@ format_ip(IpAddress,Pos)when Pos > 0->
format_ip(IpAddress, _Pos)->
"1" ++ IpAddress.
-remove_htacess(Path)->
+remove_htaccess(Path)->
file:delete(filename:join([Path,"ht/open/dummy.html"])),
file:delete(filename:join([Path,"ht/secret/dummy.html"])),
file:delete(filename:join([Path,"ht/secret/top_secret/dummy.html"])),
@@ -2899,7 +2982,7 @@ remove_htacess(Path)->
file:delete(filename:join([Path,"ht/secret/top_secret/.htaccess"])),
file:delete(filename:join([Path,"ht","users.file"])),
file:delete(filename:join([Path,"ht","groups.file"])),
- remove_htacess_dirs(Path).
+ remove_htaccess_dirs(Path).
dos_hostname_poll(Type, Host, Port, Node, Hosts) ->
@@ -2939,35 +3022,66 @@ create_range_data(Path) ->
"12345678901234567890",
"12345678901234567890"])).
-%% create_ipv6_config(Config, FileName, Ipv6Address) ->
-%% ServerRoot = ?config(server_root, Config),
-%% TcTopDir = ?config(tc_top_dir, Config),
-%% Port = ?config(port, Config),
-%% SockType = ?config(sock_type, Config),
-%%
-%% MaxHdrSz = io_lib:format("~p", [256]),
-%% MaxHdrAct = io_lib:format("~p", [close]),
-%%
-%% Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi"
-%% " mod_include mod_dir mod_get mod_head"
-%% " mod_log mod_disk_log mod_trace",
-%%
-%% HttpConfig = [cline(["BindAddress ", "[" ++ Ipv6Address ++"]|inet6"]),
-%% cline(["Port ", integer_to_list(Port)]),
-%% cline(["ServerName ", "httpc_test"]),
-%% cline(["SocketType ", atom_to_list(SockType)]),
-%% cline([Mod_order]),
-%% cline(["ServerRoot ", ServerRoot]),
-%% cline(["DocumentRoot ",
-%% filename:join(ServerRoot, "htdocs")]),
-%% cline(["MaxHeaderSize ",MaxHdrSz]),
-%% cline(["MaxHeaderAction ",MaxHdrAct]),
-%% cline(["DirectoryIndex ", "index.html "]),
-%% cline(["DefaultType ", "text/plain"])],
-%% ConfigFile = filename:join([TcTopDir,FileName]),
-%% {ok, Fd} = file:open(ConfigFile, [write]),
-%% ok = file:write(Fd, lists:flatten(HttpConfig)),
-%% ok = file:close(Fd).
+create_ipv6_config(Config, FileName, Ipv6Address) ->
+ ServerRoot = ?config(server_root, Config),
+ TcTopDir = ?config(tc_top_dir, Config),
+ Port = ?config(port, Config),
+ SockType = ?config(sock_type, Config),
+ Mods = io_lib:format("~p", [httpd_mod]),
+ Funcs = io_lib:format("~p", [ssl_password_cb]),
+ Host = ?config(ipv6_host, Config),
+
+ MaxHdrSz = io_lib:format("~p", [256]),
+ MaxHdrAct = io_lib:format("~p", [close]),
+
+ Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi"
+ " mod_include mod_dir mod_get mod_head"
+ " mod_log mod_disk_log mod_trace",
+
+ SSL =
+ if
+ (SockType =:= ssl) orelse
+ (SockType =:= ossl) orelse
+ (SockType =:= essl) ->
+ [cline(["SSLCertificateFile ",
+ filename:join(ServerRoot, "ssl/ssl_server.pem")]),
+ cline(["SSLCertificateKeyFile ",
+ filename:join(ServerRoot, "ssl/ssl_server.pem")]),
+ cline(["SSLCACertificateFile ",
+ filename:join(ServerRoot, "ssl/ssl_server.pem")]),
+ cline(["SSLPasswordCallbackModule ", Mods]),
+ cline(["SSLPasswordCallbackFunction ", Funcs]),
+ cline(["SSLVerifyClient 0"]),
+ cline(["SSLVerifyDepth 1"])];
+ true ->
+ []
+ end,
+
+ BindAddress = "[" ++ Ipv6Address ++"]|inet6",
+
+ HttpConfig =
+ [cline(["BindAddress ", BindAddress]),
+ cline(["Port ", integer_to_list(Port)]),
+ cline(["ServerName ", Host]),
+ cline(["SocketType ", atom_to_list(SockType)]),
+ cline([Mod_order]),
+ cline(["ServerRoot ", ServerRoot]),
+ cline(["DocumentRoot ", filename:join(ServerRoot, "htdocs")]),
+ cline(["MaxHeaderSize ",MaxHdrSz]),
+ cline(["MaxHeaderAction ",MaxHdrAct]),
+ cline(["DirectoryIndex ", "index.html "]),
+ cline(["DefaultType ", "text/plain"]),
+ SSL],
+ ConfigFile = filename:join([TcTopDir,FileName]),
+ {ok, Fd} = file:open(ConfigFile, [write]),
+ ok = file:write(Fd, lists:flatten(HttpConfig)),
+ ok = file:close(Fd).
+
+
+%% tsp(F) ->
+%% inets_test_lib:tsp(F).
+tsp(F, A) ->
+ inets_test_lib:tsp(F, A).
tsf(Reason) ->
- test_server:fail(Reason).
+ inets_test_lib:tsf(Reason).
diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl
index 3189a758a5..2903aaafa5 100644
--- a/lib/inets/test/httpd_test_lib.erl
+++ b/lib/inets/test/httpd_test_lib.erl
@@ -22,7 +22,7 @@
-include("inets_test_lib.hrl").
%% Poll functions
--export([verify_request/6, verify_request/7, is_expect/1]).
+-export([verify_request/6, verify_request/7, verify_request/8, is_expect/1]).
-record(state, {request, % string()
socket, % socket()
@@ -81,33 +81,57 @@
%%------------------------------------------------------------------
verify_request(SocketType, Host, Port, Node, RequestStr, Options) ->
verify_request(SocketType, Host, Port, Node, RequestStr, Options, 30000).
-verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut) ->
- {ok, Socket} = inets_test_lib:connect_bin(SocketType, Host, Port),
+verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options)
+ when is_list(TranspOpts) ->
+ verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, 30000);
+verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut)
+ when (is_integer(TimeOut) orelse (TimeOut =:= infinity)) ->
+ verify_request(SocketType, Host, Port, [], Node, RequestStr, Options, TimeOut).
+verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, TimeOut) ->
+ tsp("verify_request -> entry with"
+ "~n SocketType: ~p"
+ "~n Host: ~p"
+ "~n Port: ~p"
+ "~n TranspOpts: ~p"
+ "~n Node: ~p"
+ "~n Options: ~p"
+ "~n TimeOut: ~p",
+ [SocketType, Host, Port, TranspOpts, Node, Options, TimeOut]),
+ case (catch inets_test_lib:connect_bin(SocketType, Host, Port, TranspOpts)) of
+ {ok, Socket} ->
+ tsp("verify_request -> connected - now send message"),
+ SendRes = inets_test_lib:send(SocketType, Socket, RequestStr),
+ tsp("verify_request -> send result: "
+ "~n ~p", [SendRes]),
+ State = case inets_regexp:match(RequestStr, "printenv") of
+ nomatch ->
+ #state{};
+ _ ->
+ #state{print = true}
+ end,
+
+ case request(State#state{request = RequestStr,
+ socket = Socket}, TimeOut) of
+ {error, Reason} ->
+ tsp("request failed: "
+ "~n Reason: ~p", [Reason]),
+ {error, Reason};
+ NewState ->
+ tsp("validate reply: "
+ "~n NewState: ~p", [NewState]),
+ ValidateResult =
+ validate(RequestStr, NewState, Options, Node, Port),
+ tsp("validation result: "
+ "~n ~p", [ValidateResult]),
+ inets_test_lib:close(SocketType, Socket),
+ ValidateResult
+ end;
- _SendRes = inets_test_lib:send(SocketType, Socket, RequestStr),
-
- State = case inets_regexp:match(RequestStr, "printenv") of
- nomatch ->
- #state{};
- _ ->
- #state{print = true}
- end,
-
- case request(State#state{request = RequestStr,
- socket = Socket}, TimeOut) of
- {error, Reason} ->
- tsp("request failed: "
- "~n Reason: ~p", [Reason]),
- {error, Reason};
- NewState ->
- tsp("validate reply: "
- "~n NewState: ~p", [NewState]),
- ValidateResult = validate(RequestStr, NewState, Options,
- Node, Port),
- tsp("validation result: "
- "~n ~p", [ValidateResult]),
- inets_test_lib:close(SocketType, Socket),
- ValidateResult
+ ConnectError ->
+ tsp("verify_request -> connect failed: "
+ "~n ~p"
+ "~n", [ConnectError]),
+ tsf({connect_failure, ConnectError})
end.
request(#state{mfa = {Module, Function, Args},
@@ -214,7 +238,10 @@ validate(RequestStr, #state{status_line = {Version, StatusCode, _},
headers = Headers,
body = Body}, Options, N, P) ->
- %io:format("Status~p: H:~p B:~p~n", [StatusCode, Headers, Body]),
+ %% tsp("validate -> entry with"
+ %% "~n StatusCode: ~p"
+ %% "~n Headers: ~p"
+ %% "~n Body: ~p", [StatusCode, Headers, Body]),
check_version(Version, Options),
case lists:keysearch(statuscode, 1, Options) of
{value, _} ->
@@ -342,8 +369,10 @@ print(_, _, #state{print = false}) ->
ok.
-%% tsp(F) ->
-%% tsp(F, []).
+tsp(F) ->
+ inets_test_lib:tsp(F).
tsp(F, A) ->
- test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]).
+ inets_test_lib:tsp(F, A).
+tsf(Reason) ->
+ inets_test_lib:tsf(Reason).
diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl
index 6cedaf9638..2e19c41f16 100644
--- a/lib/inets/test/inets_test_lib.erl
+++ b/lib/inets/test/inets_test_lib.erl
@@ -26,18 +26,64 @@
-export([start_http_server/1, start_http_server/2]).
-export([start_http_server_ssl/1, start_http_server_ssl/2]).
-export([hostname/0]).
--export([connect_bin/3, connect_byte/3, send/3, close/2]).
+-export([connect_bin/3, connect_bin/4,
+ connect_byte/3, connect_byte/4,
+ send/3, close/2]).
-export([copy_file/3, copy_files/2, copy_dirs/2, del_dirs/1]).
-export([info/4, log/4, debug/4, print/4]).
+-export([tsp/1, tsp/2, tsf/1]).
-export([check_body/1]).
-export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]).
--export([oscmd/1]).
+-export([oscmd/1, has_ipv6_support/1]).
-export([non_pc_tc_maybe_skip/4, os_based_skip/1, skip/3, fail/3]).
-export([flush/0]).
-export([start_node/1, stop_node/1]).
%% -- Misc os command and stuff
+has_ipv6_support(Config) ->
+ case lists:keysearch(ipv6_hosts, 1, Config) of
+ false ->
+ %% Do a basic check to se if
+ %% our own host has a working IPv6 address...
+ tsp("has_ipv6_support -> no ipv6_hosts config"),
+ {ok, Hostname} = inet:gethostname(),
+ case inet:getaddrs(Hostname, inet6) of
+ {ok, [Addr|_]} when is_tuple(Addr) andalso
+ (element(1, Addr) =/= 0) ->
+ %% We actually need to test that the addr can be used,
+ %% this is done by attempting to create a (tcp)
+ %% listen socket
+ tsp("has_ipv6_support -> check Addr: ~p", [Addr]),
+ case (catch gen_tcp:listen(0, [inet6, {ip, Addr}])) of
+ {ok, LSock} ->
+ tsp("has_ipv6_support -> we are ipv6 host"),
+ gen_tcp:close(LSock),
+ {ok, Addr};
+ _ ->
+ undefined
+ end;
+ _ ->
+ undefined
+ end;
+ {value, {_, Hosts}} when is_list(Hosts) ->
+ %% Check if our host is in the list of *known* IPv6 hosts
+ tsp("has_ipv6_support -> Hosts: ~p", [Hosts]),
+ {ok, Hostname} = inet:gethostname(),
+ case lists:member(list_to_atom(Hostname), Hosts) of
+ true ->
+ tsp("has_ipv6_support -> we are known ipv6 host"),
+ {ok, [Addr|_]} = inet:getaddrs(Hostname, inet6),
+ {ok, Addr};
+ false ->
+ undefined
+ end;
+
+ _ ->
+ undefined
+
+ end.
+
oscmd(Cmd) ->
string:strip(os:cmd(Cmd), right, $\n).
@@ -87,31 +133,34 @@ start_http_server(Conf) ->
start_http_server(Conf, ?HTTP_DEFAULT_SSL_KIND).
start_http_server(Conf, essl = _SslTag) ->
+ tsp("start_http_server(essl) -> entry - try start crypto and public_key"),
application:start(crypto),
+ application:start(public_key),
do_start_http_server(Conf);
-start_http_server(Conf, _SslTag) ->
+start_http_server(Conf, SslTag) ->
+ tsp("start_http_server(~w) -> entry", [SslTag]),
do_start_http_server(Conf).
do_start_http_server(Conf) ->
- tsp("start http server with "
+ tsp("do_start_http_server -> entry with"
"~n Conf: ~p"
"~n", [Conf]),
application:load(inets),
case application:set_env(inets, services, [{httpd, Conf}]) of
ok ->
+ tsp("start_http_server -> httpd conf stored in inets app env"),
case application:start(inets) of
ok ->
+ tsp("start_http_server -> inets started"),
ok;
Error1 ->
- test_server:format("<ERROR> Failed starting application: "
- "~n Error: ~p"
- "~n", [Error1]),
+ tsp("<ERROR> Failed starting application: "
+ "~n Error1: ~p", [Error1]),
Error1
end;
Error2 ->
- test_server:format("<ERROR> Failed set application env: "
- "~n Error: ~p"
- "~n", [Error2]),
+ tsp("<ERROR> Failed set application env: "
+ "~n Error: ~p", [Error2]),
Error2
end.
@@ -285,29 +334,45 @@ os_based_skip(_) ->
%% Host -> atom() | string() | {A, B, C, D}
%% Port -> integer()
-connect_bin(ssl, Host, Port) ->
- connect(ssl, Host, Port, [binary, {packet,0}]);
-connect_bin(ossl, Host, Port) ->
- connect(ssl, Host, Port, [{ssl_imp, old}, binary, {packet,0}]);
-connect_bin(essl, Host, Port) ->
- connect(ssl, Host, Port, [{ssl_imp, new}, binary, {packet,0}, {reuseaddr, true}]);
-connect_bin(ip_comm, Host, Port) ->
- Opts = [inet6, binary, {packet,0}],
+connect_bin(SockType, Host, Port) ->
+ connect_bin(SockType, Host, Port, []).
+
+connect_bin(ssl, Host, Port, Opts0) ->
+ Opts = [binary, {packet,0} | Opts0],
+ connect(ssl, Host, Port, Opts);
+connect_bin(ossl, Host, Port, Opts0) ->
+ Opts = [{ssl_imp, old}, binary, {packet,0} | Opts0],
+ connect(ssl, Host, Port, Opts);
+connect_bin(essl, Host, Port, Opts0) ->
+ Opts = [{ssl_imp, new}, binary, {packet,0}, {reuseaddr, true} | Opts0],
+ connect(ssl, Host, Port, Opts);
+connect_bin(ip_comm, Host, Port, Opts0) ->
+ Opts = [binary, {packet, 0} | Opts0],
connect(ip_comm, Host, Port, Opts).
+
+connect_byte(SockType, Host, Port) ->
+ connect_byte(SockType, Host, Port, []).
-connect_byte(ssl, Host, Port) ->
- connect(ssl, Host, Port, [{packet,0}]);
-connect_byte(ossl, Host, Port) ->
- connect(ssl, Host, Port, [{ssl_imp, old}, {packet,0}]);
-connect_byte(essl, Host, Port) ->
- connect(ssl, Host, Port, [{ssl_imp, new}, {packet,0}]);
-connect_byte(ip_comm, Host, Port) ->
- Opts = [inet6, {packet,0}],
+connect_byte(ssl, Host, Port, Opts0) ->
+ Opts = [{packet,0} | Opts0],
+ connect(ssl, Host, Port, Opts);
+connect_byte(ossl, Host, Port, Opts0) ->
+ Opts = [{ssl_imp, old}, {packet,0} | Opts0],
+ connect(ssl, Host, Port, Opts);
+connect_byte(essl, Host, Port, Opts0) ->
+ Opts = [{ssl_imp, new}, {packet,0} | Opts0],
+ connect(ssl, Host, Port, Opts);
+connect_byte(ip_comm, Host, Port, Opts0) ->
+ Opts = [{packet,0} | Opts0],
connect(ip_comm, Host, Port, Opts).
connect(ssl, Host, Port, Opts) ->
+ tsp("connect(ssl) -> entry with"
+ "~n Host: ~p"
+ "~n Port: ~p"
+ "~n Opts: ~p", [Host, Port, Opts]),
ssl:start(),
%% Does not support ipv6 in old ssl
case ssl:connect(Host, Port, Opts) of
@@ -319,21 +384,28 @@ connect(ssl, Host, Port, Opts) ->
Error
end;
connect(ip_comm, Host, Port, Opts) ->
+ tsp("connect(ip_comm) -> entry with"
+ "~n Host: ~p"
+ "~n Port: ~p"
+ "~n Opts: ~p", [Host, Port, Opts]),
case gen_tcp:connect(Host,Port, Opts) of
{ok, Socket} ->
- %% tsp("connect success"),
+ tsp("connect success"),
{ok, Socket};
{error, nxdomain} ->
- tsp("nxdomain opts: ~p", [Opts]),
+ tsp("connect error nxdomain when opts: ~p", [Opts]),
connect(ip_comm, Host, Port, lists:delete(inet6, Opts));
{error, eafnosupport} ->
- tsp("eafnosupport opts: ~p", [Opts]),
+ tsp("connect error eafnosupport when opts: ~p", [Opts]),
+ connect(ip_comm, Host, Port, lists:delete(inet6, Opts));
+ {error, econnreset} ->
+ tsp("connect error econnreset when opts: ~p", [Opts]),
connect(ip_comm, Host, Port, lists:delete(inet6, Opts));
{error, enetunreach} ->
- tsp("eafnosupport opts: ~p", [Opts]),
+ tsp("connect error eafnosupport when opts: ~p", [Opts]),
connect(ip_comm, Host, Port, lists:delete(inet6, Opts));
{error, {enfile,_}} ->
- tsp("Error enfile"),
+ tsp("connect error enfile when opts: ~p", [Opts]),
{error, enfile};
Error ->
tsp("Unexpected error: "
@@ -414,7 +486,22 @@ flush() ->
tsp(F) ->
tsp(F, []).
tsp(F, A) ->
- test_server:format("~p ~p ~p:" ++ F ++ "~n", [node(), self(), ?MODULE | A]).
+ Timestamp = formated_timestamp(),
+ test_server:format("*** ~s ~p ~p ~w:" ++ F ++ "~n",
+ [Timestamp, node(), self(), ?MODULE | A]).
tsf(Reason) ->
test_server:fail(Reason).
+
+formated_timestamp() ->
+ format_timestamp( os:timestamp() ).
+
+format_timestamp({_N1, _N2, N3} = Now) ->
+ {Date, Time} = calendar:now_to_datetime(Now),
+ {YYYY,MM,DD} = Date,
+ {Hour,Min,Sec} = Time,
+ FormatDate =
+ io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
+ [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
+ lists:flatten(FormatDate).
+
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index c0e25a30e3..4abc1733d3 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 5.6
+INETS_VSN = 5.7
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c
index 3dd7da08b9..11e311d72d 100644
--- a/lib/odbc/c_src/odbcserver.c
+++ b/lib/odbc/c_src/odbcserver.c
@@ -90,7 +90,7 @@
Datatype - USER_INT | USER_SMALL_INT | {USER_DECIMAL, Precision, Scale} |
{USER_NMERIC, Precision, Scale} | {USER_CHAR, Max} | {USER_VARCHAR, Max} |
{USER_WVARCHAR, Max} | {USER_FLOAT, Precision} | USER_REAL | USER_DOUBLE |
- USER_TIMESTAMP
+ USER_TIMESTAMP | {USER_WLONGVARCHAR, Max}
Scale - integer
Precision - integer
Max - integer
@@ -1228,7 +1228,7 @@ static db_result_msg encode_column_name_list(SQLSMALLINT num_of_columns,
&nullable)))
DO_EXIT(EXIT_DESC);
- if(sql_type == SQL_LONGVARCHAR || sql_type == SQL_LONGVARBINARY)
+ if(sql_type == SQL_LONGVARCHAR || sql_type == SQL_LONGVARBINARY || sql_type == SQL_WLONGVARCHAR)
size = MAXCOLSIZE;
(columns(state)[i]).type.decimal_digits = dec_digits;
@@ -1537,6 +1537,11 @@ static void encode_data_type(SQLSMALLINT sql_type, SQLINTEGER size,
case SQL_LONGVARCHAR:
ei_x_encode_atom(&dynamic_buffer(state), "SQL_LONGVARCHAR");
break;
+ case SQL_WLONGVARCHAR:
+ ei_x_encode_tuple_header(&dynamic_buffer(state), 2);
+ ei_x_encode_atom(&dynamic_buffer(state), "sql_wlongvarchar");
+ ei_x_encode_long(&dynamic_buffer(state), size);
+ break;
case SQL_VARBINARY:
ei_x_encode_atom(&dynamic_buffer(state), "SQL_VARBINARY");
break;
@@ -2139,10 +2144,14 @@ static void init_param_column(param_array *params, byte *buffer, int *index,
break;
case USER_WCHAR:
case USER_WVARCHAR:
- if(user_type == USER_WCHAR) {
- params->type.sql = SQL_WCHAR;
- } else {
- params->type.sql = SQL_WVARCHAR;
+ case USER_WLONGVARCHAR:
+ switch (user_type) {
+ case USER_WCHAR:
+ params->type.sql = SQL_WCHAR; break;
+ case USER_WVARCHAR:
+ params->type.sql = SQL_WVARCHAR; break;
+ default:
+ params->type.sql = SQL_WLONGVARCHAR; break;
}
ei_decode_long(buffer, index, &length);
/* Max string length + string terminator */
@@ -2308,6 +2317,7 @@ static db_result_msg map_sql_2_c_column(db_column* column)
break;
case SQL_WCHAR:
case SQL_WVARCHAR:
+ case SQL_WLONGVARCHAR:
column -> type.len = (column -> type.col_size + 1)*sizeof(SQLWCHAR);
column -> type.c = SQL_C_WCHAR;
column -> type.strlen_or_indptr = SQL_NTS;
diff --git a/lib/odbc/c_src/odbcserver.h b/lib/odbc/c_src/odbcserver.h
index 3e2b22ab7d..314fbf32c6 100644
--- a/lib/odbc/c_src/odbcserver.h
+++ b/lib/odbc/c_src/odbcserver.h
@@ -115,6 +115,7 @@
#define USER_WCHAR 12
#define USER_WVARCHAR 13
#define USER_TIMESTAMP 14
+#define USER_WLONGVARCHAR 15
/*------------------------ TYPDEFS ----------------------------------*/
diff --git a/lib/odbc/doc/src/databases.xml b/lib/odbc/doc/src/databases.xml
index a6ba0e5245..09f5a5af5b 100644
--- a/lib/odbc/doc/src/databases.xml
+++ b/lib/odbc/doc/src/databases.xml
@@ -205,6 +205,10 @@ when p >= 16 </cell>
<cell align="left" valign="middle">String | Binary (configurable)</cell>
</row>
<row>
+ <cell align="left" valign="middle">SQL_WLONGVARCHAR(size) </cell>
+ <cell align="left" valign="middle">Unicode binary encoded as UTF16 little endian.</cell>
+ </row>
+ <row>
<cell align="left" valign="middle">SQL_BINARY</cell>
<cell align="left" valign="middle">String | Binary (configurable)</cell>
</row>
diff --git a/lib/odbc/doc/src/odbc.xml b/lib/odbc/doc/src/odbc.xml
index 70d8cfbe22..11ca97f743 100644
--- a/lib/odbc/doc/src/odbc.xml
+++ b/lib/odbc/doc/src/odbc.xml
@@ -102,7 +102,7 @@
{sql_decimal, precision(), scale()} |
{sql_numeric, precision(), scale()} |
{sql_char, size()} | {sql_wchar, size()} | {sql_varchar, size()} | {sql_wvarchar, size()}| {sql_float, precision()} |
- {sql_float, precision()} | sql_real | sql_double | sql_bit | atom()
+ {sql_wlongvarchar, size()} | {sql_float, precision()} | sql_real | sql_double | sql_bit | atom()
</code>
<code type="none">
precision() = integer() </code>
diff --git a/lib/odbc/src/odbc.appup.src b/lib/odbc/src/odbc.appup.src
index 2a6667ccd3..f3a3af8c29 100644
--- a/lib/odbc/src/odbc.appup.src
+++ b/lib/odbc/src/odbc.appup.src
@@ -1,8 +1,10 @@
%% -*- erlang -*-
{"%VSN%",
[
- {"2.10.9", [{restart_application, ssl}]}
+ {"2.10.10", [{restart_application, odbc}]},
+ {"2.10.9", [{restart_application, odbc}]}
],
[
- {"2.10.9", [{restart_application, ssl}]}
+ {"2.10.10", [{restart_application, odbc}]},
+ {"2.10.9", [{restart_application, odbc}]}
]}.
diff --git a/lib/odbc/src/odbc.erl b/lib/odbc/src/odbc.erl
index 2634450d4a..68497292db 100644
--- a/lib/odbc/src/odbc.erl
+++ b/lib/odbc/src/odbc.erl
@@ -925,6 +925,9 @@ fix_params({{sql_wchar, Max}, InOut, Values}) ->
fix_params({{sql_wvarchar, Max}, InOut, Values}) ->
NewValues = string_terminate(Values),
{?USER_WVARCHAR, Max, fix_inout(InOut), NewValues};
+fix_params({{sql_wlongvarchar, Max}, InOut, Values}) ->
+ NewValues = string_terminate(Values),
+ {?USER_WLONGVARCHAR, Max, fix_inout(InOut), NewValues};
fix_params({{sql_float, Precision}, InOut, Values}) ->
{?USER_FLOAT, Precision, fix_inout(InOut), Values};
fix_params({sql_real, InOut, Values}) ->
diff --git a/lib/odbc/src/odbc_internal.hrl b/lib/odbc/src/odbc_internal.hrl
index aa60120f9a..c0e7d9657b 100644
--- a/lib/odbc/src/odbc_internal.hrl
+++ b/lib/odbc/src/odbc_internal.hrl
@@ -72,6 +72,7 @@
-define(USER_WCHAR, 12).
-define(USER_WVARCHAR, 13).
-define(USER_TIMESTAMP, 14).
+-define(USER_WLONGVARCHAR, 15).
%% INPUT & OUTPUT TYPE
-define(IN, 0).
diff --git a/lib/odbc/test/mysql.erl b/lib/odbc/test/mysql.erl
index 76ffd3ecc9..49068c4356 100644
--- a/lib/odbc/test/mysql.erl
+++ b/lib/odbc/test/mysql.erl
@@ -98,10 +98,6 @@ create_text_table() ->
" (FIELD text)".
%-------------------------------------------------------------------------
-create_unicode_table() ->
- " (FIELD text)".
-
-%-------------------------------------------------------------------------
create_timestamp_table() ->
" (FIELD TIMESTAMP)".
diff --git a/lib/odbc/test/odbc_data_type_SUITE.erl b/lib/odbc/test/odbc_data_type_SUITE.erl
index 84c99e183b..099ae0aa7d 100644
--- a/lib/odbc/test/odbc_data_type_SUITE.erl
+++ b/lib/odbc/test/odbc_data_type_SUITE.erl
@@ -45,7 +45,7 @@ all() ->
case odbc_test_lib:odbc_check() of
ok ->
[{group, char},{group, fixed_char}, {group, binary_char},
- {group, fixed_binary_char},
+ {group, fixed_binary_char}, {group, unicode},
{group, int}, {group, floats},
{group, dec_and_num}, timestamp];
Other -> {skip, Other}
@@ -55,18 +55,18 @@ groups() ->
[{char, [],
[varchar_lower_limit,
varchar_upper_limit, varchar_no_padding,
- text_lower_limit, text_upper_limit, unicode]},
+ text_lower_limit, text_upper_limit]},
{fixed_char, [],
[char_fixed_lower_limit, char_fixed_upper_limit,
char_fixed_padding]},
{binary_char, [],
[binary_varchar_lower_limit,
binary_varchar_upper_limit, binary_varchar_no_padding,
- binary_text_lower_limit, binary_text_upper_limit,
- unicode]},
+ binary_text_lower_limit, binary_text_upper_limit]},
{fixed_binary_char, [], [binary_char_fixed_lower_limit,
binary_char_fixed_upper_limit,
binary_char_fixed_padding]},
+ {unicode, [], [utf8, nchar, nvarchar]},
{int, [],
[tiny_int_lower_limit, tiny_int_upper_limit,
small_int_lower_limit, small_int_upper_limit,
@@ -88,14 +88,24 @@ init_per_group(GroupName, Config) when GroupName == fixed_char;
Config
end;
+init_per_group(unicode, Config) ->
+ %% Uses parameterized queries
+ case {os:type(), erlang:system_info(wordsize)} of
+ {{unix, _}, 4} ->
+ Config;
+ {{unix, _}, _} ->
+ {skip, "Not supported by driver"};
+ _ ->
+ Config
+ end;
+
+
init_per_group(_GroupName, Config) ->
Config.
end_per_group(_GroupName, Config) ->
Config.
-
-
%%--------------------------------------------------------------------
%% Function: init_per_suite(Config) -> Config
%% Config - [tuple()]
@@ -164,6 +174,16 @@ init_per_testcase(param_insert_tiny_int = Case, Config) ->
false ->
{skip, "Not supported by driver"}
end;
+
+init_per_testcase(Case, Config) when Case == nchar;
+ Case == nvarchar ->
+ case ?RDBMS of
+ sqlserver ->
+ common_init_per_testcase(Case, Config);
+ _ ->
+ {skip, "Not supported by driver"}
+ end;
+
init_per_testcase(Case, Config) ->
common_init_per_testcase(Case, Config).
@@ -173,7 +193,9 @@ common_init_per_testcase(Case, Config) ->
"binary" ++ _ ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{binary_strings, on}] ++ PlatformOptions);
- "unicode" ->
+ LCase when LCase == "utf8";
+ LCase == "nchar";
+ LCase == "nvarchar" ->
{ok, Ref} = odbc:connect(?RDBMS:connection_string(),
[{binary_strings, on}] ++ PlatformOptions);
_ ->
@@ -1435,17 +1457,16 @@ num_bignum(Config) when is_list(Config) ->
["FIELD"] = odbc_test_lib:to_upper(Fields1).
%%------------------------------------------------------------------------
-unicode(doc) ->
+utf8(doc) ->
["Test unicode support"];
-unicode(suit) ->
+utf8(suit) ->
[];
-unicode(Config) when is_list(Config) ->
+utf8(Config) when is_list(Config) ->
Ref = ?config(connection_ref, Config),
Table = ?config(tableName, Config),
- {updated, _} = % Value == 0 || -1 driver dependent!
- odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
- ?RDBMS:create_unicode_table()),
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ "(FIELD text)"),
+
Latin1Data = ["���������",
"testasdf",
"Row 3",
@@ -1458,41 +1479,7 @@ unicode(Config) when is_list(Config) ->
"Row 10",
"Row 11",
"Row 12"],
-
- case ?RDBMS of
- sqlserver ->
- w_char_support_win(Ref, Table, Latin1Data);
- postgres ->
- direct_utf8(Ref, Table, Latin1Data);
- mysql ->
- direct_utf8(Ref, Table, Latin1Data);
- oracle ->
- {skip, "not currently supported"}
- end.
-
-w_char_support_win(Ref, Table, Latin1Data) ->
- UnicodeIn = lists:map(fun(S) ->
- unicode:characters_to_binary(S,latin1,{utf16,little})
- end,
- Latin1Data),
-
- test_server:format("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]),
-
- {updated, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) values(?)",
- [{{sql_wvarchar,50},UnicodeIn}]),
-
- {selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table),
-
- test_server:format("UnicodeOut: ~p~n", [UnicodeOut]),
- Result = lists:map(fun({Unicode}) ->
- unicode:characters_to_list(Unicode,{utf16,little})
- end,
- UnicodeOut),
- Latin1Data = Result.
-
-
-direct_utf8(Ref, Table, Latin1Data) ->
UnicodeIn = lists:map(fun(String) ->
unicode:characters_to_binary(String,latin1,utf8)
end,
@@ -1513,6 +1500,37 @@ direct_utf8(Ref, Table, Latin1Data) ->
test_server:format("Result: ~p ~n", [Result]),
Latin1Data = Result.
+%%------------------------------------------------------------------------
+
+nchar(doc) ->
+ ["Test unicode nchar support in sqlserver"];
+nchar(suit) ->
+ [];
+nchar(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ "(FIELD nchar(50))"),
+
+ w_char_support(Ref, Table, sql_wvarchar, 50).
+
+%%------------------------------------------------------------------------
+
+nvarchar(doc) ->
+ ["Test 'unicode' nvarchar support"];
+nvarchar(suit) ->
+ [];
+nvarchar(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ "(FIELD nvarchar(50))"),
+
+ w_char_support(Ref, Table, sql_wlongvarchar, 50).
%%------------------------------------------------------------------------
timestamp(doc) ->
@@ -1541,3 +1559,43 @@ timestamp(Config) when is_list(Config) ->
TimeStamps = lists:map(fun(Value) -> {Value} end, Data),
{selected,_, TimeStamps} = odbc:sql_query(Ref, "SELECT * FROM " ++ Table).
+%%------------------------------------------------------------------------
+
+w_char_support(Ref, Table, CharType, Size) ->
+ Latin1Data = ["���������",
+ "testasdf",
+ "Row 3",
+ "Row 4",
+ "Row 5",
+ "Row 6",
+ "Row 7",
+ "Row 8",
+ "Row 9",
+ "Row 10",
+ "Row 11",
+ "Row 12"],
+
+ UnicodeIn = lists:map(fun(S) ->
+ unicode:characters_to_binary(S,latin1,{utf16,little})
+ end,
+ Latin1Data),
+
+ test_server:format("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]),
+
+ {updated, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) values(?)",
+ [{{CharType, Size},UnicodeIn}]),
+
+ {selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table),
+
+ test_server:format("UnicodeOut: ~p~n", [UnicodeOut]),
+
+ PadResult = lists:map(fun({Unicode}) ->
+ unicode:characters_to_list(Unicode,{utf16,little})
+ end,
+ UnicodeOut),
+
+ test_server:format("Result: ~p~n", [PadResult]),
+
+ Result = lists:map(fun(Str) -> string:strip(Str) end, PadResult),
+
+ Latin1Data = Result.
diff --git a/lib/odbc/test/oracle.erl b/lib/odbc/test/oracle.erl
index 786280701d..d74863d8c1 100644
--- a/lib/odbc/test/oracle.erl
+++ b/lib/odbc/test/oracle.erl
@@ -108,10 +108,6 @@ create_text_table() ->
" (FIELD long)". %Oracle long is variable length char data
%-------------------------------------------------------------------------
-create_unicode_table() ->
- " (FIELD nvarchar(50))".
-
-%-------------------------------------------------------------------------
create_timestamp_table() ->
" (FIELD DATETIME)".
diff --git a/lib/odbc/test/postgres.erl b/lib/odbc/test/postgres.erl
index 9c7eed271f..26a2913d46 100644
--- a/lib/odbc/test/postgres.erl
+++ b/lib/odbc/test/postgres.erl
@@ -135,10 +135,6 @@ create_text_table() ->
" (FIELD text)".
%-------------------------------------------------------------------------
-create_unicode_table() ->
- " (FIELD text)".
-
-%-------------------------------------------------------------------------
create_timestamp_table() ->
" (FIELD TIMESTAMP)".
diff --git a/lib/odbc/test/sqlserver.erl b/lib/odbc/test/sqlserver.erl
index 13930cd5ae..59252d4276 100644
--- a/lib/odbc/test/sqlserver.erl
+++ b/lib/odbc/test/sqlserver.erl
@@ -123,10 +123,6 @@ create_text_table() ->
" (FIELD text)".
%-------------------------------------------------------------------------
-create_unicode_table() ->
- " (FIELD nvarchar(50))".
-
-%-------------------------------------------------------------------------
create_timestamp_table() ->
" (FIELD DATETIME)".
diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk
index 42a51be33e..120ed9ee3d 100644
--- a/lib/odbc/vsn.mk
+++ b/lib/odbc/vsn.mk
@@ -1 +1 @@
-ODBC_VSN = 2.10.10
+ODBC_VSN = 2.10.11
diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl
index 76c064f1e7..8b0f64eb45 100644
--- a/lib/reltool/src/reltool_sys_win.erl
+++ b/lib/reltool/src/reltool_sys_win.erl
@@ -54,7 +54,9 @@
whitelist,
blacklist,
derived,
- fgraph_wins
+ fgraph_wins,
+ app_box,
+ mod_box
}).
-define(WIN_WIDTH, 800).
@@ -86,6 +88,11 @@
-define(blacklist, "Excluded").
-define(derived, "Derived").
+-define(safe_config,{sys,[{incl_cond,exclude},
+ {app,kernel,[{incl_cond,include}]},
+ {app,stdlib,[{incl_cond,include}]},
+ {app,sasl,[{incl_cond,include}]}]}).
+
-record(root_data, {dir}).
-record(lib_data, {dir, tree, item}).
-record(escript_data, {file, tree, item}).
@@ -102,7 +109,7 @@
start_link(Opts) ->
proc_lib:start_link(?MODULE,
init,
- [[{parent, self()} | Opts]],
+ [[{safe_config, false}, {parent, self()} | Opts]],
infinity,
[]).
@@ -126,53 +133,73 @@ init(Options) ->
exit({Reason, erlang:get_stacktrace()})
end.
-do_init([{parent, Parent} | Options]) ->
+do_init([{safe_config, Safe}, {parent, Parent} | Options]) ->
case reltool_server:start_link(Options) of
{ok, ServerPid, C, Sys} ->
process_flag(trap_exit, C#common.trap_exit),
- S = #state{parent_pid = Parent,
- server_pid = ServerPid,
- common = C,
- config_file = filename:absname("config.reltool"),
- target_dir = filename:absname("reltool_target_dir"),
- app_wins = [],
- sys = Sys,
- fgraph_wins = []},
wx:new(),
wx:debug(C#common.wx_debug),
- S2 = create_window(S),
%% wx_misc:beginBusyCursor(),
case reltool_server:get_status(ServerPid) of
{ok, Warnings} ->
exit_dialog(Warnings),
- {ok, Sys2} = reltool_server:get_sys(ServerPid),
- S3 = S2#state{sys = Sys2},
+ {ok, Sys} = reltool_server:get_sys(ServerPid),
+ S = #state{parent_pid = Parent,
+ server_pid = ServerPid,
+ common = C,
+ config_file = filename:absname("config.reltool"),
+ target_dir = filename:absname("reltool_target_dir"),
+ app_wins = [],
+ sys = Sys,
+ fgraph_wins = []},
+ S2 = create_window(S),
S5 = wx:batch(fun() ->
Title = atom_to_list(?APPLICATION),
- wxFrame:setTitle(S3#state.frame,
+ wxFrame:setTitle(S2#state.frame,
Title),
%% wxFrame:setMinSize(Frame,
%% {?WIN_WIDTH, ?WIN_HEIGHT}),
wxStatusBar:setStatusText(
- S3#state.status_bar,
+ S2#state.status_bar,
"Done."),
- S4 = redraw_apps(S3),
- redraw_libs(S4)
+ S3 = redraw_apps(S2),
+ S4 = redraw_libs(S3),
+ redraw_config_page(S4)
end),
%% wx_misc:endBusyCursor(),
%% wxFrame:destroy(Frame),
proc_lib:init_ack(S#state.parent_pid, {ok, self()}),
loop(S5);
{error, Reason} ->
- io:format("~p(~p): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]),
- exit(Reason)
+ restart_server_safe_config(Safe,Parent,Reason)
end;
{error, Reason} ->
io:format("~p(~p): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]),
exit(Reason)
end.
+restart_server_safe_config(true,_Parent,Reason) ->
+ io:format("~p(~p): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]),
+ exit(Reason);
+restart_server_safe_config(false,Parent,Reason) ->
+ Strings =
+ [{?wxBLACK,"Could not start reltool server:\n\n"},
+ {?wxRED,Reason++"\n\n"},
+ {?wxBLACK,
+ io_lib:format(
+ "Resetting the configuration to:~n~n ~p~n~n"
+ "Do you want to continue with this configuration?",
+ [?safe_config])}],
+
+ case question_dialog_2("Reltool server start error", Strings) of
+ ?wxID_OK ->
+ do_init([{safe_config,true},{parent,Parent},?safe_config]);
+ ?wxID_CANCEL ->
+ io:format("~p(~p): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]),
+ exit(Reason)
+ end.
+
exit_dialog([]) ->
ok;
exit_dialog(Warnings) ->
@@ -606,6 +633,13 @@ create_config_page(#state{sys = Sys, book = Book} = S) ->
{proportion, 1}]),
wxPanel:setSizer(Panel, Sizer),
wxNotebook:addPage(Book, Panel, ?SYS_PAGE, []),
+ S#state{app_box = AppBox, mod_box = ModBox}.
+
+redraw_config_page(#state{sys = Sys, app_box = AppBox, mod_box = ModBox} = S) ->
+ AppChoice = reltool_utils:incl_cond_to_index(Sys#sys.incl_cond),
+ wxRadioBox:setSelection(AppBox, AppChoice),
+ ModChoice = reltool_utils:mod_cond_to_index(Sys#sys.mod_cond),
+ wxRadioBox:setSelection(ModBox, ModChoice),
S.
create_main_release_page(#state{book = Book} = S) ->
@@ -640,15 +674,15 @@ create_main_release_page(#state{book = Book} = S) ->
add_release_page(Book, #rel{name = RelName, rel_apps = RelApps}) ->
Panel = wxPanel:new(Book, []),
Sizer = wxBoxSizer:new(?wxHORIZONTAL),
- RelBox = wxRadioBox:new(Panel,
- ?wxID_ANY,
- "Applications included in the release " ++ RelName,
- ?wxDefaultPosition,
- ?wxDefaultSize,
- [atom_to_list(RA#rel_app.name) || RA <- RelApps],
- []),
- %% wxRadioBox:setSelection(RelBox, 2), % mandatory
- wxEvtHandler:connect(RelBox, command_radiobox_selected,
+ AppNames = [kernel, stdlib |
+ [RA#rel_app.name || RA <- RelApps] -- [kernel, stdlib]],
+ RelBox = wxListBox:new(
+ Panel,?wxID_ANY,
+ [{pos,?wxDefaultPosition},
+ {size,?wxDefaultSize},
+ {choices,[[atom_to_list(AppName)] || AppName <- AppNames]},
+ {style,?wxLB_EXTENDED}]),
+ wxEvtHandler:connect(RelBox, command_listbox_selected,
[{userData, {config_rel_cond, RelName}}]),
RelToolTip = "Choose which applications that shall "
"be included in the release resource file.",
@@ -1363,7 +1397,8 @@ refresh(S) ->
[ok = reltool_app_win:refresh(AW#app_win.pid) || AW <- S#state.app_wins],
S2 = S#state{sys = Sys},
S3 = redraw_libs(S2),
- redraw_apps(S3).
+ S4 = redraw_apps(S3),
+ redraw_config_page(S4).
question_dialog(Question, Details) ->
%% Parent = S#state.frame,
@@ -1420,6 +1455,44 @@ display_message(Message, Icon) ->
wxMessageDialog:showModal(Dialog),
wxMessageDialog:destroy(Dialog).
+%% Strings = [{Color,String}]
+question_dialog_2(DialogLabel, Strings) ->
+ %% Parent = S#state.frame,
+ Parent = wx:typeCast(wx:null(), wxWindow),
+ %% [{style, ?wxYES_NO bor ?wxICON_ERROR bor ?wx}]),
+ DialogStyle = ?wxRESIZE_BORDER bor ?wxCAPTION bor ?wxSYSTEM_MENU bor
+ ?wxMINIMIZE_BOX bor ?wxMAXIMIZE_BOX bor ?wxCLOSE_BOX,
+ Dialog = wxDialog:new(Parent, ?wxID_ANY, DialogLabel,
+ [{style, DialogStyle}]),
+ Color = wxWindow:getBackgroundColour(Dialog),
+ TextStyle = ?wxTE_READONLY bor ?wxTE_MULTILINE bor ?wxHSCROLL,
+ Text = wxTextCtrl:new(Dialog, ?wxID_ANY,
+ [{size, {600, 400}}, {style, TextStyle}]),
+ wxWindow:setBackgroundColour(Text, Color),
+ TextAttr = wxTextAttr:new(),
+ add_text(Text,TextAttr,Strings),
+ Sizer = wxBoxSizer:new(?wxVERTICAL),
+ wxSizer:add(Sizer, Text, [{border, 2}, {flag, ?wxEXPAND}, {proportion, 1}]),
+ ButtSizer = wxDialog:createStdDialogButtonSizer(Dialog, ?wxOK bor ?wxCANCEL),
+ wxSizer:add(Sizer, ButtSizer, [{border, 2}, {flag, ?wxEXPAND}]),
+ wxPanel:setSizer(Dialog, Sizer),
+ wxSizer:fit(Sizer, Dialog),
+ wxSizer:setSizeHints(Sizer, Dialog),
+ Answer = wxDialog:showModal(Dialog),
+ wxDialog:destroy(Dialog),
+ Answer.
+
+add_text(Text,Attr,[{Color,String}|Strings]) ->
+ wxTextAttr:setTextColour(Attr, Color),
+ wxTextCtrl:setDefaultStyle(Text, Attr),
+ wxTextCtrl:appendText(Text, String),
+ add_text(Text,Attr,Strings);
+add_text(_,_,[]) ->
+ ok.
+
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% sys callbacks
diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl
index eb29787103..ab54b1d00b 100644
--- a/lib/sasl/src/release_handler.erl
+++ b/lib/sasl/src/release_handler.erl
@@ -291,7 +291,8 @@ check_script(Script, LibDirs) ->
release_handler_1:check_script(Script, LibDirs).
%%-----------------------------------------------------------------
-%% eval_script(Script, Apps, LibDirs, Opts) -> {ok, UnPurged} |
+%% eval_script(Script, Apps, LibDirs, NewLibs, Opts) ->
+%% {ok, UnPurged} |
%% restart_new_emulator |
%% {error, Error}
%% {'EXIT', Reason}
@@ -299,9 +300,13 @@ check_script(Script, LibDirs) ->
%% net_kernel:monitor_nodes(true) before calling this function.
%% No! No other process than the release_handler can ever call this
%% function, if sync_nodes is used.
-%%-----------------------------------------------------------------
-eval_script(Script, Apps, LibDirs, Opts) ->
- catch release_handler_1:eval_script(Script, Apps, LibDirs, Opts).
+%%
+%% LibDirs is a list of all applications, while NewLibs is a list of
+%% applications that have changed version between the current and the
+%% new release.
+%% -----------------------------------------------------------------
+eval_script(Script, Apps, LibDirs, NewLibs, Opts) ->
+ catch release_handler_1:eval_script(Script, Apps, LibDirs, NewLibs, Opts).
%%-----------------------------------------------------------------
%% Func: create_RELEASES(Root, RelFile, LibDirs) -> ok | {error, Reason}
@@ -405,6 +410,7 @@ eval_appup_script(App, ToVsn, ToDir, Script) ->
Res = release_handler_1:eval_script(Script,
[], % [AppSpec]
[{App, ToVsn, ToDir}],
+ [{App, ToVsn, ToDir}],
[]), % [Opt]
case Res of
{ok, _Unpurged} ->
@@ -906,7 +912,9 @@ do_install_release(#state{start_prg = StartPrg,
EnvBefore = application_controller:prep_config_change(),
Apps = change_appl_data(RelDir, Release, Masters),
LibDirs = Release#release.libs,
- case eval_script(Script, Apps, LibDirs, Opts) of
+ NewLibs = get_new_libs(LatestRelease#release.libs,
+ Release#release.libs),
+ case eval_script(Script, Apps, LibDirs, NewLibs, Opts) of
{ok, []} ->
application_controller:config_change(EnvBefore),
mon_nodes(false),
@@ -1946,3 +1954,25 @@ safe_write_file_m(File, Data, Masters) ->
filename:basename(File),
filename:basename(Backup)}})
end.
+
+%%-----------------------------------------------------------------
+%% Figure out which applications that have changed version between the
+%% two releases. The paths for these applications must always be
+%% updated, even if the relup script does not load any modules. See
+%% OTP-9402.
+%%
+%% A different situation is when the same application version is used
+%% in old and new release, but the path has changed. This is not
+%% handled here - instead it must be explicitely indicated by the
+%% 'update_paths' option to release_handler:install_release/2 if the
+%% code path shall be updated then.
+%% -----------------------------------------------------------------
+get_new_libs([{App,Vsn,LibDir}|CurrentLibs], NewLibs) ->
+ case lists:keyfind(App,1,NewLibs) of
+ {App,NewVsn,_} = LibInfo when NewVsn =/= Vsn ->
+ [LibInfo | get_new_libs(CurrentLibs,NewLibs)];
+ _ ->
+ get_new_libs(CurrentLibs,NewLibs)
+ end;
+get_new_libs([],_) ->
+ [].
diff --git a/lib/sasl/src/release_handler_1.erl b/lib/sasl/src/release_handler_1.erl
index 8d050fb7b0..ff62f847ac 100644
--- a/lib/sasl/src/release_handler_1.erl
+++ b/lib/sasl/src/release_handler_1.erl
@@ -19,7 +19,7 @@
-module(release_handler_1).
%% External exports
--export([eval_script/3, eval_script/4, check_script/2]).
+-export([eval_script/1, eval_script/5, check_script/2]).
-export([get_current_vsn/1]). %% exported because used in a test case
-record(eval_state, {bins = [], stopped = [], suspended = [], apps = [],
@@ -33,11 +33,11 @@
%% libdirs = [{Lib, LibVsn, LibDir}] - Maps Lib to Vsn and Directory
%% unpurged = [{Mod, soft_purge | brutal_purge}]
%% vsns = [{Mod, OldVsn, NewVsn}] - remember the old vsn of a mod
-%% before it is removed/a new vsn is loaded; the new vsn
+%% before a new vsn is loaded; the new vsn
%% is kept in case of a downgrade, where the code_change
%% function receives the vsn of the module to downgrade
%% *to*.
-%% newlibs = [{Lib, Dir}] - list of all new libs; used to change
+%% newlibs = [{Lib, LibVsn, LibDir}] - list of all new libs; used to change
%% the code path
%% opts = [{Tag, Value}] - list of options
%%-----------------------------------------------------------------
@@ -63,10 +63,11 @@ check_script(Script, LibDirs) ->
{error, {old_processes, Mod}}
end.
-eval_script(Script, Apps, LibDirs) ->
- eval_script(Script, Apps, LibDirs, []).
+%% eval_script/1 - For testing only - no apps added, just testing instructions
+eval_script(Script) ->
+ eval_script(Script, [], [], [], []).
-eval_script(Script, Apps, LibDirs, Opts) ->
+eval_script(Script, Apps, LibDirs, NewLibs, Opts) ->
case catch check_old_processes(Script) of
ok ->
{Before, After} = split_instructions(Script),
@@ -75,6 +76,7 @@ eval_script(Script, Apps, LibDirs, Opts) ->
end,
#eval_state{apps = Apps,
libdirs = LibDirs,
+ newlibs = NewLibs,
opts = Opts},
Before) of
EvalState2 when is_record(EvalState2, eval_state) ->
@@ -214,16 +216,15 @@ check_old_code(Mod) ->
%%-----------------------------------------------------------------
eval({load_object_code, {Lib, LibVsn, Modules}}, EvalState) ->
case lists:keysearch(Lib, 1, EvalState#eval_state.libdirs) of
- {value, {Lib, LibVsn, LibDir}} ->
- Ebin = filename:join(LibDir, "ebin"),
+ {value, {Lib, LibVsn, LibDir} = LibInfo} ->
Ext = code:objfile_extension(),
{NewBins, NewVsns} =
lists:foldl(fun(Mod, {Bins, Vsns}) ->
File = lists:concat([Mod, Ext]),
- FName = filename:join(Ebin, File),
+ FName = filename:join([LibDir, "ebin", File]),
case erl_prim_loader:get_file(FName) of
{ok, Bin, FName2} ->
- NVsns = add_new_vsn(Mod, Bin, Vsns),
+ NVsns = add_vsns(Mod, Bin, Vsns),
{[{Mod, Bin, FName2} | Bins],NVsns};
error ->
throw({error, {no_such_file,FName}})
@@ -232,7 +233,7 @@ eval({load_object_code, {Lib, LibVsn, Modules}}, EvalState) ->
{EvalState#eval_state.bins,
EvalState#eval_state.vsns},
Modules),
- NewLibs = [{Lib, Ebin} | EvalState#eval_state.newlibs],
+ NewLibs = lists:keystore(Lib,1,EvalState#eval_state.newlibs,LibInfo),
EvalState#eval_state{bins = NewBins,
newlibs = NewLibs,
vsns = NewVsns};
@@ -242,15 +243,14 @@ eval({load_object_code, {Lib, LibVsn, Modules}}, EvalState) ->
eval(point_of_no_return, EvalState) ->
Libs = case get_opt(update_paths, EvalState, false) of
false ->
- EvalState#eval_state.newlibs; % [{Lib, Path}]
+ EvalState#eval_state.newlibs;
true ->
- lists:map(fun({Lib, _LibVsn, LibDir}) ->
- Ebin= filename:join(LibDir,"ebin"),
- {Lib, Ebin}
- end,
- EvalState#eval_state.libdirs)
+ EvalState#eval_state.libdirs
end,
- lists:foreach(fun({Lib, Path}) -> code:replace_path(Lib, Path) end,
+ lists:foreach(fun({Lib, _LibVsn, LibDir}) ->
+ Ebin = filename:join(LibDir,"ebin"),
+ code:replace_path(Lib, Ebin)
+ end,
Libs),
EvalState;
eval({load, {Mod, _PrePurgeMethod, PostPurgeMethod}}, EvalState) ->
@@ -258,32 +258,21 @@ eval({load, {Mod, _PrePurgeMethod, PostPurgeMethod}}, EvalState) ->
{value, {_Mod, Bin, File}} = lists:keysearch(Mod, 1, Bins),
% load_binary kills all procs running old code
% if soft_purge, we know that there are no such procs now
- Vsns = EvalState#eval_state.vsns,
- NewVsns = add_old_vsn(Mod, Vsns),
code:load_binary(Mod, File, Bin),
% Now, the prev current is old. There might be procs
% running it. Find them.
Unpurged = do_soft_purge(Mod,PostPurgeMethod,EvalState#eval_state.unpurged),
EvalState#eval_state{bins = lists:keydelete(Mod, 1, Bins),
- unpurged = Unpurged,
- vsns = NewVsns};
+ unpurged = Unpurged};
eval({remove, {Mod, _PrePurgeMethod, PostPurgeMethod}}, EvalState) ->
- % purge kills all procs running old code
- % if soft_purge, we know that there are no such procs now
- Vsns = EvalState#eval_state.vsns,
- NewVsns = add_old_vsn(Mod, Vsns),
+ %% purge kills all procs running old code
+ %% if soft_purge, we know that there are no such procs now
code:purge(Mod),
code:delete(Mod),
- % Now, the prev current is old. There might be procs
- % running it. Find them.
- Unpurged =
- case code:soft_purge(Mod) of
- true -> EvalState#eval_state.unpurged;
- false -> [{Mod, PostPurgeMethod} | EvalState#eval_state.unpurged]
- end,
-%% Bins = EvalState#eval_state.bins,
-%% EvalState#eval_state{bins = lists:keydelete(Mod, 1, Bins),
- EvalState#eval_state{unpurged = Unpurged, vsns = NewVsns};
+ %% Now, the prev current is old. There might be procs
+ %% running it. Find them.
+ Unpurged = do_soft_purge(Mod,PostPurgeMethod,EvalState#eval_state.unpurged),
+ EvalState#eval_state{unpurged = Unpurged};
eval({purge, Modules}, EvalState) ->
% Now, if there are any processes still executing old code, OR
% if some new processes started after suspend but before load,
@@ -606,26 +595,20 @@ sync_nodes(Id, Nodes) ->
end,
NNodes).
-add_old_vsn(Mod, Vsns) ->
+add_vsns(Mod, NewBin, Vsns) ->
+ OldVsn = get_current_vsn(Mod),
+ NewVsn = get_vsn(NewBin),
case lists:keysearch(Mod, 1, Vsns) of
- {value, {Mod, undefined, NewVsn}} ->
- OldVsn = get_current_vsn(Mod),
- lists:keyreplace(Mod, 1, Vsns, {Mod, OldVsn, NewVsn});
- {value, {Mod, _OldVsn, _NewVsn}} ->
- Vsns;
+ {value, {Mod, OldVsn0, NewVsn0}} ->
+ lists:keyreplace(Mod, 1, Vsns, {Mod,
+ replace_undefined(OldVsn0,OldVsn),
+ replace_undefined(NewVsn0,NewVsn)});
false ->
- OldVsn = get_current_vsn(Mod),
- [{Mod, OldVsn, undefined} | Vsns]
+ [{Mod, OldVsn, NewVsn} | Vsns]
end.
-add_new_vsn(Mod, Bin, Vsns) ->
- NewVsn = get_vsn(Bin),
- case lists:keysearch(Mod, 1, Vsns) of
- {value, {Mod, OldVsn, undefined}} ->
- lists:keyreplace(Mod, 1, Vsns, {Mod, OldVsn, NewVsn});
- false ->
- [{Mod, undefined, NewVsn} | Vsns]
- end.
+replace_undefined(undefined,Vsn) -> Vsn;
+replace_undefined(Vsn,_) -> Vsn.
%%-----------------------------------------------------------------
%% Func: get_current_vsn/1
@@ -645,7 +628,9 @@ get_current_vsn(Mod) ->
{ok, Bin, _File2} ->
get_vsn(Bin);
error ->
- throw({error, {no_such_file, File}})
+ %% This is the case when a new module is added, there will
+ %% be no current version of it at the time of this call.
+ undefined
end.
%%-----------------------------------------------------------------
diff --git a/lib/sasl/src/systools_lib.erl b/lib/sasl/src/systools_lib.erl
index b652c109fe..f951647b79 100644
--- a/lib/sasl/src/systools_lib.erl
+++ b/lib/sasl/src/systools_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -176,21 +176,26 @@ add_dirs(RegName, Dirs, Root) ->
regexp_match(RegName, D0, Root) ->
case file:list_dir(D0) of
{ok, Files} when length(Files) > 0 ->
- FR = fun(F) ->
- case regexp:match(F, RegName) of
- {match,1,N} when N == length(F) ->
- DirF = join(D0, F, Root),
- case dir_p(DirF) of
- true ->
- {true, DirF};
+ case re:compile(RegName) of
+ {ok, MP} ->
+ FR = fun(F) ->
+ case re:run(F, MP) of
+ {match,[{0,N}]} when N == length(F) ->
+ DirF = join(D0, F, Root),
+ case dir_p(DirF) of
+ true ->
+ {true, DirF};
+ _ ->
+ false
+ end;
_ ->
false
- end;
- _ ->
- false
- end
- end,
- {true,lists:zf(FR, Files)};
+ end
+ end,
+ {true,lists:zf(FR, Files)};
+ _ ->
+ false
+ end;
_ ->
false
end.
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index 16267ba0d4..9c7733b7ec 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -55,7 +55,7 @@ win32_cases() ->
%% Cases that can be run on all platforms
cases() ->
- [otp_2740, otp_2760, otp_5761, instructions, eval_appup].
+ [otp_2740, otp_2760, otp_5761, otp_9402, otp_9417, instructions, eval_appup].
groups() ->
[{release,[],
@@ -393,7 +393,7 @@ instructions(Conf) when is_list(Conf) ->
{stop, [aa]},
{apply, {?MODULE, no_cc, []}},
{start, [aa]}],
- {ok, _} = release_handler_1:eval_script(S1, [], []),
+ {ok, _} = release_handler_1:eval_script(S1),
case whereis(cc) of
Pid2 when is_pid(Pid2) -> ok;
@@ -403,17 +403,17 @@ instructions(Conf) when is_list(Conf) ->
%% Make bb run old version of b.
S2 = [point_of_no_return,
{remove, {b, soft_purge, soft_purge}}],
- {ok, [{b, soft_purge}]} = release_handler_1:eval_script(S2, [], []),
+ {ok, [{b, soft_purge}]} = release_handler_1:eval_script(S2),
check_bstate("first", [FirstBB]),
false = code:is_loaded(b),
- {error,{old_processes,b}} = release_handler_1:eval_script(S2,[],[]),
+ {error,{old_processes,b}} = release_handler_1:eval_script(S2),
check_bstate("first", [FirstBB]),
%% Let supervisor restart bb with new code
S3 = [point_of_no_return,
{purge, [b]}],
- {ok, []} = release_handler_1:eval_script(S3, [], []),
+ {ok, []} = release_handler_1:eval_script(S3),
ok = wait_for(bb),
check_bstate("second", []),
SecondBB = whereis(bb),
@@ -446,7 +446,7 @@ instructions(Conf) when is_list(Conf) ->
%% Let supervisor restart bb yet another time
S4 = [point_of_no_return,
{remove, {b, brutal_purge, soft_purge}}],
- {ok, HopefullyEmpty} = release_handler_1:eval_script(S4, [], []),
+ {ok, HopefullyEmpty} = release_handler_1:eval_script(S4),
ok = wait_for(bb),
FourthBB = whereis(bb),
@@ -566,8 +566,7 @@ otp_2760(Conf) ->
%% Execute the relup script and check that app1 is unloaded
{ok, [{"after", [{_Rel1Vsn, _Descr, Script}], _}]} =
file:consult(filename:join(Rel2Dir, "relup")),
- {ok, []} = rpc:call(Node, release_handler_1, eval_script,
- [Script, [], []]),
+ {ok, []} = rpc:call(Node, release_handler_1, eval_script, [Script]),
false = rpc:call(Node, code, is_loaded, [app1]),
true = stop_node(Node),
@@ -658,6 +657,126 @@ otp_5761(Conf) when is_list(Conf) ->
true = stop_node(Node),
ok.
+
+%% When a new version of an application is added, but no module is
+%% changed - the path was not updated - i.e. code:priv_dir would point
+%% to the old location.
+otp_9402(Conf) when is_list(Conf) ->
+ %% Set some paths
+ PrivDir = priv_dir(Conf),
+ Dir = filename:join(PrivDir,"otp_9402"),
+ LibDir = filename:join(?config(data_dir, Conf), "lib"),
+
+ %% Create the releases
+ Rel1 = create_and_install_fake_first_release(Dir,
+ [{a,"1.1",LibDir}]),
+ Rel2 = create_fake_upgrade_release(Dir,
+ "2",
+ [{a,"1.2",LibDir}],
+ {Rel1,Rel1,[LibDir]}),
+ Rel1Dir = filename:dirname(Rel1),
+ Rel2Dir = filename:dirname(Rel2),
+
+ %% Start a slave node
+ {ok, Node} = t_start_node(otp_9402, Rel1, filename:join(Rel1Dir,"sys.config")),
+
+ %% Check path
+ Dir1 = filename:join([LibDir, "a-1.1"]),
+ Dir1 = rpc:call(Node, code, lib_dir, [a]),
+ ABeam = rpc:call(Node, code, which, [a]),
+
+ %% Install second release, with no changed modules
+ {ok, RelVsn2} =
+ rpc:call(Node, release_handler, set_unpacked,
+ [Rel2++".rel", [{a,"1.2",LibDir}]]),
+ ok = rpc:call(Node, release_handler, install_file,
+ [RelVsn2, filename:join(Rel2Dir, "relup")]),
+ ok = rpc:call(Node, release_handler, install_file,
+ [RelVsn2, filename:join(Rel2Dir, "start.boot")]),
+ ok = rpc:call(Node, release_handler, install_file,
+ [RelVsn2, filename:join(Rel2Dir, "sys.config")]),
+
+ {ok, RelVsn1, []} =
+ rpc:call(Node, release_handler, install_release, [RelVsn2]),
+
+ %% Check path
+ Dir2 = filename:join([LibDir, "a-1.2"]),
+ Dir2 = rpc:call(Node, code, lib_dir, [a]),
+ APrivDir2 = rpc:call(Node, code, priv_dir, [a]),
+ true = filelib:is_regular(filename:join(APrivDir2,"file")),
+
+ %% Just to make sure no modules have been re-loaded
+ ABeam = rpc:call(Node, code, which, [a]),
+
+ %% Install RelVsn1 again
+ {ok, _OtherVsn, []} =
+ rpc:call(Node, release_handler, install_release, [RelVsn1]),
+
+ %% Check path
+ Dir1 = rpc:call(Node, code, lib_dir, [a]),
+ APrivDir1 = rpc:call(Node, code, priv_dir, [a]),
+ false = filelib:is_regular(filename:join(APrivDir1,"file")),
+
+ %% Just to make sure no modules have been re-loaded
+ ABeam = rpc:call(Node, code, which, [a]),
+
+ ok.
+
+
+%% When a module is deleted in an appup instruction, the upgrade
+%% failed if the module was not loaded.
+otp_9417(Conf) when is_list(Conf) ->
+ %% Set some paths
+ PrivDir = priv_dir(Conf),
+ Dir = filename:join(PrivDir,"otp_9417"),
+ LibDir = filename:join(?config(data_dir, Conf), "lib"),
+
+ %% Create the releases
+ Rel1 = create_and_install_fake_first_release(Dir,
+ [{b,"1.0",LibDir}]),
+ Rel2 = create_fake_upgrade_release(Dir,
+ "2",
+ [{b,"2.0",LibDir}],
+ {Rel1,Rel1,[LibDir]}),
+ Rel1Dir = filename:dirname(Rel1),
+ Rel2Dir = filename:dirname(Rel2),
+
+ %% Start a slave node
+ {ok, Node} = t_start_node(otp_9417, Rel1, filename:join(Rel1Dir,"sys.config")),
+
+ %% Check paths
+ Dir1 = filename:join([LibDir, "b-1.0"]),
+ Dir1 = rpc:call(Node, code, lib_dir, [b]),
+ BLibBeam = filename:join([Dir1,"ebin","b_lib.beam"]),
+ BLibBeam = rpc:call(Node,code,which,[b_lib]),
+ false = rpc:call(Node,code,is_loaded,[b_lib]),
+ false = rpc:call(Node,code,is_loaded,[b_server]),
+
+ %% Install second release, which removes b_lib module
+ {ok, RelVsn2} =
+ rpc:call(Node, release_handler, set_unpacked,
+ [Rel2++".rel", [{b,"2.0",LibDir}]]),
+ ok = rpc:call(Node, release_handler, install_file,
+ [RelVsn2, filename:join(Rel2Dir, "relup")]),
+ ok = rpc:call(Node, release_handler, install_file,
+ [RelVsn2, filename:join(Rel2Dir, "start.boot")]),
+ ok = rpc:call(Node, release_handler, install_file,
+ [RelVsn2, filename:join(Rel2Dir, "sys.config")]),
+
+ {ok, _RelVsn1, []} =
+ rpc:call(Node, release_handler, install_release, [RelVsn2]),
+
+ %% Check that the module does no longer exist
+ false = rpc:call(Node, code, is_loaded, [b_lib]),
+ non_existing = rpc:call(Node, code, which, [b_lib]),
+
+ %% And check some paths
+ Dir2 = filename:join([LibDir, "b-2.0"]),
+ Dir2 = rpc:call(Node, code, lib_dir, [b]),
+ BServerBeam = filename:join([Dir2,"ebin","b_server.beam"]),
+ {file,BServerBeam} = rpc:call(Node,code,is_loaded,[b_server]),
+ ok.
+
%% Test upgrade and downgrade of applications
eval_appup(Conf) when is_list(Conf) ->
diff --git a/lib/sasl/test/release_handler_SUITE_data/Makefile.src b/lib/sasl/test/release_handler_SUITE_data/Makefile.src
index 85e25fdc2f..a12e526d2e 100644
--- a/lib/sasl/test/release_handler_SUITE_data/Makefile.src
+++ b/lib/sasl/test/release_handler_SUITE_data/Makefile.src
@@ -1,14 +1,19 @@
EFLAGS=+debug_info
P2B= \
- P2B/a-2.0/ebin/a.beam \
- P2B/a-2.0/ebin/a_sup.beam
+ P2B/a-2.0/ebin/a.@EMULATOR@ \
+ P2B/a-2.0/ebin/a_sup.@EMULATOR@
LIB= \
- lib/a-1.1/ebin/a.beam \
- lib/a-1.1/ebin/a_sup.beam \
- lib/a-1.0/ebin/a.beam \
- lib/a-1.0/ebin/a_sup.beam \
+ lib/a-1.2/ebin/a.@EMULATOR@ \
+ lib/a-1.2/ebin/a_sup.@EMULATOR@ \
+ lib/a-1.1/ebin/a.@EMULATOR@ \
+ lib/a-1.1/ebin/a_sup.@EMULATOR@ \
+ lib/a-1.0/ebin/a.@EMULATOR@ \
+ lib/a-1.0/ebin/a_sup.@EMULATOR@ \
+ lib/b-1.0/ebin/b_server.@EMULATOR@ \
+ lib/b-1.0/ebin/b_lib.@EMULATOR@ \
+ lib/b-2.0/ebin/b_server.@EMULATOR@
APP= \
app1_app2/lib1/app1-1.0/ebin/app1_sup.@EMULATOR@ \
@@ -57,6 +62,21 @@ lib/a-1.1/ebin/a_sup.@EMULATOR@: lib/a-1.1/src/a_sup.erl
erlc $(EFLAGS) -olib/a-1.1/ebin lib/a-1.1/src/a_sup.erl
+lib/a-1.2/ebin/a.@EMULATOR@: lib/a-1.2/src/a.erl
+ erlc $(EFLAGS) -olib/a-1.2/ebin lib/a-1.2/src/a.erl
+lib/a-1.2/ebin/a_sup.@EMULATOR@: lib/a-1.2/src/a_sup.erl
+ erlc $(EFLAGS) -olib/a-1.2/ebin lib/a-1.2/src/a_sup.erl
+
+lib/b-1.0/ebin/b_server.@EMULATOR@: lib/b-1.0/src/b_server.erl
+ erlc $(EFLAGS) -olib/b-1.0/ebin lib/b-1.0/src/b_server.erl
+lib/b-1.0/ebin/b_lib.@EMULATOR@: lib/b-1.0/src/b_lib.erl
+ erlc $(EFLAGS) -olib/b-1.0/ebin lib/b-1.0/src/b_lib.erl
+
+lib/b-2.0/ebin/b_server.@EMULATOR@: lib/b-2.0/src/b_server.erl
+ erlc $(EFLAGS) -olib/b-2.0/ebin lib/b-2.0/src/b_server.erl
+
+
+
app1_app2/lib1/app1-1.0/ebin/app1_sup.@EMULATOR@: app1_app2/lib1/app1-1.0/src/app1_sup.erl
erlc $(EFLAGS) -oapp1_app2/lib1/app1-1.0/ebin app1_app2/lib1/app1-1.0/src/app1_sup.erl
app1_app2/lib1/app1-1.0/ebin/app1_server.@EMULATOR@: app1_app2/lib1/app1-1.0/src/app1_server.erl
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/README b/lib/sasl/test/release_handler_SUITE_data/lib/README
new file mode 100644
index 0000000000..667d21d4cf
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/README
@@ -0,0 +1,16 @@
+a-1.0:
+start version
+
+a-1.1:
+can be upgraded to from a-1.0. Module a has changed
+
+a-1.2:
+can be upgraded to from a-1.1.
+No module have changed, but priv dir is added including one 'file'
+
+b-1.0:
+start version, includes b_lib and b_server
+
+b-2.0:
+can be upgraded to from b-1.0.
+Removes b_lib and updates b_server
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.app
index e938137f67..b38722f06d 100644
--- a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.app
@@ -1,7 +1,7 @@
{application, a,
[{description, "A CXC 138 11"},
- {vsn, "1.0"},
- {modules, [{a, 1}, {a_sup,1}]},
+ {vsn, "1.2"},
+ {modules, [{a, 2}, {a_sup,1}]},
{registered, [a_sup]},
{applications, [kernel, stdlib]},
{env, [{key1, val1}]},
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.appup b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.appup
new file mode 100644
index 0000000000..3df0546316
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.appup
@@ -0,0 +1,3 @@
+{"1.2",
+ [{"1.1",[]}],
+ [{"1.1",[]}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/priv/file b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/priv/file
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/priv/file
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/src/a.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/src/a.erl
new file mode 100644
index 0000000000..c082ad5339
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/src/a.erl
@@ -0,0 +1,54 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+-module(a).
+
+
+-behaviour(gen_server).
+
+%% External exports
+-export([start_link/0, a/0, b/0]).
+%% Internal exports
+-export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]).
+
+start_link() -> gen_server:start_link({local, aa}, a, [], []).
+
+a() -> gen_server:call(aa, a).
+b() -> gen_server:call(aa, b).
+
+%%-----------------------------------------------------------------
+%% Callback functions from gen_server
+%%-----------------------------------------------------------------
+init([]) ->
+ process_flag(trap_exit, true),
+ {ok, {state, bval}}.
+
+handle_call(a, _From, State) ->
+ X = application:get_all_env(a),
+ {reply, X, State};
+
+handle_call(b, _From, State) ->
+ {reply, {ok, element(2, State)}, State}.
+
+handle_info(_, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(1, Extra, State) ->
+ {ok, {state, bval}}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/src/a_sup.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/src/a_sup.erl
new file mode 100644
index 0000000000..a141c1767b
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/src/a_sup.erl
@@ -0,0 +1,37 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+-module(a_sup).
+
+
+-behaviour(supervisor).
+
+%% External exports
+-export([start/2]).
+
+%% Internal exports
+-export([init/1]).
+
+start(_, _) ->
+ supervisor:start_link({local, a_sup}, a_sup, []).
+
+init([]) ->
+ SupFlags = {one_for_one, 4, 3600},
+ Config = {a,
+ {a, start_link, []},
+ permanent, 2000, worker, [a]},
+ {ok, {SupFlags, [Config]}}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app
new file mode 100644
index 0000000000..00347b2754
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app
@@ -0,0 +1,7 @@
+%% -*- erlang -*-
+{application, b,
+ [{description, "B CXC 138 12"},
+ {vsn, "1.0"},
+ {modules, [{b_server, 1},{b_lib, 1}]},
+ {registered, [b_server]},
+ {applications, [kernel, stdlib]}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/src/b_lib.erl b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/src/b_lib.erl
new file mode 100644
index 0000000000..7e8a308a5e
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/src/b_lib.erl
@@ -0,0 +1,3 @@
+-module(b_lib).
+-compile(export_all).
+foo() -> ok.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/src/b_server.erl b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/src/b_server.erl
new file mode 100644
index 0000000000..e1a80a076f
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/src/b_server.erl
@@ -0,0 +1,37 @@
+-module(b_server).
+
+-behaviour(gen_server).
+
+%% API
+-export([start_link/0]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+
+-record(state, {}).
+
+start_link() ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+init([]) ->
+ {ok, #state{}}.
+
+handle_call(_Request, _From, State) ->
+ Reply = ok,
+ {reply, Reply, State}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(OldVsn, State, Extra) ->
+ file:write_file("/tmp/b_server",io_lib:format("~p~n",[{"1.0",OldVsn,Extra}])),
+ {ok, State}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app
new file mode 100644
index 0000000000..73c8e42b32
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app
@@ -0,0 +1,7 @@
+%% -*- erlang -*-
+{application, b,
+ [{description, "B CXC 138 12"},
+ {vsn, "2.0"},
+ {modules, [{b_server, 1}]},
+ {registered, [b_server]},
+ {applications, [kernel, stdlib]}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.appup b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.appup
new file mode 100644
index 0000000000..d261a37732
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.appup
@@ -0,0 +1,3 @@
+{"2.0",
+ [{"1.0",[{delete_module,b_lib}, {update,b_server,{advanced,[]}}]}],
+ [{"1.0",[{add_module,b_lib},{update,b_server,{advanced,[]}}]}]}.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/src/b_lib.erl b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/src/b_lib.erl
new file mode 100644
index 0000000000..7e8a308a5e
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/src/b_lib.erl
@@ -0,0 +1,3 @@
+-module(b_lib).
+-compile(export_all).
+foo() -> ok.
diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/src/b_server.erl b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/src/b_server.erl
new file mode 100644
index 0000000000..f8bfbdaff7
--- /dev/null
+++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/src/b_server.erl
@@ -0,0 +1,37 @@
+-module(b_server).
+
+-behaviour(gen_server).
+
+%% API
+-export([start_link/0]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+
+-record(state, {}).
+
+start_link() ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+init([]) ->
+ {ok, #state{}}.
+
+handle_call(_Request, _From, State) ->
+ Reply = ok,
+ {reply, Reply, State}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(OldVsn, State, Extra) ->
+ file:write_file("/tmp/b_server",io_lib:format("~p~n",[{"2.0",OldVsn,Extra}])),
+ {ok, State}.
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 0da6bbee5b..566068beaf 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -480,7 +480,6 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
</func>
<func>
- <name>getopts(Socket) -> </name>
<name>getopts(Socket, OptionNames) ->
{ok, [socketoption()]} | {error, Reason}</name>
<fsummary>Get the value of the specified options.</fsummary>
@@ -489,8 +488,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
<v>OptionNames = [atom()]</v>
</type>
<desc>
- <p>Get the value of the specified socket options, if no
- options are specified all options are returned.
+ <p>Get the value of the specified socket options.
</p>
</desc>
</func>
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index a5e8e7e5c2..a0aedbbbee 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -112,7 +112,7 @@ connect(Socket, SslOptions) when is_port(Socket) ->
connect(Socket, SslOptions0, Timeout) when is_port(Socket) ->
EmulatedOptions = emulated_options(),
{ok, InetValues} = inet:getopts(Socket, EmulatedOptions),
- inet:setopts(Socket, internal_inet_values()),
+ ok = inet:setopts(Socket, internal_inet_values()),
try handle_options(SslOptions0 ++ InetValues, client) of
{ok, #config{cb=CbInfo, ssl=SslOptions, emulated=EmOpts}} ->
case inet:peername(Socket) of
@@ -238,7 +238,7 @@ ssl_accept(#sslsocket{} = Socket, Timeout) ->
ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
EmulatedOptions = emulated_options(),
{ok, InetValues} = inet:getopts(Socket, EmulatedOptions),
- inet:setopts(Socket, internal_inet_values()),
+ ok = inet:setopts(Socket, internal_inet_values()),
try handle_options(SslOptions ++ InetValues, server) of
{ok, #config{cb=CbInfo,ssl=SslOpts, emulated=EmOpts}} ->
{ok, Port} = inet:port(Socket),
@@ -406,25 +406,51 @@ cipher_suites(openssl) ->
%%
%% Description: Gets options
%%--------------------------------------------------------------------
-getopts(#sslsocket{fd = new_ssl, pid = Pid}, OptTags) when is_pid(Pid) ->
- ssl_connection:get_opts(Pid, OptTags);
-getopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, OptTags) ->
- inet:getopts(ListenSocket, OptTags);
-getopts(#sslsocket{} = Socket, Options) ->
+getopts(#sslsocket{fd = new_ssl, pid = Pid}, OptionTags) when is_pid(Pid), is_list(OptionTags) ->
+ ssl_connection:get_opts(Pid, OptionTags);
+getopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, OptionTags) when is_list(OptionTags) ->
+ try inet:getopts(ListenSocket, OptionTags) of
+ {ok, _} = Result ->
+ Result;
+ {error, InetError} ->
+ {error, {eoptions, {inet_options, OptionTags, InetError}}}
+ catch
+ _:_ ->
+ {error, {eoptions, {inet_options, OptionTags}}}
+ end;
+getopts(#sslsocket{fd = new_ssl}, OptionTags) ->
+ {error, {eoptions, {inet_options, OptionTags}}};
+getopts(#sslsocket{} = Socket, OptionTags) ->
ensure_old_ssl_started(),
- ssl_broker:getopts(Socket, Options).
+ ssl_broker:getopts(Socket, OptionTags).
%%--------------------------------------------------------------------
-spec setopts(#sslsocket{}, [proplists:property()]) -> ok | {error, reason()}.
%%
%% Description: Sets options
%%--------------------------------------------------------------------
-setopts(#sslsocket{fd = new_ssl, pid = Pid}, Opts0) when is_pid(Pid) ->
- Opts = proplists:expand([{binary, [{mode, binary}]},
- {list, [{mode, list}]}], Opts0),
- ssl_connection:set_opts(Pid, Opts);
-setopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, OptTags) ->
- inet:setopts(ListenSocket, OptTags);
+setopts(#sslsocket{fd = new_ssl, pid = Pid}, Options0) when is_pid(Pid), is_list(Options0) ->
+ try proplists:expand([{binary, [{mode, binary}]},
+ {list, [{mode, list}]}], Options0) of
+ Options ->
+ ssl_connection:set_opts(Pid, Options)
+ catch
+ _:_ ->
+ {error, {eoptions, {not_a_proplist, Options0}}}
+ end;
+
+setopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, Options) when is_list(Options) ->
+ try inet:setopts(ListenSocket, Options) of
+ ok ->
+ ok;
+ {error, InetError} ->
+ {error, {eoptions, {inet_options, Options, InetError}}}
+ catch
+ _:Error ->
+ {error, {eoptions, {inet_options, Options, Error}}}
+ end;
+setopts(#sslsocket{fd = new_ssl}, Options) ->
+ {error, {eoptions,{not_a_proplist, Options}}};
setopts(#sslsocket{} = Socket, Options) ->
ensure_old_ssl_started(),
ssl_broker:setopts(Socket, Options).
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 8c0c2bfa5d..422ea6404b 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,9 +30,9 @@
-include("ssl_internal.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([trusted_cert_and_path/2,
- certificate_chain/2,
- file_to_certificats/1,
+-export([trusted_cert_and_path/3,
+ certificate_chain/3,
+ file_to_certificats/2,
validate_extension/3,
is_valid_extkey_usage/2,
is_valid_key_usage/2,
@@ -46,14 +46,14 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec trusted_cert_and_path([der_cert()], certdb_ref()) ->
+-spec trusted_cert_and_path([der_cert()], db_handle(), certdb_ref()) ->
{der_cert() | unknown_ca, [der_cert()]}.
%%
%% Description: Extracts the root cert (if not presents tries to
%% look it up, if not found {bad_cert, unknown_ca} will be added verification
%% errors. Returns {RootCert, Path, VerifyErrors}
%%--------------------------------------------------------------------
-trusted_cert_and_path(CertChain, CertDbRef) ->
+trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef) ->
Path = [Cert | _] = lists:reverse(CertChain),
OtpCert = public_key:pkix_decode_cert(Cert, otp),
SignedAndIssuerID =
@@ -66,7 +66,7 @@ trusted_cert_and_path(CertChain, CertDbRef) ->
{ok, IssuerId} ->
{other, IssuerId};
{error, issuer_not_found} ->
- case find_issuer(OtpCert, no_candidate) of
+ case find_issuer(OtpCert, no_candidate, CertDbHandle) of
{ok, IssuerId} ->
{other, IssuerId};
Other ->
@@ -82,7 +82,7 @@ trusted_cert_and_path(CertChain, CertDbRef) ->
{self, _} when length(Path) == 1 ->
{selfsigned_peer, Path};
{_ ,{SerialNr, Issuer}} ->
- case ssl_manager:lookup_trusted_cert(CertDbRef, SerialNr, Issuer) of
+ case ssl_manager:lookup_trusted_cert(CertDbHandle, CertDbRef, SerialNr, Issuer) of
{ok, {BinCert,_}} ->
{BinCert, Path};
_ ->
@@ -92,23 +92,23 @@ trusted_cert_and_path(CertChain, CertDbRef) ->
end.
%%--------------------------------------------------------------------
--spec certificate_chain(undefined | binary(), certdb_ref()) ->
+-spec certificate_chain(undefined | binary(), db_handle(), certdb_ref()) ->
{error, no_cert} | {ok, [der_cert()]}.
%%
%% Description: Return the certificate chain to send to peer.
%%--------------------------------------------------------------------
-certificate_chain(undefined, _CertsDbRef) ->
+certificate_chain(undefined, _, _) ->
{error, no_cert};
-certificate_chain(OwnCert, CertsDbRef) ->
+certificate_chain(OwnCert, CertDbHandle, CertsDbRef) ->
ErlCert = public_key:pkix_decode_cert(OwnCert, otp),
- certificate_chain(ErlCert, OwnCert, CertsDbRef, [OwnCert]).
+ certificate_chain(ErlCert, OwnCert, CertDbHandle, CertsDbRef, [OwnCert]).
%%--------------------------------------------------------------------
--spec file_to_certificats(string()) -> [der_cert()].
+-spec file_to_certificats(string(), term()) -> [der_cert()].
%%
%% Description: Return list of DER encoded certificates.
%%--------------------------------------------------------------------
-file_to_certificats(File) ->
- {ok, List} = ssl_manager:cache_pem_file(File),
+file_to_certificats(File, DbHandle) ->
+ {ok, List} = ssl_manager:cache_pem_file(File, DbHandle),
[Bin || {'Certificate', Bin, not_encrypted} <- List].
%%--------------------------------------------------------------------
-spec validate_extension(term(), #'Extension'{} | {bad_cert, atom()} | valid,
@@ -180,7 +180,7 @@ signature_type(?'id-dsa-with-sha1') ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-certificate_chain(OtpCert, _Cert, CertsDbRef, Chain) ->
+certificate_chain(OtpCert, _Cert, CertDbHandle, CertsDbRef, Chain) ->
IssuerAndSelfSigned =
case public_key:pkix_is_self_signed(OtpCert) of
true ->
@@ -191,11 +191,11 @@ certificate_chain(OtpCert, _Cert, CertsDbRef, Chain) ->
case IssuerAndSelfSigned of
{_, true = SelfSigned} ->
- certificate_chain(CertsDbRef, Chain, ignore, ignore, SelfSigned);
+ certificate_chain(CertDbHandle, CertsDbRef, Chain, ignore, ignore, SelfSigned);
{{error, issuer_not_found}, SelfSigned} ->
- case find_issuer(OtpCert, no_candidate) of
+ case find_issuer(OtpCert, no_candidate, CertDbHandle) of
{ok, {SerialNr, Issuer}} ->
- certificate_chain(CertsDbRef, Chain,
+ certificate_chain(CertDbHandle, CertsDbRef, Chain,
SerialNr, Issuer, SelfSigned);
_ ->
%% Guess the the issuer must be the root
@@ -205,19 +205,19 @@ certificate_chain(OtpCert, _Cert, CertsDbRef, Chain) ->
{ok, lists:reverse(Chain)}
end;
{{ok, {SerialNr, Issuer}}, SelfSigned} ->
- certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, SelfSigned)
+ certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, SelfSigned)
end.
-certificate_chain(_CertsDbRef, Chain, _SerialNr, _Issuer, true) ->
+certificate_chain(_,_, Chain, _SerialNr, _Issuer, true) ->
{ok, lists:reverse(Chain)};
-certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) ->
- case ssl_manager:lookup_trusted_cert(CertsDbRef,
+certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) ->
+ case ssl_manager:lookup_trusted_cert(CertDbHandle, CertsDbRef,
SerialNr, Issuer) of
{ok, {IssuerCert, ErlCert}} ->
ErlCert = public_key:pkix_decode_cert(IssuerCert, otp),
certificate_chain(ErlCert, IssuerCert,
- CertsDbRef, [IssuerCert | Chain]);
+ CertDbHandle, CertsDbRef, [IssuerCert | Chain]);
_ ->
%% The trusted cert may be obmitted from the chain as the
%% counter part needs to have it anyway to be able to
@@ -227,8 +227,8 @@ certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) ->
{ok, lists:reverse(Chain)}
end.
-find_issuer(OtpCert, PrevCandidateKey) ->
- case ssl_manager:issuer_candidate(PrevCandidateKey) of
+find_issuer(OtpCert, PrevCandidateKey, CertDbHandle) ->
+ case ssl_manager:issuer_candidate(PrevCandidateKey, CertDbHandle) of
no_more_candidates ->
{error, issuer_not_found};
{Key, {_Cert, ErlCertCandidate}} ->
@@ -236,7 +236,7 @@ find_issuer(OtpCert, PrevCandidateKey) ->
true ->
public_key:pkix_issuer_id(ErlCertCandidate, self);
false ->
- find_issuer(OtpCert, Key)
+ find_issuer(OtpCert, Key, CertDbHandle)
end
end.
diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl
index 3eceefa304..0560a02110 100644
--- a/lib/ssl/src/ssl_certificate_db.erl
+++ b/lib/ssl/src/ssl_certificate_db.erl
@@ -26,8 +26,8 @@
-include_lib("public_key/include/public_key.hrl").
-export([create/0, remove/1, add_trusted_certs/3,
- remove_trusted_certs/2, lookup_trusted_cert/3, issuer_candidate/1,
- lookup_cached_certs/1, cache_pem_file/4, uncache_pem_file/2, lookup/2]).
+ remove_trusted_certs/2, lookup_trusted_cert/4, issuer_candidate/2,
+ lookup_cached_certs/2, cache_pem_file/4, uncache_pem_file/2, lookup/2]).
-type time() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}.
@@ -36,19 +36,19 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec create() -> certdb_ref().
+-spec create() -> [db_handle()].
%%
%% Description: Creates a new certificate db.
-%% Note: lookup_trusted_cert/3 may be called from any process but only
+%% Note: lookup_trusted_cert/4 may be called from any process but only
%% the process that called create may call the other functions.
%%--------------------------------------------------------------------
create() ->
- [ets:new(certificate_db_name(), [named_table, set, protected]),
- ets:new(ssl_file_to_ref, [named_table, set, protected]),
+ [ets:new(ssl_otp_certificate_db, [set, protected]),
+ ets:new(ssl_file_to_ref, [set, protected]),
ets:new(ssl_pid_to_file, [bag, private])].
%%--------------------------------------------------------------------
--spec remove(certdb_ref()) -> term().
+-spec remove([db_handle()]) -> term().
%%
%% Description: Removes database db
%%--------------------------------------------------------------------
@@ -56,7 +56,7 @@ remove(Dbs) ->
lists:foreach(fun(Db) -> true = ets:delete(Db) end, Dbs).
%%--------------------------------------------------------------------
--spec lookup_trusted_cert(reference(), serialnumber(), issuer()) ->
+-spec lookup_trusted_cert(db_handle(), certdb_ref(), serialnumber(), issuer()) ->
undefined | {ok, {der_cert(), #'OTPCertificate'{}}}.
%%
@@ -64,19 +64,19 @@ remove(Dbs) ->
%% <SerialNumber, Issuer>. Ref is used as it is specified
%% for each connection which certificates are trusted.
%%--------------------------------------------------------------------
-lookup_trusted_cert(Ref, SerialNumber, Issuer) ->
- case lookup({Ref, SerialNumber, Issuer}, certificate_db_name()) of
+lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) ->
+ case lookup({Ref, SerialNumber, Issuer}, DbHandle) of
undefined ->
undefined;
[Certs] ->
{ok, Certs}
end.
-lookup_cached_certs(File) ->
- ets:lookup(certificate_db_name(), {file, File}).
+lookup_cached_certs(DbHandle, File) ->
+ ets:lookup(DbHandle, {file, File}).
%%--------------------------------------------------------------------
--spec add_trusted_certs(pid(), string() | {der, list()}, certdb_ref()) -> {ok, certdb_ref()}.
+-spec add_trusted_certs(pid(), string() | {der, list()}, [db_handle()]) -> {ok, [db_handle()]}.
%%
%% Description: Adds the trusted certificates from file <File> to the
%% runtime database. Returns Ref that should be handed to lookup_trusted_cert
@@ -100,7 +100,7 @@ add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) ->
insert(Pid, File, PidToFileDb),
{ok, Ref}.
%%--------------------------------------------------------------------
--spec cache_pem_file(pid(), string(), time(), certdb_ref()) -> term().
+-spec cache_pem_file(pid(), string(), time(), [db_handle()]) -> term().
%%
%% Description: Cache file as binary in DB
%%--------------------------------------------------------------------
@@ -112,7 +112,7 @@ cache_pem_file(Pid, File, Time, [CertsDb, _FileToRefDb, PidToFileDb]) ->
{ok, Content}.
%--------------------------------------------------------------------
--spec uncache_pem_file(string(), certdb_ref()) -> no_return().
+-spec uncache_pem_file(string(), [db_handle()]) -> no_return().
%%
%% Description: If a cached file is no longer valid (changed on disk)
%% we must terminate the connections using the old file content, and
@@ -130,7 +130,7 @@ uncache_pem_file(File, [_CertsDb, _FileToRefDb, PidToFileDb]) ->
%%--------------------------------------------------------------------
--spec remove_trusted_certs(pid(), certdb_ref()) -> term().
+-spec remove_trusted_certs(pid(), [db_handle()]) -> term().
%%
%% Description: Removes trusted certs originating from
@@ -161,7 +161,7 @@ remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) ->
end.
%%--------------------------------------------------------------------
--spec issuer_candidate(no_candidate | cert_key() | {file, term()}) ->
+-spec issuer_candidate(no_candidate | cert_key() | {file, term()}, term()) ->
{cert_key(),{der_cert(), #'OTPCertificate'{}}} | no_more_candidates.
%%
%% Description: If a certificat does not define its issuer through
@@ -169,32 +169,30 @@ remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) ->
%% try to find the issuer in the database over known
%% certificates.
%%--------------------------------------------------------------------
-issuer_candidate(no_candidate) ->
- Db = certificate_db_name(),
+issuer_candidate(no_candidate, Db) ->
case ets:first(Db) of
'$end_of_table' ->
no_more_candidates;
{file, _} = Key ->
- issuer_candidate(Key);
+ issuer_candidate(Key, Db);
Key ->
[Cert] = lookup(Key, Db),
{Key, Cert}
end;
-issuer_candidate(PrevCandidateKey) ->
- Db = certificate_db_name(),
+issuer_candidate(PrevCandidateKey, Db) ->
case ets:next(Db, PrevCandidateKey) of
'$end_of_table' ->
no_more_candidates;
{file, _} = Key ->
- issuer_candidate(Key);
+ issuer_candidate(Key, Db);
Key ->
[Cert] = lookup(Key, Db),
{Key, Cert}
end.
%%--------------------------------------------------------------------
--spec lookup(term(), term()) -> term() | undefined.
+-spec lookup(term(), db_handle()) -> term() | undefined.
%%
%% Description: Looks up an element in a certificat <Db>.
%%--------------------------------------------------------------------
@@ -212,9 +210,6 @@ lookup(Key, Db) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-certificate_db_name() ->
- ssl_otp_certificate_db.
-
insert(Key, Data, Db) ->
true = ets:insert(Db, {Key, Data}).
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 2c452837f8..21b021afb0 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -70,6 +70,7 @@
%% {{md5_hash, sha_hash}, {prev_md5, prev_sha}} (binary())
tls_handshake_hashes, % see above
tls_cipher_texts, % list() received but not deciphered yet
+ cert_db, %
session, % #session{} from ssl_handshake.hrl
session_cache, %
session_cache_cb, %
@@ -305,12 +306,13 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options,
Hashes0 = ssl_handshake:init_hashes(),
try ssl_init(SSLOpts0, Role) of
- {ok, Ref, CacheRef, OwnCert, Key, DHParams} ->
+ {ok, Ref, CertDbHandle, CacheHandle, OwnCert, Key, DHParams} ->
Session = State0#state.session,
State = State0#state{tls_handshake_hashes = Hashes0,
session = Session#session{own_certificate = OwnCert},
cert_db_ref = Ref,
- session_cache = CacheRef,
+ cert_db = CertDbHandle,
+ session_cache = CacheHandle,
private_key = Key,
diffie_hellman_params = DHParams},
{ok, hello, State, get_timeout(State)}
@@ -500,9 +502,10 @@ certify(#certificate{asn1_certificates = []},
certify(#certificate{} = Cert,
#state{negotiated_version = Version,
role = Role,
+ cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
ssl_options = Opts} = State) ->
- case ssl_handshake:certify(Cert, CertDbRef, Opts#ssl_options.depth,
+ case ssl_handshake:certify(Cert, CertDbHandle, CertDbRef, Opts#ssl_options.depth,
Opts#ssl_options.verify,
Opts#ssl_options.verify_fun, Role) of
{PeerCert, PublicKeyInfo} ->
@@ -859,23 +862,23 @@ handle_sync_event({set_opts, Opts0}, _From, StateName,
#state{socket_options = Opts1,
socket = Socket,
user_data_buffer = Buffer} = State0) ->
- Opts = set_socket_opts(Socket, Opts0, Opts1, []),
+ {Reply, Opts} = set_socket_opts(Socket, Opts0, Opts1, []),
State1 = State0#state{socket_options = Opts},
if
Opts#socket_options.active =:= false ->
- {reply, ok, StateName, State1, get_timeout(State1)};
+ {reply, Reply, StateName, State1, get_timeout(State1)};
Buffer =:= <<>>, Opts1#socket_options.active =:= false ->
%% Need data, set active once
{Record, State2} = next_record_if_active(State1),
case next_state(StateName, Record, State2) of
{next_state, StateName, State, Timeout} ->
- {reply, ok, StateName, State, Timeout};
+ {reply, Reply, StateName, State, Timeout};
{stop, Reason, State} ->
{stop, Reason, State}
end;
Buffer =:= <<>> ->
%% Active once already set
- {reply, ok, StateName, State1, get_timeout(State1)};
+ {reply, Reply, StateName, State1, get_timeout(State1)};
true ->
case application_data(<<>>, State1) of
Stop = {stop,_,_} ->
@@ -883,7 +886,7 @@ handle_sync_event({set_opts, Opts0}, _From, StateName,
{Record, State2} ->
case next_state(StateName, Record, State2) of
{next_state, StateName, State, Timeout} ->
- {reply, ok, StateName, State, Timeout};
+ {reply, Reply, StateName, State, Timeout};
{stop, Reason, State} ->
{stop, Reason, State}
end
@@ -1044,19 +1047,19 @@ start_fsm(Role, Host, Port, Socket, Opts, User, {CbModule, _,_, _} = CbInfo,
end.
ssl_init(SslOpts, Role) ->
- {ok, CertDbRef, CacheRef, OwnCert} = init_certificates(SslOpts, Role),
+ {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert} = init_certificates(SslOpts, Role),
PrivateKey =
- init_private_key(SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile,
+ init_private_key(CertDbHandle, SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile,
SslOpts#ssl_options.password, Role),
- DHParams = init_diffie_hellman(SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role),
- {ok, CertDbRef, CacheRef, OwnCert, PrivateKey, DHParams}.
+ DHParams = init_diffie_hellman(CertDbHandle, SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role),
+ {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert, PrivateKey, DHParams}.
init_certificates(#ssl_options{cacerts = CaCerts,
cacertfile = CACertFile,
certfile = CertFile,
cert = Cert}, Role) ->
- {ok, CertDbRef, CacheRef} =
+ {ok, CertDbRef, CertDbHandle, CacheHandle} =
try
Certs = case CaCerts of
undefined ->
@@ -1064,44 +1067,44 @@ init_certificates(#ssl_options{cacerts = CaCerts,
_ ->
{der, CaCerts}
end,
- {ok, _, _} = ssl_manager:connection_init(Certs, Role)
+ {ok, _, _, _} = ssl_manager:connection_init(Certs, Role)
catch
Error:Reason ->
handle_file_error(?LINE, Error, Reason, CACertFile, ecacertfile,
erlang:get_stacktrace())
end,
- init_certificates(Cert, CertDbRef, CacheRef, CertFile, Role).
+ init_certificates(Cert, CertDbRef, CertDbHandle, CacheHandle, CertFile, Role).
-init_certificates(undefined, CertDbRef, CacheRef, "", _) ->
- {ok, CertDbRef, CacheRef, undefined};
+init_certificates(undefined, CertDbRef, CertDbHandle, CacheHandle, "", _) ->
+ {ok, CertDbRef, CertDbHandle, CacheHandle, undefined};
-init_certificates(undefined, CertDbRef, CacheRef, CertFile, client) ->
+init_certificates(undefined, CertDbRef, CertDbHandle, CacheHandle, CertFile, client) ->
try
- [OwnCert] = ssl_certificate:file_to_certificats(CertFile),
- {ok, CertDbRef, CacheRef, OwnCert}
+ [OwnCert] = ssl_certificate:file_to_certificats(CertFile, CertDbHandle),
+ {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert}
catch _Error:_Reason ->
- {ok, CertDbRef, CacheRef, undefined}
+ {ok, CertDbRef, CertDbHandle, CacheHandle, undefined}
end;
-init_certificates(undefined, CertDbRef, CacheRef, CertFile, server) ->
+init_certificates(undefined, CertDbRef, CertDbHandle, CacheRef, CertFile, server) ->
try
- [OwnCert] = ssl_certificate:file_to_certificats(CertFile),
- {ok, CertDbRef, CacheRef, OwnCert}
+ [OwnCert] = ssl_certificate:file_to_certificats(CertFile, CertDbHandle),
+ {ok, CertDbRef, CertDbHandle, CacheRef, OwnCert}
catch
Error:Reason ->
handle_file_error(?LINE, Error, Reason, CertFile, ecertfile,
erlang:get_stacktrace())
end;
-init_certificates(Cert, CertDbRef, CacheRef, _, _) ->
- {ok, CertDbRef, CacheRef, Cert}.
+init_certificates(Cert, CertDbRef, CertDbHandle, CacheRef, _, _) ->
+ {ok, CertDbRef, CertDbHandle, CacheRef, Cert}.
-init_private_key(undefined, "", _Password, _Client) ->
+init_private_key(_, undefined, "", _Password, _Client) ->
undefined;
-init_private_key(undefined, KeyFile, Password, _) ->
+init_private_key(DbHandle, undefined, KeyFile, Password, _) ->
try
- {ok, List} = ssl_manager:cache_pem_file(KeyFile),
+ {ok, List} = ssl_manager:cache_pem_file(KeyFile, DbHandle),
[PemEntry] = [PemEntry || PemEntry = {PKey, _ , _} <- List,
- PKey =:= 'RSAPrivateKey' orelse
+ PKey =:= 'RSAPrivateKey' orelse
PKey =:= 'DSAPrivateKey'],
public_key:pem_entry_decode(PemEntry, Password)
catch
@@ -1110,9 +1113,9 @@ init_private_key(undefined, KeyFile, Password, _) ->
erlang:get_stacktrace())
end;
-init_private_key({rsa, PrivateKey}, _, _,_) ->
+init_private_key(_,{rsa, PrivateKey}, _, _,_) ->
public_key:der_decode('RSAPrivateKey', PrivateKey);
-init_private_key({dsa, PrivateKey},_,_,_) ->
+init_private_key(_,{dsa, PrivateKey},_,_,_) ->
public_key:der_decode('DSAPrivateKey', PrivateKey).
-spec(handle_file_error(_,_,_,_,_,_) -> no_return()).
@@ -1128,15 +1131,15 @@ file_error(Line, Error, Reason, File, Throw, Stack) ->
error_logger:error_report(Report),
throw(Throw).
-init_diffie_hellman(Params, _,_) when is_binary(Params)->
+init_diffie_hellman(_,Params, _,_) when is_binary(Params)->
public_key:der_decode('DHParameter', Params);
-init_diffie_hellman(_,_, client) ->
+init_diffie_hellman(_,_,_, client) ->
undefined;
-init_diffie_hellman(_,undefined, _) ->
+init_diffie_hellman(_,_,undefined, _) ->
?DEFAULT_DIFFIE_HELLMAN_PARAMS;
-init_diffie_hellman(_, DHParamFile, server) ->
+init_diffie_hellman(DbHandle,_, DHParamFile, server) ->
try
- {ok, List} = ssl_manager:cache_pem_file(DHParamFile),
+ {ok, List} = ssl_manager:cache_pem_file(DHParamFile,DbHandle),
case [Entry || Entry = {'DHParameter', _ , _} <- List] of
[Entry] ->
public_key:pem_entry_decode(Entry);
@@ -1180,11 +1183,12 @@ certify_client(#state{client_certificate_requested = true, role = client,
connection_states = ConnectionStates0,
transport_cb = Transport,
negotiated_version = Version,
+ cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
session = #session{own_certificate = OwnCert},
socket = Socket,
tls_handshake_hashes = Hashes0} = State) ->
- Certificate = ssl_handshake:certificate(OwnCert, CertDbRef, client),
+ Certificate = ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client),
{BinCert, ConnectionStates1, Hashes1} =
encode_handshake(Certificate, Version, ConnectionStates0, Hashes0),
Transport:send(Socket, BinCert),
@@ -1365,9 +1369,10 @@ certify_server(#state{transport_cb = Transport,
negotiated_version = Version,
connection_states = ConnectionStates,
tls_handshake_hashes = Hashes,
+ cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
session = #session{own_certificate = OwnCert}} = State) ->
- case ssl_handshake:certificate(OwnCert, CertDbRef, server) of
+ case ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, server) of
CertMsg = #certificate{} ->
{BinCertMsg, NewConnectionStates, NewHashes} =
encode_handshake(CertMsg, Version, ConnectionStates, Hashes),
@@ -1454,12 +1459,13 @@ rsa_key_exchange(_, _) ->
request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer},
connection_states = ConnectionStates0,
+ cert_db = CertDbHandle,
cert_db_ref = CertDbRef,
tls_handshake_hashes = Hashes0,
negotiated_version = Version,
socket = Socket,
transport_cb = Transport} = State) ->
- Msg = ssl_handshake:certificate_request(ConnectionStates0, CertDbRef),
+ Msg = ssl_handshake:certificate_request(ConnectionStates0, CertDbHandle, CertDbRef),
{BinMsg, ConnectionStates1, Hashes1} =
encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
Transport:send(Socket, BinMsg),
@@ -2040,31 +2046,67 @@ get_socket_opts(Socket, [active | Tags], SockOpts, Acc) ->
get_socket_opts(Socket, Tags, SockOpts,
[{active, SockOpts#socket_options.active} | Acc]);
get_socket_opts(Socket, [Tag | Tags], SockOpts, Acc) ->
- case inet:getopts(Socket, [Tag]) of
+ try inet:getopts(Socket, [Tag]) of
{ok, [Opt]} ->
get_socket_opts(Socket, Tags, SockOpts, [Opt | Acc]);
{error, Error} ->
- {error, Error}
- end.
+ {error, {eoptions, {inet_option, Tag, Error}}}
+ catch
+ %% So that inet behavior does not crash our process
+ _:Error -> {error, {eoptions, {inet_option, Tag, Error}}}
+ end;
+get_socket_opts(_,Opts, _,_) ->
+ {error, {eoptions, {inet_option, Opts, function_clause}}}.
set_socket_opts(_, [], SockOpts, []) ->
- SockOpts;
+ {ok, SockOpts};
set_socket_opts(Socket, [], SockOpts, Other) ->
%% Set non emulated options
- inet:setopts(Socket, Other),
- SockOpts;
-set_socket_opts(Socket, [{mode, Mode}| Opts], SockOpts, Other) ->
+ try inet:setopts(Socket, Other) of
+ ok ->
+ {ok, SockOpts};
+ {error, InetError} ->
+ {{error, {eoptions, {inet_options, Other, InetError}}}, SockOpts}
+ catch
+ _:Error ->
+ %% So that inet behavior does not crash our process
+ {{error, {eoptions, {inet_options, Other, Error}}}, SockOpts}
+ end;
+
+set_socket_opts(Socket, [{mode, Mode}| Opts], SockOpts, Other) when Mode == list; Mode == binary ->
set_socket_opts(Socket, Opts,
SockOpts#socket_options{mode = Mode}, Other);
-set_socket_opts(Socket, [{packet, Packet}| Opts], SockOpts, Other) ->
+set_socket_opts(_, [{mode, _} = Opt| _], SockOpts, _) ->
+ {{error, {eoptions, {inet_opt, Opt}}}, SockOpts};
+set_socket_opts(Socket, [{packet, Packet}| Opts], SockOpts, Other) when Packet == raw;
+ Packet == 0;
+ Packet == 1;
+ Packet == 2;
+ Packet == 4;
+ Packet == asn1;
+ Packet == cdr;
+ Packet == sunrm;
+ Packet == fcgi;
+ Packet == tpkt;
+ Packet == line;
+ Packet == http;
+ Packet == http_bin ->
set_socket_opts(Socket, Opts,
SockOpts#socket_options{packet = Packet}, Other);
-set_socket_opts(Socket, [{header, Header}| Opts], SockOpts, Other) ->
+set_socket_opts(_, [{packet, _} = Opt| _], SockOpts, _) ->
+ {{error, {eoptions, {inet_opt, Opt}}}, SockOpts};
+set_socket_opts(Socket, [{header, Header}| Opts], SockOpts, Other) when is_integer(Header) ->
set_socket_opts(Socket, Opts,
SockOpts#socket_options{header = Header}, Other);
-set_socket_opts(Socket, [{active, Active}| Opts], SockOpts, Other) ->
+set_socket_opts(_, [{header, _} = Opt| _], SockOpts, _) ->
+ {{error,{eoptions, {inet_opt, Opt}}}, SockOpts};
+set_socket_opts(Socket, [{active, Active}| Opts], SockOpts, Other) when Active == once;
+ Active == true;
+ Active == false ->
set_socket_opts(Socket, Opts,
SockOpts#socket_options{active = Active}, Other);
+set_socket_opts(_, [{active, _} = Opt| _], SockOpts, _) ->
+ {{error, {eoptions, {inet_opt, Opt}} }, SockOpts};
set_socket_opts(Socket, [Opt | Opts], SockOpts, Other) ->
set_socket_opts(Socket, Opts, SockOpts, [Opt | Other]).
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 1f4c44d115..4e74aec4ac 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -31,9 +31,9 @@
-include_lib("public_key/include/public_key.hrl").
-export([master_secret/4, client_hello/6, server_hello/4, hello/4,
- hello_request/0, certify/6, certificate/3,
+ hello_request/0, certify/7, certificate/4,
client_certificate_verify/5, certificate_verify/5,
- certificate_request/2, key_exchange/2, server_key_exchange_hash/2,
+ certificate_request/3, key_exchange/2, server_key_exchange_hash/2,
finished/4, verify_connection/5, get_tls_handshake/2,
decode_client_key/3, server_hello_done/0,
encode_handshake/2, init_hashes/0, update_hashes/2,
@@ -106,7 +106,7 @@ hello_request() ->
%%--------------------------------------------------------------------
-spec hello(#server_hello{} | #client_hello{}, #ssl_options{},
- #connection_states{} | {port_num(), #session{}, cache_ref(),
+ #connection_states{} | {port_num(), #session{}, db_handle(),
atom(), #connection_states{}, binary()},
boolean()) -> {tls_version(), session_id(), #connection_states{}}|
{tls_version(), {resumed | new, #session{}},
@@ -173,13 +173,13 @@ hello(#client_hello{client_version = ClientVersion, random = Random,
end.
%%--------------------------------------------------------------------
--spec certify(#certificate{}, term(), integer() | nolimit,
+-spec certify(#certificate{}, db_handle(), certdb_ref(), integer() | nolimit,
verify_peer | verify_none, {fun(), term},
client | server) -> {der_cert(), public_key_info()} | #alert{}.
%%
%% Description: Handles a certificate handshake message
%%--------------------------------------------------------------------
-certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef,
+certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
MaxPathLen, _Verify, VerifyFunAndState, Role) ->
[PeerCert | _] = ASN1Certs,
@@ -208,7 +208,7 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef,
end,
{TrustedErlCert, CertPath} =
- ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbRef),
+ ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef),
case public_key:pkix_path_validation(TrustedErlCert,
CertPath,
@@ -222,13 +222,13 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef,
end.
%%--------------------------------------------------------------------
--spec certificate(der_cert(), term(), client | server) -> #certificate{} | #alert{}.
+-spec certificate(der_cert(), db_handle(), certdb_ref(), client | server) -> #certificate{} | #alert{}.
%%
%% Description: Creates a certificate message.
%%--------------------------------------------------------------------
-certificate(OwnCert, CertDbRef, client) ->
+certificate(OwnCert, CertDbHandle, CertDbRef, client) ->
Chain =
- case ssl_certificate:certificate_chain(OwnCert, CertDbRef) of
+ case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of
{ok, CertChain} ->
CertChain;
{error, _} ->
@@ -239,8 +239,8 @@ certificate(OwnCert, CertDbRef, client) ->
end,
#certificate{asn1_certificates = Chain};
-certificate(OwnCert, CertDbRef, server) ->
- case ssl_certificate:certificate_chain(OwnCert, CertDbRef) of
+certificate(OwnCert, CertDbHandle, CertDbRef, server) ->
+ case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of
{ok, Chain} ->
#certificate{asn1_certificates = Chain};
{error, _} ->
@@ -302,17 +302,17 @@ certificate_verify(Signature, {?'id-dsa' = Algorithm, PublicKey, PublicKeyParams
%%--------------------------------------------------------------------
--spec certificate_request(#connection_states{}, certdb_ref()) ->
+-spec certificate_request(#connection_states{}, db_handle(), certdb_ref()) ->
#certificate_request{}.
%%
%% Description: Creates a certificate_request message, called by the server.
%%--------------------------------------------------------------------
-certificate_request(ConnectionStates, CertDbRef) ->
+certificate_request(ConnectionStates, CertDbHandle, CertDbRef) ->
#connection_state{security_parameters =
#security_parameters{cipher_suite = CipherSuite}} =
ssl_record:pending_connection_state(ConnectionStates, read),
Types = certificate_types(CipherSuite),
- Authorities = certificate_authorities(CertDbRef),
+ Authorities = certificate_authorities(CertDbHandle, CertDbRef),
#certificate_request{
certificate_types = Types,
certificate_authorities = Authorities
@@ -1071,8 +1071,8 @@ certificate_types({KeyExchange, _, _, _})
certificate_types(_) ->
<<?BYTE(?RSA_SIGN)>>.
-certificate_authorities(CertDbRef) ->
- Authorities = certificate_authorities_from_db(CertDbRef),
+certificate_authorities(CertDbHandle, CertDbRef) ->
+ Authorities = certificate_authorities_from_db(CertDbHandle, CertDbRef),
Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) ->
OTPSubj = TBSCert#'OTPTBSCertificate'.subject,
DNEncodedBin = public_key:pkix_encode('Name', OTPSubj, otp),
@@ -1084,18 +1084,18 @@ certificate_authorities(CertDbRef) ->
end,
list_to_binary([Enc(Cert) || {_, Cert} <- Authorities]).
-certificate_authorities_from_db(CertDbRef) ->
- certificate_authorities_from_db(CertDbRef, no_candidate, []).
+certificate_authorities_from_db(CertDbHandle, CertDbRef) ->
+ certificate_authorities_from_db(CertDbHandle, CertDbRef, no_candidate, []).
-certificate_authorities_from_db(CertDbRef, PrevKey, Acc) ->
- case ssl_manager:issuer_candidate(PrevKey) of
+certificate_authorities_from_db(CertDbHandle,CertDbRef, PrevKey, Acc) ->
+ case ssl_manager:issuer_candidate(PrevKey, CertDbHandle) of
no_more_candidates ->
lists:reverse(Acc);
{{CertDbRef, _, _} = Key, Cert} ->
- certificate_authorities_from_db(CertDbRef, Key, [Cert|Acc]);
+ certificate_authorities_from_db(CertDbHandle, CertDbRef, Key, [Cert|Acc]);
{Key, _Cert} ->
%% skip certs not from this ssl connection
- certificate_authorities_from_db(CertDbRef, Key, Acc)
+ certificate_authorities_from_db(CertDbHandle, CertDbRef, Key, Acc)
end.
digitally_signed(Hash, #'RSAPrivateKey'{} = Key) ->
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index c28daa271e..cc66246068 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -33,8 +33,8 @@
-type session_id() :: 0 | binary().
-type tls_version() :: {integer(), integer()}.
-type tls_atom_version() :: sslv3 | tlsv1.
--type cache_ref() :: term().
--type certdb_ref() :: term().
+-type certdb_ref() :: reference().
+-type db_handle() :: term().
-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | dh_anon.
-type der_cert() :: binary().
-type private_key() :: #'RSAPrivateKey'{} | #'DSAPrivateKey'{}.
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index 541ca1e918..b02815bfd8 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -28,8 +28,8 @@
%% Internal application API
-export([start_link/1,
- connection_init/2, cache_pem_file/1,
- lookup_trusted_cert/3, issuer_candidate/1, client_session_id/4,
+ connection_init/2, cache_pem_file/2,
+ lookup_trusted_cert/4, issuer_candidate/2, client_session_id/4,
server_session_id/4,
register_session/2, register_session/3, invalidate_session/2,
invalidate_session/3]).
@@ -50,7 +50,8 @@
session_cache_cb,
session_lifetime,
certificate_db,
- session_validation_timer
+ session_validation_timer,
+ last_delay_timer %% Keep for testing purposes
}).
-define('24H_in_msec', 8640000).
@@ -72,45 +73,45 @@ start_link(Opts) ->
%%--------------------------------------------------------------------
-spec connection_init(string()| {der, list()}, client | server) ->
- {ok, reference(), cache_ref()}.
+ {ok, certdb_ref(), db_handle(), db_handle()}.
%%
%% Description: Do necessary initializations for a new connection.
%%--------------------------------------------------------------------
connection_init(Trustedcerts, Role) ->
call({connection_init, Trustedcerts, Role}).
%%--------------------------------------------------------------------
--spec cache_pem_file(string()) -> {ok, term()} | {error, reason()}.
+-spec cache_pem_file(string(), term()) -> {ok, term()} | {error, reason()}.
%%
%% Description: Cach a pem file and return its content.
%%--------------------------------------------------------------------
-cache_pem_file(File) ->
+cache_pem_file(File, DbHandle) ->
try file:read_file_info(File) of
{ok, #file_info{mtime = LastWrite}} ->
- cache_pem_file(File, LastWrite)
+ cache_pem_file(File, LastWrite, DbHandle)
catch
_:Reason ->
{error, Reason}
end.
%%--------------------------------------------------------------------
--spec lookup_trusted_cert(reference(), serialnumber(), issuer()) ->
+-spec lookup_trusted_cert(term(), reference(), serialnumber(), issuer()) ->
undefined |
{ok, {der_cert(), #'OTPCertificate'{}}}.
%%
%% Description: Lookup the trusted cert with Key = {reference(),
%% serialnumber(), issuer()}.
%% --------------------------------------------------------------------
-lookup_trusted_cert(Ref, SerialNumber, Issuer) ->
- ssl_certificate_db:lookup_trusted_cert(Ref, SerialNumber, Issuer).
+lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) ->
+ ssl_certificate_db:lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer).
%%--------------------------------------------------------------------
--spec issuer_candidate(cert_key() | no_candidate) ->
+-spec issuer_candidate(cert_key() | no_candidate, term()) ->
{cert_key(),
{der_cert(),
#'OTPCertificate'{}}} | no_more_candidates.
%%
%% Description: Return next issuer candidate.
%%--------------------------------------------------------------------
-issuer_candidate(PrevCandidateKey) ->
- ssl_certificate_db:issuer_candidate(PrevCandidateKey).
+issuer_candidate(PrevCandidateKey, DbHandle) ->
+ ssl_certificate_db:issuer_candidate(PrevCandidateKey, DbHandle).
%%--------------------------------------------------------------------
-spec client_session_id(host(), port_num(), #ssl_options{},
der_cert() | undefined) -> session_id().
@@ -192,19 +193,20 @@ init([Opts]) ->
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call({{connection_init, "", _Role}, Pid}, _From,
- #state{session_cache = Cache} = State) ->
+ #state{certificate_db = [CertDb |_],
+ session_cache = Cache} = State) ->
erlang:monitor(process, Pid),
- Result = {ok, make_ref(), Cache},
+ Result = {ok, make_ref(),CertDb, Cache},
{reply, Result, State};
handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From,
- #state{certificate_db = Db,
+ #state{certificate_db = [CertDb|_] =Db,
session_cache = Cache} = State) ->
erlang:monitor(process, Pid),
Result =
try
{ok, Ref} = ssl_certificate_db:add_trusted_certs(Pid, Trustedcerts, Db),
- {ok, Ref, Cache}
+ {ok, Ref, CertDb, Cache}
catch
_:Reason ->
{error, Reason}
@@ -273,15 +275,17 @@ handle_cast({invalidate_session, Host, Port,
#state{session_cache = Cache,
session_cache_cb = CacheCb} = State) ->
CacheCb:update(Cache, {{Host, Port}, ID}, Session#session{is_resumable = false}),
- timer:send_after(delay_time(), self(), {delayed_clean_session, {{Host, Port}, ID}}),
- {noreply, State};
+ TRef =
+ erlang:send_after(delay_time(), self(), {delayed_clean_session, {{Host, Port}, ID}}),
+ {noreply, State#state{last_delay_timer = TRef}};
handle_cast({invalidate_session, Port, #session{session_id = ID} = Session},
#state{session_cache = Cache,
session_cache_cb = CacheCb} = State) ->
CacheCb:update(Cache, {Port, ID}, Session#session{is_resumable = false}),
- timer:send_after(delay_time(), self(), {delayed_clean_session, {Port, ID}}),
- {noreply, State};
+ TRef =
+ erlang:send_after(delay_time(), self(), {delayed_clean_session, {Port, ID}}),
+ {noreply, State#state{last_delay_timer = TRef}};
handle_cast({recache_pem, File, LastWrite, Pid, From},
#state{certificate_db = [_, FileToRefDb, _]} = State0) ->
@@ -408,8 +412,8 @@ session_validation({{Port, _}, Session}, LifeTime) ->
validate_session(Port, Session, LifeTime),
LifeTime.
-cache_pem_file(File, LastWrite) ->
- case ssl_certificate_db:lookup_cached_certs(File) of
+cache_pem_file(File, LastWrite, DbHandle) ->
+ case ssl_certificate_db:lookup_cached_certs(DbHandle,File) of
[{_, {Mtime, Content}}] ->
case LastWrite of
Mtime ->
diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl
index dc4b7a711c..85c9fcb61c 100644
--- a/lib/ssl/src/ssl_session.erl
+++ b/lib/ssl/src/ssl_session.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -48,7 +48,7 @@ is_new(_ClientSuggestion, _ServerDecision) ->
true.
%%--------------------------------------------------------------------
--spec id({host(), port_num(), #ssl_options{}}, cache_ref(), atom(),
+-spec id({host(), port_num(), #ssl_options{}}, db_handle(), atom(),
undefined | binary()) -> binary().
%%
%% Description: Should be called by the client side to get an id
@@ -63,7 +63,7 @@ id(ClientInfo, Cache, CacheCb, OwnCert) ->
end.
%%--------------------------------------------------------------------
--spec id(port_num(), binary(), #ssl_options{}, cache_ref(),
+-spec id(port_num(), binary(), #ssl_options{}, db_handle(),
atom(), seconds(), binary()) -> binary().
%%
%% Description: Should be called by the server side to get an id
diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl
index c1be6691be..66610817be 100644
--- a/lib/ssl/src/ssl_session_cache.erl
+++ b/lib/ssl/src/ssl_session_cache.erl
@@ -31,7 +31,7 @@
-type key() :: {{host(), port_num()}, session_id()} | {port_num(), session_id()}.
%%--------------------------------------------------------------------
--spec init(list()) -> cache_ref(). %% Returns reference to the cache (opaque)
+-spec init(list()) -> db_handle(). %% Returns reference to the cache (opaque)
%%
%% Description: Return table reference. Called by ssl_manager process.
%%--------------------------------------------------------------------
@@ -39,7 +39,7 @@ init(_) ->
ets:new(cache_name(), [set, protected]).
%%--------------------------------------------------------------------
--spec terminate(cache_ref()) -> any(). %%
+-spec terminate(db_handle()) -> any().
%%
%% Description: Handles cache table at termination of ssl manager.
%%--------------------------------------------------------------------
@@ -47,7 +47,7 @@ terminate(Cache) ->
ets:delete(Cache).
%%--------------------------------------------------------------------
--spec lookup(cache_ref(), key()) -> #session{} | undefined.
+-spec lookup(db_handle(), key()) -> #session{} | undefined.
%%
%% Description: Looks up a cach entry. Should be callable from any
%% process.
@@ -61,7 +61,7 @@ lookup(Cache, Key) ->
end.
%%--------------------------------------------------------------------
--spec update(cache_ref(), key(), #session{}) -> any().
+-spec update(db_handle(), key(), #session{}) -> any().
%%
%% Description: Caches a new session or updates a already cached one.
%% Will only be called from the ssl_manager process.
@@ -70,7 +70,7 @@ update(Cache, Key, Session) ->
ets:insert(Cache, {Key, Session}).
%%--------------------------------------------------------------------
--spec delete(cache_ref(), key()) -> any().
+-spec delete(db_handle(), key()) -> any().
%%
%% Description: Delets a cache entry.
%% Will only be called from the ssl_manager process.
@@ -79,7 +79,7 @@ delete(Cache, Key) ->
ets:delete(Cache, Key).
%%--------------------------------------------------------------------
--spec foldl(fun(), term(), cache_ref()) -> term().
+-spec foldl(fun(), term(), db_handle()) -> term().
%%
%% Description: Calls Fun(Elem, AccIn) on successive elements of the
%% cache, starting with AccIn == Acc0. Fun/2 must return a new
@@ -91,7 +91,7 @@ foldl(Fun, Acc0, Cache) ->
ets:foldl(Fun, Acc0, Cache).
%%--------------------------------------------------------------------
--spec select_session(cache_ref(), {host(), port_num()} | port_num()) -> [#session{}].
+-spec select_session(db_handle(), {host(), port_num()} | port_num()) -> [#session{}].
%%
%% Description: Selects a session that could be reused. Should be callable
%% from any process.
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index ecb5228a8b..37a021e7cf 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -25,7 +25,6 @@
-compile(export_all).
-include_lib("common_test/include/ct.hrl").
--include("test_server_line.hrl").
-include_lib("public_key/include/public_key.hrl").
-include("ssl_alert.hrl").
@@ -210,6 +209,10 @@ all() ->
empty_protocol_versions, controlling_process,
controller_dies, client_closes_socket, peercert,
connect_dist, peername, sockname, socket_options,
+ invalid_inet_get_option, invalid_inet_get_option_not_list,
+ invalid_inet_get_option_improper_list,
+ invalid_inet_set_option, invalid_inet_set_option_not_list,
+ invalid_inet_set_option_improper_list,
misc_ssl_options, versions, cipher_suites, upgrade,
upgrade_with_timeout, tcp_connect, tcp_connect_big, ipv6, ekeyfile,
ecertfile, ecacertfile, eoptions, shutdown,
@@ -254,7 +257,7 @@ all() ->
%%different_ca_peer_sign,
no_reuses_session_server_restart_new_cert,
no_reuses_session_server_restart_new_cert_file, reuseaddr,
- hibernate
+ hibernate, connect_twice
].
groups() ->
@@ -810,8 +813,218 @@ socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) ->
{ok,[{nodelay,false}]} = ssl:getopts(Socket, [nodelay]),
ssl:setopts(Socket, [{nodelay, true}]),
{ok,[{nodelay, true}]} = ssl:getopts(Socket, [nodelay]),
+ {ok, All} = ssl:getopts(Socket, []),
+ test_server:format("All opts ~p~n", [All]),
ok.
+
+
+%%--------------------------------------------------------------------
+invalid_inet_get_option(doc) ->
+ ["Test handling of invalid inet options in getopts"];
+
+invalid_inet_get_option(suite) ->
+ [];
+
+invalid_inet_get_option(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_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, get_invalid_inet_option, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+get_invalid_inet_option(Socket) ->
+ {error, {eoptions, {inet_option, foo, _}}} = ssl:getopts(Socket, [foo]),
+ ok.
+
+%%--------------------------------------------------------------------
+invalid_inet_get_option_not_list(doc) ->
+ ["Test handling of invalid type in getopts"];
+
+invalid_inet_get_option_not_list(suite) ->
+ [];
+
+invalid_inet_get_option_not_list(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_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, get_invalid_inet_option_not_list, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+get_invalid_inet_option_not_list(Socket) ->
+ {error, {eoptions, {inet_options, some_invalid_atom_here}}}
+ = ssl:getopts(Socket, some_invalid_atom_here),
+ ok.
+
+%%--------------------------------------------------------------------
+invalid_inet_get_option_improper_list(doc) ->
+ ["Test handling of invalid type in getopts"];
+
+invalid_inet_get_option_improper_list(suite) ->
+ [];
+
+invalid_inet_get_option_improper_list(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_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, get_invalid_inet_option_improper_list, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+get_invalid_inet_option_improper_list(Socket) ->
+ {error, {eoptions, {inet_option, foo,_}}} = ssl:getopts(Socket, [packet | foo]),
+ ok.
+
+%%--------------------------------------------------------------------
+invalid_inet_set_option(doc) ->
+ ["Test handling of invalid inet options in setopts"];
+
+invalid_inet_set_option(suite) ->
+ [];
+
+invalid_inet_set_option(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_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, set_invalid_inet_option, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+set_invalid_inet_option(Socket) ->
+ {error, {eoptions, {inet_opt, {packet, foo}}}} = ssl:setopts(Socket, [{packet, foo}]),
+ {error, {eoptions, {inet_opt, {header, foo}}}} = ssl:setopts(Socket, [{header, foo}]),
+ {error, {eoptions, {inet_opt, {active, foo}}}} = ssl:setopts(Socket, [{active, foo}]),
+ {error, {eoptions, {inet_opt, {mode, foo}}}} = ssl:setopts(Socket, [{mode, foo}]),
+ ok.
+%%--------------------------------------------------------------------
+invalid_inet_set_option_not_list(doc) ->
+ ["Test handling of invalid type in setopts"];
+
+invalid_inet_set_option_not_list(suite) ->
+ [];
+
+invalid_inet_set_option_not_list(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_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, set_invalid_inet_option_not_list, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+set_invalid_inet_option_not_list(Socket) ->
+ {error, {eoptions, {not_a_proplist, some_invalid_atom_here}}}
+ = ssl:setopts(Socket, some_invalid_atom_here),
+ ok.
+
+%%--------------------------------------------------------------------
+invalid_inet_set_option_improper_list(doc) ->
+ ["Test handling of invalid tye in setopts"];
+
+invalid_inet_set_option_improper_list(suite) ->
+ [];
+
+invalid_inet_set_option_improper_list(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_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, set_invalid_inet_option_improper_list, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+set_invalid_inet_option_improper_list(Socket) ->
+ {error, {eoptions, {not_a_proplist, [{packet, 0} | {foo, 2}]}}} =
+ ssl:setopts(Socket, [{packet, 0} | {foo, 2}]),
+ ok.
+
%%--------------------------------------------------------------------
misc_ssl_options(doc) ->
["Test what happens when we give valid options"];
@@ -3338,6 +3551,7 @@ reuseaddr(Config) when is_list(Config) ->
{options, [{active, false} | ClientOpts]}]),
test_server:sleep(?SLEEP),
ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
Server1 =
ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
@@ -3395,6 +3609,54 @@ hibernate(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+
+connect_twice(doc) ->
+ [""];
+connect_twice(suite) ->
+ [];
+connect_twice(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_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, send_recv_result, []}},
+ {options, [{keepalive, true},{active, false}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{keepalive, true},{active, false}
+ | ClientOpts]}]),
+ Server ! listen,
+
+ {Client1, #sslsocket{}} =
+ ssl_test_lib:start_client([return_socket,
+ {node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{keepalive, true},{active, false}
+ | ClientOpts]}]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:check_result(Server, ok, Client1, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ ssl_test_lib:close(Client1).
+
+
+%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
send_recv_result(Socket) ->
@@ -3472,7 +3734,7 @@ session_cache_process_mnesia(suite) ->
session_cache_process_mnesia(Config) when is_list(Config) ->
session_cache_process(mnesia,Config).
-session_cache_process(Type,Config) when is_list(Config) ->
+session_cache_process(_Type,Config) when is_list(Config) ->
reuse_session(Config).
init([Type]) ->
diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl
index 5d96b457ed..5ea45018e6 100644
--- a/lib/ssl/test/ssl_session_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_session_cache_SUITE.erl
@@ -26,6 +26,7 @@
-include_lib("common_test/include/ct.hrl").
+-define(DELAY, 500).
-define(SLEEP, 500).
-define(TIMEOUT, 60000).
-define(LONG_TIMEOUT, 600000).
@@ -102,7 +103,7 @@ init_per_testcase(session_cleanup, Config0) ->
ssl:stop(),
application:load(ssl),
application:set_env(ssl, session_lifetime, 5),
- application:set_env(ssl, session_delay_cleanup_time, ?SLEEP),
+ application:set_env(ssl, session_delay_cleanup_time, ?DELAY),
ssl:start(),
[{watchdog, Dog} | Config];
@@ -178,7 +179,7 @@ end_per_group(_GroupName, Config) ->
%%--------------------------------------------------------------------
session_cleanup(doc) ->
["Test that sessions are cleand up eventually, so that the session table "
- "does grow and grow ..."];
+ "does not grow and grow ..."];
session_cleanup(suite) ->
[];
session_cleanup(Config)when is_list(Config) ->
@@ -211,6 +212,7 @@ session_cleanup(Config)when is_list(Config) ->
[_, _,_, _, Prop] = StatusInfo,
State = state(Prop),
Cache = element(2, State),
+ SessionTimer = element(6, State),
Id = proplists:get_value(session_id, SessionInfo),
CSession = ssl_session_cache:lookup(Cache, {{Hostname, Port}, Id}),
@@ -220,8 +222,14 @@ session_cleanup(Config)when is_list(Config) ->
true = SSession =/= undefined,
%% Make sure session has expired and been cleaned up
- test_server:sleep(5000), %% Expire time
- test_server:sleep((?SLEEP*20), %% Clean up delay (very small in this test case) + some extra time
+ check_timer(SessionTimer),
+ test_server:sleep(?DELAY *2), %% Delay time + some extra time
+
+ DelayTimer = get_delay_timer(),
+
+ check_timer(DelayTimer),
+
+ test_server:sleep(?SLEEP), %% Make sure clean has had to run
undefined = ssl_session_cache:lookup(Cache, {{Hostname, Port}, Id}),
undefined = ssl_session_cache:lookup(Cache, {Port, Id}),
@@ -235,6 +243,27 @@ state([{data,[{"State", State}]} | _]) ->
state([_ | Rest]) ->
state(Rest).
+check_timer(Timer) ->
+ case erlang:read_timer(Timer) of
+ false ->
+ {status, _, _, _} = sys:get_status(whereis(ssl_manager)),
+ ok;
+ Int ->
+ test_server:sleep(Int),
+ check_timer(Timer)
+ end.
+
+get_delay_timer() ->
+ {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
+ [_, _,_, _, Prop] = StatusInfo,
+ State = state(Prop),
+ case element(7, State) of
+ undefined ->
+ test_server:sleep(?SLEEP),
+ get_delay_timer();
+ DelayTimer ->
+ DelayTimer
+ end.
%%--------------------------------------------------------------------
session_cache_process_list(doc) ->
["Test reuse of sessions (short handshake)"];
@@ -252,7 +281,6 @@ session_cache_process_mnesia(suite) ->
session_cache_process_mnesia(Config) when is_list(Config) ->
session_cache_process(mnesia,Config).
-
%%--------------------------------------------------------------------
%%% Session cache API callbacks
%%--------------------------------------------------------------------
diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl
index 718ca2e91a..10b2ed2e49 100644
--- a/lib/stdlib/src/erl_scan.erl
+++ b/lib/stdlib/src/erl_scan.erl
@@ -408,7 +408,12 @@ set_attr(line, {Line,Column}, Fun) when ?ALINE(Line), ?COLUMN(Column) ->
end;
set_attr(line=Tag, Attrs, Fun) when is_list(Attrs) ->
{line,Line} = lists:keyfind(Tag, 1, Attrs),
- lists:keyreplace(Tag, 1, Attrs, {line,Fun(Line)});
+ case lists:keyreplace(Tag, 1, Attrs, {line,Fun(Line)}) of
+ [{line,Ln}] when ?ALINE(Ln) ->
+ Ln;
+ As ->
+ As
+ end;
set_attr(T1, T2, T3) ->
erlang:error(badarg, [T1,T2,T3]).
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index 54c7283abf..165e03e506 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -100,7 +100,7 @@ fwrite(Format, Args) ->
-spec fread(Format, String) -> Result when
Format :: string(),
String :: string(),
- Result :: {'ok', InputList :: chars(), LeftOverChars :: string()}
+ Result :: {'ok', InputList :: [term()], LeftOverChars :: string()}
| {'more', RestFormat :: string(),
Nchars :: non_neg_integer(),
InputStack :: chars()}
@@ -115,7 +115,7 @@ fread(Chars, Format) ->
Format :: string(),
Return :: {'more', Continuation1 :: continuation()}
| {'done', Result, LeftOverChars :: string()},
- Result :: {'ok', InputList :: chars()}
+ Result :: {'ok', InputList :: [term()]}
| 'eof'
| {'error', What :: term()}.
diff --git a/lib/stdlib/src/queue.erl b/lib/stdlib/src/queue.erl
index 4c6b4d710b..afe917b151 100644
--- a/lib/stdlib/src/queue.erl
+++ b/lib/stdlib/src/queue.erl
@@ -56,16 +56,14 @@
new() -> {[],[]}. %{RearList,FrontList}
%% O(1)
--spec is_queue(Term) -> boolean() when
- Term :: term().
+-spec is_queue(Term :: term()) -> boolean().
is_queue({R,F}) when is_list(R), is_list(F) ->
true;
is_queue(_) ->
false.
%% O(1)
--spec is_empty(Q) -> boolean() when
- Q :: queue().
+-spec is_empty(Q :: queue()) -> boolean().
is_empty({[],[]}) ->
true;
is_empty({In,Out}) when is_list(In), is_list(Out) ->
@@ -74,16 +72,14 @@ is_empty(Q) ->
erlang:error(badarg, [Q]).
%% O(len(Q))
--spec len(Q) -> non_neg_integer() when
- Q :: queue().
+-spec len(Q :: queue()) -> non_neg_integer().
len({R,F}) when is_list(R), is_list(F) ->
length(R)+length(F);
len(Q) ->
erlang:error(badarg, [Q]).
%% O(len(Q))
--spec to_list(Q) -> list() when
- Q :: queue().
+-spec to_list(Q :: queue()) -> list().
to_list({In,Out}) when is_list(In), is_list(Out) ->
Out++lists:reverse(In, []);
to_list(Q) ->
@@ -92,8 +88,7 @@ to_list(Q) ->
%% Create queue from list
%%
%% O(length(L))
--spec from_list(L) -> queue() when
- L :: list().
+-spec from_list(L :: list()) -> queue().
from_list(L) when is_list(L) ->
f2r(L);
from_list(L) ->
@@ -102,9 +97,7 @@ from_list(L) ->
%% Return true or false depending on if element is in queue
%%
%% O(length(Q)) worst case
--spec member(Item, Q) -> boolean() when
- Item :: term(),
- Q :: queue().
+-spec member(Item :: term(), Q :: queue()) -> boolean().
member(X, {R,F}) when is_list(R), is_list(F) ->
lists:member(X, R) orelse lists:member(X, F);
member(X, Q) ->
@@ -117,10 +110,7 @@ member(X, Q) ->
%% Put at least one element in each list, if it is cheap
%%
%% O(1)
--spec in(Item, Q1) -> Q2 when
- Item :: term(),
- Q1 :: queue(),
- Q2 :: queue().
+-spec in(Item :: term(), Q1 :: queue()) -> Q2 :: queue().
in(X, {[_]=In,[]}) ->
{[X], In};
in(X, {In,Out}) when is_list(In), is_list(Out) ->
@@ -132,10 +122,7 @@ in(X, Q) ->
%% Put at least one element in each list, if it is cheap
%%
%% O(1)
--spec in_r(Item, Q1) -> Q2 when
- Item :: term(),
- Q1 :: queue(),
- Q2 :: queue().
+-spec in_r(Item :: term(), Q1 :: queue()) -> Q2 :: queue().
in_r(X, {[],[_]=F}) ->
{F,[X]};
in_r(X, {R,F}) when is_list(R), is_list(F) ->
@@ -146,10 +133,9 @@ in_r(X, Q) ->
%% Take from head/front
%%
%% O(1) amortized, O(len(Q)) worst case
--spec out(Q1) -> Result when
- Q1 :: queue(),
- Q2 :: queue(),
- Result :: {{value, Item :: term()}, Q2} | {empty, Q1}.
+-spec out(Q1 :: queue()) ->
+ {{value, Item :: term()}, Q2 :: queue()} |
+ {empty, Q1 :: queue()}.
out({[],[]}=Q) ->
{empty,Q};
out({[V],[]}) ->
@@ -167,10 +153,9 @@ out(Q) ->
%% Take from tail/rear
%%
%% O(1) amortized, O(len(Q)) worst case
--spec out_r(Q1) -> Result when
- Q1 :: queue(),
- Q2 :: queue(),
- Result :: {{value, Item :: term()}, Q2} | {empty, Q1}.
+-spec out_r(Q1 :: queue()) ->
+ {{value, Item :: term()}, Q2 :: queue()} |
+ {empty, Q1 :: queue()}.
out_r({[],[]}=Q) ->
{empty,Q};
out_r({[],[V]}) ->
@@ -191,9 +176,7 @@ out_r(Q) ->
%% Return the first element in the queue
%%
%% O(1) since the queue is supposed to be well formed
--spec get(Q) -> Item when
- Q :: queue(),
- Item :: term().
+-spec get(Q :: queue()) -> Item :: term().
get({[],[]}=Q) ->
erlang:error(empty, [Q]);
get({R,F}) when is_list(R), is_list(F) ->
@@ -212,9 +195,7 @@ get([_|R], []) -> % malformed queue -> O(len(Q))
%% Return the last element in the queue
%%
%% O(1) since the queue is supposed to be well formed
--spec get_r(Q) -> Item when
- Q :: queue(),
- Item :: term().
+-spec get_r(Q :: queue()) -> Item :: term().
get_r({[],[]}=Q) ->
erlang:error(empty, [Q]);
get_r({[H|_],F}) when is_list(F) ->
@@ -229,9 +210,7 @@ get_r(Q) ->
%% Return the first element in the queue
%%
%% O(1) since the queue is supposed to be well formed
--spec peek(Q) -> 'empty' | {'value',Item} when
- Q :: queue(),
- Item :: term().
+-spec peek(Q :: queue()) -> empty | {value,Item :: term()}.
peek({[],[]}) ->
empty;
peek({R,[H|_]}) when is_list(R) ->
@@ -246,9 +225,7 @@ peek(Q) ->
%% Return the last element in the queue
%%
%% O(1) since the queue is supposed to be well formed
--spec peek_r(Q) -> 'empty' | {'value',Item} when
- Q :: queue(),
- Item :: term().
+-spec peek_r(Q :: queue()) -> empty | {value,Item :: term()}.
peek_r({[],[]}) ->
empty;
peek_r({[H|_],F}) when is_list(F) ->
@@ -263,9 +240,7 @@ peek_r(Q) ->
%% Remove the first element and return resulting queue
%%
%% O(1) amortized
--spec drop(Q1) -> Q2 when
- Q1 :: queue(),
- Q2 :: queue().
+-spec drop(Q1 :: queue()) -> Q2 :: queue().
drop({[],[]}=Q) ->
erlang:error(empty, [Q]);
drop({[_],[]}) ->
@@ -283,9 +258,7 @@ drop(Q) ->
%% Remove the last element and return resulting queue
%%
%% O(1) amortized
--spec drop_r(Q1) -> Q2 when
- Q1 :: queue(),
- Q2 :: queue().
+-spec drop_r(Q1 :: queue()) -> Q2 :: queue().
drop_r({[],[]}=Q) ->
erlang:error(empty, [Q]);
drop_r({[],[_]}) ->
@@ -306,9 +279,7 @@ drop_r(Q) ->
%% Return reversed queue
%%
%% O(1)
--spec reverse(Q1) -> Q2 when
- Q1 :: queue(),
- Q2 :: queue().
+-spec reverse(Q1 :: queue()) -> Q2 :: queue().
reverse({R,F}) when is_list(R), is_list(F) ->
{F,R};
reverse(Q) ->
@@ -318,10 +289,7 @@ reverse(Q) ->
%%
%% Q2 empty: O(1)
%% else: O(len(Q1))
--spec join(Q1, Q2) -> Q3 when
- Q1 :: queue(),
- Q2 :: queue(),
- Q3 :: queue().
+-spec join(Q1 :: queue(), Q2 :: queue()) -> Q3 :: queue().
join({R,F}=Q, {[],[]}) when is_list(R), is_list(F) ->
Q;
join({[],[]}, {R,F}=Q) when is_list(R), is_list(F) ->
@@ -335,11 +303,8 @@ join(Q1, Q2) ->
%%
%% N = 0..len(Q)
%% O(max(N, len(Q)))
--spec split(N, Q1) -> {Q2,Q3} when
- N :: non_neg_integer(),
- Q1 :: queue(),
- Q2 :: queue(),
- Q3 :: queue().
+-spec split(N :: non_neg_integer(), Q1 :: queue()) ->
+ {Q2 :: queue(),Q3 :: queue()}.
split(0, {R,F}=Q) when is_list(R), is_list(F) ->
{{[],[]},Q};
split(N, {R,F}=Q) when is_integer(N), N >= 1, is_list(R), is_list(F) ->
@@ -380,10 +345,8 @@ split_r1_to_f2(N, [X|R1], F1, R2, F2) ->
%%
%% Fun(_) -> List: O(length(List) * len(Q))
%% else: O(len(Q)
--spec filter(Fun, Q1) -> Q2 when
- Fun :: fun((Item :: term()) -> boolean() | list()),
- Q1 :: queue(),
- Q2 :: queue().
+-spec filter(Fun, Q1 :: queue()) -> Q2 :: queue() when
+ Fun :: fun((Item :: term()) -> boolean() | list()).
filter(Fun, {R0,F0}) when is_function(Fun, 1), is_list(R0), is_list(F0) ->
F = filter_f(Fun, F0),
R = filter_r(Fun, R0),
@@ -459,10 +422,7 @@ filter_r(Fun, [X|R0]) ->
%% Cons to head
%%
--spec cons(Item, Q1) -> Q2 when
- Item :: term(),
- Q1 :: queue(),
- Q2 :: queue().
+-spec cons(Item :: term(), Q1 :: queue()) -> Q2 :: queue().
cons(X, Q) ->
in_r(X, Q).
@@ -471,9 +431,7 @@ cons(X, Q) ->
%% Return the first element in the queue
%%
%% O(1) since the queue is supposed to be well formed
--spec head(Q) -> Item when
- Q :: queue(),
- Item :: term().
+-spec head(Q :: queue()) -> Item :: term().
head({[],[]}=Q) ->
erlang:error(empty, [Q]);
head({R,F}) when is_list(R), is_list(F) ->
@@ -483,9 +441,7 @@ head(Q) ->
%% Remove head element and return resulting queue
%%
--spec tail(Q1) -> Q2 when
- Q1 :: queue(),
- Q2 :: queue().
+-spec tail(Q1 :: queue()) -> Q2 :: queue().
tail(Q) ->
drop(Q).
@@ -493,35 +449,22 @@ tail(Q) ->
%% Cons to tail
%%
--spec snoc(Q1, Item) -> Q2 when
- Q1 :: queue(),
- Q2 :: queue(),
- Item :: term().
+-spec snoc(Q1 :: queue(), Item :: term()) -> Q2 :: queue().
snoc(Q, X) ->
in(X, Q).
%% Return last element
--spec daeh(Q) -> Item when
- Q :: queue(),
- Item :: term().
+-spec daeh(Q :: queue()) -> Item :: term().
daeh(Q) -> get_r(Q).
--spec last(Q) -> Item when
- Q :: queue(),
- Item :: term().
+-spec last(Q :: queue()) -> Item :: term().
last(Q) -> get_r(Q).
%% Remove last element and return resulting queue
--spec liat(Q1) -> Q2 when
- Q1 :: queue(),
- Q2 :: queue().
+-spec liat(Q1 :: queue()) -> Q2 :: queue().
liat(Q) -> drop_r(Q).
--spec lait(Q1) -> Q2 when
- Q1 :: queue(),
- Q2 :: queue().
+-spec lait(Q1 :: queue()) -> Q2 :: queue().
lait(Q) -> drop_r(Q). %% Oops, mis-spelled 'tail' reversed. Forget this one.
--spec init(Q1) -> Q2 when
- Q1 :: queue(),
- Q2 :: queue().
+-spec init(Q1 :: queue()) -> Q2 :: queue().
init(Q) -> drop_r(Q).
%%--------------------------------------------------------------------------
diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl
index 31a4f94294..4298b2c701 100644
--- a/lib/stdlib/test/erl_scan_SUITE.erl
+++ b/lib/stdlib/test/erl_scan_SUITE.erl
@@ -737,6 +737,10 @@ set_attribute() ->
(catch {foo, erl_scan:set_attribute(line, [], F2)}), % type error
?line {'EXIT',{badarg,_}} =
(catch {foo, erl_scan:set_attribute(column, [], F2)}), % type error
+
+ %% OTP-9412
+ ?line 8 = erl_scan:set_attribute(line, [{line,{nos,'X',8}}],
+ fun({nos,_V,VL}) -> VL end),
ok.
column_errors() ->
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 9d348b5f1a..c9d82ec5f5 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -5517,20 +5517,21 @@ etsmem() ->
case erlang:system_info({allocator,ets_alloc}) of
false -> undefined;
MemInfo ->
- MSBCS = lists:foldl(
- fun ({instance, _, L}, Acc) ->
- {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L),
- {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L),
- [MBCS,SBCS | Acc]
- end,
- [],
- MemInfo),
+ CS = lists:foldl(
+ fun ({instance, _, L}, Acc) ->
+ {value,{_,SBMBCS}} = lists:keysearch(sbmbcs, 1, L),
+ {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L),
+ {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L),
+ [SBMBCS,MBCS,SBCS | Acc]
+ end,
+ [],
+ MemInfo),
lists:foldl(
fun(L, {Bl0,BlSz0}) ->
{value,{_,Bl,_,_}} = lists:keysearch(blocks, 1, L),
{value,{_,BlSz,_,_}} = lists:keysearch(blocks_size, 1, L),
{Bl0+Bl,BlSz0+BlSz}
- end, {0,0}, MSBCS)
+ end, {0,0}, CS)
end},
{Mem,AllTabs}.
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index c79a5002fb..ff5e4c629a 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -114,10 +114,9 @@ end_per_group(_GroupName, Config) ->
Config.
init_per_testcase(count_children_memory, Config) ->
- MemoryState = erlang:system_info(allocator),
- case count_children_allocator_test(MemoryState) of
- true -> Config;
- false ->
+ try erlang:memory() of
+ _ -> Config
+ catch error:notsup ->
{skip, "+Meamin used during test; erlang:memory/1 not available"}
end;
init_per_testcase(_Case, Config) ->
@@ -1032,17 +1031,6 @@ count_children_memory(Config) when is_list(Config) ->
[terminate(SupPid, Pid, child, kill) || {undefined, Pid, worker, _Modules} <- Children3],
[1,0,0,0] = get_child_counts(sup_test).
-count_children_allocator_test(MemoryState) ->
- Allocators = [temp_alloc, eheap_alloc, binary_alloc, ets_alloc,
- driver_alloc, sl_alloc, ll_alloc, fix_alloc, std_alloc,
- sys_alloc],
- MemoryStateList = element(4, MemoryState),
- AllocTypes = [lists:keyfind(Alloc, 1, MemoryStateList)
- || Alloc <- Allocators],
- AllocStates = [lists:keyfind(e, 1, AllocValue)
- || {_Type, AllocValue} <- AllocTypes],
- lists:all(fun(State) -> State == {e, true} end, AllocStates).
-
%%-------------------------------------------------------------------------
do_not_save_start_parameters_for_temporary_children(doc) ->
["Temporary children shall not be restarted so they should not "
diff --git a/lib/tools/doc/src/xref.xml b/lib/tools/doc/src/xref.xml
index 75ffa25311..17de66bb22 100644
--- a/lib/tools/doc/src/xref.xml
+++ b/lib/tools/doc/src/xref.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2000</year><year>2010</year>
+ <year>2000</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -1465,8 +1465,8 @@ Evaluates a predefined analysis.
<name>start(NameOrOptions) -> Return</name>
<fsummary>Create an Xref server.</fsummary>
<type>
- <v>Name = atom()()</v>
- <v>XrefOrOptions = Xref | Options</v>
+ <v>NameOrOptions = Name | Options</v>
+ <v>Name = atom()</v>
<v>Options = [Option] | Option</v>
<v>Option = {xref_mode, mode()} | term()</v>
<v>Return = {ok, pid()} | {error, {already_started, pid()}}</v>
@@ -1483,7 +1483,7 @@ Evaluates a predefined analysis.
<name>start(Name, Options) -> Return</name>
<fsummary>Create an Xref server.</fsummary>
<type>
- <v>Name = atom()()</v>
+ <v>Name = atom()</v>
<v>Options = [Option] | Option</v>
<v>Option = {xref_mode, mode()} | term()</v>
<v>Return = {ok, pid()} | {error, {already_started, pid()}}</v>