diff options
336 files changed, 15384 insertions, 5063 deletions
diff --git a/.gitignore b/.gitignore index d67116d6e3..54bfadea9a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,10 @@ # Match at any level. + +# emacs *~ +# vim +.*.sw[a-z] + autom4te.cache *.beam *.asn1db @@ -162,6 +167,7 @@ make/win32/ /erts/emulator/test/*_SUITE_make.erl /erts/emulator/test/*_SUITE_data/Makefile /erts/test/install_SUITE_data/install_bin +/erts/test/autoimport_SUITE_data/erlang.xml # asn1 diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 443d8622bf..a1211bbf0c 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -747,9 +747,124 @@ case "$THR_LIB_NAME" in if test $found_win32_winnt = no; then AC_MSG_ERROR([-D_WIN32_WINNT missing in CPPFLAGS]) fi - ethr_have_native_atomics=yes - ethr_have_native_spinlock=yes + AC_DEFINE(ETHR_WIN32_THREADS, 1, [Define if you have win32 threads]) + + have_ilckd=no + AC_MSG_CHECKING([for _InterlockedCompareExchange64()]) + AC_TRY_LINK([ + #define WIN32_LEAN_AND_MEAN + #include <windows.h> + ], + [ + volatile __int64 *var; + _InterlockedCompareExchange64(var, (__int64) 1, (__int64) 0); + return 0; + ], + have_ilckd=yes) + AC_MSG_RESULT([$have_ilckd]) + test $have_ilckd = yes && AC_DEFINE(ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE64, 1, [Define if you have _InterlockedCompareExchange64()]) + + AC_CHECK_SIZEOF(void *) + case "$ac_cv_sizeof_void_p-$have_ilckd" in + 8-no) + ethr_have_native_atomics=no + ethr_have_native_spinlock=no;; + *) + ethr_have_native_atomics=yes + ethr_have_native_spinlock=yes;; + esac + + have_ilckd=no + AC_MSG_CHECKING([for _InterlockedDecrement64()]) + AC_TRY_LINK([ + #define WIN32_LEAN_AND_MEAN + #include <windows.h> + ], + [ + volatile __int64 *var; + _InterlockedDecrement64(var); + return 0; + ], + have_ilckd=yes) + AC_MSG_RESULT([$have_ilckd]) + test $have_ilckd = yes && AC_DEFINE(ETHR_HAVE__INTERLOCKEDDECREMENT64, 1, [Define if you have _InterlockedDecrement64()]) + + have_ilckd=no + AC_MSG_CHECKING([for _InterlockedIncrement64()]) + AC_TRY_LINK([ + #define WIN32_LEAN_AND_MEAN + #include <windows.h> + ], + [ + volatile __int64 *var; + _InterlockedIncrement64(var); + return 0; + ], + have_ilckd=yes) + AC_MSG_RESULT([$have_ilckd]) + test $have_ilckd = yes && AC_DEFINE(ETHR_HAVE__INTERLOCKEDINCREMENT64, 1, [Define if you have _InterlockedIncrement64()]) + + have_ilckd=no + AC_MSG_CHECKING([for _InterlockedExchangeAdd64()]) + AC_TRY_LINK([ + #define WIN32_LEAN_AND_MEAN + #include <windows.h> + ], + [ + volatile __int64 *var; + _InterlockedExchangeAdd64(var, (__int64) 1); + return 0; + ], + have_ilckd=yes) + AC_MSG_RESULT([$have_ilckd]) + test $have_ilckd = yes && AC_DEFINE(ETHR_HAVE__INTERLOCKEDEXCHANGEADD64, 1, [Define if you have _InterlockedExchangeAdd64()]) + + have_ilckd=no + AC_MSG_CHECKING([for _InterlockedExchange64()]) + AC_TRY_LINK([ + #define WIN32_LEAN_AND_MEAN + #include <windows.h> + ], + [ + volatile __int64 *var; + _InterlockedExchange64(var, (__int64) 1); + return 0; + ], + have_ilckd=yes) + AC_MSG_RESULT([$have_ilckd]) + test $have_ilckd = yes && AC_DEFINE(ETHR_HAVE__INTERLOCKEDEXCHANGE64, 1, [Define if you have _InterlockedExchange64()]) + + have_ilckd=no + AC_MSG_CHECKING([for _InterlockedAnd64()]) + AC_TRY_LINK([ + #define WIN32_LEAN_AND_MEAN + #include <windows.h> + ], + [ + volatile __int64 *var; + _InterlockedAnd64(var, (__int64) 1); + return 0; + ], + have_ilckd=yes) + AC_MSG_RESULT([$have_ilckd]) + test $have_ilckd = yes && AC_DEFINE(ETHR_HAVE__INTERLOCKEDAND64, 1, [Define if you have _InterlockedAnd64()]) + + have_ilckd=no + AC_MSG_CHECKING([for _InterlockedOr64()]) + AC_TRY_LINK([ + #define WIN32_LEAN_AND_MEAN + #include <windows.h> + ], + [ + volatile __int64 *var; + _InterlockedOr64(var, (__int64) 1); + return 0; + ], + have_ilckd=yes) + AC_MSG_RESULT([$have_ilckd]) + test $have_ilckd = yes && AC_DEFINE(ETHR_HAVE__INTERLOCKEDOR64, 1, [Define if you have _InterlockedOr64()]) + ;; pthread) @@ -1087,6 +1202,28 @@ fi AC_CHECK_SIZEOF(void *) AC_DEFINE_UNQUOTED(ETHR_SIZEOF_PTR, $ac_cv_sizeof_void_p, [Define to the size of pointers]) +AC_CHECK_SIZEOF(int) +AC_DEFINE_UNQUOTED(ETHR_SIZEOF_INT, $ac_cv_sizeof_int, [Define to the size of int]) +AC_CHECK_SIZEOF(long) +AC_DEFINE_UNQUOTED(ETHR_SIZEOF_LONG, $ac_cv_sizeof_long, [Define to the size of long]) +AC_CHECK_SIZEOF(long long) +AC_DEFINE_UNQUOTED(ETHR_SIZEOF_LONG_LONG, $ac_cv_sizeof_long_long, [Define to the size of long long]) +AC_CHECK_SIZEOF(__int64) +AC_DEFINE_UNQUOTED(ETHR_SIZEOF___INT64, $ac_cv_sizeof___int64, [Define to the size of __int64]) + + +case X$erl_xcomp_bigendian in + X) ;; + Xyes|Xno) ac_cv_c_bigendian=$erl_xcomp_bigendian;; + *) AC_MSG_ERROR([Bad erl_xcomp_bigendian value: $erl_xcomp_bigendian]);; +esac + +AC_C_BIGENDIAN + +if test "$ac_cv_c_bigendian" = "yes"; then + AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian]) +fi + AC_ARG_ENABLE(native-ethr-impls, AS_HELP_STRING([--disable-native-ethr-impls], [disable native ethread implementations]), diff --git a/erts/configure.in b/erts/configure.in index 6e983a07b0..627f734409 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -580,6 +580,11 @@ AC_SUBST(WFLAGS) AC_SUBST(CFLAG_RUNTIME_LIBRARY_PATH) AC_CHECK_SIZEOF(void *) # Needed for ARCH and smp checks below +if test "x$ac_cv_sizeof_void_p" = x8; then + AC_SUBST(EXTERNAL_WORD_SIZE, 64) +else + AC_SUBST(EXTERNAL_WORD_SIZE, 32) +fi dnl dnl Figure out operating system and cpu architecture diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 1703ce0942..77181d3407 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,273 @@ </header> <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 5.8.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix format_man_pages so it handles all man sections + and remove warnings/errors in various man pages. </p> + <p> + Own Id: OTP-8600</p> + </item> + <item> + <p> + The <c>configure</c> command line argument <seealso + marker="doc/installation_guide:INSTALL#How-to-Build-and-Install-ErlangOTP_A-Closer-Look-at-the-individual-Steps_Configuring">--enable-ethread-pre-pentium4-compatibility</seealso> + had no effect. This option is now also automatically + enabled if required on the build machine.</p> + <p> + Own Id: OTP-8847</p> + </item> + <item> + <p> + Windows 2003 and Windows XP pre SP3 would sometimes not + start the Erlang R14B VM at all due to a bug in the cpu + topology detection. The bug affects Windows only, no + other platform is even remotely affected. The bug is now + corrected.</p> + <p> + Own Id: OTP-8876</p> + </item> + <item> + <p> + The HiPE run-time in the 64-bit emulator could do a + 64-bit write to a 32-bit struct field. It happened to be + harmless on Intel/AMD processors. Corrected. (Thanks to + Mikael Pettersson.)</p> + <p> + Own Id: OTP-8877</p> + </item> + <item> + <p> + A bug in <seealso + marker="erl_driver#erl_drv_tsd_get">erl_drv_tsd_get()</seealso> + and <seealso + marker="erl_nif#enif_tsd_get">enif_tsd_get()</seealso> + could cause an emulator crash. These functions are + currently not used in OTP. That is, the crash only occur + on systems with user implemented NIF libraries, or + drivers that use one of these functions.</p> + <p> + Own Id: OTP-8889</p> + </item> + <item> + <p> + Calling <c>erlang:system_info({cpu_topology, + CpuTopologyType})</c> with another <c>CpuTopologyType</c> + element than one of the documented atoms <c>defined</c>, + <c>detected</c>, or <c>used</c> caused an emulator crash. + (Thanks to Paul Guyot)</p> + <p> + Own Id: OTP-8914</p> + </item> + <item> + <p> + The ERTS internal rwlock implementation could get into an + inconsistent state. This bug was very seldom triggered, + but could be during heavy contention. The bug was + introduced in R14B (erts-5.8.1).</p> + <p> + The bug was most likely to be triggered when using the + <c>read_concurrency</c> option on an ETS table that was + frequently accessed from multiple processes doing lots of + writes and reads. That is, in a situation where you + typically don't want to use the <c>read_concurrency</c> + option in the first place.</p> + <p> + Own Id: OTP-8925 Aux Id: OTP-8544 </p> + </item> + <item> + <p> + Tracing to port could cause an emulator crash when + unloading the trace driver.</p> + <p> + Own Id: OTP-8932</p> + </item> + <item> + <p> + Removed use of CancelIoEx on Windows that had been shown + to cause problems with some drivers.</p> + <p> + Own Id: OTP-8937</p> + </item> + <item> + <p> + The fallback implementation used when no native atomic + implementation was found did not compile. (Thanks to + Patrick Baggett, and Tuncer Ayaz)</p> + <p> + Own Id: OTP-8944</p> + </item> + <item> + <p> + Some integer values used during load balancing could + under rare circumstances wrap causing a load unbalance + between schedulers.</p> + <p> + Own Id: OTP-8950</p> + </item> + <item> + <p> + The windows VM now correctly handles appending to large + files (> 4GB).</p> + <p> + Own Id: OTP-8958</p> + </item> + <item> + <p> + Name resolving of IPv6 addresses has been implemented for + Windows versions that support it. The use of ancient + resolver flags (AI_V4MAPPED | AI_ADDRCONFIG) to the + getaddrinfo() function has been removed since e.g FreeBSD + regard mapped IPv4 addresses to be a security problem and + the semantics of the address configured flag is + uncertain.</p> + <p> + Own Id: OTP-8969</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The help texts produced by the <c>configure</c> scripts + in the top directory and in the erts directory have been + aligned and cleaned up.</p> + <p> + Own Id: OTP-8859</p> + </item> + <item> + <p> + When the runtime system had fewer schedulers than logical + processors, the system could get an unnecessarily large + amount reader groups.</p> + <p> + Own Id: OTP-8861</p> + </item> + <item> + <p> + <c>run_rel</c> has been updated to support Solaris's + /dev/ptmx device and to load the necessary STREAMS + modules so that <c>to_erl</c> can provide terminal echo + of keyboard input. (Thanks to Ryan Tilder.)</p> + <p> + Own Id: OTP-8878</p> + </item> + <item> + <p> + The Erlang VM now supports Unicode filenames. The feature + is turned on by default on systems where Unicode + filenames are mandatory (Windows and MacOSX), but can be + enabled on other systems with the '+fnu' emulator option. + Enabling the Unicode filename feature on systems where it + is not default is however considered experimental and not + to be used for production. Together with the Unicode file + name support, the concept of "raw filenames" is + introduced, which means filenames provided without + implicit unicode encoding translation. Raw filenames are + provided as binaries, not lists. For further information, + see stdlib users guide and the chapter about using + Unicode in Erlang. Also see the file module manual page.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-8887</p> + </item> + <item> + <p>Buffer overflows have been prevented in <c>erlc</c>, + <c>dialyzer</c>, <c>typer</c>, <c>run_test</c>, + <c>heart</c>, <c>escript</c>, and <c>erlexec</c>.</p> + (Thanks to Michael Santos.) + <p> + Own Id: OTP-8892</p> + </item> + <item> + <p> + The runtime system is now less eager to suspend processes + sending messages over the distribution. The default value + of the distribution buffer busy limit has also been + increased from 128 KB to 1 MB. This in order to improve + throughput.</p> + <p> + Own Id: OTP-8901</p> + </item> + <item> + <p> + The distribution buffer busy limit can now be configured + at system startup. For more information see the + documentation of the <c>erl</c> <seealso + marker="erl#+zdbbl">+zdbbl</seealso> command line flag. + (Thanks to Scott Lystig Fritchie)</p> + <p> + Own Id: OTP-8912</p> + </item> + <item> + <p> + The inet driver internal buffer stack implementation has + been rewritten in order to reduce lock contention.</p> + <p> + Own Id: OTP-8916</p> + </item> + <item> + <p> + New ETS option <c>compressed</c>, to enable a more + compact storage format at the expence of heavier table + operations. For test and evaluation, <c>erl +ec</c> can + be used to force compression on all ETS tables.</p> + <p> + Own Id: OTP-8922 Aux Id: seq11658 </p> + </item> + <item> + <p> + There is now a new function inet:getifaddrs/0 modeled + after C library function getifaddrs() on BSD and LInux + that reports existing interfaces and their addresses on + the host. This replaces the undocumented and unsupported + inet:getiflist/0 and inet:ifget/2.</p> + <p> + Own Id: OTP-8926</p> + </item> + <item> + <p> + Support for detection of CPU topology and binding of + schedulers on FreeBSD 8 have been added. (Thanks to Paul + Guyot)</p> + <p> + Own Id: OTP-8939</p> + </item> + <item> + <p> + Several bugs related to hibernate/3 and HiPE have been + corrected. (Thanks to Paul Guyot.)</p> + <p> + Own Id: OTP-8952</p> + </item> + <item> + <p> + Support for soft and hard links on Windows versions and + filesystems that support them is added.</p> + <p> + Own Id: OTP-8955</p> + </item> + <item> + <p> + The win32 virtual machine is now linked large address + aware. his allows the Erlang VM to use up to 3 gigs of + address space on Windows instead of the default of 2 + gigs.</p> + <p> + Own Id: OTP-8956</p> + </item> + </list> + </section> + +</section> + <section><title>Erts 5.8.1.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 6c33e2ca16..f04df354a8 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -505,8 +505,10 @@ ifdef HIPE_ENABLED OPCODE_TABLES += hipe/hipe_ops.tab endif -$(TTF_DIR)/beam_opcodes.h $(TTF_DIR)/beam_opcodes.c: $(OPCODE_TABLES) - LANG=C $(PERL) utils/beam_makeops -outdir $(TTF_DIR) \ +$(TTF_DIR)/beam_opcodes.h $(TTF_DIR)/beam_opcodes.c: $(OPCODE_TABLES) utils/beam_makeops + LANG=C $(PERL) utils/beam_makeops \ + -wordsize @EXTERNAL_WORD_SIZE@ \ + -outdir $(TTF_DIR) \ -emulator $(OPCODE_TABLES) # bif and atom table diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c index 682f31b83f..31910888d1 100644 --- a/erts/emulator/beam/beam_bp.c +++ b/erts/emulator/beam/beam_bp.c @@ -950,8 +950,8 @@ static int set_function_break(Module *modp, BeamInstr *pc, int bif, MatchSetUnref(old_match_spec); } else { BpDataCount *bdc = (BpDataCount *) bd; - long count = 0; - long res = 0; + erts_aint_t count = 0; + erts_aint_t res = 0; ASSERT(! match_spec); ASSERT(is_nil(tracer_pid)); diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h index ebc171078d..bd8a7249a7 100644 --- a/erts/emulator/beam/beam_bp.h +++ b/erts/emulator/beam/beam_bp.h @@ -157,7 +157,7 @@ do { \ BpData **bds = (BpData **) (pc)[-4]; \ BpDataCount *bdc = NULL; \ Uint ix = bp_sched2ix_proc( (p) ); \ - long count = 0; \ + erts_aint_t count = 0; \ \ ASSERT((pc)[-5] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); \ ASSERT(bds); \ diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index b0bf14b94f..2855241b91 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -48,7 +48,6 @@ void dbg_bt(Process* p, Eterm* sp); void dbg_where(BeamInstr* addr, Eterm x0, Eterm* reg); -static void print_big(int to, void *to_arg, Eterm* addr); static int print_op(int to, void *to_arg, int op, int size, BeamInstr* addr); Eterm erts_debug_same_2(Process* p, Eterm term1, Eterm term2) @@ -157,6 +156,25 @@ void debug_dump_code(BeamInstr *I, int num) } #endif +BIF_RETTYPE +erts_debug_instructions_0(BIF_ALIST_0) +{ + int i = 0; + Uint needed = num_instructions * 2; + Eterm* hp; + Eterm res = NIL; + + for (i = 0; i < num_instructions; i++) { + needed += 2*strlen(opc[i].name); + } + hp = HAlloc(BIF_P, needed); + for (i = num_instructions-1; i >= 0; i--) { + Eterm s = erts_bld_string_n(&hp, 0, opc[i].name, strlen(opc[i].name)); + res = erts_bld_cons(&hp, 0, s, res); + } + return res; +} + Eterm erts_debug_disassemble_1(Process* p, Eterm addr) { @@ -312,6 +330,7 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) BeamInstr packed = 0; /* Accumulator for packed operations. */ BeamInstr args[8]; /* Arguments for this instruction. */ BeamInstr* ap; /* Pointer to arguments. */ + BeamInstr* unpacked; /* Unpacked arguments */ start_prog = opc[op].pack; @@ -360,6 +379,12 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) *ap++ = packed & BEAM_LOOSE_MASK; packed >>= BEAM_LOOSE_SHIFT; break; +#ifdef ARCH_64 + case 'w': /* Shift 32 steps */ + *ap++ = packed & BEAM_WIDE_MASK; + packed >>= BEAM_WIDE_SHIFT; + break; +#endif case 'p': *sp++ = *--ap; break; @@ -386,7 +411,7 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) break; case 'x': /* x(N) */ if (reg_index(ap[0]) == 0) { - erts_print(to, to_arg, "X[0]"); + erts_print(to, to_arg, "x[0]"); } else { erts_print(to, to_arg, "x(%d)", reg_index(ap[0])); } @@ -506,6 +531,7 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) ap++; break; case 'P': /* Byte offset into tuple (see beam_load.c) */ + case 'Q': /* Like 'P', but packable */ erts_print(to, to_arg, "%d", (*ap / sizeof(Eterm)) - 1); ap++; break; @@ -526,9 +552,12 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) * Print more information about certain instructions. */ + unpacked = ap; ap = addr + size; switch (op) { - case op_i_select_val_sfI: + case op_i_select_val_rfI: + case op_i_select_val_xfI: + case op_i_select_val_yfI: { int n = ap[-1]; @@ -540,7 +569,24 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) } } break; - case op_i_jump_on_val_sfII: + case op_i_select_tuple_arity_rfI: + case op_i_select_tuple_arity_xfI: + case op_i_select_tuple_arity_yfI: + { + int n = ap[-1]; + + while (n > 0) { + Uint arity = arityval(ap[0]); + erts_print(to, to_arg, " {%d} f(" HEXF ")", arity, ap[1]); + ap += 2; + size += 2; + n--; + } + } + break; + case op_i_jump_on_val_rfII: + case op_i_jump_on_val_xfII: + case op_i_jump_on_val_yfII: { int n; for (n = ap[-2]; n > 0; n--) { @@ -550,39 +596,46 @@ print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) } } break; - case op_i_select_big_sf: - while (ap[0]) { - Eterm *bigp = (Eterm *) ap; - int arity = thing_arityval(*bigp); - print_big(to, to_arg, bigp); - size += TermWords(arity+1); - ap += TermWords(arity+1); - erts_print(to, to_arg, " f(" HEXF ") ", ap[0]); - ap++; - size++; + case op_i_jump_on_val_zero_rfI: + case op_i_jump_on_val_zero_xfI: + case op_i_jump_on_val_zero_yfI: + { + int n; + for (n = ap[-1]; n > 0; n--) { + erts_print(to, to_arg, "f(" HEXF ") ", ap[0]); + ap++; + size++; + } + } + break; + case op_i_put_tuple_rI: + case op_i_put_tuple_xI: + case op_i_put_tuple_yI: + { + int n = unpacked[-1]; + + while (n > 0) { + if (!is_header(ap[0])) { + erts_print(to, to_arg, " %T", (Eterm) ap[0]); + } else { + switch ((ap[0] >> 2) & 0x03) { + case R_REG_DEF: + erts_print(to, to_arg, " x(0)"); + break; + case X_REG_DEF: + erts_print(to, to_arg, " x(%d)", ap[0] >> 4); + break; + case Y_REG_DEF: + erts_print(to, to_arg, " y(%d)", ap[0] >> 4); + break; + } + } + ap++, size++, n--; + } } - ap++; - size++; break; } erts_print(to, to_arg, "\n"); return size; } - -static void -print_big(int to, void *to_arg, Eterm* addr) -{ - int i; - int k; - - i = BIG_SIZE(addr); - if (BIG_SIGN(addr)) - erts_print(to, to_arg, "-#integer(%d) = {", i); - else - erts_print(to, to_arg, "#integer(%d) = {", i); - erts_print(to, to_arg, "0x%x", BIG_DIGIT(addr, 0)); - for (k = 1; k < i; k++) - erts_print(to, to_arg, ",0x%x", BIG_DIGIT(addr, k)); - erts_print(to, to_arg, "}"); -} diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 8a0e12dd4f..16741aa2d7 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -344,6 +344,8 @@ extern int count_instructions; #define xb(N) (*(Eterm *) (((unsigned char *)reg) + (N))) #define yb(N) (*(Eterm *) (((unsigned char *)E) + (N))) #define fb(N) (*(double *) (((unsigned char *)&(freg[0].fd)) + (N))) +#define Qb(N) (N) +#define Ib(N) (N) #define x(N) reg[N] #define y(N) E[N] #define r(N) x##N @@ -472,6 +474,13 @@ extern int count_instructions; HEAP_SPACE_VERIFIED(need); \ } while (0) +#define TestHeapPutList(Need, Reg) \ + do { \ + TestHeap((Need), 1); \ + PutList(Reg, r(0), r(0), StoreSimpleDest); \ + CHECK_TERM(r(0)); \ + } while (0) + #ifdef HYBRID #ifdef INCREMENTAL #define TestGlobalHeap(Nh, Live, hp) \ @@ -516,6 +525,11 @@ extern int count_instructions; SWAPIN; \ } while (0) +#define PutTuple(Dst, Arity) \ + do { \ + Dst = make_tuple(HTOP); \ + pt_arity = (Arity); \ + } while (0) /* * Check that we haven't used the reductions and jump to function pointed to by @@ -674,6 +688,11 @@ extern int count_instructions; SET_I((BeamInstr *) CallDest); \ Dispatch(); +#define MoveJump(Src) \ + r(0) = (Src); \ + SET_I((BeamInstr *) Arg(0)); \ + Goto(*I); + #define GetList(Src, H, T) do { \ Eterm* tmp_ptr = list_val(Src); \ H = CAR(tmp_ptr); \ @@ -723,16 +742,8 @@ extern int count_instructions; (Dest) = (* (Eterm *) EXPAND_POINTER(tmp_arg1)); \ } while (0) -#define PutTuple(Arity, Src, Dest) \ - ASSERT(is_arity_value(Arity)); \ - Dest = make_tuple(HTOP); \ - HTOP[0] = (Arity); \ - HTOP[1] = (Src); \ - HTOP += 2 - -#define Put(Word) *HTOP++ = (Word) - #define EqualImmed(X, Y, Action) if (X != Y) { Action; } +#define NotEqualImmed(X, Y, Action) if (X == Y) { Action; } #define IsFloat(Src, Fail) if (is_not_float(Src)) { Fail; } @@ -984,8 +995,41 @@ extern int count_instructions; #define IsPid(Src, Fail) if (is_not_pid(Src)) { Fail; } #define IsRef(Src, Fail) if (is_not_ref(Src)) { Fail; } -static BifFunction translate_gc_bif(void* gcf); -static BeamInstr* handle_error(Process* c_p, BeamInstr* pc, Eterm* reg, BifFunction bf); +/* + * process_main() is already huge, so we want to avoid inlining + * into it. Especially functions that are seldom used. + */ +#ifdef __GNUC__ +# define NOINLINE __attribute__((__noinline__)) +#else +# define NOINLINE +#endif + +/* + * The following functions are called directly by process_main(). + * Don't inline them. + */ +static BifFunction translate_gc_bif(void* gcf) NOINLINE; +static BeamInstr* handle_error(Process* c_p, BeamInstr* pc, + Eterm* reg, BifFunction bf) NOINLINE; +static BeamInstr* call_error_handler(Process* p, BeamInstr* ip, + Eterm* reg, Eterm func) NOINLINE; +static BeamInstr* fixed_apply(Process* p, Eterm* reg, Uint arity) NOINLINE; +static BeamInstr* apply(Process* p, Eterm module, Eterm function, + Eterm args, Eterm* reg) NOINLINE; +static int hibernate(Process* c_p, Eterm module, Eterm function, + Eterm args, Eterm* reg) NOINLINE; +static BeamInstr* call_fun(Process* p, int arity, + Eterm* reg, Eterm args) NOINLINE; +static BeamInstr* apply_fun(Process* p, Eterm fun, + Eterm args, Eterm* reg) NOINLINE; +static Eterm new_fun(Process* p, Eterm* reg, + ErlFunEntry* fe, int num_free) NOINLINE; + + +/* + * Functions not directly called by process_main(). OK to inline. + */ static BeamInstr* next_catch(Process* c_p, Eterm *reg); static void terminate_proc(Process* c_p, Eterm Value); static Eterm add_stacktrace(Process* c_p, Eterm Value, Eterm exc); @@ -993,16 +1037,6 @@ static void save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg, BifFunction bf, Eterm args); static struct StackTrace * get_trace_from_exc(Eterm exc); static Eterm make_arglist(Process* c_p, Eterm* reg, int a); -static Eterm call_error_handler(Process* p, BeamInstr* ip, Eterm* reg); -static Eterm call_breakpoint_handler(Process* p, BeamInstr* fi, Eterm* reg); -static BeamInstr* fixed_apply(Process* p, Eterm* reg, Uint arity); -static BeamInstr* apply(Process* p, Eterm module, Eterm function, - Eterm args, Eterm* reg); -static int hibernate(Process* c_p, Eterm module, Eterm function, - Eterm args, Eterm* reg); -static BeamInstr* call_fun(Process* p, int arity, Eterm* reg, Eterm args); -static BeamInstr* apply_fun(Process* p, Eterm fun, Eterm args, Eterm* reg); -static Eterm new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free); #if defined(VXWORKS) static int init_done; @@ -1146,6 +1180,8 @@ void process_main(void) Uint temp_bits; /* Temporary used by BsSkipBits2 & BsGetInteger2 */ + Eterm pt_arity; /* Used by do_put_tuple */ + ERL_BITS_DECLARE_STATEP; /* Has to be last declaration */ @@ -1246,6 +1282,52 @@ void process_main(void) #define STORE_ARITH_RESULT(res) StoreBifResult(2, (res)); #define ARITH_FUNC(name) erts_gc_##name + { + Eterm increment_reg_val; + Eterm increment_val; + Uint live; + Eterm result; + + OpCase(i_increment_yIId): + increment_reg_val = yb(Arg(0)); + goto do_increment; + + OpCase(i_increment_xIId): + increment_reg_val = xb(Arg(0)); + goto do_increment; + + OpCase(i_increment_rIId): + increment_reg_val = r(0); + I--; + + do_increment: + increment_val = Arg(1); + if (is_small(increment_reg_val)) { + Sint i = signed_val(increment_reg_val) + increment_val; + ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i)); + if (MY_IS_SSMALL(i)) { + result = make_small(i); + store_result: + StoreBifResult(3, result); + } + } + + live = Arg(2); + SWAPOUT; + reg[0] = r(0); + reg[live] = increment_reg_val; + reg[live+1] = make_small(increment_val); + result = erts_gc_mixed_plus(c_p, reg, live); + r(0) = reg[0]; + SWAPIN; + ERTS_HOLE_CHECK(c_p); + if (is_value(result)) { + goto store_result; + } + ASSERT(c_p->freason != BADMATCH || is_value(c_p->fvalue)); + goto find_func_info; + } + OpCase(i_plus_jId): { Eterm result; @@ -1309,6 +1391,52 @@ void process_main(void) } Next(1); + { + Eterm is_eq_exact_lit_val; + + OpCase(i_is_eq_exact_literal_xfc): + is_eq_exact_lit_val = xb(Arg(0)); + I++; + goto do_is_eq_exact_literal; + + OpCase(i_is_eq_exact_literal_yfc): + is_eq_exact_lit_val = yb(Arg(0)); + I++; + goto do_is_eq_exact_literal; + + OpCase(i_is_eq_exact_literal_rfc): + is_eq_exact_lit_val = r(0); + + do_is_eq_exact_literal: + if (!eq(Arg(1), is_eq_exact_lit_val)) { + ClauseFail(); + } + Next(2); + } + + { + Eterm is_ne_exact_lit_val; + + OpCase(i_is_ne_exact_literal_xfc): + is_ne_exact_lit_val = xb(Arg(0)); + I++; + goto do_is_ne_exact_literal; + + OpCase(i_is_ne_exact_literal_yfc): + is_ne_exact_lit_val = yb(Arg(0)); + I++; + goto do_is_ne_exact_literal; + + OpCase(i_is_ne_exact_literal_rfc): + is_ne_exact_lit_val = r(0); + + do_is_ne_exact_literal: + if (eq(Arg(1), is_ne_exact_lit_val)) { + ClauseFail(); + } + Next(2); + } + OpCase(i_move_call_only_fcr): { r(0) = Arg(1); } @@ -1392,6 +1520,17 @@ void process_main(void) NextPF(1, next); } + OpCase(move_x1_c): { + x(1) = Arg(0); + Next(1); + } + + OpCase(move_x2_c): { + x(2) = Arg(0); + Next(1); + } + + OpCase(return): { SET_I(c_p->cp); /* @@ -1405,16 +1544,6 @@ void process_main(void) Goto(*I); } - OpCase(test_heap_1_put_list_Iy): { - BeamInstr *next; - - PreFetch(2, next); - TestHeap(Arg(0), 1); - PutList(yb(Arg(1)), r(0), r(0), StoreSimpleDest); - CHECK_TERM(r(0)); - NextPF(2, next); - } - /* * Send is almost a standard call-BIF with two arguments, except for: * 1) It cannot be traced. @@ -1447,24 +1576,36 @@ void process_main(void) goto find_func_info; } - OpCase(i_element_jssd): { - Eterm index; - Eterm tuple; - - /* - * Inlined version of element/2 for speed. - */ - GetArg2(1, index, tuple); - if (is_small(index) && is_tuple(tuple)) { - Eterm* tp = tuple_val(tuple); - - if ((signed_val(index) >= 1) && - (signed_val(index) <= arityval(*tp))) { - Eterm result = tp[signed_val(index)]; - StoreBifResult(3, result); - } - } - } + { + Eterm element_index; + Eterm element_tuple; + + OpCase(i_element_xjsd): + element_tuple = xb(Arg(0)); + I++; + goto do_element; + + OpCase(i_element_yjsd): + element_tuple = yb(Arg(0)); + I++; + goto do_element; + + OpCase(i_element_rjsd): + element_tuple = r(0); + /* Fall through */ + + do_element: + GetArg1(1, element_index); + if (is_small(element_index) && is_tuple(element_tuple)) { + Eterm* tp = tuple_val(element_tuple); + + if ((signed_val(element_index) >= 1) && + (signed_val(element_index) <= arityval(*tp))) { + Eterm result = tp[signed_val(element_index)]; + StoreBifResult(2, result); + } + } + } /* Fall through */ OpCase(badarg_j): @@ -1472,24 +1613,32 @@ void process_main(void) c_p->freason = BADARG; goto lb_Cl_error; - OpCase(i_fast_element_jIsd): { - Eterm tuple; - - /* - * Inlined version of element/2 for even more speed. - * The first argument is an untagged integer >= 1. - * The second argument is guaranteed to be a register operand. - */ - GetArg1(2, tuple); - if (is_tuple(tuple)) { - Eterm* tp = tuple_val(tuple); - tmp_arg2 = Arg(1); - if (tmp_arg2 <= arityval(*tp)) { - Eterm result = tp[tmp_arg2]; - StoreBifResult(3, result); - } - } + { + Eterm fast_element_tuple; + + OpCase(i_fast_element_rjId): + fast_element_tuple = r(0); + + do_fast_element: + if (is_tuple(fast_element_tuple)) { + Eterm* tp = tuple_val(fast_element_tuple); + Eterm pos = Arg(1); /* Untagged integer >= 1 */ + if (pos <= arityval(*tp)) { + Eterm result = tp[pos]; + StoreBifResult(2, result); + } + } goto badarg; + + OpCase(i_fast_element_xjId): + fast_element_tuple = xb(Arg(0)); + I++; + goto do_fast_element; + + OpCase(i_fast_element_yjId): + fast_element_tuple = yb(Arg(0)); + I++; + goto do_fast_element; } OpCase(catch_yf): @@ -1842,8 +1991,87 @@ void process_main(void) NextPF(0, next); } - OpCase(i_select_val_sfI): - GetArg1(0, tmp_arg1); + + { + Eterm select_val2; + + OpCase(i_select_tuple_arity2_yfAfAf): + select_val2 = yb(Arg(0)); + goto do_select_tuple_arity2; + + OpCase(i_select_tuple_arity2_xfAfAf): + select_val2 = xb(Arg(0)); + goto do_select_tuple_arity2; + + OpCase(i_select_tuple_arity2_rfAfAf): + select_val2 = r(0); + I--; + + do_select_tuple_arity2: + if (is_not_tuple(select_val2)) { + goto select_val2_fail; + } + select_val2 = *tuple_val(select_val2); + goto do_select_val2; + + OpCase(i_select_val2_yfcfcf): + select_val2 = yb(Arg(0)); + goto do_select_val2; + + OpCase(i_select_val2_xfcfcf): + select_val2 = xb(Arg(0)); + goto do_select_val2; + + OpCase(i_select_val2_rfcfcf): + select_val2 = r(0); + I--; + + do_select_val2: + if (select_val2 == Arg(2)) { + I += 2; + } else if (select_val2 == Arg(4)) { + I += 4; + } + + select_val2_fail: + SET_I((BeamInstr *) Arg(1)); + Goto(*I); + } + + { + Eterm select_val; + + OpCase(i_select_tuple_arity_xfI): + select_val = xb(Arg(0)); + goto do_select_tuple_arity; + + OpCase(i_select_tuple_arity_yfI): + select_val = yb(Arg(0)); + goto do_select_tuple_arity; + + OpCase(i_select_tuple_arity_rfI): + select_val = r(0); + I--; + + do_select_tuple_arity: + if (is_tuple(select_val)) { + select_val = *tuple_val(select_val); + goto do_binary_search; + } + SET_I((BeamInstr *) Arg(1)); + Goto(*I); + + OpCase(i_select_val_xfI): + select_val = xb(Arg(0)); + goto do_binary_search; + + OpCase(i_select_val_yfI): + select_val = yb(Arg(0)); + goto do_binary_search; + + OpCase(i_select_val_rfI): + select_val = r(0); + I--; do_binary_search: { @@ -1880,9 +2108,9 @@ void process_main(void) unsigned int boffset = ((unsigned int)bdiff >> 1) & ~(sizeof(struct Pairs)-1); mid = (struct Pairs*)((char*)low + boffset); - if (tmp_arg1 < mid->val) { + if (select_val < mid->val) { high = mid; - } else if (tmp_arg1 > mid->val) { + } else if (select_val > mid->val) { low = mid + 1; } else { SET_I(mid->addr); @@ -1892,16 +2120,28 @@ void process_main(void) SET_I((BeamInstr *) Arg(1)); Goto(*I); } + } - OpCase(i_jump_on_val_zero_sfI): { - Eterm index; - - GetArg1(0, index); - if (is_small(index)) { - index = signed_val(index); - if (index < Arg(2)) { - SET_I((BeamInstr *) (&Arg(3))[index]); + Eterm jump_on_val_zero_index; + + OpCase(i_jump_on_val_zero_yfI): + jump_on_val_zero_index = yb(Arg(0)); + goto do_jump_on_val_zero_index; + + OpCase(i_jump_on_val_zero_xfI): + jump_on_val_zero_index = xb(Arg(0)); + goto do_jump_on_val_zero_index; + + OpCase(i_jump_on_val_zero_rfI): + jump_on_val_zero_index = r(0); + I--; + + do_jump_on_val_zero_index: + if (is_small(jump_on_val_zero_index)) { + jump_on_val_zero_index = signed_val(jump_on_val_zero_index); + if (jump_on_val_zero_index < Arg(2)) { + SET_I((BeamInstr *) (&Arg(3))[jump_on_val_zero_index]); Goto(*I); } } @@ -1909,15 +2149,27 @@ void process_main(void) Goto(*I); } - OpCase(i_jump_on_val_sfII): { - Eterm index; + Eterm jump_on_val_index; - GetArg1(0, index); - if (is_small(index)) { - index = (Uint) (signed_val(index) - Arg(3)); - if (index < Arg(2)) { - SET_I((BeamInstr *) (&Arg(4))[index]); + + OpCase(i_jump_on_val_yfII): + jump_on_val_index = yb(Arg(0)); + goto do_jump_on_val_index; + + OpCase(i_jump_on_val_xfII): + jump_on_val_index = xb(Arg(0)); + goto do_jump_on_val_index; + + OpCase(i_jump_on_val_rfII): + jump_on_val_index = r(0); + I--; + + do_jump_on_val_index: + if (is_small(jump_on_val_index)) { + jump_on_val_index = (Uint) (signed_val(jump_on_val_index) - Arg(3)); + if (jump_on_val_index < Arg(2)) { + SET_I((BeamInstr *) (&Arg(4))[jump_on_val_index]); Goto(*I); } } @@ -1925,6 +2177,32 @@ void process_main(void) Goto(*I); } + do_put_tuple: { + Eterm* hp = HTOP; + + *hp++ = make_arityval(pt_arity); + + do { + Eterm term = *I++; + switch (term & _TAG_IMMED1_MASK) { + case (R_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: + *hp++ = r(0); + break; + case (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: + *hp++ = x(term >> _TAG_IMMED1_SIZE); + break; + case (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER: + *hp++ = y(term >> _TAG_IMMED1_SIZE); + break; + default: + *hp++ = term; + break; + } + } while (--pt_arity != 0); + HTOP = hp; + Goto(*I); + } + /* * All guards with zero arguments have special instructions: * self/0 @@ -2562,23 +2840,25 @@ void process_main(void) OpCase(i_int_bnot_jsId): { - GetArg1(1, tmp_arg1); - if (is_small(tmp_arg1)) { - tmp_arg1 = make_small(~signed_val(tmp_arg1)); + Eterm bnot_val; + + GetArg1(1, bnot_val); + if (is_small(bnot_val)) { + bnot_val = make_small(~signed_val(bnot_val)); } else { Uint live = Arg(2); SWAPOUT; reg[0] = r(0); - reg[live] = tmp_arg1; - tmp_arg1 = erts_gc_bnot(c_p, reg, live); + reg[live] = bnot_val; + bnot_val = erts_gc_bnot(c_p, reg, live); r(0) = reg[0]; SWAPIN; ERTS_HOLE_CHECK(c_p); - if (is_nil(tmp_arg1)) { + if (is_nil(bnot_val)) { goto lb_Cl_error; } } - StoreBifResult(3, tmp_arg1); + StoreBifResult(3, bnot_val); } badarith: @@ -2833,121 +3113,6 @@ void process_main(void) goto do_schedule1; } - OpCase(i_select_tuple_arity_sfI): - { - GetArg1(0, tmp_arg1); - - if (is_tuple(tmp_arg1)) { - tmp_arg1 = *tuple_val(tmp_arg1); - goto do_binary_search; - } - SET_I((BeamInstr *) Arg(1)); - Goto(*I); - } - - OpCase(i_select_big_sf): - { - Eterm* bigp; - Uint arity; - Eterm* given; - Uint given_arity; - Uint given_size; - - GetArg1(0, tmp_arg1); - if (is_big(tmp_arg1)) { - - /* - * The loader has sorted the bignumbers in descending order - * on the arity word. Therefore, we know that the search - * has failed as soon as we encounter an arity word less than - * the arity word of the given number. There is a zero word - * (less than any valid arity word) stored after the last bignumber. - */ - - given = big_val(tmp_arg1); - given_arity = given[0]; - given_size = thing_arityval(given_arity); - bigp = (Eterm *) &Arg(2); - while ((arity = bigp[0]) > given_arity) { - bigp += (TermWords(thing_arityval(arity) + 1) + 1) * (sizeof(BeamInstr)/sizeof(Eterm)); - } - while (bigp[0] == given_arity) { - if (memcmp(bigp+1, given+1, sizeof(Eterm)*given_size) == 0) { - BeamInstr *tmp = - ((BeamInstr *) (UWord) bigp) + TermWords(given_size + 1); - SET_I((BeamInstr *) *tmp); - Goto(*I); - } - bigp += (TermWords(thing_arityval(arity) + 1) + 1) * (sizeof(BeamInstr)/sizeof(Eterm)); - } - } - - /* - * Failed. - */ - - SET_I((BeamInstr *) Arg(1)); - Goto(*I); - } - -#if defined(ARCH_64) && !HALFWORD_HEAP - OpCase(i_select_float_sfI): - { - Uint f; - int n; - struct ValLabel { - Uint f; - BeamInstr* addr; - }; - struct ValLabel* ptr; - - GetArg1(0, tmp_arg1); - ASSERT(is_float(tmp_arg1)); - f = float_val(tmp_arg1)[1]; - n = Arg(2); - ptr = (struct ValLabel *) &Arg(3); - while (n-- > 0) { - if (ptr->f == f) { - SET_I(ptr->addr); - Goto(*I); - } - ptr++; - } - SET_I((Eterm *) Arg(1)); - Goto(*I); - } -#else - OpCase(i_select_float_sfI): - { - Uint fpart1; - Uint fpart2; - int n; - struct ValLabel { - Uint fpart1; - Uint fpart2; - BeamInstr* addr; - }; - struct ValLabel* ptr; - - GetArg1(0, tmp_arg1); - ASSERT(is_float(tmp_arg1)); - fpart1 = float_val(tmp_arg1)[1]; - fpart2 = float_val(tmp_arg1)[2]; - - n = Arg(2); - ptr = (struct ValLabel *) &Arg(3); - while (n-- > 0) { - if (ptr->fpart1 == fpart1 && ptr->fpart2 == fpart2) { - SET_I(ptr->addr); - Goto(*I); - } - ptr++; - } - SET_I((BeamInstr *) Arg(1)); - Goto(*I); - } -#endif - OpCase(set_tuple_element_sdP): { Eterm element; Eterm tuple; @@ -2993,15 +3158,17 @@ void process_main(void) the first argument. We also handle atom tags in the first argument for backwards compatibility. */ - GetArg2(0, tmp_arg1, tmp_arg2); - c_p->fvalue = tmp_arg2; + Eterm raise_val1; + Eterm raise_val2; + GetArg2(0, raise_val1, raise_val2); + c_p->fvalue = raise_val2; if (c_p->freason == EXC_NULL) { /* a safety check for the R10-0 case; should not happen */ c_p->ftrace = NIL; c_p->freason = EXC_ERROR; } /* for R10-0 code, keep existing c_p->ftrace and hope it's correct */ - switch (tmp_arg1) { + switch (raise_val1) { case am_throw: c_p->freason = EXC_THROWN & ~EXF_SAVETRACE; break; @@ -3017,8 +3184,8 @@ void process_main(void) passed from a user! Currently only expecting generated calls. */ struct StackTrace *s; - c_p->ftrace = tmp_arg1; - s = get_trace_from_exc(tmp_arg1); + c_p->ftrace = raise_val1; + s = get_trace_from_exc(raise_val1); if (s == NULL) { c_p->freason = EXC_ERROR; } else { @@ -3029,11 +3196,24 @@ void process_main(void) goto find_func_info; } - OpCase(badmatch_s): { - GetArg1(0, tmp_arg1); - c_p->fvalue = tmp_arg1; - c_p->freason = BADMATCH; - } + { + Eterm badmatch_val; + + OpCase(badmatch_y): + badmatch_val = yb(Arg(0)); + goto do_badmatch; + + OpCase(badmatch_x): + badmatch_val = xb(Arg(0)); + goto do_badmatch; + + OpCase(badmatch_r): + badmatch_val = r(0); + + do_badmatch: + c_p->fvalue = badmatch_val; + c_p->freason = BADMATCH; + } /* Fall through here */ find_func_info: { @@ -3056,12 +3236,11 @@ void process_main(void) */ SWAPOUT; reg[0] = r(0); - tmp_arg1 = call_error_handler(c_p, I-3, reg); + I = call_error_handler(c_p, I-3, reg, am_undefined_function); r(0) = reg[0]; SWAPIN; - if (tmp_arg1) { - SET_I(c_p->i); - Dispatch(); + if (I) { + Goto(*I); } /* Fall through */ @@ -3084,128 +3263,142 @@ void process_main(void) } } - OpCase(call_nif): - { - /* - * call_nif is always first instruction in function: - * - * I[-3]: Module - * I[-2]: Function - * I[-1]: Arity - * I[0]: &&call_nif - * I[1]: Function pointer to NIF function - * I[2]: Pointer to erl_module_nif - */ - BifFunction vbf; - - c_p->current = I-3; /* current and vbf set to please handle_error */ - SWAPOUT; - c_p->fcalls = FCALLS - 1; - PROCESS_MAIN_CHK_LOCKS(c_p); - tmp_arg2 = I[-1]; - ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); + { + Eterm nif_bif_result; + Eterm bif_nif_arity; - ASSERT(!ERTS_PROC_IS_EXITING(c_p)); - { - typedef Eterm NifF(struct enif_environment_t*, int argc, Eterm argv[]); - NifF* fp = vbf = (NifF*) I[1]; - struct enif_environment_t env; - erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2]); - reg[0] = r(0); - tmp_arg1 = (*fp)(&env, tmp_arg2, reg); - erts_post_nif(&env); - } - ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(tmp_arg1)); - PROCESS_MAIN_CHK_LOCKS(c_p); - goto apply_bif_or_nif_epilogue; - - OpCase(apply_bif): - /* - * At this point, I points to the code[3] in the export entry for - * the BIF: - * - * code[0]: Module - * code[1]: Function - * code[2]: Arity - * code[3]: &&apply_bif - * code[4]: Function pointer to BIF function - */ + OpCase(call_nif): + { + /* + * call_nif is always first instruction in function: + * + * I[-3]: Module + * I[-2]: Function + * I[-1]: Arity + * I[0]: &&call_nif + * I[1]: Function pointer to NIF function + * I[2]: Pointer to erl_module_nif + */ + BifFunction vbf; - c_p->current = I-3; /* In case we apply process_info/1,2 or load_nif/1 */ - c_p->i = I; /* In case we apply check_process_code/2. */ - c_p->arity = 0; /* To allow garbage collection on ourselves - * (check_process_code/2). - */ - SWAPOUT; - c_p->fcalls = FCALLS - 1; - vbf = (BifFunction) Arg(0); - PROCESS_MAIN_CHK_LOCKS(c_p); - tmp_arg2 = I[-1]; - ASSERT(tmp_arg2 <= 3); - ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); - switch (tmp_arg2) { - case 3: + c_p->current = I-3; /* current and vbf set to please handle_error */ + SWAPOUT; + c_p->fcalls = FCALLS - 1; + PROCESS_MAIN_CHK_LOCKS(c_p); + bif_nif_arity = I[-1]; + ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); + + ASSERT(!ERTS_PROC_IS_EXITING(c_p)); { - Eterm (*bf)(Process*, Eterm, Eterm, Eterm, BeamInstr*) = vbf; - ASSERT(!ERTS_PROC_IS_EXITING(c_p)); - tmp_arg1 = (*bf)(c_p, r(0), x(1), x(2), I); - ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(tmp_arg1)); - PROCESS_MAIN_CHK_LOCKS(c_p); + typedef Eterm NifF(struct enif_environment_t*, int argc, Eterm argv[]); + NifF* fp = vbf = (NifF*) I[1]; + struct enif_environment_t env; + erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2]); + reg[0] = r(0); + nif_bif_result = (*fp)(&env, bif_nif_arity, reg); + erts_post_nif(&env); } - break; - case 2: - { - Eterm (*bf)(Process*, Eterm, Eterm, BeamInstr*) = vbf; - ASSERT(!ERTS_PROC_IS_EXITING(c_p)); - tmp_arg1 = (*bf)(c_p, r(0), x(1), I); - ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(tmp_arg1)); - PROCESS_MAIN_CHK_LOCKS(c_p); + ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(nif_bif_result)); + PROCESS_MAIN_CHK_LOCKS(c_p); + goto apply_bif_or_nif_epilogue; + + OpCase(apply_bif): + /* + * At this point, I points to the code[3] in the export entry for + * the BIF: + * + * code[0]: Module + * code[1]: Function + * code[2]: Arity + * code[3]: &&apply_bif + * code[4]: Function pointer to BIF function + */ + + c_p->current = I-3; /* In case we apply process_info/1,2 or load_nif/1 */ + c_p->i = I; /* In case we apply check_process_code/2. */ + c_p->arity = 0; /* To allow garbage collection on ourselves + * (check_process_code/2). + */ + SWAPOUT; + c_p->fcalls = FCALLS - 1; + vbf = (BifFunction) Arg(0); + PROCESS_MAIN_CHK_LOCKS(c_p); + bif_nif_arity = I[-1]; + ASSERT(bif_nif_arity <= 3); + ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); + switch (bif_nif_arity) { + case 3: + { + Eterm (*bf)(Process*, Eterm, Eterm, Eterm, BeamInstr*) = vbf; + ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + nif_bif_result = (*bf)(c_p, r(0), x(1), x(2), I); + ASSERT(!ERTS_PROC_IS_EXITING(c_p) || + is_non_value(nif_bif_result)); + PROCESS_MAIN_CHK_LOCKS(c_p); + } + break; + case 2: + { + Eterm (*bf)(Process*, Eterm, Eterm, BeamInstr*) = vbf; + ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + nif_bif_result = (*bf)(c_p, r(0), x(1), I); + ASSERT(!ERTS_PROC_IS_EXITING(c_p) || + is_non_value(nif_bif_result)); + PROCESS_MAIN_CHK_LOCKS(c_p); + } + break; + case 1: + { + Eterm (*bf)(Process*, Eterm, BeamInstr*) = vbf; + ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + nif_bif_result = (*bf)(c_p, r(0), I); + ASSERT(!ERTS_PROC_IS_EXITING(c_p) || + is_non_value(nif_bif_result)); + PROCESS_MAIN_CHK_LOCKS(c_p); + } + break; + case 0: + { + Eterm (*bf)(Process*, BeamInstr*) = vbf; + ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + nif_bif_result = (*bf)(c_p, I); + ASSERT(!ERTS_PROC_IS_EXITING(c_p) || + is_non_value(nif_bif_result)); + PROCESS_MAIN_CHK_LOCKS(c_p); + break; + } + default: + erl_exit(1, "apply_bif: invalid arity: %u\n", + bif_nif_arity); } - break; - case 1: - { - Eterm (*bf)(Process*, Eterm, BeamInstr*) = vbf; - ASSERT(!ERTS_PROC_IS_EXITING(c_p)); - tmp_arg1 = (*bf)(c_p, r(0), I); - ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(tmp_arg1)); - PROCESS_MAIN_CHK_LOCKS(c_p); + + apply_bif_or_nif_epilogue: + ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); + ERTS_HOLE_CHECK(c_p); + if (c_p->mbuf) { + reg[0] = r(0); + nif_bif_result = erts_gc_after_bif_call(c_p, nif_bif_result, + reg, bif_nif_arity); + r(0) = reg[0]; } - break; - case 0: - { - Eterm (*bf)(Process*, BeamInstr*) = vbf; - ASSERT(!ERTS_PROC_IS_EXITING(c_p)); - tmp_arg1 = (*bf)(c_p, I); - ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(tmp_arg1)); - PROCESS_MAIN_CHK_LOCKS(c_p); - break; + SWAPIN; /* There might have been a garbage collection. */ + FCALLS = c_p->fcalls; + if (is_value(nif_bif_result)) { + r(0) = nif_bif_result; + CHECK_TERM(r(0)); + SET_I(c_p->cp); + Goto(*I); + } else if (c_p->freason == TRAP) { + SET_I(*((BeamInstr **) (UWord) ((c_p)->def_arg_reg + 3))); + r(0) = c_p->def_arg_reg[0]; + x(1) = c_p->def_arg_reg[1]; + x(2) = c_p->def_arg_reg[2]; + Dispatch(); } - } -apply_bif_or_nif_epilogue: - ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); - ERTS_HOLE_CHECK(c_p); - if (c_p->mbuf) { reg[0] = r(0); - tmp_arg1 = erts_gc_after_bif_call(c_p, tmp_arg1, reg, tmp_arg2); - r(0) = reg[0]; - } - SWAPIN; /* There might have been a garbage collection. */ - FCALLS = c_p->fcalls; - if (is_value(tmp_arg1)) { - r(0) = tmp_arg1; - CHECK_TERM(r(0)); - SET_I(c_p->cp); - Goto(*I); - } else if (c_p->freason == TRAP) { - SET_I(*((BeamInstr **) (UWord) ((c_p)->def_arg_reg + 3))); - r(0) = c_p->def_arg_reg[0]; - x(1) = c_p->def_arg_reg[1]; - x(2) = c_p->def_arg_reg[2]; - Dispatch(); + I = handle_error(c_p, c_p->cp, reg, vbf); + goto post_error_handling; } - reg[0] = r(0); - I = handle_error(c_p, c_p->cp, reg, vbf); - goto post_error_handling; } OpCase(i_get_sd): @@ -3218,11 +3411,26 @@ apply_bif_or_nif_epilogue: StoreBifResult(1, result); } - OpCase(case_end_s): - GetArg1(0, tmp_arg1); - c_p->fvalue = tmp_arg1; - c_p->freason = EXC_CASE_CLAUSE; - goto find_func_info; + { + Eterm case_end_val; + + OpCase(case_end_x): + case_end_val = xb(Arg(0)); + goto do_case_end; + + OpCase(case_end_y): + case_end_val = yb(Arg(0)); + goto do_case_end; + + OpCase(case_end_r): + case_end_val = r(0); + I--; + + do_case_end: + c_p->fvalue = case_end_val; + c_p->freason = EXC_CASE_CLAUSE; + goto find_func_info; + } OpCase(if_end): c_p->freason = EXC_IF_CLAUSE; @@ -3235,10 +3443,13 @@ apply_bif_or_nif_epilogue: } OpCase(try_case_end_s): - GetArg1(0, tmp_arg1); - c_p->fvalue = tmp_arg1; - c_p->freason = EXC_TRY_CLAUSE; - goto find_func_info; + { + Eterm try_case_end_val; + GetArg1(0, try_case_end_val); + c_p->fvalue = try_case_end_val; + c_p->freason = EXC_TRY_CLAUSE; + goto find_func_info; + } /* * Construction of binaries using new instructions. @@ -3786,19 +3997,20 @@ apply_bif_or_nif_epilogue: Eterm header; BeamInstr *next; Uint slots; + Eterm context; OpCase(i_bs_start_match2_rfIId): { - tmp_arg1 = r(0); + context = r(0); do_start_match: slots = Arg(2); - if (!is_boxed(tmp_arg1)) { + if (!is_boxed(context)) { ClauseFail(); } PreFetch(4, next); - header = *boxed_val(tmp_arg1); + header = *boxed_val(context); if (header_is_bin_matchstate(header)) { - ErlBinMatchState* ms = (ErlBinMatchState *) boxed_val(tmp_arg1); + ErlBinMatchState* ms = (ErlBinMatchState *) boxed_val(context); Uint actual_slots = HEADER_NUM_SLOTS(header); ms->save_offset[0] = ms->mb.offset; if (actual_slots < slots) { @@ -3806,8 +4018,8 @@ apply_bif_or_nif_epilogue: Uint live = Arg(1); Uint wordsneeded = ERL_BIN_MATCHSTATE_SIZE(slots); - TestHeapPreserve(wordsneeded, live, tmp_arg1); - ms = (ErlBinMatchState *) boxed_val(tmp_arg1); + TestHeapPreserve(wordsneeded, live, context); + ms = (ErlBinMatchState *) boxed_val(context); dst = (ErlBinMatchState *) HTOP; *dst = *ms; *HTOP = HEADER_BIN_MATCHSTATE(slots); @@ -3819,12 +4031,12 @@ apply_bif_or_nif_epilogue: Eterm result; Uint live = Arg(1); Uint wordsneeded = ERL_BIN_MATCHSTATE_SIZE(slots); - TestHeapPreserve(wordsneeded, live, tmp_arg1); + TestHeapPreserve(wordsneeded, live, context); HEAP_TOP(c_p) = HTOP; #ifdef DEBUG c_p->stop = E; /* Needed for checking in HeapOnlyAlloc(). */ #endif - result = erts_bs_start_match_2(c_p, tmp_arg1, slots); + result = erts_bs_start_match_2(c_p, context, slots); HTOP = HEAP_TOP(c_p); HEAP_SPACE_VERIFIED(0); if (is_non_value(result)) { @@ -3838,12 +4050,12 @@ apply_bif_or_nif_epilogue: NextPF(4, next); } OpCase(i_bs_start_match2_xfIId): { - tmp_arg1 = xb(Arg(0)); + context = xb(Arg(0)); I++; goto do_start_match; } OpCase(i_bs_start_match2_yfIId): { - tmp_arg1 = yb(Arg(0)); + context = yb(Arg(0)); I++; goto do_start_match; } @@ -3936,93 +4148,105 @@ apply_bif_or_nif_epilogue: NextPF(2, next); } + { + Eterm bs_get_integer8_context; + OpCase(i_bs_get_integer_8_rfd): { - tmp_arg1 = r(0); - goto do_bs_get_integer_8; - } + bs_get_integer8_context = r(0); + goto do_bs_get_integer_8; + } OpCase(i_bs_get_integer_8_xfd): { - tmp_arg1 = xb(Arg(0)); - I++; - } + bs_get_integer8_context = xb(Arg(0)); + I++; + } do_bs_get_integer_8: { - ErlBinMatchBuffer *_mb; - Eterm _result; - _mb = ms_matchbuffer(tmp_arg1); - if (_mb->size - _mb->offset < 8) { - ClauseFail(); - } - if (BIT_OFFSET(_mb->offset) != 0) { - _result = erts_bs_get_integer_2(c_p, 8, 0, _mb); - } else { - _result = make_small(_mb->base[BYTE_OFFSET(_mb->offset)]); - _mb->offset += 8; + ErlBinMatchBuffer *_mb; + Eterm _result; + _mb = ms_matchbuffer(bs_get_integer8_context); + if (_mb->size - _mb->offset < 8) { + ClauseFail(); + } + if (BIT_OFFSET(_mb->offset) != 0) { + _result = erts_bs_get_integer_2(c_p, 8, 0, _mb); + } else { + _result = make_small(_mb->base[BYTE_OFFSET(_mb->offset)]); + _mb->offset += 8; + } + StoreBifResult(1, _result); } - StoreBifResult(1, _result); } - OpCase(i_bs_get_integer_16_rfd): { - tmp_arg1 = r(0); + { + Eterm bs_get_integer_16_context; + + OpCase(i_bs_get_integer_16_rfd): + bs_get_integer_16_context = r(0); goto do_bs_get_integer_16; - } - OpCase(i_bs_get_integer_16_xfd): { - tmp_arg1 = xb(Arg(0)); + OpCase(i_bs_get_integer_16_xfd): + bs_get_integer_16_context = xb(Arg(0)); I++; - } - do_bs_get_integer_16: { - ErlBinMatchBuffer *_mb; - Eterm _result; - _mb = ms_matchbuffer(tmp_arg1); - if (_mb->size - _mb->offset < 16) { - ClauseFail(); - } - if (BIT_OFFSET(_mb->offset) != 0) { - _result = erts_bs_get_integer_2(c_p, 16, 0, _mb); - } else { - _result = make_small(get_int16(_mb->base+BYTE_OFFSET(_mb->offset))); - _mb->offset += 16; + do_bs_get_integer_16: + { + ErlBinMatchBuffer *_mb; + Eterm _result; + _mb = ms_matchbuffer(bs_get_integer_16_context); + if (_mb->size - _mb->offset < 16) { + ClauseFail(); + } + if (BIT_OFFSET(_mb->offset) != 0) { + _result = erts_bs_get_integer_2(c_p, 16, 0, _mb); + } else { + _result = make_small(get_int16(_mb->base+BYTE_OFFSET(_mb->offset))); + _mb->offset += 16; + } + StoreBifResult(1, _result); } - StoreBifResult(1, _result); } - OpCase(i_bs_get_integer_32_rfId): { - tmp_arg1 = r(0); + { + Eterm bs_get_integer_32_context; + + OpCase(i_bs_get_integer_32_rfId): + bs_get_integer_32_context = r(0); goto do_bs_get_integer_32; - } + - OpCase(i_bs_get_integer_32_xfId): { - tmp_arg1 = xb(Arg(0)); + OpCase(i_bs_get_integer_32_xfId): + bs_get_integer_32_context = xb(Arg(0)); I++; - } - do_bs_get_integer_32: { - ErlBinMatchBuffer *_mb; - Uint32 _integer; - Eterm _result; - _mb = ms_matchbuffer(tmp_arg1); - if (_mb->size - _mb->offset < 32) { ClauseFail(); } - if (BIT_OFFSET(_mb->offset) != 0) { - _integer = erts_bs_get_unaligned_uint32(_mb); - } else { - _integer = get_int32(_mb->base + _mb->offset/8); - } - _mb->offset += 32; + + do_bs_get_integer_32: + { + ErlBinMatchBuffer *_mb; + Uint32 _integer; + Eterm _result; + _mb = ms_matchbuffer(bs_get_integer_32_context); + if (_mb->size - _mb->offset < 32) { ClauseFail(); } + if (BIT_OFFSET(_mb->offset) != 0) { + _integer = erts_bs_get_unaligned_uint32(_mb); + } else { + _integer = get_int32(_mb->base + _mb->offset/8); + } + _mb->offset += 32; #if !defined(ARCH_64) || HALFWORD_HEAP - if (IS_USMALL(0, _integer)) { + if (IS_USMALL(0, _integer)) { #endif - _result = make_small(_integer); + _result = make_small(_integer); #if !defined(ARCH_64) || HALFWORD_HEAP - } else { - TestHeap(BIG_UINT_HEAP_SIZE, Arg(1)); - _result = uint_to_big((Uint) _integer, HTOP); - HTOP += BIG_UINT_HEAP_SIZE; - HEAP_SPACE_VERIFIED(0); - } + } else { + TestHeap(BIG_UINT_HEAP_SIZE, Arg(1)); + _result = uint_to_big((Uint) _integer, HTOP); + HTOP += BIG_UINT_HEAP_SIZE; + HEAP_SPACE_VERIFIED(0); + } #endif - StoreBifResult(2, _result); + StoreBifResult(2, _result); + } } /* Operands: Size Live Fail Flags Dst */ @@ -4120,54 +4344,64 @@ apply_bif_or_nif_epilogue: StoreBifResult(3, result); } - /* Operands: MatchContext Fail Dst */ + { + Eterm get_utf8_context; + + /* Operands: MatchContext Fail Dst */ OpCase(i_bs_get_utf8_rfd): { - tmp_arg1 = r(0); - goto do_bs_get_utf8; - } + get_utf8_context = r(0); + goto do_bs_get_utf8; + } OpCase(i_bs_get_utf8_xfd): { - tmp_arg1 = xb(Arg(0)); - I++; - } + get_utf8_context = xb(Arg(0)); + I++; + } - /* - * tmp_arg1 = match_context - * Operands: Fail Dst - */ + /* + * get_utf8_context = match_context + * Operands: Fail Dst + */ - do_bs_get_utf8: { - Eterm result = erts_bs_get_utf8(ms_matchbuffer(tmp_arg1)); - if (is_non_value(result)) { - ClauseFail(); + do_bs_get_utf8: { + Eterm result = erts_bs_get_utf8(ms_matchbuffer(get_utf8_context)); + if (is_non_value(result)) { + ClauseFail(); + } + StoreBifResult(1, result); } - StoreBifResult(1, result); } - /* Operands: MatchContext Fail Flags Dst */ + { + Eterm get_utf16_context; + + /* Operands: MatchContext Fail Flags Dst */ OpCase(i_bs_get_utf16_rfId): { - tmp_arg1 = r(0); - goto do_bs_get_utf16; - } + get_utf16_context = r(0); + goto do_bs_get_utf16; + } OpCase(i_bs_get_utf16_xfId): { - tmp_arg1 = xb(Arg(0)); - I++; - } + get_utf16_context = xb(Arg(0)); + I++; + } - /* - * tmp_arg1 = match_context - * Operands: Fail Flags Dst - */ - do_bs_get_utf16: { - Eterm result = erts_bs_get_utf16(ms_matchbuffer(tmp_arg1), Arg(1)); - if (is_non_value(result)) { - ClauseFail(); + /* + * get_utf16_context = match_context + * Operands: Fail Flags Dst + */ + do_bs_get_utf16: { + Eterm result = erts_bs_get_utf16(ms_matchbuffer(get_utf16_context), + Arg(1)); + if (is_non_value(result)) { + ClauseFail(); + } + StoreBifResult(2, result); } - StoreBifResult(2, result); } { + Eterm context_to_binary_context; ErlBinMatchBuffer* mb; ErlSubBin* sb; Uint size; @@ -4176,27 +4410,29 @@ apply_bif_or_nif_epilogue: Uint hole_size; OpCase(bs_context_to_binary_r): { - tmp_arg1 = x0; + context_to_binary_context = x0; I -= 2; goto do_context_to_binary; } /* Unfortunately, inlining can generate this instruction. */ OpCase(bs_context_to_binary_y): { - tmp_arg1 = yb(Arg(0)); + context_to_binary_context = yb(Arg(0)); goto do_context_to_binary0; } OpCase(bs_context_to_binary_x): { - tmp_arg1 = xb(Arg(0)); + context_to_binary_context = xb(Arg(0)); do_context_to_binary0: I--; } do_context_to_binary: - if (is_boxed(tmp_arg1) && header_is_bin_matchstate(*boxed_val(tmp_arg1))) { - ErlBinMatchState* ms = (ErlBinMatchState *) boxed_val(tmp_arg1); + if (is_boxed(context_to_binary_context) && + header_is_bin_matchstate(*boxed_val(context_to_binary_context))) { + ErlBinMatchState* ms; + ms = (ErlBinMatchState *) boxed_val(context_to_binary_context); mb = &ms->mb; offs = ms->save_offset[0]; size = mb->size - offs; @@ -4205,17 +4441,17 @@ apply_bif_or_nif_epilogue: Next(2); OpCase(i_bs_get_binary_all_reuse_rfI): { - tmp_arg1 = x0; + context_to_binary_context = x0; goto do_bs_get_binary_all_reuse; } OpCase(i_bs_get_binary_all_reuse_xfI): { - tmp_arg1 = xb(Arg(0)); + context_to_binary_context = xb(Arg(0)); I++; } do_bs_get_binary_all_reuse: - mb = ms_matchbuffer(tmp_arg1); + mb = ms_matchbuffer(context_to_binary_context); size = mb->size - mb->offset; if (size % Arg(1) != 0) { ClauseFail(); @@ -4224,7 +4460,7 @@ apply_bif_or_nif_epilogue: do_bs_get_binary_all_reuse_common: orig = mb->orig; - sb = (ErlSubBin *) boxed_val(tmp_arg1); + sb = (ErlSubBin *) boxed_val(context_to_binary_context); hole_size = 1 + header_arity(sb->thing_word) - ERL_SUB_BIN_SIZE; sb->thing_word = HEADER_SUB_BIN; sb->size = BYTE_OFFSET(size); @@ -4240,12 +4476,14 @@ apply_bif_or_nif_epilogue: } { + Eterm match_string_context; + OpCase(i_bs_match_string_rfII): { - tmp_arg1 = r(0); + match_string_context = r(0); goto do_bs_match_string; } OpCase(i_bs_match_string_xfII): { - tmp_arg1 = xb(Arg(0)); + match_string_context = xb(Arg(0)); I++; } @@ -4260,7 +4498,7 @@ apply_bif_or_nif_epilogue: PreFetch(3, next); bits = Arg(1); bytes = (byte *) Arg(2); - mb = ms_matchbuffer(tmp_arg1); + mb = ms_matchbuffer(match_string_context); if (mb->size - mb->offset < bits) { ClauseFail(); } @@ -4723,7 +4961,7 @@ apply_bif_or_nif_epilogue: NextPF(2, next); } - OpCase(fmove_new_ld): { + OpCase(fmove_ld): { Eterm fr = Arg(0); Eterm dest = make_float(HTOP); @@ -4753,11 +4991,6 @@ apply_bif_or_nif_epilogue: NextPF(2, next); } - /* - * Old allocating fmove. - */ - - #ifdef NO_FPE_SIGNALS OpCase(fclearerror): OpCase(i_fcheckerror): @@ -4969,12 +5202,11 @@ apply_bif_or_nif_epilogue: OpCase(i_debug_breakpoint): { SWAPOUT; reg[0] = r(0); - tmp_arg1 = call_breakpoint_handler(c_p, I-3, reg); + I = call_error_handler(c_p, I-3, reg, am_breakpoint); r(0) = reg[0]; SWAPIN; - if (tmp_arg1) { - SET_I(c_p->i); - Dispatch(); + if (I) { + Goto(*I); } goto no_error_handler; } @@ -5724,8 +5956,8 @@ build_stacktrace(Process* c_p, Eterm exc) { } -static Eterm -call_error_handler(Process* p, BeamInstr* fi, Eterm* reg) +static BeamInstr* +call_error_handler(Process* p, BeamInstr* fi, Eterm* reg, Eterm func) { Eterm* hp; Export* ep; @@ -5737,14 +5969,12 @@ call_error_handler(Process* p, BeamInstr* fi, Eterm* reg) /* * Search for the error_handler module. */ - ep = erts_find_function(erts_proc_get_error_handler(p), - am_undefined_function, 3); + ep = erts_find_function(erts_proc_get_error_handler(p), func, 3); if (ep == NULL) { /* No error handler */ p->current = fi; p->freason = EXC_UNDEF; return 0; } - p->i = ep->address; /* * Create a list with all arguments in the x registers. @@ -5764,63 +5994,14 @@ call_error_handler(Process* p, BeamInstr* fi, Eterm* reg) } /* - * Set up registers for call to error_handler:undefined_function/3. + * Set up registers for call to error_handler:<func>/3. */ reg[0] = fi[0]; reg[1] = fi[1]; reg[2] = args; - return 1; -} - -static Eterm -call_breakpoint_handler(Process* p, BeamInstr* fi, Eterm* reg) -{ - Eterm* hp; - Export* ep; - int arity; - Eterm args; - Uint sz; - int i; - - /* - * Search for error handler module. - */ - ep = erts_find_function(erts_proc_get_error_handler(p), - am_breakpoint, 3); - if (ep == NULL) { /* No error handler */ - p->current = fi; - p->freason = EXC_UNDEF; - return 0; - } - p->i = ep->address; - - /* - * Create a list with all arguments in the x registers. - */ - - arity = fi[2]; - sz = 2 * arity; - if (HeapWordsLeft(p) < sz) { - erts_garbage_collect(p, sz, reg, arity); - } - hp = HEAP_TOP(p); - HEAP_TOP(p) += sz; - args = NIL; - for (i = arity-1; i >= 0; i--) { - args = CONS(hp, reg[i], args); - hp += 2; - } - - /* - * Set up registers for call to error_handler:breakpoint/3. - */ - reg[0] = fi[0]; - reg[1] = fi[1]; - reg[2] = args; - return 1; + return ep->address; } - static Export* apply_setup_error_handler(Process* p, Eterm module, Eterm function, Uint arity, Eterm* reg) diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index df5602b040..4e0d19dafa 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2010. 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 @@ -89,13 +89,12 @@ typedef struct { } Label; /* - * Type for a operand for a generic instruction. + * Type for an operand for a generic instruction. */ typedef struct { unsigned type; /* Type of operand. */ - BeamInstr val; /* Value of operand. */ - Uint bigarity; /* Arity for bignumbers (only). */ + BeamInstr val; /* Value of operand. */ } GenOpArg; /* @@ -326,11 +325,6 @@ typedef struct { Literal* literals; /* Array of literals. */ LiteralPatch* literal_patches; /* Operands that need to be patched. */ Uint total_literal_size; /* Total heap size for all literals. */ - - /* - * Floating point. - */ - int new_float_instructions; /* New allocation scheme for floating point. */ } LoaderState; typedef struct { @@ -476,12 +470,14 @@ static int read_code_header(LoaderState* stp); static int load_code(LoaderState* stp); static GenOp* gen_element(LoaderState* stp, GenOpArg Fail, GenOpArg Index, GenOpArg Tuple, GenOpArg Dst); -static GenOp* gen_split_values(LoaderState* stp, GenOpArg S, GenOpArg Fail, +static GenOp* gen_split_values(LoaderState* stp, GenOpArg S, + GenOpArg TypeFail, GenOpArg Fail, GenOpArg Size, GenOpArg* Rest); static GenOp* gen_select_val(LoaderState* stp, GenOpArg S, GenOpArg Fail, GenOpArg Size, GenOpArg* Rest); -static GenOp* gen_select_big(LoaderState* stp, GenOpArg S, GenOpArg Fail, - GenOpArg Size, GenOpArg* Rest); +static GenOp* gen_select_literals(LoaderState* stp, GenOpArg S, + GenOpArg Fail, GenOpArg Size, + GenOpArg* Rest); static GenOp* const_select_val(LoaderState* stp, GenOpArg S, GenOpArg Fail, GenOpArg Size, GenOpArg* Rest); static GenOp* gen_func_info(LoaderState* stp, GenOpArg mod, GenOpArg Func, @@ -818,7 +814,6 @@ init_state(LoaderState* stp) stp->total_literal_size = 0; stp->literal_patches = 0; stp->string_patches = 0; - stp->new_float_instructions = 0; stp->may_load_nif = 0; stp->on_load = 0; } @@ -1618,7 +1613,6 @@ load_code(LoaderState* stp) BeamInstr val; BeamInstr words = 0; - stp->new_float_instructions = 1; GetTagAndValue(stp, tag, n); VerifyTag(stp, tag, TAG_u); while (n-- > 0) { @@ -1772,7 +1766,7 @@ load_code(LoaderState* stp) } stp->specific_op = specific; - CodeNeed(opc[stp->specific_op].sz+2); /* Extra margin for packing */ + CodeNeed(opc[stp->specific_op].sz+16); /* Extra margin for packing */ code[ci++] = BeamOpCode(stp->specific_op); } @@ -1936,7 +1930,8 @@ load_code(LoaderState* stp) } code[ci++] = (BeamInstr) stp->import[i].bf; break; - case 'P': /* Byte offset into tuple */ + case 'P': /* Byte offset into tuple or stack */ + case 'Q': /* Like 'P', but packable */ VerifyTag(stp, tag, TAG_u); tmp = tmp_op->a[arg].val; code[ci++] = (BeamInstr) ((tmp_op->a[arg].val+1) * sizeof(Eterm)); @@ -1957,84 +1952,6 @@ load_code(LoaderState* stp) } /* - * Load any list arguments using the primitive tags. - */ - - for ( ; arg < tmp_op->arity; arg++) { - switch (tmp_op->a[arg].type) { - case TAG_i: - CodeNeed(1); - code[ci++] = make_small(tmp_op->a[arg].val); - break; - case TAG_u: - case TAG_a: - case TAG_v: - CodeNeed(1); - code[ci++] = tmp_op->a[arg].val; - break; - case TAG_f: - CodeNeed(1); - code[ci] = stp->labels[tmp_op->a[arg].val].patches; - stp->labels[tmp_op->a[arg].val].patches = ci; - ci++; - break; - case TAG_q: - { - Eterm lit; - - lit = stp->literals[tmp_op->a[arg].val].term; - if (is_big(lit)) { - Eterm* bigp; - Eterm *tmp; - Uint size; - Uint term_size; - - bigp = big_val(lit); - term_size = bignum_header_arity(*bigp); - size = TermWords(term_size + 1); - CodeNeed(size); - tmp = (Eterm *) (code + ci); - *tmp++ = *bigp++; - while (term_size-- > 0) { - *tmp++ = *bigp++; - } - ci +=size; - } else if (is_float(lit)) { -#if defined(ARCH_64) && !HALFWORD_HEAP - CodeNeed(1); - code[ci++] = float_val(stp->literals[tmp_op->a[arg].val].term)[1]; -#elif HALFWORD_HEAP - Eterm* fptr; - Uint size; - Eterm *tmp; - - fptr = float_val(stp->literals[tmp_op->a[arg].val].term)+1; - size = TermWords(2); - CodeNeed(size); - tmp = (Eterm *) (code + ci); - *tmp++ = *fptr++; - *tmp = *fptr; - ci += size; -#else - Eterm* fptr; - - fptr = float_val(stp->literals[tmp_op->a[arg].val].term)+1; - CodeNeed(2); - code[ci++] = *fptr++; - code[ci++] = *fptr; -#endif - } else { - LoadError0(stp, "literal is neither float nor big"); - } - } - break; - default: - LoadError1(stp, "unsupported primitive type '%c'", - tag_to_letter[tmp_op->a[arg].type]); - } - } - - /* * The packing engine. */ if (opc[stp->specific_op].pack[0]) { @@ -2057,6 +1974,11 @@ load_code(LoaderState* stp) case '6': /* Shift 16 steps */ packed = (packed << BEAM_LOOSE_SHIFT) | code[--ci]; break; +#ifdef ARCH_64 + case 'w': /* Shift 32 steps */ + packed = (packed << BEAM_WIDE_SHIFT) | code[--ci]; + break; +#endif case 'p': /* Put instruction (from stack). */ code[ci++] = *--sp; break; @@ -2072,6 +1994,58 @@ load_code(LoaderState* stp) } /* + * Load any list arguments using the primitive tags. + */ + + for ( ; arg < tmp_op->arity; arg++) { + switch (tmp_op->a[arg].type) { + case TAG_i: + CodeNeed(1); + code[ci++] = make_small(tmp_op->a[arg].val); + break; + case TAG_u: + case TAG_a: + case TAG_v: + CodeNeed(1); + code[ci++] = tmp_op->a[arg].val; + break; + case TAG_f: + CodeNeed(1); + code[ci] = stp->labels[tmp_op->a[arg].val].patches; + stp->labels[tmp_op->a[arg].val].patches = ci; + ci++; + break; + case TAG_r: + CodeNeed(1); + code[ci++] = (R_REG_DEF << _TAG_PRIMARY_SIZE) | + TAG_PRIMARY_HEADER; + break; + case TAG_x: + CodeNeed(1); + code[ci++] = (tmp_op->a[arg].val << _TAG_IMMED1_SIZE) | + (X_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER; + break; + case TAG_y: + CodeNeed(1); + code[ci++] = (tmp_op->a[arg].val << _TAG_IMMED1_SIZE) | + (Y_REG_DEF << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER; + break; + case TAG_n: + CodeNeed(1); + code[ci++] = NIL; + break; + case TAG_q: + CodeNeed(1); + new_literal_patch(stp, ci); + code[ci++] = tmp_op->a[arg].val; + break; + default: + LoadError1(stp, "unsupported primitive type '%c'", + tag_to_letter[tmp_op->a[arg].type]); + } + } + + /* * Handle a few special cases. */ switch (stp->specific_op) { @@ -2239,11 +2213,12 @@ use_jump_tab(LoaderState* stp, GenOpArg Size, GenOpArg* Rest) } /* - * Predicate to test whether all values in a table are big numbers. + * Predicate to test whether all values in a table are either + * floats or bignums. */ static int -all_values_are_big(LoaderState* stp, GenOpArg Size, GenOpArg* Rest) +floats_or_bignums(LoaderState* stp, GenOpArg Size, GenOpArg* Rest) { int i; @@ -2255,9 +2230,6 @@ all_values_are_big(LoaderState* stp, GenOpArg Size, GenOpArg* Rest) if (Rest[i].type != TAG_q) { return 0; } - if (is_not_big(stp->literals[Rest[i].val].term)) { - return 0; - } if (Rest[i+1].type != TAG_f) { return 0; } @@ -2317,6 +2289,14 @@ mixed_types(LoaderState* stp, GenOpArg Size, GenOpArg* Rest) return 0; } +static int +same_label(LoaderState* stp, GenOpArg Target, GenOpArg Label) +{ + return Target.type = TAG_f && Label.type == TAG_u && + Target.val == Label.val; +} + + /* * Generate an instruction for element/2. */ @@ -2328,23 +2308,23 @@ gen_element(LoaderState* stp, GenOpArg Fail, GenOpArg Index, GenOp* op; NEW_GENOP(stp, op); - op->op = genop_i_element_4; op->arity = 4; - op->a[0] = Fail; - op->a[1] = Index; - op->a[2] = Tuple; - op->a[3] = Dst; op->next = NULL; - /* - * If safe, generate a faster instruction. - */ - if (Index.type == TAG_i && Index.val > 0 && (Tuple.type == TAG_r || Tuple.type == TAG_x || Tuple.type == TAG_y)) { op->op = genop_i_fast_element_4; - op->a[1].type = TAG_u; - op->a[1].val = Index.val; + op->a[0] = Tuple; + op->a[1] = Fail; + op->a[2].type = TAG_u; + op->a[2].val = Index.val; + op->a[3] = Dst; + } else { + op->op = genop_i_element_4; + op->a[0] = Tuple; + op->a[1] = Fail; + op->a[2] = Index; + op->a[3] = Dst; } return op; @@ -2595,8 +2575,6 @@ binary_too_big_bits(LoaderState* stp, GenOpArg Size) return Size.type == TAG_u && (((Size.val+7)/8) >> (8*sizeof(Uint)-3) != 0); } -#define new_float_allocation(Stp) ((Stp)->new_float_instructions) - static GenOp* gen_put_binary(LoaderState* stp, GenOpArg Fail,GenOpArg Size, GenOpArg Unit, GenOpArg Flags, GenOpArg Src) @@ -2809,6 +2787,52 @@ gen_skip_bits2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms, return op; } +static GenOp* +gen_increment(LoaderState* stp, GenOpArg Reg, GenOpArg Integer, + GenOpArg Live, GenOpArg Dst) +{ + GenOp* op; + + NEW_GENOP(stp, op); + op->op = genop_i_increment_4; + op->arity = 4; + op->next = NULL; + op->a[0] = Reg; + op->a[1].type = TAG_u; + op->a[1].val = Integer.val; + op->a[2] = Live; + op->a[3] = Dst; + return op; +} + +static GenOp* +gen_increment_from_minus(LoaderState* stp, GenOpArg Reg, GenOpArg Integer, + GenOpArg Live, GenOpArg Dst) +{ + GenOp* op; + + NEW_GENOP(stp, op); + op->op = genop_i_increment_4; + op->arity = 4; + op->next = NULL; + op->a[0] = Reg; + op->a[1].type = TAG_u; + op->a[1].val = -Integer.val; + op->a[2] = Live; + op->a[3] = Dst; + return op; +} + +/* + * Test whether the negation of the given number is small. + */ +static int +negation_is_small(LoaderState* stp, GenOpArg Int) +{ + return Int.type == TAG_i && IS_SSMALL(-Int.val); +} + + static int smp(LoaderState* stp) { @@ -3000,6 +3024,21 @@ gen_select_tuple_arity(LoaderState* stp, GenOpArg S, GenOpArg Fail, ASSERT(op->a[i].val < op->a[i+2].val); } #endif + + /* + * Use a special-cased instruction if there are only two values. + */ + if (size == 2) { + op->op = genop_i_select_tuple_arity2_6; + op->arity--; + op->a[2].type = TAG_u; + op->a[2].val = arityval(op->a[3].val); + op->a[3] = op->a[4]; + op->a[4].type = TAG_u; + op->a[4].val = arityval(op->a[5].val); + op->a[5] = op->a[6]; + } + return op; } @@ -3009,18 +3048,24 @@ gen_select_tuple_arity(LoaderState* stp, GenOpArg S, GenOpArg Fail, */ static GenOp* -gen_split_values(LoaderState* stp, GenOpArg S, GenOpArg Fail, - GenOpArg Size, GenOpArg* Rest) +gen_split_values(LoaderState* stp, GenOpArg S, GenOpArg TypeFail, + GenOpArg Fail, GenOpArg Size, GenOpArg* Rest) { GenOp* op1; GenOp* op2; GenOp* label; - Uint type; + GenOp* is_integer; int i; ASSERT(Size.val >= 2 && Size.val % 2 == 0); + NEW_GENOP(stp, is_integer); + is_integer->op = genop_is_integer_2; + is_integer->arity = 2; + is_integer->a[0] = TypeFail; + is_integer->a[1] = S; + NEW_GENOP(stp, label); label->op = genop_label_1; label->arity = 1; @@ -3046,15 +3091,13 @@ gen_split_values(LoaderState* stp, GenOpArg S, GenOpArg Fail, op2->a[2].type = TAG_u; op2->a[2].val = 0; - op1->next = label; - label->next = op2; - op2->next = NULL; - - type = Rest[0].type; + /* + * Split the list. + */ ASSERT(Size.type == TAG_u); for (i = 0; i < Size.val; i += 2) { - GenOp* op = (Rest[i].type == type) ? op1 : op2; + GenOp* op = (Rest[i].type == TAG_q) ? op2 : op1; int dst = 3 + op->a[2].val; ASSERT(Rest[i+1].type == TAG_f); @@ -3063,13 +3106,36 @@ gen_split_values(LoaderState* stp, GenOpArg S, GenOpArg Fail, op->arity += 2; op->a[2].val += 2; } + ASSERT(op1->a[2].val > 0); + ASSERT(op2->a[2].val > 0); /* - * None of the instructions should have zero elements in the list. + * Order the instruction sequence appropriately. */ - ASSERT(op1->a[2].val > 0); - ASSERT(op2->a[2].val > 0); + if (TypeFail.val == Fail.val) { + /* + * select_val L1 S ... (small numbers) + * label L1 + * is_integer Fail S + * select_val Fail S ... (bignums) + */ + op1->next = label; + label->next = is_integer; + is_integer->next = op2; + } else { + /* + * is_integer TypeFail S + * select_val L1 S ... (small numbers) + * label L1 + * select_val Fail S ... (bignums) + */ + is_integer->next = op1; + op1->next = label; + label->next = op2; + op1 = is_integer; + } + op2->next = NULL; return op1; } @@ -3091,6 +3157,29 @@ gen_jump_tab(LoaderState* stp, GenOpArg S, GenOpArg Fail, GenOpArg Size, GenOpAr ASSERT(Size.val >= 2 && Size.val % 2 == 0); /* + * If there is only one choice, don't generate a jump table. + */ + if (Size.val == 2) { + GenOp* jump; + + NEW_GENOP(stp, op); + op->arity = 3; + op->op = genop_is_ne_exact_3; + op->a[0] = Rest[1]; + op->a[1] = S; + op->a[2] = Rest[0]; + + NEW_GENOP(stp, jump); + jump->next = NULL; + jump->arity = 1; + jump->op = genop_jump_1; + jump->a[0] = Fail; + + op->next = jump; + return op; + } + + /* * Calculate the minimum and maximum values and size of jump table. */ @@ -3162,8 +3251,9 @@ genopargcompare(GenOpArg* a, GenOpArg* b) } /* - * Generate a select_val instruction. We know that a jump table is not suitable, - * and that all values are of the same type (integer, atoms, floats; never bignums). + * Generate a select_val instruction. We know that a jump table + * is not suitable, and that all values are of the same type + * (integer or atoms). */ static GenOp* @@ -3177,12 +3267,7 @@ gen_select_val(LoaderState* stp, GenOpArg S, GenOpArg Fail, NEW_GENOP(stp, op); op->next = NULL; - if (Rest[0].type != TAG_q) { - op->op = genop_i_select_val_3; - } else { - ASSERT(is_float(stp->literals[Rest[0].val].term)); - op->op = genop_i_select_float_3; - } + op->op = genop_i_select_val_3; GENOP_ARITY(op, arity); op->a[0] = S; op->a[1] = Fail; @@ -3204,19 +3289,19 @@ gen_select_val(LoaderState* stp, GenOpArg S, GenOpArg Fail, } #endif - return op; -} - -/* - * Compare function for qsort(). - */ + /* + * Use a special-cased instruction if there are only two values. + */ + if (size == 2) { + op->op = genop_i_select_val2_6; + op->arity--; + op->a[2] = op->a[3]; + op->a[3] = op->a[4]; + op->a[4] = op->a[5]; + op->a[5] = op->a[6]; + } -static int -genbigcompare(GenOpArg* a, GenOpArg* b) -{ - int val = (int)(b->bigarity - a->bigarity); - - return val != 0 ? val : ((int) (a->val - b->val)); + return op; } /* @@ -3224,37 +3309,35 @@ genbigcompare(GenOpArg* a, GenOpArg* b) */ static GenOp* -gen_select_big(LoaderState* stp, GenOpArg S, GenOpArg Fail, +gen_select_literals(LoaderState* stp, GenOpArg S, GenOpArg Fail, GenOpArg Size, GenOpArg* Rest) { GenOp* op; - int arity = Size.val + 2 + 1; - int size = Size.val / 2; + GenOp* jump; + GenOp** prev_next = &op; + int i; - NEW_GENOP(stp, op); - op->next = NULL; - op->op = genop_i_select_big_2; - GENOP_ARITY(op, arity); - op->a[0] = S; - op->a[1] = Fail; for (i = 0; i < Size.val; i += 2) { + GenOp* op; ASSERT(Rest[i].type == TAG_q); - op->a[i+2] = Rest[i]; - op->a[i+2].bigarity = *big_val(stp->literals[op->a[i+2].val].term); - op->a[i+3] = Rest[i+1]; - } - ASSERT(i+2 == arity-1); - op->a[arity-1].type = TAG_u; - op->a[arity-1].val = 0; - - /* - * Sort the values in descending arity order. - */ - - qsort(op->a+2, size, 2*sizeof(GenOpArg), - (int (*)(const void *, const void *)) genbigcompare); + NEW_GENOP(stp, op); + op->op = genop_is_ne_exact_3; + op->arity = 3; + op->a[0] = Rest[i+1]; + op->a[1] = S; + op->a[2] = Rest[i]; + *prev_next = op; + prev_next = &op->next; + } + + NEW_GENOP(stp, jump); + jump->next = NULL; + jump->op = genop_jump_1; + jump->arity = 1; + jump->a[0] = Fail; + *prev_next = jump; return op; } @@ -3272,7 +3355,6 @@ const_select_val(LoaderState* stp, GenOpArg S, GenOpArg Fail, int i; ASSERT(Size.type == TAG_u); - ASSERT(S.type == TAG_q); NEW_GENOP(stp, op); op->next = NULL; @@ -3283,18 +3365,32 @@ const_select_val(LoaderState* stp, GenOpArg S, GenOpArg Fail, * Search for a literal matching the controlling expression. */ - if (S.type == TAG_q) { - Eterm expr = stp->literals[S.val].term; - for (i = 0; i < Size.val; i += 2) { - if (Rest[i].type == TAG_q) { - Eterm term = stp->literals[Rest[i].val].term; - if (eq(term, expr)) { - ASSERT(Rest[i+1].type == TAG_f); - op->a[0] = Rest[i+1]; - return op; + switch (S.type) { + case TAG_q: + { + Eterm expr = stp->literals[S.val].term; + for (i = 0; i < Size.val; i += 2) { + if (Rest[i].type == TAG_q) { + Eterm term = stp->literals[Rest[i].val].term; + if (eq(term, expr)) { + ASSERT(Rest[i+1].type == TAG_f); + op->a[0] = Rest[i+1]; + return op; + } } } } + break; + case TAG_i: + case TAG_a: + for (i = 0; i < Size.val; i += 2) { + if (Rest[i].val == S.val && Rest[i].type == S.type) { + ASSERT(Rest[i+1].type == TAG_f); + op->a[0] = Rest[i+1]; + return op; + } + } + break; } /* @@ -3477,6 +3573,56 @@ gen_guard_bif3(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif, return op; } +static GenOp* +tuple_append_put5(LoaderState* stp, GenOpArg Arity, GenOpArg Dst, + GenOpArg* Puts, GenOpArg S1, GenOpArg S2, GenOpArg S3, + GenOpArg S4, GenOpArg S5) +{ + GenOp* op; + int arity = Arity.val; /* Arity of tuple, not the instruction */ + int i; + + NEW_GENOP(stp, op); + op->next = NULL; + GENOP_ARITY(op, arity+2+5); + op->op = genop_i_put_tuple_2; + op->a[0] = Dst; + op->a[1].type = TAG_u; + op->a[1].val = arity + 5; + for (i = 0; i < arity; i++) { + op->a[i+2] = Puts[i]; + } + op->a[arity+2] = S1; + op->a[arity+3] = S2; + op->a[arity+4] = S3; + op->a[arity+5] = S4; + op->a[arity+6] = S5; + return op; +} + +static GenOp* +tuple_append_put(LoaderState* stp, GenOpArg Arity, GenOpArg Dst, + GenOpArg* Puts, GenOpArg S) +{ + GenOp* op; + int arity = Arity.val; /* Arity of tuple, not the instruction */ + int i; + + NEW_GENOP(stp, op); + op->next = NULL; + GENOP_ARITY(op, arity+2+1); + op->op = genop_i_put_tuple_2; + op->a[0] = Dst; + op->a[1].type = TAG_u; + op->a[1].val = arity + 1; + for (i = 0; i < arity; i++) { + op->a[i+2] = Puts[i]; + } + op->a[arity+2] = S; + return op; +} + + /* * Freeze the code in memory, move the string table into place, @@ -3876,11 +4022,23 @@ transform_engine(LoaderState* st) if (i == 0) goto restart; break; +#if defined(TOP_is_eq) case TOP_is_eq: ASSERT(ap < instr->arity); if (*pc++ != instr->a[ap].val) goto restart; break; +#endif + case TOP_is_type_eq: + mask = *pc++; + + ASSERT(ap < instr->arity); + ASSERT(instr->a[ap].type < BEAM_NUM_TAGS); + if (((1 << instr->a[ap].type) & mask) == 0) + goto restart; + if (*pc++ != instr->a[ap].val) + goto restart; + break; case TOP_is_same_var: ASSERT(ap < instr->arity); i = *pc++; @@ -4001,14 +4159,17 @@ transform_engine(LoaderState* st) case TOP_rest_args: { int n = *pc++; + int formal_arity = gen_opc[instr->op].arity; + int num_vars = n + (instr->arity - formal_arity); + int j = formal_arity; + var = erts_alloc(ERTS_ALC_T_LOADER_TMP, - instr->arity * sizeof(GenOpArg)); + num_vars * sizeof(GenOpArg)); for (i = 0; i < n; i++) { var[i] = def_vars[i]; } - while (i < instr->arity) { - var[i] = instr->a[i]; - i++; + while (i < num_vars) { + var[i++] = instr->a[j++]; } } break; @@ -5315,6 +5476,9 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) if (state.lambdas != state.def_lambdas) { erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.lambdas); } + erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.labels); + erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.atom); + erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.export); if (bin != NULL) { driver_free_binary(bin); } @@ -5326,9 +5490,18 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) if (code != NULL) { erts_free(ERTS_ALC_T_CODE, code); } + if (state.labels != NULL) { + erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.labels); + } if (state.lambdas != state.def_lambdas) { erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.lambdas); } + if (state.atom != NULL) { + erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.atom); + } + if (state.export != NULL) { + erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.export); + } if (bin != NULL) { driver_free_binary(bin); } diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 6e9755ad48..bb237e378a 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -813,7 +813,7 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1) so.min_heap_size = H_MIN_SIZE; so.min_vheap_size = BIN_VH_MIN_SIZE; so.priority = PRIORITY_NORMAL; - so.max_gen_gcs = (Uint16) erts_smp_atomic_read(&erts_max_gen_gcs); + so.max_gen_gcs = (Uint16) erts_smp_atomic32_read(&erts_max_gen_gcs); so.scheduler = 0; /* @@ -1351,9 +1351,10 @@ BIF_RETTYPE exit_2(BIF_ALIST_2) #ifdef ERTS_SMP if (rp == BIF_P) rp_locks &= ~ERTS_PROC_LOCK_MAIN; - else + if (rp_locks) + erts_smp_proc_unlock(rp, rp_locks); + if (rp != BIF_P) erts_smp_proc_dec_refc(rp); - erts_smp_proc_unlock(rp, rp_locks); #endif /* * We may have exited ourselves and may have to take action. @@ -3269,12 +3270,13 @@ BIF_RETTYPE ports_0(BIF_ALIST_0) erts_smp_mtx_lock(&ports_snapshot_mtx); /* One snapshot at a time */ - erts_smp_atomic_set(&erts_dead_ports_ptr, (long) (port_buf + erts_max_ports)); + erts_smp_atomic_set(&erts_dead_ports_ptr, + (erts_aint_t) (port_buf + erts_max_ports)); next_ss = erts_smp_atomic_inctest(&erts_ports_snapshot); if (erts_smp_atomic_read(&erts_ports_alive) > 0) { - long i; + erts_aint_t i; for (i = erts_max_ports-1; i >= 0; i--) { Port* prt = &erts_port[i]; erts_smp_port_state_lock(prt); @@ -3289,7 +3291,7 @@ BIF_RETTYPE ports_0(BIF_ALIST_0) } dead_ports = (Eterm*)erts_smp_atomic_xchg(&erts_dead_ports_ptr, - (long)NULL); + (erts_aint_t) NULL); erts_smp_mtx_unlock(&ports_snapshot_mtx); ASSERT(pp <= dead_ports); @@ -3300,7 +3302,7 @@ BIF_RETTYPE ports_0(BIF_ALIST_0) ASSERT((alive+dead) <= erts_max_ports); if (alive+dead > 0) { - long i; + erts_aint_t i; Eterm *hp = HAlloc(BIF_P, (alive+dead)*2); for (i = 0; i < alive; i++) { @@ -3796,7 +3798,8 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) goto error; } nval = (n > (Sint) ((Uint16) -1)) ? ((Uint16) -1) : ((Uint16) n); - oval = (Uint) erts_smp_atomic_xchg(&erts_max_gen_gcs, (long) nval); + oval = (Uint) erts_smp_atomic32_xchg(&erts_max_gen_gcs, + (erts_aint32_t) nval); BIF_RET(make_small(oval)); } else if (BIF_ARG_1 == am_min_heap_size) { int oval = H_MIN_SIZE; @@ -4139,7 +4142,7 @@ void erts_init_bif(void) erts_smp_spinlock_init(&make_ref_lock, "make_ref"); erts_smp_mtx_init(&ports_snapshot_mtx, "ports_snapshot"); - erts_smp_atomic_init(&erts_dead_ports_ptr, (long)NULL); + erts_smp_atomic_init(&erts_dead_ports_ptr, (erts_aint_t) NULL); /* * bif_return_trap/1 is a hidden BIF that bifs that need to diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 60b4b1946b..d9dd80fa8b 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -660,6 +660,7 @@ bif erts_debug:display/1 bif 'erl.system.debug':display/1 ebif_erts_debug_display_1 bif erts_debug:dist_ext_to_term/2 bif 'erl.system.debug':dist_ext_to_term/2 ebif_erts_debug_dist_ext_to_term_2 +bif erts_debug:instructions/0 # # Monitor testing bif's... diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index f339e19761..d255cf3558 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -98,7 +98,7 @@ process_killer(void) switch(j) { case 'k': if (rp->status == P_WAITING) { - Uint32 rp_locks = ERTS_PROC_LOCKS_XSIG_SEND; + ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND; erts_smp_proc_inc_refc(rp); erts_smp_proc_lock(rp, rp_locks); (void) erts_send_exit_signal(NULL, @@ -558,7 +558,7 @@ do_break(void) #endif #ifdef DEBUG case 't': - p_slpq(); + erts_p_slpq(); return; case 'b': bin_check(); @@ -624,9 +624,9 @@ bin_check(void) erts_printf("Process %T holding binary data \n", rp->id); printed = 1; } - erts_printf("0x%08lx orig_size: %ld, norefs = %ld\n", - (unsigned long)bp->val, - (long)bp->val->orig_size, + erts_printf("%p orig_size: %bpd, norefs = %bpd\n", + bp->val, + bp->val->orig_size, erts_smp_atomic_read(&bp->val->refc)); } } diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 694460d702..02910fad90 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -917,6 +917,7 @@ int erts_net_message(Port *prt, Eterm token_size; ErtsMonitor *mon; ErtsLink *lnk; + Uint tuple_arity; int res; #ifdef ERTS_DIST_MSG_DBG int orig_len = len; @@ -1003,29 +1004,23 @@ int erts_net_message(Port *prt, #endif if (is_not_tuple(arg) || - (tuple = tuple_val(arg), arityval(*tuple) < 1) || + (tuple = tuple_val(arg), (tuple_arity = arityval(*tuple)) < 1) || is_not_small(tuple[1])) { - erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); - erts_dsprintf(dsbufp, "Invalid distribution message: %.200T", arg); - erts_send_error_to_logger_nogl(dsbufp); - goto data_error; + goto invalid_message; } token_size = 0; switch (type = unsigned_val(tuple[1])) { case DOP_LINK: + if (tuple_arity != 3) { + goto invalid_message; + } from = tuple[2]; to = tuple[3]; /* local proc to link to */ if (is_not_pid(from) || is_not_pid(to)) { - erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); - PURIFY_MSG("data error"); - erts_dsprintf(dsbufp, - "Invalid DOP_LINK distribution message: %.200T", - arg); - erts_send_error_to_logger_nogl(dsbufp); - goto data_error; + goto invalid_message; } rp = erts_pid2proc_opt(NULL, 0, @@ -1064,8 +1059,14 @@ int erts_net_message(Port *prt, case DOP_UNLINK: { ErtsDistLinkData dld; + if (tuple_arity != 3) { + goto invalid_message; + } from = tuple[2]; to = tuple[3]; + if (is_not_pid(from) || is_not_pid(to)) { + goto invalid_message; + } rp = erts_pid2proc_opt(NULL, 0, to, ERTS_PROC_LOCK_LINK, @@ -1092,11 +1093,19 @@ int erts_net_message(Port *prt, /* A remote process wants to monitor us, we get: {DOP_MONITOR_P, Remote pid, local pid or name, ref} */ Eterm name; + + if (tuple_arity != 4) { + goto invalid_message; + } watcher = tuple[2]; watched = tuple[3]; /* local proc to monitor */ ref = tuple[4]; + if (is_not_ref(ref)) { + goto invalid_message; + } + if (is_atom(watched)) { name = watched; rp = erts_whereis_process(NULL, 0, @@ -1138,10 +1147,17 @@ int erts_net_message(Port *prt, We get {DOP_DEMONITOR_P, Remote pid, Local pid or name, ref}, We need only the ref of course */ + if (tuple_arity != 4) { + goto invalid_message; + } /* watcher = tuple[2]; */ /* watched = tuple[3]; May be an atom in case of monitor name */ ref = tuple[4]; + if(is_not_ref(ref)) { + goto invalid_message; + } + erts_smp_de_links_lock(dep); mon = erts_remove_monitor(&(dep->monitors),ref); erts_smp_de_links_unlock(dep); @@ -1166,10 +1182,11 @@ int erts_net_message(Port *prt, erts_destroy_monitor(mon); break; - case DOP_NODE_LINK: /* XXX never sent ?? */ - break; - case DOP_REG_SEND_TT: + if (tuple_arity != 5) { + goto invalid_message; + } + token_size = size_object(tuple[5]); /* Fall through ... */ case DOP_REG_SEND: @@ -1180,12 +1197,19 @@ int erts_net_message(Port *prt, * There is intentionally no testing of the cookie (it is always '') * from R9B and onwards. */ + if (type != DOP_REG_SEND_TT && tuple_arity != 4) { + goto invalid_message; + } + #ifdef ERTS_DIST_MSG_DBG dist_msg_dbg(&ede, "MSG", buf, orig_len); #endif from = tuple[2]; to = tuple[4]; + if (is_not_pid(from) || is_not_atom(to)){ + goto invalid_message; + } rp = erts_whereis_process(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC); if (rp) { Uint xsize = (type == DOP_REG_SEND @@ -1217,6 +1241,10 @@ int erts_net_message(Port *prt, break; case DOP_SEND_TT: + if (tuple_arity != 4) { + goto invalid_message; + } + token_size = size_object(tuple[4]); /* Fall through ... */ case DOP_SEND: @@ -1227,8 +1255,13 @@ int erts_net_message(Port *prt, #ifdef ERTS_DIST_MSG_DBG dist_msg_dbg(&ede, "MSG", buf, orig_len); #endif - + if (type != DOP_SEND_TT && tuple_arity != 3) { + goto invalid_message; + } to = tuple[3]; + if (is_not_pid(to)) { + goto invalid_message; + } rp = erts_pid2proc_opt(NULL, 0, to, 0, ERTS_P2P_FLG_SMP_INC_REFC); if (rp) { Uint xsize = type == DOP_SEND ? 0 : ERTS_HEAP_FRAG_SIZE(token_size); @@ -1266,11 +1299,19 @@ int erts_net_message(Port *prt, Eterm sysname; ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_MSG_SEND|ERTS_PROC_LOCK_LINK; + if (tuple_arity != 5) { + goto invalid_message; + } + /* watched = tuple[2]; */ /* remote proc which died */ /* watcher = tuple[3]; */ ref = tuple[4]; reason = tuple[5]; + if(is_not_ref(ref)) { + goto invalid_message; + } + erts_smp_de_links_lock(dep); sysname = dep->sysname; mon = erts_remove_monitor(&(dep->monitors), ref); @@ -1317,24 +1358,25 @@ int erts_net_message(Port *prt, ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCKS_XSIG_SEND; /* 'from', which 'to' is linked to, died */ if (type == DOP_EXIT) { - from = tuple[2]; - to = tuple[3]; - reason = tuple[4]; - token = NIL; + if (tuple_arity != 4) { + goto invalid_message; + } + + from = tuple[2]; + to = tuple[3]; + reason = tuple[4]; + token = NIL; } else { - from = tuple[2]; - to = tuple[3]; - token = tuple[4]; - reason = tuple[5]; + if (tuple_arity != 5) { + goto invalid_message; + } + from = tuple[2]; + to = tuple[3]; + token = tuple[4]; + reason = tuple[5]; } - if (is_not_internal_pid(to)) { - erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); - PURIFY_MSG("data error"); - erts_dsprintf(dsbufp, - "Invalid DOP_EXIT distribution message: %.200T", - arg); - erts_send_error_to_logger_nogl(dsbufp); - goto data_error; + if (is_not_pid(from) || is_not_internal_pid(to)) { + goto invalid_message; } rp = erts_pid2proc(NULL, 0, to, rp_locks); @@ -1381,15 +1423,24 @@ int erts_net_message(Port *prt, ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND; /* 'from' is send an exit signal to 'to' */ if (type == DOP_EXIT2) { - from = tuple[2]; - to = tuple[3]; - reason = tuple[4]; - token = NIL; + if (tuple_arity != 4) { + goto invalid_message; + } + from = tuple[2]; + to = tuple[3]; + reason = tuple[4]; + token = NIL; } else { - from = tuple[2]; - to = tuple[3]; - token = tuple[4]; - reason = tuple[5]; + if (tuple_arity != 5) { + goto invalid_message; + } + from = tuple[2]; + to = tuple[3]; + token = tuple[4]; + reason = tuple[5]; + } + if (is_not_pid(from) || is_not_internal_pid(to)) { + goto invalid_message; } rp = erts_pid2proc_opt(NULL, 0, to, rp_locks, ERTS_P2P_FLG_SMP_INC_REFC); @@ -1408,10 +1459,14 @@ int erts_net_message(Port *prt, break; } case DOP_GROUP_LEADER: + if (tuple_arity != 3) { + goto invalid_message; + } from = tuple[2]; /* Group leader */ to = tuple[3]; /* new member */ - if (is_not_pid(from)) - break; + if (is_not_pid(from) || is_not_pid(to)) { + goto invalid_message; + } rp = erts_pid2proc(NULL, 0, to, ERTS_PROC_LOCK_MAIN); if (!rp) @@ -1420,16 +1475,8 @@ int erts_net_message(Port *prt, erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MAIN); break; - default: { - erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); - erts_dsprintf(dsbufp, - "Illegal value in distribution dispatch switch: " - "%.200T", - arg); - erts_send_error_to_logger_nogl(dsbufp); - PURIFY_MSG("data error"); - goto data_error; - } + default: + goto invalid_message; } erts_cleanup_offheap(&off_heap); @@ -1441,8 +1488,14 @@ int erts_net_message(Port *prt, UnUseTmpHeapNoproc(DIST_CTL_DEFAULT_SIZE); ERTS_SMP_CHK_NO_PROC_LOCKS; return 0; - + invalid_message: + { + erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); + erts_dsprintf(dsbufp, "Invalid distribution message: %.200T", arg); + erts_send_error_to_logger_nogl(dsbufp); + } data_error: + PURIFY_MSG("data error"); erts_cleanup_offheap(&off_heap); #ifndef HYBRID /* FIND ME! */ if (ctl != ctl_default) { diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h index 9ccc3e5ba9..695a4fc3fe 100644 --- a/erts/emulator/beam/dist.h +++ b/erts/emulator/beam/dist.h @@ -52,7 +52,7 @@ #define DOP_SEND 2 #define DOP_EXIT 3 #define DOP_UNLINK 4 -#define DOP_NODE_LINK 5 +/* Ancient DOP_NODE_LINK (5) was here, can be reused */ #define DOP_REG_SEND 6 #define DOP_GROUP_LEADER 7 #define DOP_EXIT2 8 @@ -69,7 +69,6 @@ /* distribution trap functions */ extern Export* dsend2_trap; extern Export* dsend3_trap; -/*extern Export* dsend_nosuspend_trap;*/ extern Export* dlink_trap; extern Export* dunlink_trap; extern Export* dmonitor_node_trap; diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 7793f60f4f..e85e2d7e3f 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -1568,7 +1568,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) Eterm atoms[sizeof(size)/sizeof(Uint)]; Uint *uintps[sizeof(size)/sizeof(Uint)]; Eterm euints[sizeof(size)/sizeof(Uint)]; - int need_atom; int want_tot_or_sys; int length; Eterm res = THE_NON_VALUE; @@ -1756,7 +1755,6 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) /* Calculate values needed... */ want_tot_or_sys = want.total || want.system; - need_atom = ERTS_MEM_NEED_ALL_ALCU || want.atom; if (ERTS_MEM_NEED_ALL_ALCU) { size.total = 0; diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 408ffd12f7..b7b9c6a133 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -263,6 +263,8 @@ type XPORTS_LIST SHORT_LIVED SYSTEM extra_port_list type PROC_LCK_WTR LONG_LIVED SYSTEM proc_lock_waiter type PROC_LCK_QS LONG_LIVED SYSTEM proc_lock_queues type RUNQ_BLNS LONG_LIVED SYSTEM run_queue_balancing +type MISC_AUX_WORK_Q LONG_LIVED SYSTEM misc_aux_work_q +type MISC_AUX_WORK SHORT_LIVED SYSTEM misc_aux_work +endif # diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index 2c2e283f65..c9cdcb87a6 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -1193,7 +1193,7 @@ int erts_ddll_driver_ok(DE_Handle *dh) static void ddll_no_more_references(void *vdh) { DE_Handle *dh = (DE_Handle *) vdh; - int x; + erts_aint_t x; lock_drv_list(); @@ -1604,7 +1604,7 @@ static int do_load_driver_entry(DE_Handle *dh, char *path, char *name) erts_sys_ddll_close(dh->handle); return ERL_DE_LOAD_ERROR_BAD_NAME; } - erts_smp_atomic_init(&(dh->refc), (long) 0); + erts_smp_atomic_init(&(dh->refc), (erts_aint_t) 0); dh->port_count = 0; dh->full_path = erts_alloc(ERTS_ALC_T_DDLL_HANDLE, sys_strlen(path) + 1); sys_strcpy(dh->full_path, path); @@ -1672,7 +1672,7 @@ static int load_driver_entry(DE_Handle **dhp, char *path, char *name) dh->handle = NULL; dh->procs = NULL; dh->port_count = 0; - erts_refc_init(&(dh->refc), (long) 0); + erts_refc_init(&(dh->refc), (erts_aint_t) 0); dh->status = -1; dh->reload_full_path = NULL; dh->reload_driver_name = NULL; diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 75d8db880c..4a717d7271 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2020,7 +2020,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) res = TUPLE2(hp, am_sequential_tracer, val); BIF_RET(res); } else if (BIF_ARG_1 == am_garbage_collection){ - Uint val = (Uint) erts_smp_atomic_read(&erts_max_gen_gcs); + Uint val = (Uint) erts_smp_atomic32_read(&erts_max_gen_gcs); Eterm tup; hp = HAlloc(BIF_P, 3+2 + 3+2 + 3+2); @@ -2035,7 +2035,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(res); } else if (BIF_ARG_1 == am_fullsweep_after){ - Uint val = (Uint) erts_smp_atomic_read(&erts_max_gen_gcs); + Uint val = (Uint) erts_smp_atomic32_read(&erts_max_gen_gcs); hp = HAlloc(BIF_P, 3); res = TUPLE2(hp, am_fullsweep_after, make_small(val)); BIF_RET(res); @@ -3430,8 +3430,8 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) */ if (ERTS_IS_ATOM_STR("available_internal_state", BIF_ARG_1) && (BIF_ARG_2 == am_true || BIF_ARG_2 == am_false)) { - long on = (long) (BIF_ARG_2 == am_true); - long prev_on = erts_smp_atomic_xchg(&available_internal_state, on); + erts_aint_t on = (erts_aint_t) (BIF_ARG_2 == am_true); + erts_aint_t prev_on = erts_smp_atomic_xchg(&available_internal_state, on); if (on) { erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); erts_dsprintf(dsbufp, "Process %T ", BIF_P->id); @@ -3628,7 +3628,7 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) } else if (ERTS_IS_ATOM_STR("hipe_test_reschedule_suspend", BIF_ARG_1)) { /* Used by hipe test suites */ - long flag = erts_smp_atomic_read(&hipe_test_reschedule_flag); + erts_aint_t flag = erts_smp_atomic_read(&hipe_test_reschedule_flag); if (!flag && BIF_ARG_2 != am_false) { erts_smp_atomic_set(&hipe_test_reschedule_flag, 1); erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL); @@ -3703,7 +3703,7 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) #ifdef ERTS_ENABLE_LOCK_COUNT static Eterm lcnt_build_lock_stats_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_stats_t *stats, Eterm res) { - unsigned long tries = 0, colls = 0; + Uint tries = 0, colls = 0; unsigned long timer_s = 0, timer_ns = 0, timer_n = 0; unsigned int line = 0; @@ -3716,8 +3716,8 @@ static Eterm lcnt_build_lock_stats_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_s * [{{file, line}, {tries, colls, {seconds, nanoseconds, n_blocks}}}] */ - tries = (unsigned long) ethr_atomic_read(&stats->tries); - colls = (unsigned long) ethr_atomic_read(&stats->colls); + tries = (Uint) ethr_atomic_read(&stats->tries); + colls = (Uint) ethr_atomic_read(&stats->colls); line = stats->line; timer_s = stats->timer.s; diff --git a/erts/emulator/beam/erl_bif_timer.c b/erts/emulator/beam/erl_bif_timer.c index 4ae2f6ebf4..3508e8e0dc 100644 --- a/erts/emulator/beam/erl_bif_timer.c +++ b/erts/emulator/beam/erl_bif_timer.c @@ -478,7 +478,7 @@ setup_bif_timer(Uint32 xflags, tab_insert(btm); ASSERT(btm == tab_find(ref)); btm->tm.active = 0; /* MUST be initalized */ - erl_set_timer(&btm->tm, + erts_set_timer(&btm->tm, (ErlTimeoutProc) bif_timer_timeout, (ErlCancelProc) bif_timer_cleanup, (void *) btm, @@ -550,7 +550,7 @@ BIF_RETTYPE cancel_timer_1(BIF_ALIST_1) res = am_false; } else { - Uint left = time_left(&btm->tm); + Uint left = erts_time_left(&btm->tm); if (!(btm->flags & BTM_FLG_BYNAME)) { erts_smp_proc_lock(btm->receiver.proc.ess, ERTS_PROC_LOCK_MSGQ); unlink_proc(btm); @@ -558,7 +558,7 @@ BIF_RETTYPE cancel_timer_1(BIF_ALIST_1) } tab_remove(btm); ASSERT(!tab_find(BIF_ARG_1)); - erl_cancel_timer(&btm->tm); + erts_cancel_timer(&btm->tm); erts_smp_btm_rwunlock(); res = erts_make_integer(left, BIF_P); } @@ -587,7 +587,7 @@ BIF_RETTYPE read_timer_1(BIF_ALIST_1) res = am_false; } else { - Uint left = time_left(&btm->tm); + Uint left = erts_time_left(&btm->tm); res = erts_make_integer(left, BIF_P); } @@ -613,7 +613,7 @@ erts_print_bif_timer_info(int to, void *to_arg) : btm->receiver.proc.ess->id); erts_print(to, to_arg, "=timer:%T\n", receiver); erts_print(to, to_arg, "Message: %T\n", btm->message); - erts_print(to, to_arg, "Time left: %d ms\n", time_left(&btm->tm)); + erts_print(to, to_arg, "Time left: %d ms\n", erts_time_left(&btm->tm)); } } @@ -640,7 +640,7 @@ erts_cancel_bif_timers(Process *p, ErtsProcLocks plocks) tab_remove(btm); tmp_btm = btm; btm = btm->receiver.proc.next; - erl_cancel_timer(&tmp_btm->tm); + erts_cancel_timer(&tmp_btm->tm); } p->bif_timers = NULL; diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index 88d2c06246..6f8a7436d5 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -555,10 +555,11 @@ fmt_int(byte *buf, Uint sz, Eterm val, Uint size, Uint flags) { unsigned long offs; - ASSERT(size != 0); offs = BIT_OFFSET(size); if (is_small(val)) { Sint v = signed_val(val); + + ASSERT(size != 0); /* Tested by caller */ if (flags & BSF_LITTLE) { /* Little endian */ sz--; COPY_VAL(buf,1,v,sz); @@ -578,6 +579,9 @@ fmt_int(byte *buf, Uint sz, Eterm val, Uint size, Uint flags) ErtsDigit* dp = big_v(val); int n = MIN(sz,ds); + if (size == 0) { + return 0; + } if (flags & BSF_LITTLE) { sz -= n; /* pad with this amount */ if (sign) { @@ -729,15 +733,13 @@ erts_new_bs_put_integer(ERL_BITS_PROTO_3(Eterm arg, Uint num_bits, unsigned flag Uint b; byte *iptr; - if (num_bits == 0) { - return 1; - } - bit_offset = BIT_OFFSET(bin_offset); if (is_small(arg)) { Uint rbits = 8 - bit_offset; - if (bit_offset + num_bits <= 8) { + if (num_bits == 0) { + return 1; + } else if (bit_offset + num_bits <= 8) { /* * All bits are in the same byte. */ diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c index db95c4a5d4..8a6b4d8d6c 100644 --- a/erts/emulator/beam/erl_cpu_topology.c +++ b/erts/emulator/beam/erl_cpu_topology.c @@ -487,7 +487,7 @@ erts_sched_check_cpu_bind_post_suspend(ErtsSchedulerData *esdp) /* Make sure we check if we should bind to a cpu or not... */ if (esdp->run_queue->flags & ERTS_RUNQ_FLG_SHARED_RUNQ) - erts_smp_atomic_set(&esdp->chk_cpu_bind, 1); + erts_smp_atomic32_set(&esdp->chk_cpu_bind, 1); else esdp->run_queue->flags |= ERTS_RUNQ_FLG_CHK_CPU_BIND; } @@ -503,7 +503,7 @@ erts_sched_check_cpu_bind(ErtsSchedulerData *esdp) erts_cpu_groups_callback_call_t *cgcc; #ifdef ERTS_SMP if (erts_common_run_queue) - erts_smp_atomic_set(&esdp->chk_cpu_bind, 0); + erts_smp_atomic32_set(&esdp->chk_cpu_bind, 0); else { esdp->run_queue->flags &= ~ERTS_RUNQ_FLG_CHK_CPU_BIND; } diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 8577354d27..3173d3510e 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -219,47 +219,68 @@ Export ets_select_continue_exp; * Static traps */ static Export ets_delete_continue_exp; - -static ERTS_INLINE DbTable* db_ref(DbTable* tb, db_lock_kind_t kind) -{ - if (tb != NULL && kind != LCK_READ) { - erts_refc_inc(&tb->common.ref, 2); - } - return tb; -} - -static ERTS_INLINE DbTable* db_unref(DbTable* tb, db_lock_kind_t kind) + +static void +free_dbtable(DbTable* tb) { - if (kind != LCK_READ && !erts_refc_dectest(&tb->common.ref, 0)) { #ifdef HARDDEBUG if (erts_smp_atomic_read(&tb->common.memory_size) != sizeof(DbTable)) { - erts_fprintf(stderr, "ets: db_unref memory remain=%ld fix=%x\n", - erts_smp_atomic_read(&tb->common.memory_size)-sizeof(DbTable), + erts_fprintf(stderr, "ets: free_dbtable memory remain=%ld fix=%x\n", + erts_smp_atomic_read(&tb->common.memory_size)-sizeof(DbTable), tb->common.fixations); } - erts_fprintf(stderr, "ets: db_unref(%T) deleted!!!\r\n", + erts_fprintf(stderr, "ets: free_dbtable(%T) deleted!!!\r\n", tb->common.id); - erts_fprintf(stderr, "ets: db_unref: meta_pid_to_tab common.memory_size = %ld\n", + erts_fprintf(stderr, "ets: free_dbtable: meta_pid_to_tab common.memory_size = %ld\n", erts_smp_atomic_read(&meta_pid_to_tab->common.memory_size)); print_table(ERTS_PRINT_STDOUT, NULL, 1, meta_pid_to_tab); - erts_fprintf(stderr, "ets: db_unref: meta_pid_to_fixed_tab common.memory_size = %ld\n", + erts_fprintf(stderr, "ets: free_dbtable: meta_pid_to_fixed_tab common.memory_size = %ld\n", erts_smp_atomic_read(&meta_pid_to_fixed_tab->common.memory_size)); print_table(ERTS_PRINT_STDOUT, NULL, 1, meta_pid_to_fixed_tab); - #endif #ifdef ERTS_SMP erts_smp_rwmtx_destroy(&tb->common.rwlock); erts_smp_mtx_destroy(&tb->common.fixlock); #endif ASSERT(is_immed(tb->common.heir_data)); - erts_db_free(ERTS_ALC_T_DB_TABLE, tb, (void *) tb, sizeof(DbTable)); + erts_db_free(ERTS_ALC_T_DB_TABLE, tb, (void *) tb, sizeof(DbTable)); ERTS_ETS_MISC_MEM_ADD(-sizeof(DbTable)); - return NULL; - } - return tb; +} + +#ifdef ERTS_SMP +static void +chk_free_dbtable(void *vtb) +{ + DbTable * tb = (DbTable *) vtb; + ERTS_THR_MEMORY_BARRIER; + if (erts_refc_dectest(&tb->common.ref, 0) == 0) + free_dbtable(tb); +} +#endif + +static void schedule_free_dbtable(DbTable* tb) +{ + /* + * NON-SMP case: Caller is *not* allowed to access the *tb + * structure after this function has returned! + * SMP case: Caller is allowed to access the *tb structure + * until the bif has returned (we typically + * need to unlock the table lock after this + * function has returned). + */ +#ifdef ERTS_SMP + int scheds = erts_get_max_no_executing_schedulers(); + ASSERT(scheds >= 1); + ASSERT(erts_refc_read(&tb->common.ref, 0) == 0); + erts_refc_init(&tb->common.ref, scheds); + ERTS_THR_MEMORY_BARRIER; + erts_smp_schedule_misc_aux_work(0, scheds, chk_free_dbtable, tb); +#else + free_dbtable(tb); +#endif } static ERTS_INLINE void db_init_lock(DbTable* tb, int use_frequent_read_lock, @@ -270,8 +291,6 @@ static ERTS_INLINE void db_init_lock(DbTable* tb, int use_frequent_read_lock, if (use_frequent_read_lock) rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_FREQUENT_READ; #endif - erts_refc_init(&tb->common.ref, 1); - erts_refc_init(&tb->common.fixref, 0); #ifdef ERTS_SMP erts_smp_rwmtx_init_opt_x(&tb->common.rwlock, &rwmtx_opt, rwname, tb->common.the_name); @@ -280,7 +299,7 @@ static ERTS_INLINE void db_init_lock(DbTable* tb, int use_frequent_read_lock, #endif } -static ERTS_INLINE void db_lock_take_over_ref(DbTable* tb, db_lock_kind_t kind) +static ERTS_INLINE void db_lock(DbTable* tb, db_lock_kind_t kind) { #ifdef ERTS_SMP ASSERT(tb != meta_pid_to_tab && tb != meta_pid_to_fixed_tab); @@ -308,16 +327,13 @@ static ERTS_INLINE void db_lock_take_over_ref(DbTable* tb, db_lock_kind_t kind) #endif } -static ERTS_INLINE void db_lock(DbTable* tb, db_lock_kind_t kind) -{ - (void) db_ref(tb, kind); -#ifdef ERTS_SMP - db_lock_take_over_ref(tb, kind); -#endif -} - static ERTS_INLINE void db_unlock(DbTable* tb, db_lock_kind_t kind) { + /* + * In NON-SMP case tb may refer to an already deallocated + * DbTable structure. That is, ONLY the SMP case is allowed + * to follow the tb pointer! + */ #ifdef ERTS_SMP ASSERT(tb != meta_pid_to_tab && tb != meta_pid_to_fixed_tab); @@ -344,7 +360,6 @@ static ERTS_INLINE void db_unlock(DbTable* tb, db_lock_kind_t kind) } } #endif - (void) db_unref(tb, kind); /* May delete table... */ } @@ -371,6 +386,13 @@ DbTable* db_get_table_aux(Process *p, DbTable *tb = NULL; erts_smp_rwmtx_t *mtl = NULL; + /* + * IMPORTANT: Only scheduler threads are allowed + * to access tables. Memory management + * depend on it. + */ + ASSERT(erts_get_scheduler_data()); + if (is_small(id)) { Uint slot = unsigned_val(id) & meta_main_tab_slot_mask; if (!meta_already_locked) { @@ -384,12 +406,8 @@ DbTable* db_get_table_aux(Process *p, || erts_lc_rwmtx_is_rwlocked(test_mtl)); } #endif - if (slot < db_max_tabs && IS_SLOT_ALIVE(slot)) { - /* SMP: inc to prevent race, between unlock of meta_main_tab_lock - * and the table locking outside the meta_main_tab_lock - */ - tb = db_ref(meta_main_tab[slot].u.tb, kind); - } + if (slot < db_max_tabs && IS_SLOT_ALIVE(slot)) + tb = meta_main_tab[slot].u.tb; } else if (is_atom(id)) { struct meta_name_tab_entry* bucket = meta_name_tab_bucket(id,&mtl); @@ -403,16 +421,15 @@ DbTable* db_get_table_aux(Process *p, if (bucket->pu.tb != NULL) { if (is_atom(bucket->u.name_atom)) { /* single */ - if (bucket->u.name_atom == id) { - tb = db_ref(bucket->pu.tb, kind); - } + if (bucket->u.name_atom == id) + tb = bucket->pu.tb; } else { /* multi */ Uint cnt = unsigned_val(bucket->u.mcnt); Uint i; for (i=0; i<cnt; i++) { if (bucket->pu.mvec[i].u.name_atom == id) { - tb = db_ref(bucket->pu.mvec[i].pu.tb, kind); + tb = bucket->pu.mvec[i].pu.tb; break; } } @@ -420,7 +437,7 @@ DbTable* db_get_table_aux(Process *p, } } if (tb) { - db_lock_take_over_ref(tb, kind); + db_lock(tb, kind); if (tb->common.id != id || ((tb->common.status & what) == 0 && p->id != tb->common.owner)) { db_unlock(tb, kind); @@ -594,11 +611,11 @@ done: */ static ERTS_INLINE void local_fix_table(DbTable* tb) { - erts_refc_inc(&tb->common.fixref, 1); + erts_refc_inc(&tb->common.ref, 1); } static ERTS_INLINE void local_unfix_table(DbTable* tb) { - if (erts_refc_dectest(&tb->common.fixref, 0) == 0) { + if (erts_refc_dectest(&tb->common.ref, 0) == 0) { ASSERT(IS_HASH_TABLE(tb->common.status)); db_unfix_table_hash(&(tb->hash)); } @@ -1414,6 +1431,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2) tb->common.type = status & ERTS_ETS_TABLE_TYPES; /* Note, 'type' is *read only* from now on... */ #endif + erts_refc_init(&tb->common.ref, 0); db_init_lock(tb, status & (DB_FINE_LOCKED|DB_FREQ_READ), "db_tab", "db_tab_fix"); tb->common.keypos = keypos; @@ -1436,8 +1454,7 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2) "** Too many db tables **\n"); free_heir_data(tb); tb->common.meth->db_free_table(tb); - erts_db_free(ERTS_ALC_T_DB_TABLE, tb, (void *) tb, sizeof(DbTable)); - ERTS_ETS_MISC_MEM_ADD(-sizeof(DbTable)); + free_dbtable(tb); BIF_ERROR(BIF_P, SYSTEM_LIMIT); } @@ -1471,9 +1488,10 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2) free_slot(slot); erts_smp_rwmtx_rwunlock(mmtl); - db_lock_take_over_ref(tb,LCK_WRITE); + db_lock(tb,LCK_WRITE); free_heir_data(tb); tb->common.meth->db_free_table(tb); + schedule_free_dbtable(tb); db_unlock(tb,LCK_WRITE); BIF_ERROR(BIF_P, BADARG); } @@ -2845,8 +2863,7 @@ void init_db(void) meta_pid_to_tab->common.meth = &db_hash; meta_pid_to_tab->common.compress = 0; - erts_refc_init(&meta_pid_to_tab->common.ref, 1); - erts_refc_init(&meta_pid_to_tab->common.fixref, 0); + erts_refc_init(&meta_pid_to_tab->common.ref, 0); /* Neither rwlock or fixlock used db_init_lock(meta_pid_to_tab, "meta_pid_to_tab", "meta_pid_to_tab_FIX");*/ @@ -2878,8 +2895,7 @@ void init_db(void) meta_pid_to_fixed_tab->common.meth = &db_hash; meta_pid_to_fixed_tab->common.compress = 0; - erts_refc_init(&meta_pid_to_fixed_tab->common.ref, 1); - erts_refc_init(&meta_pid_to_fixed_tab->common.fixref, 0); + erts_refc_init(&meta_pid_to_fixed_tab->common.ref, 0); /* Neither rwlock or fixlock used db_init_lock(meta_pid_to_fixed_tab, "meta_pid_to_fixed_tab", "meta_pid_to_fixed_tab_FIX");*/ @@ -3037,12 +3053,10 @@ retry: to_pid, to_locks, ERTS_P2P_FLG_TRY_LOCK); if (to_proc == ERTS_PROC_LOCK_BUSY) { - db_ref(tb, LCK_NONE); /* while unlocked */ db_unlock(tb,LCK_WRITE); to_proc = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, to_pid, to_locks); db_lock(tb,LCK_WRITE); - tb = db_unref(tb, LCK_NONE); ASSERT(tb != NULL); if (tb->common.owner != p->id) { @@ -3153,13 +3167,13 @@ erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks) erts_smp_rwmtx_t *mmtl = get_meta_main_tab_lock(ix); erts_smp_rwmtx_rlock(mmtl); if (!IS_SLOT_FREE(ix)) { - tb = db_ref(GET_ANY_SLOT_TAB(ix), LCK_WRITE); + tb = GET_ANY_SLOT_TAB(ix); ASSERT(tb); } erts_smp_rwmtx_runlock(mmtl); if (tb) { int do_yield; - db_lock_take_over_ref(tb, LCK_WRITE); + db_lock(tb, LCK_WRITE); /* Ownership may have changed since we looked up the table. */ if (tb->common.owner != pid) { @@ -3241,7 +3255,7 @@ erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks) erts_smp_rwmtx_t *mmtl = get_meta_main_tab_lock(ix); erts_smp_rwmtx_rlock(mmtl); if (IS_SLOT_ALIVE(ix)) { - tb = db_ref(meta_main_tab[ix].u.tb, LCK_WRITE_REC); + tb = meta_main_tab[ix].u.tb; ASSERT(tb); } erts_smp_rwmtx_runlock(mmtl); @@ -3249,7 +3263,7 @@ erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks) int reds; DbFixation** pp; - db_lock_take_over_ref(tb, LCK_WRITE_REC); + db_lock(tb, LCK_WRITE_REC); #ifdef ERTS_SMP erts_smp_mtx_lock(&tb->common.fixlock); #endif @@ -3259,8 +3273,8 @@ erts_db_process_exiting(Process *c_p, ErtsProcLocks c_p_locks) pp = &(*pp)->next) { if ((*pp)->pid == pid) { DbFixation* fix = *pp; - long diff = -(long)fix->counter; - erts_refc_add(&tb->common.fixref,diff,0); + erts_aint_t diff = -((erts_aint_t) fix->counter); + erts_refc_add(&tb->common.ref,diff,0); *pp = fix->next; erts_db_free(ERTS_ALC_T_DB_FIXATION, tb, fix, sizeof(DbFixation)); @@ -3335,7 +3349,7 @@ static void fix_table_locked(Process* p, DbTable* tb) #ifdef ERTS_SMP erts_smp_mtx_lock(&tb->common.fixlock); #endif - erts_refc_inc(&tb->common.fixref,1); + erts_refc_inc(&tb->common.ref,1); fix = tb->common.fixations; if (fix == NULL) { get_now(&(tb->common.megasec), @@ -3389,7 +3403,7 @@ static void unfix_table_locked(Process* p, DbTable* tb, for (pp = &tb->common.fixations; *pp != NULL; pp = &(*pp)->next) { if ((*pp)->pid == p->id) { DbFixation* fix = *pp; - erts_refc_dec(&tb->common.fixref,0); + erts_refc_dec(&tb->common.ref,0); --(fix->counter); ASSERT(fix->counter >= 0); if (fix->counter > 0) { @@ -3415,11 +3429,10 @@ static void unfix_table_locked(Process* p, DbTable* tb, unlocked: if (!IS_FIXED(tb) && IS_HASH_TABLE(tb->common.status) - && erts_smp_atomic_read(&tb->hash.fixdel) != (long)NULL) { + && erts_smp_atomic_read(&tb->hash.fixdel) != (erts_aint_t)NULL) { #ifdef ERTS_SMP if (*kind_p == LCK_READ && tb->common.is_thread_safe) { /* Must have write lock while purging pseudo-deleted (OTP-8166) */ - db_ref(tb, LCK_WRITE); /* LCK_WRITE need it, but not LCK_READ */ erts_smp_rwmtx_runlock(&tb->common.rwlock); erts_smp_rwmtx_rwlock(&tb->common.rwlock); *kind_p = LCK_WRITE; @@ -3438,6 +3451,8 @@ static void free_fixations_locked(DbTable *tb) fix = tb->common.fixations; while (fix != NULL) { + erts_aint_t diff = -((erts_aint_t) fix->counter); + erts_refc_add(&tb->common.ref,diff,0); next_fix = fix->next; db_meta_lock(meta_pid_to_fixed_tab, LCK_WRITE_REC); db_erase_bag_exact2(meta_pid_to_fixed_tab, @@ -3561,10 +3576,6 @@ static int free_table_cont(Process *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. - */ erts_smp_rwmtx_rwunlock(&tb->common.rwlock); erts_smp_rwmtx_rwlock(mmtl); erts_smp_rwmtx_rwlock(&tb->common.rwlock); @@ -3579,7 +3590,7 @@ static int free_table_cont(Process *p, make_small(tb->common.slot)); db_meta_unlock(meta_pid_to_tab, LCK_WRITE_REC); } - db_unref(tb, LCK_NONE); + schedule_free_dbtable(tb); BUMP_REDS(p, 100); return 0; } diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h index cb2da603f0..e0bdebcb01 100644 --- a/erts/emulator/beam/erl_db.h +++ b/erts/emulator/beam/erl_db.h @@ -83,7 +83,8 @@ Eterm erts_ets_colliding_names(Process*, Eterm name, Uint cnt); #define ERTS_DB_ALC_MEM_UPDATE_(TAB, FREE_SZ, ALLOC_SZ) \ do { \ - long sz__ = ((long) (ALLOC_SZ)) - ((long) (FREE_SZ)); \ + erts_aint_t sz__ = (((erts_aint_t) (ALLOC_SZ)) \ + - ((erts_aint_t) (FREE_SZ))); \ ASSERT((TAB)); \ erts_smp_atomic_add(&(TAB)->common.memory_size, sz__); \ } while (0) diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 14ee63100a..1e50fee554 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -135,8 +135,8 @@ static ERTS_INLINE Uint hash_to_ix(DbTableHash* tb, HashValue hval) */ static ERTS_INLINE void add_fixed_deletion(DbTableHash* tb, int ix) { - long was_next; - long exp_next; + erts_aint_t was_next; + erts_aint_t exp_next; FixedDeletion* fixd = (FixedDeletion*) erts_db_alloc(ERTS_ALC_T_DB_FIX_DEL, (DbTable *) tb, sizeof(FixedDeletion)); @@ -146,7 +146,9 @@ static ERTS_INLINE void add_fixed_deletion(DbTableHash* tb, int ix) do { /* Lockless atomic insertion in linked list: */ exp_next = was_next; fixd->next = (FixedDeletion*) exp_next; - was_next = erts_smp_atomic_cmpxchg(&tb->fixdel, (long)fixd, exp_next); + was_next = erts_smp_atomic_cmpxchg(&tb->fixdel, + (erts_aint_t) fixd, + exp_next); }while (was_next != exp_next); } @@ -541,12 +543,12 @@ static void restore_fixdel(DbTableHash* tb, FixedDeletion* fixdel) { /*int tries = 0;*/ DEBUG_WAIT(); - if (erts_smp_atomic_cmpxchg(&tb->fixdel, (long)fixdel, - (long)NULL) != (long)NULL) { + if (erts_smp_atomic_cmpxchg(&tb->fixdel, (erts_aint_t)fixdel, + (erts_aint_t)NULL) != (erts_aint_t)NULL) { /* Oboy, must join lists */ FixedDeletion* last = fixdel; - long was_tail; - long exp_tail; + erts_aint_t was_tail; + erts_aint_t exp_tail; while (last->next != NULL) last = last->next; was_tail = erts_smp_atomic_read(&tb->fixdel); @@ -555,7 +557,7 @@ static void restore_fixdel(DbTableHash* tb, FixedDeletion* fixdel) last->next = (FixedDeletion*) exp_tail; /*++tries;*/ DEBUG_WAIT(); - was_tail = erts_smp_atomic_cmpxchg(&tb->fixdel, (long)fixdel, + was_tail = erts_smp_atomic_cmpxchg(&tb->fixdel, (erts_aint_t)fixdel, exp_tail); }while (was_tail != exp_tail); } @@ -573,7 +575,7 @@ void db_unfix_table_hash(DbTableHash *tb) || (erts_smp_lc_rwmtx_is_rlocked(&tb->common.rwlock) && !tb->common.is_thread_safe)); restart: - fixdel = (FixedDeletion*) erts_smp_atomic_xchg(&tb->fixdel, (long)NULL); + fixdel = (FixedDeletion*) erts_smp_atomic_xchg(&tb->fixdel, (erts_aint_t)NULL); while (fixdel != NULL) { FixedDeletion *fx = fixdel; int ix = fx->slot; @@ -642,8 +644,8 @@ int db_create_hash(Process *p, DbTable *tbl) erts_smp_atomic_init(&tb->szm, SEGSZ_MASK); erts_smp_atomic_init(&tb->nactive, SEGSZ); - erts_smp_atomic_init(&tb->fixdel, (long)NULL); - erts_smp_atomic_init(&tb->segtab, (long) alloc_ext_seg(tb,0,NULL)->segtab); + erts_smp_atomic_init(&tb->fixdel, (erts_aint_t)NULL); + erts_smp_atomic_init(&tb->segtab, (erts_aint_t) alloc_ext_seg(tb,0,NULL)->segtab); tb->nsegs = NSEG_1; tb->nslots = SEGSZ; @@ -1715,9 +1717,9 @@ static int db_select_delete_hash(Process *p, Eterm mpb; Eterm egot; #ifdef ERTS_SMP - int fixated_by_me = tb->common.is_thread_safe ? 0 : 1; /* ToDo: something nicer */ + erts_aint_t fixated_by_me = tb->common.is_thread_safe ? 0 : 1; /* ToDo: something nicer */ #else - int fixated_by_me = 0; + erts_aint_t fixated_by_me = 0; #endif erts_smp_rwmtx_t* lck; @@ -2124,11 +2126,11 @@ static int db_free_table_continue_hash(DbTable *tbl) sizeof(FixedDeletion)); ERTS_ETS_MISC_MEM_ADD(-sizeof(FixedDeletion)); if (++done >= 2*DELETE_RECORD_LIMIT) { - erts_smp_atomic_set(&tb->fixdel, (long)fixdel); + erts_smp_atomic_set(&tb->fixdel, (erts_aint_t)fixdel); return 0; /* Not done */ } } - erts_smp_atomic_set(&tb->fixdel, (long)NULL); + erts_smp_atomic_set(&tb->fixdel, (erts_aint_t)NULL); done /= 2; while(tb->nslots != 0) { @@ -2345,7 +2347,7 @@ static int alloc_seg(DbTableHash *tb) struct ext_segment* eseg; eseg = (struct ext_segment*) SEGTAB(tb)[seg_ix-1]; MY_ASSERT(eseg!=NULL && eseg->s.is_ext_segment); - erts_smp_atomic_set(&tb->segtab, (long) eseg->segtab); + erts_smp_atomic_set(&tb->segtab, (erts_aint_t) eseg->segtab); tb->nsegs = eseg->nsegs; } ASSERT(seg_ix < tb->nsegs); @@ -2417,7 +2419,7 @@ static int free_seg(DbTableHash *tb, int free_records) MY_ASSERT(newtop->s.is_ext_segment); if (newtop->prev_segtab != NULL) { /* Time to use a smaller segtab */ - erts_smp_atomic_set(&tb->segtab, (long)newtop->prev_segtab); + erts_smp_atomic_set(&tb->segtab, (erts_aint_t)newtop->prev_segtab); tb->nsegs = seg_ix; ASSERT(tb->nsegs == EXTSEG(SEGTAB(tb))->nsegs); } @@ -2434,7 +2436,7 @@ static int free_seg(DbTableHash *tb, int free_records) if (seg_ix > 0) { if (seg_ix < tb->nsegs) SEGTAB(tb)[seg_ix] = NULL; } else { - erts_smp_atomic_set(&tb->segtab, (long)NULL); + erts_smp_atomic_set(&tb->segtab, (erts_aint_t)NULL); } #endif tb->nslots -= SEGSZ; diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index e773361619..2852fb93fe 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -915,7 +915,7 @@ BIF_RETTYPE db_set_trace_control_word_1(Process *p, Eterm new) if (val != ((Uint32)val)) BIF_ERROR(p, BADARG); - old_tcw = (Uint32) erts_smp_atomic_xchg(&trace_control_word, (long) val); + old_tcw = (Uint32) erts_smp_atomic_xchg(&trace_control_word, (erts_aint_t) val); BIF_RET(erts_make_integer((Uint) old_tcw, p)); } diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index 10ba755e80..58ad39d772 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -206,8 +206,7 @@ typedef struct db_fixation { */ typedef struct db_table_common { - erts_refc_t ref; - erts_refc_t fixref; /* fixation counter */ + erts_refc_t ref; /* fixation counter and delete counter */ #ifdef ERTS_SMP erts_smp_rwmtx_t rwlock; /* rw lock on table */ erts_smp_mtx_t fixlock; /* Protects fixations,megasec,sec,microsec */ @@ -253,7 +252,7 @@ typedef struct db_table_common { (DB_BAG | DB_SET | DB_DUPLICATE_BAG))) #define IS_TREE_TABLE(Status) (!!((Status) & \ DB_ORDERED_SET)) -#define NFIXED(T) (erts_refc_read(&(T)->common.fixref,0)) +#define NFIXED(T) (erts_refc_read(&(T)->common.ref,0)) #define IS_FIXED(T) (NFIXED(T) != 0) Eterm erts_ets_copy_object(Eterm, Process*); diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index 9733c0e5b5..13a73e01bb 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -150,6 +150,27 @@ typedef struct { #define ERL_DRV_FLAG_SOFT_BUSY (1 << 1) /* + * Integer types + */ + +typedef unsigned long ErlDrvTermData; +typedef unsigned long ErlDrvUInt; +typedef signed long ErlDrvSInt; + +#if defined(__WIN32__) +typedef unsigned __int64 ErlDrvUInt64; +typedef __int64 ErlDrvSInt64; +#elif SIZEOF_LONG == 8 +typedef unsigned long ErlDrvUInt64; +typedef long ErlDrvSInt64; +#elif SIZEOF_LONG_LONG == 8 +typedef unsigned long long ErlDrvUInt64; +typedef long long ErlDrvSInt64; +#else +#error No 64-bit integer type +#endif + +/* * A binary as seen in a driver. Note that a binary should never be * altered by the driver when it has been sent to Erlang. */ @@ -179,26 +200,6 @@ struct erl_drv_event_data { #endif typedef struct erl_drv_event_data *ErlDrvEventData; /* Event data */ -/* - * Used in monitors... - */ -typedef unsigned long ErlDrvTermData; -typedef unsigned long ErlDrvUInt; -typedef signed long ErlDrvSInt; - -#if defined(__WIN32__) -typedef unsigned __int64 ErlDrvUInt64; -typedef __int64 ErlDrvSInt64; -#elif SIZEOF_LONG == 8 -typedef unsigned long ErlDrvUInt64; -typedef long ErlDrvSInt64; -#elif SIZEOF_LONG_LONG == 8 -typedef unsigned long long ErlDrvUInt64; -typedef long long ErlDrvSInt64; -#else -#error No 64-bit integer type -#endif - /* * A driver monitor */ @@ -394,9 +395,9 @@ EXTERN int driver_exit (ErlDrvPort port, int err); EXTERN ErlDrvPDL driver_pdl_create(ErlDrvPort); EXTERN void driver_pdl_lock(ErlDrvPDL); EXTERN void driver_pdl_unlock(ErlDrvPDL); -EXTERN long driver_pdl_get_refc(ErlDrvPDL); -EXTERN long driver_pdl_inc_refc(ErlDrvPDL); -EXTERN long driver_pdl_dec_refc(ErlDrvPDL); +EXTERN ErlDrvSInt driver_pdl_get_refc(ErlDrvPDL); +EXTERN ErlDrvSInt driver_pdl_inc_refc(ErlDrvPDL); +EXTERN ErlDrvSInt driver_pdl_dec_refc(ErlDrvPDL); /* * Process monitors @@ -432,9 +433,9 @@ EXTERN ErlDrvBinary* driver_realloc_binary(ErlDrvBinary *bin, int size); EXTERN void driver_free_binary(ErlDrvBinary *bin); /* Referenc count on driver binaries */ -EXTERN long driver_binary_get_refc(ErlDrvBinary *dbp); -EXTERN long driver_binary_inc_refc(ErlDrvBinary *dbp); -EXTERN long driver_binary_dec_refc(ErlDrvBinary *dbp); +EXTERN ErlDrvSInt driver_binary_get_refc(ErlDrvBinary *dbp); +EXTERN ErlDrvSInt driver_binary_inc_refc(ErlDrvBinary *dbp); +EXTERN ErlDrvSInt driver_binary_dec_refc(ErlDrvBinary *dbp); /* Allocation interface */ EXTERN void *driver_alloc(size_t size); diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c index 84869f12d6..88947b5536 100644 --- a/erts/emulator/beam/erl_fun.c +++ b/erts/emulator/beam/erl_fun.c @@ -97,7 +97,7 @@ erts_put_fun_entry(Eterm mod, int uniq, int index) { ErlFunEntry template; ErlFunEntry* fe; - long refc; + erts_aint_t refc; ASSERT(is_atom(mod)); template.old_uniq = uniq; template.old_index = index; @@ -119,7 +119,7 @@ erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index, { ErlFunEntry template; ErlFunEntry* fe; - long refc; + erts_aint_t refc; ASSERT(is_atom(mod)); template.old_uniq = old_uniq; @@ -157,7 +157,7 @@ erts_get_fun_entry(Eterm mod, int uniq, int index) erts_fun_read_lock(); ret = (ErlFunEntry *) hash_get(&erts_fun_table, (void*) &template); if (ret) { - long refc = erts_refc_inctest(&ret->refc, 1); + erts_aint_t refc = erts_refc_inctest(&ret->refc, 1); if (refc < 2) /* Pending delete */ erts_refc_inc(&ret->refc, 1); } @@ -257,7 +257,7 @@ erts_dump_fun_entries(int to, void *to_arg) #ifdef HIPE erts_print(to, to_arg, "Native_address: %p\n", fe->native_address); #endif - erts_print(to, to_arg, "Refc: %d\n", erts_refc_read(&fe->refc, 1)); + erts_print(to, to_arg, "Refc: %ld\n", erts_refc_read(&fe->refc, 1)); b = b->next; } } diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 0f4d2a2ef9..2aa932e7d1 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -2471,7 +2471,7 @@ erts_check_off_heap2(Process *p, Eterm *htop) old = 0; for (u.hdr = MSO(p).first; u.hdr; u.hdr = u.hdr->next) { - long refc; + erts_aint_t refc; switch (thing_subtag(u.hdr->thing_word)) { case REFC_BINARY_SUBTAG: refc = erts_refc_read(&u.pb->val->refc, 1); diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 464ee750f7..0a57eb6d88 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -100,7 +100,7 @@ int erts_backtrace_depth; /* How many functions to show in a backtrace int erts_async_max_threads; /* number of threads for async support */ int erts_async_thread_suggested_stack_size; -erts_smp_atomic_t erts_max_gen_gcs; +erts_smp_atomic32_t erts_max_gen_gcs; Eterm erts_error_logger_warnings; /* What to map warning logs to, am_error, am_info or am_warning, am_error is @@ -250,7 +250,7 @@ erl_init(int ncpu) erts_init_monitors(); erts_init_gc(); - init_time(); + erts_init_time(); erts_init_sys_common_misc(); erts_init_process(ncpu); erts_init_scheduling(use_multi_run_queue, @@ -289,7 +289,7 @@ erl_init(int ncpu) erts_delay_trap = erts_export_put(am_erlang, am_delay_trap, 2); erts_late_init_process(); #if HAVE_ERTS_MSEG - erts_mseg_late_init(); /* Must be after timer (init_time()) and thread + erts_mseg_late_init(); /* Must be after timer (erts_init_time()) and thread initializations */ #endif #ifdef HIPE @@ -323,7 +323,7 @@ init_shared_memory(int argc, char **argv) #endif global_gen_gcs = 0; - global_max_gen_gcs = erts_smp_atomic_read(&erts_max_gen_gcs); + global_max_gen_gcs = (Uint16) erts_smp_atomic32_read(&erts_max_gen_gcs); global_gc_flags = erts_default_process_flags; erts_global_offheap.mso = NULL; @@ -651,7 +651,7 @@ early_init(int *argc, char **argv) /* erts_writing_erl_crash_dump = 0; #endif - erts_smp_atomic_init(&erts_max_gen_gcs, (long)((Uint16) -1)); + erts_smp_atomic32_init(&erts_max_gen_gcs, (erts_aint32_t) ((Uint16) -1)); erts_pre_init_process(); #if defined(USE_THREADS) && !defined(ERTS_SMP) @@ -856,7 +856,7 @@ erl_start(int argc, char **argv) envbufsz = sizeof(envbuf); if (erts_sys_getenv("ERL_FULLSWEEP_AFTER", envbuf, &envbufsz) == 0) { Uint16 max_gen_gcs = atoi(envbuf); - erts_smp_atomic_set(&erts_max_gen_gcs, (long) max_gen_gcs); + erts_smp_atomic32_set(&erts_max_gen_gcs, (erts_aint32_t) max_gen_gcs); } envbufsz = sizeof(envbuf); diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 04c7dbd2ec..0185baee6b 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -177,6 +177,8 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "async_id", NULL }, { "pix_lock", "address" }, { "run_queues_lists", NULL }, + { "misc_aux_work_queue", "index" }, + { "misc_aux_work_pre_alloc_lock", "address" }, { "sched_stat", NULL }, { "run_queue_sleep_list", "address" }, #endif @@ -978,10 +980,10 @@ erts_lc_trylock_force_busy_flg(erts_lc_lock_t *lck, Uint16 op_flags) /* We only force busy if a lock order violation would occur and when on an even millisecond. */ { - erts_thr_timeval_t time; - erts_thr_time_now(&time); + SysTimeval tv; + sys_gettimeofday(&tv); - if ((time.tv_nsec / 1000000) & 1) + if ((tv.tv_usec / 1000) & 1) return 0; } #endif diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c index 239773f366..a36c53560e 100644 --- a/erts/emulator/beam/erl_lock_count.c +++ b/erts/emulator/beam/erl_lock_count.c @@ -159,7 +159,7 @@ static char* lock_opt(Uint16 flag) { } static void print_lock_x(erts_lcnt_lock_t *lock, Uint16 flag, char *action, char *extra) { - long int colls, tries, w_state, r_state; + erts_aint_t colls, tries, w_state, r_state; erts_lcnt_lock_stats_t *stats = NULL; char *type; @@ -385,7 +385,7 @@ void erts_lcnt_destroy_lock(erts_lcnt_lock_t *lock) { /* lock */ void erts_lcnt_lock_opt(erts_lcnt_lock_t *lock, Uint16 option) { - long r_state = 0, w_state = 0; + erts_aint_t r_state = 0, w_state = 0; erts_lcnt_thread_data_t *eltd; if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; @@ -418,7 +418,7 @@ void erts_lcnt_lock_opt(erts_lcnt_lock_t *lock, Uint16 option) { } void erts_lcnt_lock(erts_lcnt_lock_t *lock) { - long w_state; + erts_aint_t w_state; erts_lcnt_thread_data_t *eltd; if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; @@ -471,7 +471,7 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line erts_lcnt_time_t time_wait; erts_lcnt_lock_stats_t *stats; #ifdef DEBUG - long flowstate; + erts_aint_t flowstate; #endif if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; @@ -516,8 +516,8 @@ void erts_lcnt_unlock_opt(erts_lcnt_lock_t *lock, Uint16 option) { void erts_lcnt_unlock(erts_lcnt_lock_t *lock) { #ifdef DEBUG - long w_state; - long flowstate; + erts_aint_t w_state; + erts_aint_t flowstate; #endif if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; #ifdef DEBUG @@ -552,7 +552,7 @@ void erts_lcnt_trylock_opt(erts_lcnt_lock_t *lock, int res, Uint16 option) { void erts_lcnt_trylock(erts_lcnt_lock_t *lock, int res) { /* Determine lock_state via res instead of state */ #ifdef DEBUG - long flowstate; + erts_aint_t flowstate; #endif if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return; if (res != EBUSY) { diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 8cdda395df..6daa127d23 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -235,7 +235,7 @@ erts_sysname_to_connected_dist_entry(Eterm sysname) erts_smp_rwmtx_rlock(&erts_dist_table_rwmtx); res_dep = (DistEntry *) hash_get(&erts_dist_table, (void *) &de); if (res_dep) { - long refc = erts_refc_inctest(&res_dep->refc, 1); + erts_aint_t refc = erts_refc_inctest(&res_dep->refc, 1); if (refc < 2) /* Pending delete */ erts_refc_inc(&res_dep->refc, 1); } @@ -257,7 +257,7 @@ DistEntry *erts_find_or_insert_dist_entry(Eterm sysname) { DistEntry *res; DistEntry de; - long refc; + erts_aint_t refc; res = erts_find_dist_entry(sysname); if (res) return res; @@ -279,7 +279,7 @@ DistEntry *erts_find_dist_entry(Eterm sysname) erts_smp_rwmtx_rlock(&erts_dist_table_rwmtx); res = hash_get(&erts_dist_table, (void *) &de); if (res) { - long refc = erts_refc_inctest(&res->refc, 1); + erts_aint_t refc = erts_refc_inctest(&res->refc, 1); if (refc < 2) /* Pending delete */ erts_refc_inc(&res->refc, 1); } @@ -586,7 +586,7 @@ ErlNode *erts_find_or_insert_node(Eterm sysname, Uint creation) erts_smp_rwmtx_rlock(&erts_node_table_rwmtx); res = hash_get(&erts_node_table, (void *) &ne); if (res && res != erts_this_node) { - long refc = erts_refc_inctest(&res->refc, 0); + erts_aint_t refc = erts_refc_inctest(&res->refc, 0); if (refc < 2) /* New or pending delete */ erts_refc_inc(&res->refc, 1); } @@ -598,7 +598,7 @@ ErlNode *erts_find_or_insert_node(Eterm sysname, Uint creation) res = hash_put(&erts_node_table, (void *) &ne); ASSERT(res); if (res != erts_this_node) { - long refc = erts_refc_inctest(&res->refc, 0); + erts_aint_t refc = erts_refc_inctest(&res->refc, 0); if (refc < 2) /* New or pending delete */ erts_refc_inc(&res->refc, 1); } diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index c10724b951..1b07024ca1 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -129,7 +129,7 @@ reset_handle(ErtsPortTask *ptp) { if (ptp->handle) { ASSERT(ptp == handle2task(ptp->handle)); - erts_smp_atomic_set(ptp->handle, (long) NULL); + erts_smp_atomic_set(ptp->handle, (erts_aint_t) NULL); } } @@ -138,7 +138,7 @@ set_handle(ErtsPortTask *ptp, ErtsPortTaskHandle *pthp) { ptp->handle = pthp; if (pthp) { - erts_smp_atomic_set(pthp, (long) ptp); + erts_smp_atomic_set(pthp, (erts_aint_t) ptp); ASSERT(ptp == handle2task(ptp->handle)); } } @@ -568,7 +568,7 @@ erts_port_task_schedule(Eterm id, ErtsRunQueue *xrunq = erts_check_emigration_need(runq, ERTS_PORT_PRIO_LEVEL); if (xrunq) { /* Port emigrated ... */ - erts_smp_atomic_set(&pp->run_queue, (long) xrunq); + erts_smp_atomic_set(&pp->run_queue, (erts_aint_t) xrunq); erts_smp_runq_unlock(runq); runq = xrunq; } @@ -727,7 +727,8 @@ resume_after_block(void *vd) ErtsPortTaskExeBlockData *d = (ErtsPortTaskExeBlockData *) vd; erts_smp_runq_lock(d->runq); if (d->resp) - *d->resp = erts_smp_atomic_read(&erts_port_task_outstanding_io_tasks) != (long) 0; + *d->resp = (erts_smp_atomic_read(&erts_port_task_outstanding_io_tasks) + != (erts_aint_t) 0); } /* @@ -748,7 +749,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) ErtsPortTask *ptp; int res = 0; int reds = ERTS_PORT_REDS_EXECUTE; - long io_tasks_executed = 0; + erts_aint_t io_tasks_executed = 0; int fpe_was_unmasked; ErtsPortTaskExeBlockData blk_data = {runq, NULL}; @@ -942,7 +943,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) } else { /* Port emigrated ... */ - erts_smp_atomic_set(&pp->run_queue, (long) xrunq); + erts_smp_atomic_set(&pp->run_queue, (erts_aint_t) xrunq); enqueue_port(xrunq, pp); ASSERT(pp->sched.exe_taskq); pp->sched.exe_taskq = NULL; @@ -953,7 +954,8 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) port_was_enqueued = 1; } - res = erts_smp_atomic_read(&erts_port_task_outstanding_io_tasks) != (long) 0; + res = (erts_smp_atomic_read(&erts_port_task_outstanding_io_tasks) + != (erts_aint_t) 0); ERTS_PT_CHK_PRES_PORTQ(runq, pp); @@ -971,7 +973,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) erts_port_release(pp); #else { - long refc; + erts_aint_t refc; erts_smp_mtx_unlock(pp->lock); refc = erts_smp_atomic_dectest(&pp->refc); ASSERT(refc >= 0); @@ -979,7 +981,8 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) erts_smp_runq_unlock(runq); erts_port_cleanup(pp); /* Might aquire runq lock */ erts_smp_runq_lock(runq); - res = erts_smp_atomic_read(&erts_port_task_outstanding_io_tasks) != (long) 0; + res = (erts_smp_atomic_read(&erts_port_task_outstanding_io_tasks) + != (erts_aint_t) 0); } } #endif @@ -1112,7 +1115,7 @@ erts_port_migrate(Port *prt, int *prt_locked, if (!ERTS_PORT_IS_IN_RUNQ(from_rq, prt)) return ERTS_MIGRATE_FAILED_NOT_IN_RUNQ; dequeue_port(from_rq, prt); - erts_smp_atomic_set(&prt->run_queue, (long) to_rq); + erts_smp_atomic_set(&prt->run_queue, (erts_aint_t) to_rq); enqueue_port(to_rq, prt); return ERTS_MIGRATE_SUCCESS; } @@ -1125,7 +1128,7 @@ erts_port_migrate(Port *prt, int *prt_locked, void erts_port_task_init(void) { - erts_smp_atomic_init(&erts_port_task_outstanding_io_tasks, (long) 0); + erts_smp_atomic_init(&erts_port_task_outstanding_io_tasks, (erts_aint_t) 0); init_port_task_alloc(); init_port_taskq_alloc(); } diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h index f12d02da0c..714b4ea7dd 100644 --- a/erts/emulator/beam/erl_port_task.h +++ b/erts/emulator/beam/erl_port_task.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * Copyright Ericsson AB 2006-2010. 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 @@ -79,7 +79,7 @@ ERTS_GLB_INLINE int erts_port_task_have_outstanding_io_tasks(void); ERTS_GLB_INLINE void erts_port_task_handle_init(ErtsPortTaskHandle *pthp) { - erts_smp_atomic_init(pthp, (long) NULL); + erts_smp_atomic_init(pthp, (erts_aint_t) NULL); } ERTS_GLB_INLINE int diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index fc950af8ce..ddfc27a93f 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -127,21 +127,22 @@ ErtsLcPSDLocks erts_psd_required_locks[ERTS_PSD_SIZE]; int erts_disable_proc_not_running_opt; -#define ERTS_SCHDLR_SSPND_CHNG_WAITER (((long) 1) << 0) -#define ERTS_SCHDLR_SSPND_CHNG_MSB (((long) 1) << 1) -#define ERTS_SCHDLR_SSPND_CHNG_ONLN (((long) 1) << 2) +#define ERTS_SCHDLR_SSPND_CHNG_WAITER (((erts_aint32_t) 1) << 0) +#define ERTS_SCHDLR_SSPND_CHNG_MSB (((erts_aint32_t) 1) << 1) +#define ERTS_SCHDLR_SSPND_CHNG_ONLN (((erts_aint32_t) 1) << 2) #ifndef DEBUG #define ERTS_SCHDLR_SSPND_CHNG_SET(VAL, OLD_VAL) \ - erts_smp_atomic_set(&schdlr_sspnd.changing, (VAL)) + erts_smp_atomic32_set(&schdlr_sspnd.changing, (VAL)) #else #define ERTS_SCHDLR_SSPND_CHNG_SET(VAL, OLD_VAL) \ do { \ - long old_val__ = erts_smp_atomic_xchg(&schdlr_sspnd.changing, \ - (VAL)); \ + erts_aint32_t old_val__; \ + old_val__ = erts_smp_atomic32_xchg(&schdlr_sspnd.changing, \ + (VAL)); \ ASSERT(old_val__ == (OLD_VAL)); \ } while (0) @@ -154,10 +155,10 @@ static struct { int online; int curr_online; int wait_curr_online; - erts_smp_atomic_t changing; - erts_smp_atomic_t active; + erts_smp_atomic32_t changing; + erts_smp_atomic32_t active; struct { - erts_smp_atomic_t ongoing; + erts_smp_atomic32_t ongoing; long wait_active; ErtsProcList *procs; } msb; /* Multi Scheduling Block */ @@ -165,11 +166,11 @@ static struct { static struct { erts_smp_mtx_t update_mtx; - erts_smp_atomic_t active_runqs; + erts_smp_atomic32_t active_runqs; int last_active_runqs; - erts_smp_atomic_t used_runqs; + erts_smp_atomic32_t used_runqs; int forced_check_balance; - erts_smp_atomic_t checking_balance; + erts_smp_atomic32_t checking_balance; int halftime; int full_reds_history_index; struct { @@ -199,11 +200,11 @@ static erts_tsd_key_t sched_data_key; static erts_smp_mtx_t proc_tab_mtx; -static erts_smp_atomic_t function_calls; +static erts_smp_atomic32_t function_calls; #ifdef ERTS_SMP -static erts_smp_atomic_t doing_sys_schedule; -static erts_smp_atomic_t no_empty_run_queues; +static erts_smp_atomic32_t doing_sys_schedule; +static erts_smp_atomic32_t no_empty_run_queues; #else /* !ERTS_SMP */ ErtsSchedulerData *erts_scheduler_data; #endif @@ -247,7 +248,10 @@ Uint erts_num_active_procs; Process** erts_active_procs; #endif -static erts_smp_atomic_t process_count; +#if ERTS_MAX_PROCESSES > 0x7fffffff +#error "Need to store process_count in another type" +#endif +static erts_smp_atomic32_t process_count; typedef struct ErtsTermProcElement_ ErtsTermProcElement; struct ErtsTermProcElement_ { @@ -407,7 +411,7 @@ erts_init_process(int ncpu) init_proclist_alloc(); - erts_smp_atomic_init(&process_count, 0); + erts_smp_atomic32_init(&process_count, 0); if (erts_use_r9_pids_ports) { proc_bits = ERTS_R9_PROC_BITS; @@ -568,7 +572,7 @@ erts_psd_set_init(Process *p, ErtsProcLocks plocks, int ix, void *data) #ifdef ERTS_SMP void -erts_sched_finish_poke(ErtsSchedulerSleepInfo *ssi, long flags) +erts_sched_finish_poke(ErtsSchedulerSleepInfo *ssi, erts_aint32_t flags) { switch (flags & ERTS_SSI_FLGS_SLEEP_TYPE) { case ERTS_SSI_FLG_POLL_SLEEPING: @@ -586,6 +590,122 @@ erts_sched_finish_poke(ErtsSchedulerSleepInfo *ssi, long flags) } } +typedef struct erts_misc_aux_work_t_ erts_misc_aux_work_t; +struct erts_misc_aux_work_t_ { + erts_misc_aux_work_t *next; + void (*func)(void *); + void *arg; +}; + +typedef struct { + erts_smp_mtx_t mtx; + erts_misc_aux_work_t *first; + erts_misc_aux_work_t *last; +} erts_misc_aux_work_q_t; + +typedef union { + erts_misc_aux_work_q_t data; + char align[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(erts_misc_aux_work_q_t))]; +} erts_algnd_misc_aux_work_q_t; + +static erts_algnd_misc_aux_work_q_t *misc_aux_work_queues; + +ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(misc_aux_work, + erts_misc_aux_work_t, + 200, + ERTS_ALC_T_MISC_AUX_WORK) + +static void +init_misc_aux_work(void) +{ + int ix; + + init_misc_aux_work_alloc(); + + misc_aux_work_queues = erts_alloc(ERTS_ALC_T_MISC_AUX_WORK_Q, + (sizeof(erts_algnd_misc_aux_work_q_t) + *(erts_no_schedulers+1))); + if ((((UWord) misc_aux_work_queues) & ERTS_CACHE_LINE_MASK) != 0) + misc_aux_work_queues = ((erts_algnd_misc_aux_work_q_t *) + ((((UWord) misc_aux_work_queues) + & ~ERTS_CACHE_LINE_MASK) + + ERTS_CACHE_LINE_SIZE)); + + for (ix = 0; ix < erts_no_schedulers; ix++) { + erts_smp_mtx_init_x(&misc_aux_work_queues[ix].data.mtx, + "misc_aux_work_queue", + make_small(ix + 1)); + misc_aux_work_queues[ix].data.first = NULL; + misc_aux_work_queues[ix].data.last = NULL; + } +} + +static void +handle_misc_aux_work(ErtsSchedulerData *esdp) +{ + int ix = (int) esdp->no - 1; + erts_misc_aux_work_t *mawp; + + erts_smp_mtx_lock(&misc_aux_work_queues[ix].data.mtx); + mawp = misc_aux_work_queues[ix].data.first; + misc_aux_work_queues[ix].data.first = NULL; + misc_aux_work_queues[ix].data.last = NULL; + erts_smp_mtx_unlock(&misc_aux_work_queues[ix].data.mtx); + + while (mawp) { + erts_misc_aux_work_t *free_mawp; + mawp->func(mawp->arg); + free_mawp = mawp; + mawp = mawp->next; + misc_aux_work_free(free_mawp); + } +} + +void +erts_smp_schedule_misc_aux_work(int ignore_self, + int max_sched, + void (*func)(void *), + void *arg) +{ + int ix, ignore_ix = -1; + + if (ignore_self) { + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + if (esdp) + ignore_ix = (int) esdp->no - 1; + } + + ASSERT(0 <= max_sched && max_sched <= erts_no_schedulers); + + for (ix = 0; ix < max_sched; ix++) { + erts_aint32_t aux_work; + erts_misc_aux_work_t *mawp; + ErtsSchedulerSleepInfo *ssi; + if (ix == ignore_ix) + continue; + + mawp = misc_aux_work_alloc(); + + mawp->func = func; + mawp->arg = arg; + mawp->next = NULL; + + erts_smp_mtx_lock(&misc_aux_work_queues[ix].data.mtx); + if (!misc_aux_work_queues[ix].data.last) + misc_aux_work_queues[ix].data.first = mawp; + else + misc_aux_work_queues[ix].data.last->next = mawp; + misc_aux_work_queues[ix].data.last = mawp; + erts_smp_mtx_unlock(&misc_aux_work_queues[ix].data.mtx); + + ssi = ERTS_SCHED_SLEEP_INFO_IX(ix); + aux_work = erts_smp_atomic32_bor(&ssi->aux_work, + ERTS_SSI_AUX_WORK_MISC); + if ((aux_work & ERTS_SSI_AUX_WORK_MISC) == 0) + erts_sched_poke(ssi); + } +} + #ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN void erts_smp_notify_check_children_needed(void) @@ -593,11 +713,11 @@ erts_smp_notify_check_children_needed(void) int i; for (i = 0; i < erts_no_schedulers; i++) { - long aux_work; + erts_aint32_t aux_work; ErtsSchedulerSleepInfo *ssi; ssi = ERTS_SCHED_SLEEP_INFO_IX(i); - aux_work = erts_smp_atomic_bor(&ssi->aux_work, - ERTS_SSI_AUX_WORK_CHECK_CHILDREN); + aux_work = erts_smp_atomic32_bor(&ssi->aux_work, + ERTS_SSI_AUX_WORK_CHECK_CHILDREN); if (!(aux_work & ERTS_SSI_AUX_WORK_CHECK_CHILDREN)) erts_sched_poke(ssi); } @@ -605,16 +725,22 @@ erts_smp_notify_check_children_needed(void) #endif #ifdef ERTS_SCHED_NEED_BLOCKABLE_AUX_WORK -static ERTS_INLINE long +static ERTS_INLINE erts_aint32_t blockable_aux_work(ErtsSchedulerData *esdp, ErtsSchedulerSleepInfo *ssi, - long aux_work) + erts_aint32_t aux_work) { if (aux_work & ERTS_SSI_BLOCKABLE_AUX_WORK_MASK) { + if (aux_work & ERTS_SSI_AUX_WORK_MISC) { + aux_work = erts_smp_atomic32_band(&ssi->aux_work, + ~ERTS_SSI_AUX_WORK_MISC); + aux_work &= ~ERTS_SSI_AUX_WORK_MISC; + handle_misc_aux_work(esdp); + } #ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN if (aux_work & ERTS_SSI_AUX_WORK_CHECK_CHILDREN) { - aux_work = erts_smp_atomic_band(&ssi->aux_work, - ~ERTS_SSI_AUX_WORK_CHECK_CHILDREN); + aux_work = erts_smp_atomic32_band(&ssi->aux_work, + ~ERTS_SSI_AUX_WORK_CHECK_CHILDREN); aux_work &= ~ERTS_SSI_AUX_WORK_CHECK_CHILDREN; erts_check_children(); } @@ -626,10 +752,10 @@ blockable_aux_work(ErtsSchedulerData *esdp, #endif #ifdef ERTS_SCHED_NEED_NONBLOCKABLE_AUX_WORK -static ERTS_INLINE long +static ERTS_INLINE erts_aint32_t nonblockable_aux_work(ErtsSchedulerData *esdp, ErtsSchedulerSleepInfo *ssi, - long aux_work) + erts_aint32_t aux_work) { if (aux_work & ERTS_SSI_NONBLOCKABLE_AUX_WORK_MASK) { @@ -694,10 +820,10 @@ prepare_for_sys_schedule(void) { #ifdef ERTS_SMP while (!erts_port_task_have_outstanding_io_tasks() - && !erts_smp_atomic_xchg(&doing_sys_schedule, 1)) { + && !erts_smp_atomic32_xchg(&doing_sys_schedule, 1)) { if (!erts_port_task_have_outstanding_io_tasks()) return 1; - erts_smp_atomic_set(&doing_sys_schedule, 0); + erts_smp_atomic32_set(&doing_sys_schedule, 0); } return 0; #else @@ -745,53 +871,55 @@ sched_active(Uint no, ErtsRunQueue *rq) static int ERTS_INLINE ongoing_multi_scheduling_block(void) { - return erts_smp_atomic_read(&schdlr_sspnd.msb.ongoing) != 0; + return erts_smp_atomic32_read(&schdlr_sspnd.msb.ongoing) != 0; } static ERTS_INLINE void empty_runq(ErtsRunQueue *rq) { - long oifls = erts_smp_atomic_band(&rq->info_flags, ~ERTS_RUNQ_IFLG_NONEMPTY); + erts_aint32_t oifls = erts_smp_atomic32_band(&rq->info_flags, + ~ERTS_RUNQ_IFLG_NONEMPTY); if (oifls & ERTS_RUNQ_IFLG_NONEMPTY) { #ifdef DEBUG - long empty = erts_smp_atomic_read(&no_empty_run_queues); + erts_aint32_t empty = erts_smp_atomic32_read(&no_empty_run_queues); /* * For a short period of time no_empty_run_queues may have * been increased twice for a specific run queue. */ ASSERT(0 <= empty && empty < 2*erts_no_run_queues); #endif - erts_smp_atomic_inc(&no_empty_run_queues); + erts_smp_atomic32_inc(&no_empty_run_queues); } } static ERTS_INLINE void non_empty_runq(ErtsRunQueue *rq) { - long oifls = erts_smp_atomic_bor(&rq->info_flags, ERTS_RUNQ_IFLG_NONEMPTY); + erts_aint32_t oifls = erts_smp_atomic32_bor(&rq->info_flags, + ERTS_RUNQ_IFLG_NONEMPTY); if (!(oifls & ERTS_RUNQ_IFLG_NONEMPTY)) { #ifdef DEBUG - long empty = erts_smp_atomic_read(&no_empty_run_queues); + erts_aint32_t empty = erts_smp_atomic32_read(&no_empty_run_queues); /* * For a short period of time no_empty_run_queues may have * been increased twice for a specific run queue. */ ASSERT(0 < empty && empty <= 2*erts_no_run_queues); #endif - erts_smp_atomic_dec(&no_empty_run_queues); + erts_smp_atomic32_dec(&no_empty_run_queues); } } -static long +static erts_aint32_t sched_prep_spin_wait(ErtsSchedulerSleepInfo *ssi) { - long oflgs; - long nflgs = (ERTS_SSI_FLG_SLEEPING - | ERTS_SSI_FLG_WAITING); - long xflgs = 0; + erts_aint32_t oflgs; + erts_aint32_t nflgs = (ERTS_SSI_FLG_SLEEPING + | ERTS_SSI_FLG_WAITING); + erts_aint32_t xflgs = 0; do { - oflgs = erts_smp_atomic_cmpxchg(&ssi->flags, nflgs, xflgs); + oflgs = erts_smp_atomic32_cmpxchg(&ssi->flags, nflgs, xflgs); if (oflgs == xflgs) return nflgs; xflgs = oflgs; @@ -799,16 +927,16 @@ sched_prep_spin_wait(ErtsSchedulerSleepInfo *ssi) return oflgs; } -static long +static erts_aint32_t sched_prep_cont_spin_wait(ErtsSchedulerSleepInfo *ssi) { - long oflgs; - long nflgs = (ERTS_SSI_FLG_SLEEPING - | ERTS_SSI_FLG_WAITING); - long xflgs = ERTS_SSI_FLG_WAITING; + erts_aint32_t oflgs; + erts_aint32_t nflgs = (ERTS_SSI_FLG_SLEEPING + | ERTS_SSI_FLG_WAITING); + erts_aint32_t xflgs = ERTS_SSI_FLG_WAITING; do { - oflgs = erts_smp_atomic_cmpxchg(&ssi->flags, nflgs, xflgs); + oflgs = erts_smp_atomic32_cmpxchg(&ssi->flags, nflgs, xflgs); if (oflgs == xflgs) return nflgs; xflgs = oflgs; @@ -817,15 +945,15 @@ sched_prep_cont_spin_wait(ErtsSchedulerSleepInfo *ssi) return oflgs; } -static long +static erts_aint32_t sched_spin_wait(ErtsSchedulerSleepInfo *ssi, int spincount) { - long until_yield = ERTS_SCHED_SPIN_UNTIL_YIELD; + int until_yield = ERTS_SCHED_SPIN_UNTIL_YIELD; int sc = spincount; - long flgs; + erts_aint32_t flgs; do { - flgs = erts_smp_atomic_read(&ssi->flags); + flgs = erts_smp_atomic32_read(&ssi->flags); if ((flgs & (ERTS_SSI_FLG_SLEEPING|ERTS_SSI_FLG_WAITING)) != (ERTS_SSI_FLG_SLEEPING|ERTS_SSI_FLG_WAITING)) { break; @@ -839,18 +967,18 @@ sched_spin_wait(ErtsSchedulerSleepInfo *ssi, int spincount) return flgs; } -static long -sched_set_sleeptype(ErtsSchedulerSleepInfo *ssi, long sleep_type) +static erts_aint32_t +sched_set_sleeptype(ErtsSchedulerSleepInfo *ssi, erts_aint32_t sleep_type) { - long oflgs; - long nflgs = ERTS_SSI_FLG_SLEEPING|ERTS_SSI_FLG_WAITING|sleep_type; - long xflgs = ERTS_SSI_FLG_SLEEPING|ERTS_SSI_FLG_WAITING; + erts_aint32_t oflgs; + erts_aint32_t nflgs = ERTS_SSI_FLG_SLEEPING|ERTS_SSI_FLG_WAITING|sleep_type; + erts_aint32_t xflgs = ERTS_SSI_FLG_SLEEPING|ERTS_SSI_FLG_WAITING; if (sleep_type == ERTS_SSI_FLG_TSE_SLEEPING) erts_tse_reset(ssi->event); while (1) { - oflgs = erts_smp_atomic_cmpxchg(&ssi->flags, nflgs, xflgs); + oflgs = erts_smp_atomic32_cmpxchg(&ssi->flags, nflgs, xflgs); if (oflgs == xflgs) return nflgs; if ((oflgs & (ERTS_SSI_FLG_SLEEPING|ERTS_SSI_FLG_WAITING)) @@ -867,14 +995,14 @@ sched_set_sleeptype(ErtsSchedulerSleepInfo *ssi, long sleep_type) != ERTS_SSI_FLG_WAITING) static void -scheduler_wait(long *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) +scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) { ErtsSchedulerSleepInfo *ssi = esdp->ssi; int spincount; - long flgs; + erts_aint32_t flgs; #if defined(ERTS_SCHED_NEED_NONBLOCKABLE_AUX_WORK) \ || defined(ERTS_SCHED_NEED_BLOCKABLE_AUX_WORK) - long aux_work; + erts_aint32_t aux_work; #endif ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); @@ -910,7 +1038,7 @@ scheduler_wait(long *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) tse_wait: #ifdef ERTS_SCHED_NEED_BLOCKABLE_AUX_WORK - aux_work = erts_smp_atomic_read(&ssi->aux_work); + aux_work = erts_smp_atomic32_read(&ssi->aux_work); tse_blockable_aux_work: aux_work = blockable_aux_work(esdp, ssi, aux_work); #endif @@ -920,7 +1048,7 @@ scheduler_wait(long *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) #ifdef ERTS_SCHED_NEED_NONBLOCKABLE_AUX_WORK #ifndef ERTS_SCHED_NEED_BLOCKABLE_AUX_WORK - aux_work = erts_smp_atomic_read(&ssi->aux_work); + aux_work = erts_smp_atomic32_read(&ssi->aux_work); #endif nonblockable_aux_work(esdp, ssi, aux_work); #endif @@ -953,7 +1081,7 @@ scheduler_wait(long *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) } #ifdef ERTS_SCHED_NEED_BLOCKABLE_AUX_WORK - aux_work = erts_smp_atomic_read(&ssi->aux_work); + aux_work = erts_smp_atomic32_read(&ssi->aux_work); if (aux_work & ERTS_SSI_BLOCKABLE_AUX_WORK_MASK) { erts_smp_activity_end(ERTS_ACTIVITY_WAIT, NULL, NULL, NULL); goto tse_blockable_aux_work; @@ -965,16 +1093,16 @@ scheduler_wait(long *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erts_smp_activity_end(ERTS_ACTIVITY_WAIT, NULL, NULL, NULL); if (flgs & ~ERTS_SSI_FLG_SUSPENDED) - erts_smp_atomic_band(&ssi->flags, ERTS_SSI_FLG_SUSPENDED); + erts_smp_atomic32_band(&ssi->flags, ERTS_SSI_FLG_SUSPENDED); erts_smp_runq_lock(rq); sched_active(esdp->no, rq); } else { - long dt; + erts_aint_t dt; - erts_smp_atomic_set(&function_calls, 0); + erts_smp_atomic32_set(&function_calls, 0); *fcalls = 0; sched_waiting_sys(esdp->no, rq); @@ -991,23 +1119,23 @@ scheduler_wait(long *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erl_sys_schedule(1); /* Might give us something to do */ - dt = do_time_read_and_reset(); - if (dt) bump_timer(dt); + dt = erts_do_time_read_and_reset(); + if (dt) erts_bump_timer(dt); sys_aux_work: #ifdef ERTS_SCHED_NEED_BLOCKABLE_AUX_WORK - aux_work = erts_smp_atomic_read(&ssi->aux_work); + aux_work = erts_smp_atomic32_read(&ssi->aux_work); aux_work = blockable_aux_work(esdp, ssi, aux_work); #endif #ifdef ERTS_SCHED_NEED_NONBLOCKABLE_AUX_WORK #ifndef ERTS_SCHED_NEED_BLOCKABLE_AUX_WORK - aux_work = erts_smp_atomic_read(&ssi->aux_work); + aux_work = erts_smp_atomic32_read(&ssi->aux_work); #endif nonblockable_aux_work(esdp, ssi, aux_work); #endif - flgs = erts_smp_atomic_read(&ssi->flags); + flgs = erts_smp_atomic32_read(&ssi->flags); if (!(flgs & ERTS_SSI_FLG_WAITING)) { ASSERT(!(flgs & ERTS_SSI_FLG_SLEEPING)); goto sys_woken; @@ -1025,7 +1153,7 @@ scheduler_wait(long *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) * call erl_sys_schedule() until it is handled. */ if (erts_port_task_have_outstanding_io_tasks()) { - erts_smp_atomic_set(&doing_sys_schedule, 0); + erts_smp_atomic32_set(&doing_sys_schedule, 0); /* * Got to check that we still got I/O tasks; otherwise * we have to continue checking for I/O... @@ -1044,7 +1172,7 @@ scheduler_wait(long *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) * sleep in erl_sys_schedule(). */ if (erts_port_task_have_outstanding_io_tasks()) { - erts_smp_atomic_set(&doing_sys_schedule, 0); + erts_smp_atomic32_set(&doing_sys_schedule, 0); /* * Got to check that we still got I/O tasks; otherwise @@ -1088,8 +1216,8 @@ scheduler_wait(long *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erl_sys_schedule(0); - dt = do_time_read_and_reset(); - if (dt) bump_timer(dt); + dt = erts_do_time_read_and_reset(); + if (dt) erts_bump_timer(dt); flgs = sched_prep_cont_spin_wait(ssi); if (flgs & ERTS_SSI_FLG_WAITING) @@ -1098,9 +1226,9 @@ scheduler_wait(long *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) sys_woken: erts_smp_runq_lock(rq); sys_locked_woken: - erts_smp_atomic_set(&doing_sys_schedule, 0); + erts_smp_atomic32_set(&doing_sys_schedule, 0); if (flgs & ~ERTS_SSI_FLG_SUSPENDED) - erts_smp_atomic_band(&ssi->flags, ERTS_SSI_FLG_SUSPENDED); + erts_smp_atomic32_band(&ssi->flags, ERTS_SSI_FLG_SUSPENDED); sched_active_sys(esdp->no, rq); } } @@ -1108,15 +1236,15 @@ scheduler_wait(long *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(rq)); } -static ERTS_INLINE long +static ERTS_INLINE erts_aint32_t ssi_flags_set_wake(ErtsSchedulerSleepInfo *ssi) { /* reset all flags but suspended */ - long oflgs; - long nflgs = 0; - long xflgs = ERTS_SSI_FLG_SLEEPING|ERTS_SSI_FLG_WAITING; + erts_aint32_t oflgs; + erts_aint32_t nflgs = 0; + erts_aint32_t xflgs = ERTS_SSI_FLG_SLEEPING|ERTS_SSI_FLG_WAITING; while (1) { - oflgs = erts_smp_atomic_cmpxchg(&ssi->flags, nflgs, xflgs); + oflgs = erts_smp_atomic32_cmpxchg(&ssi->flags, nflgs, xflgs); if (oflgs == xflgs) return oflgs; nflgs = oflgs & ERTS_SSI_FLG_SUSPENDED; @@ -1148,7 +1276,7 @@ wake_scheduler(ErtsRunQueue *rq, int incq, int one) if (!ssi) erts_smp_spin_unlock(&sl->lock); else if (one) { - long flgs; + erts_aint32_t flgs; if (ssi->prev) ssi->prev->next = ssi->next; else { @@ -1195,15 +1323,17 @@ wake_all_schedulers(void) static ERTS_INLINE int chk_wake_sched(ErtsRunQueue *crq, int ix, int activate) { - long iflgs; + erts_aint32_t iflgs; ErtsRunQueue *wrq; if (crq->ix == ix) return 0; wrq = ERTS_RUNQ_IX(ix); - iflgs = erts_smp_atomic_read(&wrq->info_flags); + iflgs = erts_smp_atomic32_read(&wrq->info_flags); if (!(iflgs & (ERTS_RUNQ_IFLG_SUSPENDED|ERTS_RUNQ_IFLG_NONEMPTY))) { if (activate) { - if (ix == erts_smp_atomic_cmpxchg(&balance_info.active_runqs, ix+1, ix)) { + if (ix == erts_smp_atomic32_cmpxchg(&balance_info.active_runqs, + ix+1, + ix)) { erts_smp_xrunq_lock(crq, wrq); wrq->flags &= ~ERTS_RUNQ_FLG_INACTIVE; erts_smp_xrunq_unlock(crq, wrq); @@ -1220,8 +1350,8 @@ wake_scheduler_on_empty_runq(ErtsRunQueue *crq) { int ix = crq->ix; int stop_ix = ix; - int active_ix = erts_smp_atomic_read(&balance_info.active_runqs); - int balance_ix = erts_smp_atomic_read(&balance_info.used_runqs); + int active_ix = erts_smp_atomic32_read(&balance_info.active_runqs); + int balance_ix = erts_smp_atomic32_read(&balance_info.used_runqs); if (active_ix > balance_ix) active_ix = balance_ix; @@ -1273,7 +1403,7 @@ erts_sched_notify_check_cpu_bind(void) int ix; if (erts_common_run_queue) { for (ix = 0; ix < erts_no_schedulers; ix++) - erts_smp_atomic_set(&ERTS_SCHEDULER_IX(ix)->chk_cpu_bind, 1); + erts_smp_atomic32_set(&ERTS_SCHEDULER_IX(ix)->chk_cpu_bind, 1); wake_all_schedulers(); } else { @@ -1441,14 +1571,15 @@ evacuate_run_queue(ErtsRunQueue *evac_rq, ErtsRunQueue *rq) erts_smp_runq_lock(evac_rq); - erts_smp_atomic_bor(&evac_rq->scheduler->ssi->flags, ERTS_SSI_FLG_SUSPENDED); + erts_smp_atomic32_bor(&evac_rq->scheduler->ssi->flags, + ERTS_SSI_FLG_SUSPENDED); evac_rq->flags &= ~ERTS_RUNQ_FLGS_IMMIGRATE_QMASK; evac_rq->flags |= (ERTS_RUNQ_FLGS_EMIGRATE_QMASK | ERTS_RUNQ_FLGS_EVACUATE_QMASK | ERTS_RUNQ_FLG_SUSPENDED); - erts_smp_atomic_bor(&evac_rq->info_flags, ERTS_RUNQ_IFLG_SUSPENDED); + erts_smp_atomic32_bor(&evac_rq->info_flags, ERTS_RUNQ_IFLG_SUSPENDED); /* * Need to set up evacuation paths first since we * may release the run queue lock on evac_rq @@ -1697,7 +1828,7 @@ static ERTS_INLINE int check_possible_steal_victim(ErtsRunQueue *rq, int *rq_lockedp, int vix) { ErtsRunQueue *vrq = ERTS_RUNQ_IX(vix); - long iflgs = erts_smp_atomic_read(&vrq->info_flags); + erts_aint32_t iflgs = erts_smp_atomic32_read(&vrq->info_flags); if (iflgs & ERTS_RUNQ_IFLG_NONEMPTY) return try_steal_task_from_victim(rq, rq_lockedp, vrq); else @@ -1727,8 +1858,8 @@ try_steal_task(ErtsRunQueue *rq) ERTS_SMP_LC_CHK_RUNQ_LOCK(rq, rq_locked); - active_rqs = erts_smp_atomic_read(&balance_info.active_runqs); - blnc_rqs = erts_smp_atomic_read(&balance_info.used_runqs); + active_rqs = erts_smp_atomic32_read(&balance_info.active_runqs); + blnc_rqs = erts_smp_atomic32_read(&balance_info.used_runqs); if (active_rqs > blnc_rqs) active_rqs = blnc_rqs; @@ -1739,7 +1870,7 @@ try_steal_task(ErtsRunQueue *rq) if (active_rqs < blnc_rqs) { int no = blnc_rqs - active_rqs; int stop_ix = vix = active_rqs + rq->ix % no; - while (erts_smp_atomic_read(&no_empty_run_queues) < blnc_rqs) { + while (erts_smp_atomic32_read(&no_empty_run_queues) < blnc_rqs) { res = check_possible_steal_victim(rq, &rq_locked, vix); if (res) goto done; @@ -1754,7 +1885,7 @@ try_steal_task(ErtsRunQueue *rq) vix = rq->ix; /* ... then try to steal a job from another active queue... */ - while (erts_smp_atomic_read(&no_empty_run_queues) < blnc_rqs) { + while (erts_smp_atomic32_read(&no_empty_run_queues) < blnc_rqs) { vix++; if (vix >= active_rqs) vix = 0; @@ -1850,15 +1981,15 @@ check_balance(ErtsRunQueue *c_rq) int forced, active, current_active, oowc, half_full_scheds, full_scheds, mmax_len, blnc_no_rqs, qix, pix, freds_hist_ix; - if (erts_smp_atomic_xchg(&balance_info.checking_balance, 1)) { + if (erts_smp_atomic32_xchg(&balance_info.checking_balance, 1)) { c_rq->check_balance_reds = INT_MAX; return; } - blnc_no_rqs = (int) erts_smp_atomic_read(&balance_info.used_runqs); + blnc_no_rqs = (int) erts_smp_atomic32_read(&balance_info.used_runqs); if (blnc_no_rqs == 1) { c_rq->check_balance_reds = INT_MAX; - erts_smp_atomic_set(&balance_info.checking_balance, 0); + erts_smp_atomic32_set(&balance_info.checking_balance, 0); return; } @@ -1866,7 +1997,7 @@ check_balance(ErtsRunQueue *c_rq) if (balance_info.halftime) { balance_info.halftime = 0; - erts_smp_atomic_set(&balance_info.checking_balance, 0); + erts_smp_atomic32_set(&balance_info.checking_balance, 0); ERTS_FOREACH_RUNQ(rq, { if (rq->waiting) @@ -1894,12 +2025,12 @@ check_balance(ErtsRunQueue *c_rq) forced = balance_info.forced_check_balance; balance_info.forced_check_balance = 0; - blnc_no_rqs = (int) erts_smp_atomic_read(&balance_info.used_runqs); + blnc_no_rqs = (int) erts_smp_atomic32_read(&balance_info.used_runqs); if (blnc_no_rqs == 1) { erts_smp_mtx_unlock(&balance_info.update_mtx); erts_smp_runq_lock(c_rq); c_rq->check_balance_reds = INT_MAX; - erts_smp_atomic_set(&balance_info.checking_balance, 0); + erts_smp_atomic32_set(&balance_info.checking_balance, 0); return; } @@ -1908,7 +2039,7 @@ check_balance(ErtsRunQueue *c_rq) if (balance_info.full_reds_history_index >= ERTS_FULL_REDS_HISTORY_SIZE) balance_info.full_reds_history_index = 0; - current_active = erts_smp_atomic_read(&balance_info.active_runqs); + current_active = erts_smp_atomic32_read(&balance_info.active_runqs); /* Read balance information for all run queues */ for (qix = 0; qix < blnc_no_rqs; qix++) { @@ -2243,10 +2374,10 @@ erts_fprintf(stderr, "--------------------------------\n"); } balance_info.last_active_runqs = active; - erts_smp_atomic_set(&balance_info.active_runqs, active); + erts_smp_atomic32_set(&balance_info.active_runqs, active); balance_info.halftime = 1; - erts_smp_atomic_set(&balance_info.checking_balance, 0); + erts_smp_atomic32_set(&balance_info.checking_balance, 0); /* Write migration paths and reset balance statistics in all queues */ for (qix = 0; qix < blnc_no_rqs; qix++) { @@ -2395,7 +2526,7 @@ erts_init_scheduling(int mrq, int no_schedulers, int no_schedulers_online) ASSERT((((UWord) erts_aligned_run_queues) & ERTS_CACHE_LINE_MASK) == 0); #ifdef ERTS_SMP - erts_smp_atomic_init(&no_empty_run_queues, 0); + erts_smp_atomic32_init(&no_empty_run_queues, 0); #endif erts_no_run_queues = n; @@ -2405,7 +2536,7 @@ erts_init_scheduling(int mrq, int no_schedulers, int no_schedulers_online) ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); rq->ix = ix; - erts_smp_atomic_init(&rq->info_flags, ERTS_RUNQ_IFLG_NONEMPTY); + erts_smp_atomic32_init(&rq->info_flags, ERTS_RUNQ_IFLG_NONEMPTY); /* make sure that the "extra" id correponds to the schedulers * id if the esdp->no <-> ix+1 mapping change. @@ -2502,9 +2633,9 @@ erts_init_scheduling(int mrq, int no_schedulers, int no_schedulers_online) ssi->next = NULL; ssi->prev = NULL; #endif - erts_smp_atomic_init(&ssi->flags, 0); + erts_smp_atomic32_init(&ssi->flags, 0); ssi->event = NULL; /* initialized in sched_thread_func */ - erts_smp_atomic_init(&ssi->aux_work, 0); + erts_smp_atomic32_init(&ssi->aux_work, 0); } #endif @@ -2555,7 +2686,7 @@ erts_init_scheduling(int mrq, int no_schedulers, int no_schedulers_online) } #ifdef ERTS_SMP - erts_smp_atomic_init(&esdp->chk_cpu_bind, 0); + erts_smp_atomic32_init(&esdp->chk_cpu_bind, 0); #endif } @@ -2563,21 +2694,21 @@ erts_init_scheduling(int mrq, int no_schedulers, int no_schedulers_online) erts_smp_mtx_init(&schdlr_sspnd.mtx, "schdlr_sspnd"); erts_smp_cnd_init(&schdlr_sspnd.cnd); - erts_smp_atomic_init(&schdlr_sspnd.changing, 0); + erts_smp_atomic32_init(&schdlr_sspnd.changing, 0); schdlr_sspnd.online = no_schedulers_online; schdlr_sspnd.curr_online = no_schedulers; - erts_smp_atomic_init(&schdlr_sspnd.msb.ongoing, 0); - erts_smp_atomic_init(&schdlr_sspnd.active, no_schedulers); + erts_smp_atomic32_init(&schdlr_sspnd.msb.ongoing, 0); + erts_smp_atomic32_init(&schdlr_sspnd.active, no_schedulers); schdlr_sspnd.msb.procs = NULL; - erts_smp_atomic_set(&balance_info.used_runqs, - erts_common_run_queue ? 1 : no_schedulers_online); - erts_smp_atomic_init(&balance_info.active_runqs, no_schedulers); + erts_smp_atomic32_set(&balance_info.used_runqs, + erts_common_run_queue ? 1 : no_schedulers_online); + erts_smp_atomic32_init(&balance_info.active_runqs, no_schedulers); balance_info.last_active_runqs = no_schedulers; erts_smp_mtx_init(&balance_info.update_mtx, "migration_info_update"); balance_info.forced_check_balance = 0; balance_info.halftime = 1; balance_info.full_reds_history_index = 0; - erts_smp_atomic_init(&balance_info.checking_balance, 0); + erts_smp_atomic32_init(&balance_info.checking_balance, 0); balance_info.prev_rise.active_runqs = 0; balance_info.prev_rise.max_len = 0; balance_info.prev_rise.reds = 0; @@ -2586,8 +2717,8 @@ erts_init_scheduling(int mrq, int no_schedulers, int no_schedulers_online) if (no_schedulers_online < no_schedulers) { if (erts_common_run_queue) { for (ix = no_schedulers_online; ix < no_schedulers; ix++) - erts_smp_atomic_bor(&ERTS_SCHED_SLEEP_INFO_IX(ix)->flags, - ERTS_SSI_FLG_SUSPENDED); + erts_smp_atomic32_bor(&ERTS_SCHED_SLEEP_INFO_IX(ix)->flags, + ERTS_SSI_FLG_SUSPENDED); } else { for (ix = no_schedulers_online; ix < erts_no_run_queues; ix++) @@ -2601,7 +2732,9 @@ erts_init_scheduling(int mrq, int no_schedulers, int no_schedulers_online) ERTS_SCHDLR_SSPND_CHNG_SET((ERTS_SCHDLR_SSPND_CHNG_ONLN | ERTS_SCHDLR_SSPND_CHNG_WAITER), 0); - erts_smp_atomic_init(&doing_sys_schedule, 0); + erts_smp_atomic32_init(&doing_sys_schedule, 0); + + init_misc_aux_work(); #else /* !ERTS_SMP */ { @@ -2615,7 +2748,7 @@ erts_init_scheduling(int mrq, int no_schedulers, int no_schedulers_online) erts_no_schedulers = 1; #endif - erts_smp_atomic_init(&function_calls, 0); + erts_smp_atomic32_init(&function_calls, 0); /* init port tasks */ erts_port_task_init(); @@ -2730,6 +2863,19 @@ resume_process(Process *p) p->rstatus = P_FREE; } +int +erts_get_max_no_executing_schedulers(void) +{ +#ifdef ERTS_SMP + if (erts_smp_atomic32_read(&schdlr_sspnd.changing)) + return (int) erts_no_schedulers; + ERTS_THR_MEMORY_BARRIER; + return (int) erts_smp_atomic32_read(&schdlr_sspnd.active); +#else + return 1; +#endif +} + #ifdef ERTS_SMP static void @@ -2748,13 +2894,13 @@ static void scheduler_ix_resume_wake(Uint ix) { ErtsSchedulerSleepInfo *ssi = ERTS_SCHED_SLEEP_INFO_IX(ix); - long xflgs = (ERTS_SSI_FLG_SLEEPING - | ERTS_SSI_FLG_TSE_SLEEPING - | ERTS_SSI_FLG_WAITING - | ERTS_SSI_FLG_SUSPENDED); - long oflgs; + erts_aint32_t xflgs = (ERTS_SSI_FLG_SLEEPING + | ERTS_SSI_FLG_TSE_SLEEPING + | ERTS_SSI_FLG_WAITING + | ERTS_SSI_FLG_SUSPENDED); + erts_aint32_t oflgs; do { - oflgs = erts_smp_atomic_cmpxchg(&ssi->flags, 0, xflgs); + oflgs = erts_smp_atomic32_cmpxchg(&ssi->flags, 0, xflgs); if (oflgs == xflgs) { erts_sched_finish_poke(ssi, oflgs); break; @@ -2763,17 +2909,17 @@ scheduler_ix_resume_wake(Uint ix) } while (oflgs & ERTS_SSI_FLG_SUSPENDED); } -static long -sched_prep_spin_suspended(ErtsSchedulerSleepInfo *ssi, long xpct) +static erts_aint32_t +sched_prep_spin_suspended(ErtsSchedulerSleepInfo *ssi, erts_aint32_t xpct) { - long oflgs; - long nflgs = (ERTS_SSI_FLG_SLEEPING - | ERTS_SSI_FLG_WAITING - | ERTS_SSI_FLG_SUSPENDED); - long xflgs = xpct; + erts_aint32_t oflgs; + erts_aint32_t nflgs = (ERTS_SSI_FLG_SLEEPING + | ERTS_SSI_FLG_WAITING + | ERTS_SSI_FLG_SUSPENDED); + erts_aint32_t xflgs = xpct; do { - oflgs = erts_smp_atomic_cmpxchg(&ssi->flags, nflgs, xflgs); + oflgs = erts_smp_atomic32_cmpxchg(&ssi->flags, nflgs, xflgs); if (oflgs == xflgs) return nflgs; xflgs = oflgs; @@ -2782,15 +2928,15 @@ sched_prep_spin_suspended(ErtsSchedulerSleepInfo *ssi, long xpct) return oflgs; } -static long +static erts_aint32_t sched_spin_suspended(ErtsSchedulerSleepInfo *ssi, int spincount) { int until_yield = ERTS_SCHED_SPIN_UNTIL_YIELD; int sc = spincount; - long flgs; + erts_aint32_t flgs; do { - flgs = erts_smp_atomic_read(&ssi->flags); + flgs = erts_smp_atomic32_read(&ssi->flags); if ((flgs & (ERTS_SSI_FLG_SLEEPING | ERTS_SSI_FLG_WAITING | ERTS_SSI_FLG_SUSPENDED)) @@ -2808,22 +2954,22 @@ sched_spin_suspended(ErtsSchedulerSleepInfo *ssi, int spincount) return flgs; } -static long +static erts_aint32_t sched_set_suspended_sleeptype(ErtsSchedulerSleepInfo *ssi) { - long oflgs; - long nflgs = (ERTS_SSI_FLG_SLEEPING - | ERTS_SSI_FLG_TSE_SLEEPING - | ERTS_SSI_FLG_WAITING - | ERTS_SSI_FLG_SUSPENDED); - long xflgs = (ERTS_SSI_FLG_SLEEPING - | ERTS_SSI_FLG_WAITING - | ERTS_SSI_FLG_SUSPENDED); + erts_aint32_t oflgs; + erts_aint32_t nflgs = (ERTS_SSI_FLG_SLEEPING + | ERTS_SSI_FLG_TSE_SLEEPING + | ERTS_SSI_FLG_WAITING + | ERTS_SSI_FLG_SUSPENDED); + erts_aint32_t xflgs = (ERTS_SSI_FLG_SLEEPING + | ERTS_SSI_FLG_WAITING + | ERTS_SSI_FLG_SUSPENDED); erts_tse_reset(ssi->event); while (1) { - oflgs = erts_smp_atomic_cmpxchg(&ssi->flags, nflgs, xflgs); + oflgs = erts_smp_atomic32_cmpxchg(&ssi->flags, nflgs, xflgs); if (oflgs == xflgs) return nflgs; if ((oflgs & (ERTS_SSI_FLG_SLEEPING @@ -2841,8 +2987,8 @@ sched_set_suspended_sleeptype(ErtsSchedulerSleepInfo *ssi) static void suspend_scheduler(ErtsSchedulerData *esdp) { - long flgs; - int changing; + erts_aint32_t flgs; + erts_aint32_t changing; long no = (long) esdp->no; ErtsSchedulerSleepInfo *ssi = esdp->ssi; long active_schedulers; @@ -2850,7 +2996,7 @@ suspend_scheduler(ErtsSchedulerData *esdp) int wake = 0; #if defined(ERTS_SCHED_NEED_NONBLOCKABLE_AUX_WORK) \ || defined(ERTS_SCHED_NEED_BLOCKABLE_AUX_WORK) - long aux_work; + erts_aint32_t aux_work; #endif /* @@ -2878,15 +3024,15 @@ suspend_scheduler(ErtsSchedulerData *esdp) flgs = sched_prep_spin_suspended(ssi, ERTS_SSI_FLG_SUSPENDED); if (flgs & ERTS_SSI_FLG_SUSPENDED) { - active_schedulers = erts_smp_atomic_dectest(&schdlr_sspnd.active); + active_schedulers = erts_smp_atomic32_dectest(&schdlr_sspnd.active); ASSERT(active_schedulers >= 1); - changing = erts_smp_atomic_read(&schdlr_sspnd.changing); + changing = erts_smp_atomic32_read(&schdlr_sspnd.changing); if (changing & ERTS_SCHDLR_SSPND_CHNG_MSB) { if (active_schedulers == schdlr_sspnd.msb.wait_active) wake = 1; if (active_schedulers == 1) { - changing = erts_smp_atomic_band(&schdlr_sspnd.changing, - ~ERTS_SCHDLR_SSPND_CHNG_MSB); + changing = erts_smp_atomic32_band(&schdlr_sspnd.changing, + ~ERTS_SCHDLR_SSPND_CHNG_MSB); changing &= ~ERTS_SCHDLR_SSPND_CHNG_MSB; } } @@ -2908,8 +3054,8 @@ suspend_scheduler(ErtsSchedulerData *esdp) && schdlr_sspnd.curr_online == schdlr_sspnd.wait_curr_online) wake = 1; if (schdlr_sspnd.online == schdlr_sspnd.curr_online) { - changing = erts_smp_atomic_band(&schdlr_sspnd.changing, - ~ERTS_SCHDLR_SSPND_CHNG_ONLN); + changing = erts_smp_atomic32_band(&schdlr_sspnd.changing, + ~ERTS_SCHDLR_SSPND_CHNG_ONLN); changing &= ~ERTS_SCHDLR_SSPND_CHNG_ONLN; } } @@ -2919,29 +3065,30 @@ suspend_scheduler(ErtsSchedulerData *esdp) wake = 0; } - flgs = erts_smp_atomic_read(&ssi->flags); + flgs = erts_smp_atomic32_read(&ssi->flags); if (!(flgs & ERTS_SSI_FLG_SUSPENDED)) break; erts_smp_mtx_unlock(&schdlr_sspnd.mtx); #ifdef ERTS_SCHED_NEED_BLOCKABLE_AUX_WORK - aux_work = erts_smp_atomic_read(&ssi->aux_work); + aux_work = erts_smp_atomic32_read(&ssi->aux_work); blockable_aux_work: blockable_aux_work(esdp, ssi, aux_work); #endif erts_smp_activity_begin(ERTS_ACTIVITY_WAIT, NULL, NULL, NULL); while (1) { - long flgs; + erts_aint32_t flgs; #ifdef ERTS_SCHED_NEED_NONBLOCKABLE_AUX_WORK #ifndef ERTS_SCHED_NEED_BLOCKABLE_AUX_WORK - aux_work = erts_smp_atomic_read(&ssi->aux_work); + aux_work = erts_smp_atomic32_read(&ssi->aux_work); #endif nonblockable_aux_work(esdp, ssi, aux_work); #endif - flgs = sched_spin_suspended(ssi, ERTS_SCHED_SUSPEND_SLEEP_SPINCOUNT); + flgs = sched_spin_suspended(ssi, + ERTS_SCHED_SUSPEND_SLEEP_SPINCOUNT); if (flgs == (ERTS_SSI_FLG_SLEEPING | ERTS_SSI_FLG_WAITING | ERTS_SSI_FLG_SUSPENDED)) { @@ -2961,13 +3108,13 @@ suspend_scheduler(ErtsSchedulerData *esdp) | ERTS_SSI_FLG_SUSPENDED)); if (!(flgs & ERTS_SSI_FLG_SUSPENDED)) break; - changing = erts_smp_atomic_read(&schdlr_sspnd.changing); + changing = erts_smp_atomic32_read(&schdlr_sspnd.changing); if (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER) break; #ifdef ERTS_SCHED_NEED_BLOCKABLE_AUX_WORK - aux_work = erts_smp_atomic_read(&ssi->aux_work); + aux_work = erts_smp_atomic32_read(&ssi->aux_work); if (aux_work & ERTS_SSI_BLOCKABLE_AUX_WORK_MASK) { erts_smp_activity_end(ERTS_ACTIVITY_WAIT, NULL, NULL, NULL); goto blockable_aux_work; @@ -2979,19 +3126,19 @@ suspend_scheduler(ErtsSchedulerData *esdp) erts_smp_activity_end(ERTS_ACTIVITY_WAIT, NULL, NULL, NULL); erts_smp_mtx_lock(&schdlr_sspnd.mtx); - changing = erts_smp_atomic_read(&schdlr_sspnd.changing); + changing = erts_smp_atomic32_read(&schdlr_sspnd.changing); } - active_schedulers = erts_smp_atomic_inctest(&schdlr_sspnd.active); - changing = erts_smp_atomic_read(&schdlr_sspnd.changing); + active_schedulers = erts_smp_atomic32_inctest(&schdlr_sspnd.active); + changing = erts_smp_atomic32_read(&schdlr_sspnd.changing); if ((changing & ERTS_SCHDLR_SSPND_CHNG_MSB) && schdlr_sspnd.online == active_schedulers) { - erts_smp_atomic_band(&schdlr_sspnd.changing, - ~ERTS_SCHDLR_SSPND_CHNG_MSB); + erts_smp_atomic32_band(&schdlr_sspnd.changing, + ~ERTS_SCHDLR_SSPND_CHNG_MSB); } ASSERT(no <= schdlr_sspnd.online); - ASSERT(!erts_smp_atomic_read(&schdlr_sspnd.msb.ongoing)); + ASSERT(!erts_smp_atomic32_read(&schdlr_sspnd.msb.ongoing)); } @@ -3020,7 +3167,7 @@ do { \ (RQ)->flags |= (ERTS_RUNQ_FLG_OUT_OF_WORK \ | ERTS_RUNQ_FLG_HALFTIME_OUT_OF_WORK); \ (RQ)->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS; \ - erts_smp_atomic_band(&(RQ)->info_flags, ~ERTS_RUNQ_IFLG_SUSPENDED); \ + erts_smp_atomic32_band(&(RQ)->info_flags, ~ERTS_RUNQ_IFLG_SUSPENDED);\ for (pix__ = 0; pix__ < ERTS_NO_PROC_PRIO_LEVELS; pix__++) { \ (RQ)->procs.prio_info[pix__].max_len = 0; \ (RQ)->procs.prio_info[pix__].reds = 0; \ @@ -3062,9 +3209,9 @@ erts_schedulers_state(Uint *total, int yield_allowed) { int res; - long changing; + erts_aint32_t changing; erts_smp_mtx_lock(&schdlr_sspnd.mtx); - changing = erts_smp_atomic_read(&schdlr_sspnd.changing); + changing = erts_smp_atomic32_read(&schdlr_sspnd.changing); if (yield_allowed && (changing & ~ERTS_SCHDLR_SSPND_CHNG_WAITER)) res = ERTS_SCHDLR_SSPND_YIELD_RESTART; else { @@ -3085,7 +3232,7 @@ erts_set_schedulers_online(Process *p, Sint *old_no) { int ix, res, no, have_unlocked_plocks; - long changing; + erts_aint32_t changing; if (new_no < 1 || erts_no_schedulers < new_no) return ERTS_SCHDLR_SSPND_EINVAL; @@ -3095,7 +3242,7 @@ erts_set_schedulers_online(Process *p, have_unlocked_plocks = 0; no = (int) new_no; - changing = erts_smp_atomic_read(&schdlr_sspnd.changing); + changing = erts_smp_atomic32_read(&schdlr_sspnd.changing); if (changing) { res = ERTS_SCHDLR_SSPND_YIELD_RESTART; } @@ -3142,7 +3289,7 @@ erts_set_schedulers_online(Process *p, ErtsRunQueue *to_rq = ERTS_RUNQ_IX(ix % no); evacuate_run_queue(from_rq, to_rq); } - erts_smp_atomic_set(&balance_info.used_runqs, no); + erts_smp_atomic32_set(&balance_info.used_runqs, no); erts_smp_mtx_unlock(&balance_info.update_mtx); erts_smp_mtx_lock(&schdlr_sspnd.mtx); } @@ -3170,8 +3317,8 @@ erts_set_schedulers_online(Process *p, for (ix = no; ix < online; ix++) { ErtsSchedulerSleepInfo *ssi; ssi = ERTS_SCHED_SLEEP_INFO_IX(ix); - erts_smp_atomic_bor(&ssi->flags, - ERTS_SSI_FLG_SUSPENDED); + erts_smp_atomic32_bor(&ssi->flags, + ERTS_SSI_FLG_SUSPENDED); } wake_all_schedulers(); } @@ -3196,7 +3343,7 @@ erts_set_schedulers_online(Process *p, for (ix = erts_no_run_queues-1; ix >= no; ix--) evacuate_run_queue(ERTS_RUNQ_IX(ix), ERTS_RUNQ_IX(ix % no)); - erts_smp_atomic_set(&balance_info.used_runqs, no); + erts_smp_atomic32_set(&balance_info.used_runqs, no); erts_smp_mtx_unlock(&balance_info.update_mtx); erts_smp_mtx_lock(&schdlr_sspnd.mtx); for (ix = no; ix < online; ix++) { @@ -3218,10 +3365,11 @@ erts_set_schedulers_online(Process *p, NULL); ASSERT(res != ERTS_SCHDLR_SSPND_DONE ? (ERTS_SCHDLR_SSPND_CHNG_WAITER - & erts_smp_atomic_read(&schdlr_sspnd.changing)) + & erts_smp_atomic32_read(&schdlr_sspnd.changing)) : (ERTS_SCHDLR_SSPND_CHNG_WAITER - == erts_smp_atomic_read(&schdlr_sspnd.changing))); - erts_smp_atomic_band(&schdlr_sspnd.changing, ~ERTS_SCHDLR_SSPND_CHNG_WAITER); + == erts_smp_atomic32_read(&schdlr_sspnd.changing))); + erts_smp_atomic32_band(&schdlr_sspnd.changing, + ~ERTS_SCHDLR_SSPND_CHNG_WAITER); } } @@ -3236,11 +3384,11 @@ ErtsSchedSuspendResult erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) { int ix, res, have_unlocked_plocks = 0; - long changing; + erts_aint32_t changing; ErtsProcList *plp; erts_smp_mtx_lock(&schdlr_sspnd.mtx); - changing = erts_smp_atomic_read(&schdlr_sspnd.changing); + changing = erts_smp_atomic32_read(&schdlr_sspnd.changing); if (changing) { res = ERTS_SCHDLR_SSPND_YIELD_RESTART; /* Yield */ } @@ -3250,7 +3398,7 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) plp->next = schdlr_sspnd.msb.procs; schdlr_sspnd.msb.procs = plp; p->flags |= F_HAVE_BLCKD_MSCHED; - ASSERT(erts_smp_atomic_read(&schdlr_sspnd.active) == 1); + ASSERT(erts_smp_atomic32_read(&schdlr_sspnd.active) == 1); ASSERT(p->scheduler_data->no == 1); res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED; } @@ -3261,11 +3409,11 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) have_unlocked_plocks = 1; erts_smp_proc_unlock(p, plocks); } - ASSERT(0 == erts_smp_atomic_read(&schdlr_sspnd.msb.ongoing)); - erts_smp_atomic_set(&schdlr_sspnd.msb.ongoing, 1); + ASSERT(0 == erts_smp_atomic32_read(&schdlr_sspnd.msb.ongoing)); + erts_smp_atomic32_set(&schdlr_sspnd.msb.ongoing, 1); if (online == 1) { res = ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED; - ASSERT(erts_smp_atomic_read(&schdlr_sspnd.active) == 1); + ASSERT(erts_smp_atomic32_read(&schdlr_sspnd.active) == 1); ASSERT(p->scheduler_data->no == 1); } else { @@ -3285,14 +3433,14 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) } if (erts_common_run_queue) { for (ix = 1; ix < online; ix++) - erts_smp_atomic_bor(&ERTS_SCHED_SLEEP_INFO_IX(ix)->flags, - ERTS_SSI_FLG_SUSPENDED); + erts_smp_atomic32_bor(&ERTS_SCHED_SLEEP_INFO_IX(ix)->flags, + ERTS_SSI_FLG_SUSPENDED); wake_all_schedulers(); } else { erts_smp_mtx_unlock(&schdlr_sspnd.mtx); erts_smp_mtx_lock(&balance_info.update_mtx); - erts_smp_atomic_set(&balance_info.used_runqs, 1); + erts_smp_atomic32_set(&balance_info.used_runqs, 1); for (ix = 0; ix < online; ix++) { ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); erts_smp_runq_lock(rq); @@ -3314,7 +3462,7 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) susp_sched_prep_block, susp_sched_resume_block, NULL); - while (erts_smp_atomic_read(&schdlr_sspnd.active) + while (erts_smp_atomic32_read(&schdlr_sspnd.active) != schdlr_sspnd.msb.wait_active) erts_smp_cnd_wait(&schdlr_sspnd.cnd, &schdlr_sspnd.mtx); erts_smp_activity_end(ERTS_ACTIVITY_WAIT, @@ -3323,11 +3471,11 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) NULL); ASSERT(res != ERTS_SCHDLR_SSPND_DONE_MSCHED_BLOCKED ? (ERTS_SCHDLR_SSPND_CHNG_WAITER - & erts_smp_atomic_read(&schdlr_sspnd.changing)) + & erts_smp_atomic32_read(&schdlr_sspnd.changing)) : (ERTS_SCHDLR_SSPND_CHNG_WAITER - == erts_smp_atomic_read(&schdlr_sspnd.changing))); - erts_smp_atomic_band(&schdlr_sspnd.changing, - ~ERTS_SCHDLR_SSPND_CHNG_WAITER); + == erts_smp_atomic32_read(&schdlr_sspnd.changing))); + erts_smp_atomic32_band(&schdlr_sspnd.changing, + ~ERTS_SCHDLR_SSPND_CHNG_WAITER); } plp = proclist_create(p); plp->next = schdlr_sspnd.msb.procs; @@ -3394,16 +3542,16 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) }); #endif p->flags &= ~F_HAVE_BLCKD_MSCHED; - erts_smp_atomic_set(&schdlr_sspnd.msb.ongoing, 0); + erts_smp_atomic32_set(&schdlr_sspnd.msb.ongoing, 0); if (schdlr_sspnd.online == 1) { /* No schedulers to resume */ - ASSERT(erts_smp_atomic_read(&schdlr_sspnd.active) == 1); + ASSERT(erts_smp_atomic32_read(&schdlr_sspnd.active) == 1); ERTS_SCHDLR_SSPND_CHNG_SET(0, ERTS_SCHDLR_SSPND_CHNG_MSB); } else if (erts_common_run_queue) { for (ix = 1; ix < schdlr_sspnd.online; ix++) - erts_smp_atomic_band(&ERTS_SCHED_SLEEP_INFO_IX(ix)->flags, - ~ERTS_SSI_FLG_SUSPENDED); + erts_smp_atomic32_band(&ERTS_SCHED_SLEEP_INFO_IX(ix)->flags, + ~ERTS_SSI_FLG_SUSPENDED); wake_all_schedulers(); } else { @@ -3429,7 +3577,7 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) evacuate_run_queue(ERTS_RUNQ_IX(ix), ERTS_RUNQ_IX(ix % online)); - erts_smp_atomic_set(&balance_info.used_runqs, online); + erts_smp_atomic32_set(&balance_info.used_runqs, online); /* Make sure that we balance soon... */ balance_info.forced_check_balance = 1; erts_smp_runq_lock(ERTS_RUNQ_IX(0)); @@ -3453,7 +3601,7 @@ void erts_dbg_multi_scheduling_return_trap(Process *p, Eterm return_value) { if (return_value == am_blocked) { - long active = erts_smp_atomic_read(&schdlr_sspnd.active); + erts_aint32_t active = erts_smp_atomic32_read(&schdlr_sspnd.active); ASSERT(1 <= active && active <= 2); ASSERT(ERTS_PROC_GET_SCHDATA(p)->no == 1); } @@ -3536,12 +3684,12 @@ sched_thread_func(void *vesdp) erts_thread_init_float(); erts_smp_mtx_lock(&schdlr_sspnd.mtx); - ASSERT(erts_smp_atomic_read(&schdlr_sspnd.changing) + ASSERT(erts_smp_atomic32_read(&schdlr_sspnd.changing) & ERTS_SCHDLR_SSPND_CHNG_ONLN); if (--schdlr_sspnd.curr_online == schdlr_sspnd.wait_curr_online) { - erts_smp_atomic_band(&schdlr_sspnd.changing, - ~ERTS_SCHDLR_SSPND_CHNG_ONLN); + erts_smp_atomic32_band(&schdlr_sspnd.changing, + ~ERTS_SCHDLR_SSPND_CHNG_ONLN); if (((ErtsSchedulerData *) vesdp)->no != 1) erts_smp_cnd_signal(&schdlr_sspnd.cnd); } @@ -4914,10 +5062,10 @@ Process *schedule(Process *p, int calls) { ErtsRunQueue *rq; ErtsRunPrioQueue *rpq; - long dt; + erts_aint_t dt; ErtsSchedulerData *esdp; int context_reds; - long fcalls; + int fcalls; int input_reductions; int actual_reds; int reds; @@ -4940,7 +5088,7 @@ Process *schedule(Process *p, int calls) esdp = erts_get_scheduler_data(); rq = erts_get_runq_current(esdp); ASSERT(esdp); - fcalls = erts_smp_atomic_read(&function_calls); + fcalls = (int) erts_smp_atomic32_read(&function_calls); actual_reds = reds = 0; erts_smp_runq_lock(rq); } else { @@ -4958,7 +5106,7 @@ Process *schedule(Process *p, int calls) reds = ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST; esdp->virtual_reds = 0; - fcalls = erts_smp_atomic_addtest(&function_calls, reds); + fcalls = (int) erts_smp_atomic32_addtest(&function_calls, reds); ASSERT(esdp && esdp == erts_get_scheduler_data()); rq = erts_get_runq_current(esdp); @@ -5059,10 +5207,10 @@ Process *schedule(Process *p, int calls) ERTS_SMP_CHK_NO_PROC_LOCKS; - dt = do_time_read_and_reset(); + dt = erts_do_time_read_and_reset(); if (dt) { erts_smp_runq_unlock(rq); - bump_timer(dt); + erts_bump_timer(dt); erts_smp_runq_lock(rq); } BM_STOP_TIMER(system); @@ -5091,14 +5239,14 @@ Process *schedule(Process *p, int calls) | ERTS_RUNQ_FLG_CHK_CPU_BIND | ERTS_RUNQ_FLG_SUSPENDED)) { if ((rq->flags & ERTS_RUNQ_FLG_SUSPENDED) - || (erts_smp_atomic_read(&esdp->ssi->flags) + || (erts_smp_atomic32_read(&esdp->ssi->flags) & ERTS_SSI_FLG_SUSPENDED)) { - ASSERT(erts_smp_atomic_read(&esdp->ssi->flags) + ASSERT(erts_smp_atomic32_read(&esdp->ssi->flags) & ERTS_SSI_FLG_SUSPENDED); suspend_scheduler(esdp); } if ((rq->flags & ERTS_RUNQ_FLG_CHK_CPU_BIND) - || erts_smp_atomic_read(&esdp->chk_cpu_bind)) { + || erts_smp_atomic32_read(&esdp->chk_cpu_bind)) { erts_sched_check_cpu_bind(esdp); } } @@ -5107,7 +5255,7 @@ Process *schedule(Process *p, int calls) || defined(ERTS_SCHED_NEED_NONBLOCKABLE_AUX_WORK) { ErtsSchedulerSleepInfo *ssi = esdp->ssi; - long aux_work = erts_smp_atomic_read(&ssi->aux_work); + erts_aint32_t aux_work = erts_smp_atomic32_read(&ssi->aux_work); if (aux_work) { erts_smp_runq_unlock(rq); #ifdef ERTS_SCHED_NEED_BLOCKABLE_AUX_WORK @@ -5149,9 +5297,9 @@ Process *schedule(Process *p, int calls) if (rq->flags & (ERTS_RUNQ_FLG_SHARED_RUNQ | ERTS_RUNQ_FLG_SUSPENDED)) { if ((rq->flags & ERTS_RUNQ_FLG_SUSPENDED) - || (erts_smp_atomic_read(&esdp->ssi->flags) + || (erts_smp_atomic32_read(&esdp->ssi->flags) & ERTS_SSI_FLG_SUSPENDED)) { - ASSERT(erts_smp_atomic_read(&esdp->ssi->flags) + ASSERT(erts_smp_atomic32_read(&esdp->ssi->flags) & ERTS_SSI_FLG_SUSPENDED); non_empty_runq(rq); goto continue_check_activities_to_run; @@ -5193,7 +5341,7 @@ Process *schedule(Process *p, int calls) * Schedule system-level activities. */ - erts_smp_atomic_set(&function_calls, 0); + erts_smp_atomic32_set(&function_calls, 0); fcalls = 0; ASSERT(!erts_port_task_have_outstanding_io_tasks()); @@ -5203,11 +5351,11 @@ Process *schedule(Process *p, int calls) #endif erts_smp_runq_unlock(rq); erl_sys_schedule(runnable); - dt = do_time_read_and_reset(); - if (dt) bump_timer(dt); + dt = erts_do_time_read_and_reset(); + if (dt) erts_bump_timer(dt); #ifdef ERTS_SMP erts_smp_runq_lock(rq); - erts_smp_atomic_set(&doing_sys_schedule, 0); + erts_smp_atomic32_set(&doing_sys_schedule, 0); goto continue_check_activities_to_run; #else if (!runnable) @@ -5235,7 +5383,7 @@ Process *schedule(Process *p, int calls) if (erts_common_run_queue->waiting) wake_scheduler(erts_common_run_queue, 0, 1); } - else if (erts_smp_atomic_read(&no_empty_run_queues) != 0) { + else if (erts_smp_atomic32_read(&no_empty_run_queues) != 0) { wake_scheduler_on_empty_runq(rq); rq->wakeup_other = 0; } @@ -5692,7 +5840,7 @@ erts_test_next_pid(int set, Uint next) Uint erts_process_count(void) { - long res = erts_smp_atomic_read(&process_count); + erts_aint32_t res = erts_smp_atomic32_read(&process_count); ASSERT(res >= 0); return (Uint) res; } @@ -5741,7 +5889,7 @@ alloc_process(void) ASSERT(!process_tab[p_next]); process_tab[p_next] = p; - erts_smp_atomic_inc(&process_count); + erts_smp_atomic32_inc(&process_count); p->id = make_internal_pid(p_serial << p_serial_shift | p_next); if (p->id == ERTS_INVALID_PID) { /* Do not use the invalid pid; change serial */ @@ -5867,7 +6015,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->min_heap_size = H_MIN_SIZE; p->min_vheap_size = BIN_VH_MIN_SIZE; p->prio = PRIORITY_NORMAL; - p->max_gen_gcs = (Uint16) erts_smp_atomic_read(&erts_max_gen_gcs); + p->max_gen_gcs = (Uint16) erts_smp_atomic32_read(&erts_max_gen_gcs); } p->skipped = 0; ASSERT(p->min_heap_size == erts_next_heap_size(p->min_heap_size, 0)); @@ -7324,8 +7472,8 @@ continue_exit_process(Process *p p->status_flags = 0; #endif process_tab[pix] = NULL; /* Time of death! */ - ASSERT(erts_smp_atomic_read(&process_count) > 0); - erts_smp_atomic_dec(&process_count); + ASSERT(erts_smp_atomic32_read(&process_count) > 0); + erts_smp_atomic32_dec(&process_count); #ifdef ERTS_SMP erts_pix_unlock(pix_lock); @@ -7465,7 +7613,7 @@ cancel_timer(Process* p) #ifdef ERTS_SMP erts_cancel_smp_ptimer(p->u.ptimer); #else - erl_cancel_timer(&p->u.tm); + erts_cancel_timer(&p->u.tm); #endif } @@ -7491,7 +7639,7 @@ set_timer(Process* p, Uint timeout) (ErlTimeoutProc) timeout_proc, timeout); #else - erl_set_timer(&p->u.tm, + erts_set_timer(&p->u.tm, (ErlTimeoutProc) timeout_proc, NULL, (void*) p, diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index c038e57b65..d927415f37 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -174,8 +174,8 @@ extern int erts_sched_thread_suggested_stack_size; #define ERTS_UNSET_RUNQ_FLG_EVACUATE(FLGS, PRIO) \ ((FLGS) &= ~ERTS_RUNQ_FLG_EVACUATE((PRIO))) -#define ERTS_RUNQ_IFLG_SUSPENDED (((long) 1) << 0) -#define ERTS_RUNQ_IFLG_NONEMPTY (((long) 1) << 1) +#define ERTS_RUNQ_IFLG_SUSPENDED (((erts_aint32_t) 1) << 0) +#define ERTS_RUNQ_IFLG_NONEMPTY (((erts_aint32_t) 1) << 1) #ifdef DEBUG @@ -219,11 +219,11 @@ typedef enum { ERTS_MIGRATE_FAILED_RUNQ_SUSPENDED } ErtsMigrateResult; -#define ERTS_SSI_FLG_SLEEPING (((long) 1) << 0) -#define ERTS_SSI_FLG_POLL_SLEEPING (((long) 1) << 1) -#define ERTS_SSI_FLG_TSE_SLEEPING (((long) 1) << 2) -#define ERTS_SSI_FLG_WAITING (((long) 1) << 3) -#define ERTS_SSI_FLG_SUSPENDED (((long) 1) << 4) +#define ERTS_SSI_FLG_SLEEPING (((erts_aint32_t) 1) << 0) +#define ERTS_SSI_FLG_POLL_SLEEPING (((erts_aint32_t) 1) << 1) +#define ERTS_SSI_FLG_TSE_SLEEPING (((erts_aint32_t) 1) << 2) +#define ERTS_SSI_FLG_WAITING (((erts_aint32_t) 1) << 3) +#define ERTS_SSI_FLG_SUSPENDED (((erts_aint32_t) 1) << 4) #define ERTS_SSI_FLGS_SLEEP_TYPE \ (ERTS_SSI_FLG_TSE_SLEEPING|ERTS_SSI_FLG_POLL_SLEEPING) @@ -236,16 +236,14 @@ typedef enum { | ERTS_SSI_FLG_WAITING \ | ERTS_SSI_FLG_SUSPENDED) - -#if !defined(ERTS_SCHED_NEED_BLOCKABLE_AUX_WORK) \ - && defined(ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN) #define ERTS_SCHED_NEED_BLOCKABLE_AUX_WORK -#endif -#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN (((long) 1) << 0) +#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN (((erts_aint32_t) 1) << 0) +#define ERTS_SSI_AUX_WORK_MISC (((erts_aint32_t) 1) << 1) #define ERTS_SSI_BLOCKABLE_AUX_WORK_MASK \ - (ERTS_SSI_AUX_WORK_CHECK_CHILDREN) + (ERTS_SSI_AUX_WORK_CHECK_CHILDREN \ + | ERTS_SSI_AUX_WORK_MISC) #define ERTS_SSI_NONBLOCKABLE_AUX_WORK_MASK \ (0) @@ -259,9 +257,9 @@ typedef struct { struct ErtsSchedulerSleepInfo_ { ErtsSchedulerSleepInfo *next; ErtsSchedulerSleepInfo *prev; - erts_smp_atomic_t flags; + erts_smp_atomic32_t flags; erts_tse_t *event; - erts_smp_atomic_t aux_work; + erts_smp_atomic32_t aux_work; }; /* times to reschedule low prio process before running */ @@ -311,7 +309,7 @@ typedef struct { struct ErtsRunQueue_ { int ix; - erts_smp_atomic_t info_flags; + erts_smp_atomic32_t info_flags; erts_smp_mtx_t mtx; erts_smp_cnd_t cnd; @@ -421,7 +419,7 @@ struct ErtsSchedulerData_ { #ifdef ERTS_SMP /* NOTE: These fields are modified under held mutexes by other threads */ - erts_smp_atomic_t chk_cpu_bind; /* Only used when common run queue */ + erts_smp_atomic32_t chk_cpu_bind; /* Only used when common run queue */ #endif }; @@ -1034,6 +1032,7 @@ int erts_sched_set_wakeup_limit(char *str); #ifdef DEBUG void erts_dbg_multi_scheduling_return_trap(Process *, Eterm); #endif +int erts_get_max_no_executing_schedulers(void); #ifdef ERTS_SMP ErtsSchedSuspendResult erts_schedulers_state(Uint *, Uint *, Uint *, int); @@ -1048,6 +1047,11 @@ int erts_is_multi_scheduling_blocked(void); Eterm erts_multi_scheduling_blockers(Process *); void erts_start_schedulers(void); void erts_smp_notify_check_children_needed(void); +void +erts_smp_schedule_misc_aux_work(int ignore_self, + int max_sched, + void (*func)(void *), + void *arg); #endif void erts_sched_notify_check_cpu_bind(void); Uint erts_active_schedulers(void); @@ -1555,7 +1559,7 @@ extern int erts_disable_proc_not_running_opt; void erts_smp_notify_inc_runq(ErtsRunQueue *runq); #ifdef ERTS_SMP -void erts_sched_finish_poke(ErtsSchedulerSleepInfo *, long); +void erts_sched_finish_poke(ErtsSchedulerSleepInfo *, erts_aint32_t); ERTS_GLB_INLINE void erts_sched_poke(ErtsSchedulerSleepInfo *ssi); #if ERTS_GLB_INLINE_INCL_FUNC_DEF @@ -1563,11 +1567,11 @@ ERTS_GLB_INLINE void erts_sched_poke(ErtsSchedulerSleepInfo *ssi); ERTS_GLB_INLINE void erts_sched_poke(ErtsSchedulerSleepInfo *ssi) { - long flags = erts_smp_atomic_read(&ssi->flags); + erts_aint32_t flags = erts_smp_atomic32_read(&ssi->flags); ASSERT(!(flags & ERTS_SSI_FLG_SLEEPING) || (flags & ERTS_SSI_FLG_WAITING)); if (flags & ERTS_SSI_FLG_SLEEPING) { - flags = erts_smp_atomic_band(&ssi->flags, ~ERTS_SSI_FLGS_SLEEP); + flags = erts_smp_atomic32_band(&ssi->flags, ~ERTS_SSI_FLGS_SLEEP); erts_sched_finish_poke(ssi, flags); } } diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c index 1bebcdb911..72560aa124 100644 --- a/erts/emulator/beam/erl_process_lock.c +++ b/erts/emulator/beam/erl_process_lock.c @@ -124,7 +124,7 @@ erts_init_proc_lock(int cpus) for (i = 0; i < ERTS_NO_OF_PIX_LOCKS; i++) { #ifdef ERTS_ENABLE_LOCK_COUNT erts_smp_spinlock_init_x(&erts_pix_locks[i].u.spnlck, - "pix_lock", make_small(i)); + "pix_lock", make_small(i)); #else erts_smp_spinlock_init(&erts_pix_locks[i].u.spnlck, "pix_lock"); #endif @@ -413,7 +413,7 @@ transfer_locks(Process *p, do { erts_tse_t *tmp = wake; wake = wake->next; - erts_atomic_set(&tmp->uaflgs, 0); + erts_atomic32_set(&tmp->uaflgs, 0); erts_tse_set(tmp); } while (wake); @@ -509,14 +509,14 @@ wait_for_locks(Process *p, ASSERT((wtr->uflgs & ~ERTS_PROC_LOCKS_ALL) == 0); - erts_atomic_set(&wtr->uaflgs, 1); + erts_atomic32_set(&wtr->uaflgs, 1); erts_pix_unlock(pix_lock); while (1) { int res; erts_tse_reset(wtr); - if (erts_atomic_read(&wtr->uaflgs) == 0) + if (erts_atomic32_read(&wtr->uaflgs) == 0) break; /* @@ -955,7 +955,7 @@ erts_proc_lock_init(Process *p) { /* We always start with all locks locked */ #if ERTS_PROC_LOCK_ATOMIC_IMPL - erts_smp_atomic_init(&p->lock.flags, (long) ERTS_PROC_LOCKS_ALL); + erts_smp_atomic32_init(&p->lock.flags, (erts_aint32_t) ERTS_PROC_LOCKS_ALL); #else p->lock.flags = ERTS_PROC_LOCKS_ALL; #endif @@ -974,7 +974,7 @@ erts_proc_lock_init(Process *p) { int i; for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++) - erts_smp_atomic_init(&p->lock.locked[i], (long) 1); + erts_smp_atomic32_init(&p->lock.locked[i], (erts_aint32_t) 1); } #endif } diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h index 4fe30c7209..355179f084 100644 --- a/erts/emulator/beam/erl_process_lock.h +++ b/erts/emulator/beam/erl_process_lock.h @@ -54,20 +54,20 @@ #define ERTS_PROC_LOCK_MAX_BIT 3 -typedef Uint32 ErtsProcLocks; +typedef erts_aint32_t ErtsProcLocks; typedef struct erts_proc_lock_queues_t_ erts_proc_lock_queues_t; typedef struct erts_proc_lock_t_ { #if ERTS_PROC_LOCK_ATOMIC_IMPL - erts_smp_atomic_t flags; + erts_smp_atomic32_t flags; #else ErtsProcLocks flags; #endif erts_proc_lock_queues_t *queues; - long refc; + Sint32 refc; #ifdef ERTS_PROC_LOCK_DEBUG - erts_smp_atomic_t locked[ERTS_PROC_LOCK_MAX_BIT+1]; + erts_smp_atomic32_t locked[ERTS_PROC_LOCK_MAX_BIT+1]; #endif #ifdef ERTS_ENABLE_LOCK_COUNT erts_lcnt_lock_t lcnt_main; @@ -270,17 +270,19 @@ typedef struct { #if ERTS_PROC_LOCK_ATOMIC_IMPL #define ERTS_PROC_LOCK_FLGS_BAND_(L, MSK) \ - ((ErtsProcLocks) erts_smp_atomic_band(&(L)->flags, (long) (MSK))) + ((ErtsProcLocks) erts_smp_atomic32_band(&(L)->flags, (erts_aint32_t) (MSK))) #define ERTS_PROC_LOCK_FLGS_BOR_(L, MSK) \ - ((ErtsProcLocks) erts_smp_atomic_bor(&(L)->flags, (long) (MSK))) + ((ErtsProcLocks) erts_smp_atomic32_bor(&(L)->flags, (erts_aint32_t) (MSK))) #define ERTS_PROC_LOCK_FLGS_CMPXCHG_ACQB_(L, NEW, EXPECTED) \ - ((ErtsProcLocks) erts_smp_atomic_cmpxchg_acqb(&(L)->flags, \ - (long) (NEW), (long) (EXPECTED))) + ((ErtsProcLocks) erts_smp_atomic32_cmpxchg_acqb(&(L)->flags, \ + (erts_aint32_t) (NEW), \ + (erts_aint32_t) (EXPECTED))) #define ERTS_PROC_LOCK_FLGS_CMPXCHG_RELB_(L, NEW, EXPECTED) \ - ((ErtsProcLocks) erts_smp_atomic_cmpxchg_relb(&(L)->flags, \ - (long) (NEW), (long) (EXPECTED))) + ((ErtsProcLocks) erts_smp_atomic32_cmpxchg_relb(&(L)->flags, \ + (erts_aint32_t) (NEW), \ + (erts_aint32_t) (EXPECTED))) #define ERTS_PROC_LOCK_FLGS_READ_(L) \ - ((ErtsProcLocks) erts_smp_atomic_read(&(L)->flags)) + ((ErtsProcLocks) erts_smp_atomic32_read(&(L)->flags)) #else /* no opt atomic ops */ @@ -619,13 +621,13 @@ erts_proc_lock_op_debug(Process *p, ErtsProcLocks locks, int locked) for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++) { ErtsProcLocks lock = ((ErtsProcLocks) 1) << i; if (locks & lock) { - long lock_count; + erts_aint32_t lock_count; if (locked) { - lock_count = erts_smp_atomic_inctest(&p->lock.locked[i]); + lock_count = erts_smp_atomic32_inctest(&p->lock.locked[i]); ERTS_LC_ASSERT(lock_count == 1); } else { - lock_count = erts_smp_atomic_dectest(&p->lock.locked[i]); + lock_count = erts_smp_atomic32_dectest(&p->lock.locked[i]); ERTS_LC_ASSERT(lock_count == 0); } } diff --git a/erts/emulator/beam/erl_smp.h b/erts/emulator/beam/erl_smp.h index b41fa70476..287327bfe1 100644 --- a/erts/emulator/beam/erl_smp.h +++ b/erts/emulator/beam/erl_smp.h @@ -54,10 +54,10 @@ typedef erts_cnd_t erts_smp_cnd_t; typedef erts_rwmtx_opt_t erts_smp_rwmtx_opt_t; typedef erts_rwmtx_t erts_smp_rwmtx_t; typedef erts_tsd_key_t erts_smp_tsd_key_t; -typedef ethr_atomic_t erts_smp_atomic_t; +typedef erts_atomic_t erts_smp_atomic_t; +typedef erts_atomic32_t erts_smp_atomic32_t; typedef erts_spinlock_t erts_smp_spinlock_t; typedef erts_rwlock_t erts_smp_rwlock_t; -typedef erts_thr_timeval_t erts_smp_thr_timeval_t; void erts_thr_fatal_error(int, char *); /* implemented in erl_init.c */ #else /* #ifdef ERTS_SMP */ @@ -83,7 +83,8 @@ typedef struct { } erts_smp_rwmtx_opt_t; typedef int erts_smp_rwmtx_t; typedef int erts_smp_tsd_key_t; -typedef long erts_smp_atomic_t; +typedef SWord erts_smp_atomic_t; +typedef Uint32 erts_smp_atomic32_t; #if __GNUC__ > 2 typedef struct { } erts_smp_spinlock_t; typedef struct { } erts_smp_rwlock_t; @@ -92,11 +93,6 @@ typedef struct { int gcc_is_buggy; } erts_smp_spinlock_t; typedef struct { int gcc_is_buggy; } erts_smp_rwlock_t; #endif -typedef struct { - long tv_sec; - long tv_nsec; -} erts_smp_thr_timeval_t; - #endif /* #ifdef ERTS_SMP */ ERTS_GLB_INLINE void erts_smp_thr_init(erts_smp_thr_init_data_t *id); @@ -164,33 +160,82 @@ ERTS_GLB_INLINE int erts_smp_rwmtx_tryrwlock(erts_smp_rwmtx_t *rwmtx); ERTS_GLB_INLINE void erts_smp_rwmtx_rwunlock(erts_smp_rwmtx_t *rwmtx); ERTS_GLB_INLINE int erts_smp_lc_rwmtx_is_rlocked(erts_smp_rwmtx_t *mtx); ERTS_GLB_INLINE int erts_smp_lc_rwmtx_is_rwlocked(erts_smp_rwmtx_t *mtx); -ERTS_GLB_INLINE void erts_smp_atomic_init(erts_smp_atomic_t *var, long i); -ERTS_GLB_INLINE void erts_smp_atomic_set(erts_smp_atomic_t *var, long i); -ERTS_GLB_INLINE long erts_smp_atomic_read(erts_smp_atomic_t *var); -ERTS_GLB_INLINE long erts_smp_atomic_inctest(erts_smp_atomic_t *incp); -ERTS_GLB_INLINE long erts_smp_atomic_dectest(erts_smp_atomic_t *decp); +ERTS_GLB_INLINE void erts_smp_atomic_init(erts_smp_atomic_t *var, + erts_aint_t i); +ERTS_GLB_INLINE void erts_smp_atomic_set(erts_smp_atomic_t *var, erts_aint_t i); +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_read(erts_smp_atomic_t *var); +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_inctest(erts_smp_atomic_t *incp); +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_dectest(erts_smp_atomic_t *decp); ERTS_GLB_INLINE void erts_smp_atomic_inc(erts_smp_atomic_t *incp); ERTS_GLB_INLINE void erts_smp_atomic_dec(erts_smp_atomic_t *decp); -ERTS_GLB_INLINE long erts_smp_atomic_addtest(erts_smp_atomic_t *addp, - long i); -ERTS_GLB_INLINE void erts_smp_atomic_add(erts_smp_atomic_t *addp, long i); -ERTS_GLB_INLINE long erts_smp_atomic_xchg(erts_smp_atomic_t *xchgp, - long new); -ERTS_GLB_INLINE long erts_smp_atomic_cmpxchg(erts_smp_atomic_t *xchgp, - long new, - long expected); -ERTS_GLB_INLINE long erts_smp_atomic_bor(erts_smp_atomic_t *var, long mask); -ERTS_GLB_INLINE long erts_smp_atomic_band(erts_smp_atomic_t *var, long mask); -ERTS_GLB_INLINE long erts_smp_atomic_read_acqb(erts_smp_atomic_t *var); -ERTS_GLB_INLINE void erts_smp_atomic_set_relb(erts_smp_atomic_t *var, long i); +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_addtest(erts_smp_atomic_t *addp, + erts_aint_t i); +ERTS_GLB_INLINE void erts_smp_atomic_add(erts_smp_atomic_t *addp, + erts_aint_t i); +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_xchg(erts_smp_atomic_t *xchgp, + erts_aint_t new); +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_cmpxchg(erts_smp_atomic_t *xchgp, + erts_aint_t new, + erts_aint_t expected); +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_bor(erts_smp_atomic_t *var, + erts_aint_t mask); +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_band(erts_smp_atomic_t *var, + erts_aint_t mask); +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_read_acqb(erts_smp_atomic_t *var); +ERTS_GLB_INLINE void erts_smp_atomic_set_relb(erts_smp_atomic_t *var, + erts_aint_t i); ERTS_GLB_INLINE void erts_smp_atomic_dec_relb(erts_smp_atomic_t *decp); -ERTS_GLB_INLINE long erts_smp_atomic_dectest_relb(erts_smp_atomic_t *decp); -ERTS_GLB_INLINE long erts_smp_atomic_cmpxchg_acqb(erts_smp_atomic_t *xchgp, - long new, - long exp); -ERTS_GLB_INLINE long erts_smp_atomic_cmpxchg_relb(erts_smp_atomic_t *xchgp, - long new, - long exp); +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_dectest_relb(erts_smp_atomic_t *decp); +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_cmpxchg_acqb(erts_smp_atomic_t *xchgp, + erts_aint_t new, + erts_aint_t exp); +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_cmpxchg_relb(erts_smp_atomic_t *xchgp, + erts_aint_t new, + erts_aint_t exp); +ERTS_GLB_INLINE void +erts_smp_atomic32_init(erts_smp_atomic32_t *var, erts_aint32_t i); +ERTS_GLB_INLINE void +erts_smp_atomic32_set(erts_smp_atomic32_t *var, erts_aint32_t i); +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_read(erts_smp_atomic32_t *var); +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_inctest(erts_smp_atomic32_t *incp); +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_dectest(erts_smp_atomic32_t *decp); +ERTS_GLB_INLINE void +erts_smp_atomic32_inc(erts_smp_atomic32_t *incp); +ERTS_GLB_INLINE void +erts_smp_atomic32_dec(erts_smp_atomic32_t *decp); +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_addtest(erts_smp_atomic32_t *addp, erts_aint32_t i); +ERTS_GLB_INLINE void +erts_smp_atomic32_add(erts_smp_atomic32_t *addp, erts_aint32_t i); +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_xchg(erts_smp_atomic32_t *xchgp, erts_aint32_t new); +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_cmpxchg(erts_smp_atomic32_t *xchgp, + erts_aint32_t new, + erts_aint32_t expected); +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_bor(erts_smp_atomic32_t *var, erts_aint32_t mask); +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_band(erts_smp_atomic32_t *var, erts_aint32_t mask); +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_read_acqb(erts_smp_atomic32_t *var); +ERTS_GLB_INLINE void +erts_smp_atomic32_set_relb(erts_smp_atomic32_t *var, erts_aint32_t i); +ERTS_GLB_INLINE void +erts_smp_atomic32_dec_relb(erts_smp_atomic32_t *decp); +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_dectest_relb(erts_smp_atomic32_t *decp); +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_cmpxchg_acqb(erts_smp_atomic32_t *xchgp, + erts_aint32_t new, + erts_aint32_t exp); +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_cmpxchg_relb(erts_smp_atomic32_t *xchgp, + erts_aint32_t new, + erts_aint32_t exp); ERTS_GLB_INLINE void erts_smp_spinlock_init_x(erts_smp_spinlock_t *lock, char *name, Eterm extra); @@ -221,7 +266,6 @@ ERTS_GLB_INLINE void erts_smp_write_lock(erts_smp_rwlock_t *lock); ERTS_GLB_INLINE void erts_smp_write_unlock(erts_smp_rwlock_t *lock); ERTS_GLB_INLINE int erts_smp_lc_rwlock_is_rlocked(erts_smp_rwlock_t *lock); ERTS_GLB_INLINE int erts_smp_lc_rwlock_is_rwlocked(erts_smp_rwlock_t *lock); -ERTS_GLB_INLINE void erts_smp_thr_time_now(erts_smp_thr_timeval_t *time); ERTS_GLB_INLINE void erts_smp_tsd_key_create(erts_smp_tsd_key_t *keyp); ERTS_GLB_INLINE void erts_smp_tsd_key_delete(erts_smp_tsd_key_t key); ERTS_GLB_INLINE void erts_smp_tsd_set(erts_smp_tsd_key_t key, void *value); @@ -611,7 +655,7 @@ erts_smp_lc_rwmtx_is_rwlocked(erts_smp_rwmtx_t *mtx) } ERTS_GLB_INLINE void -erts_smp_atomic_init(erts_smp_atomic_t *var, long i) +erts_smp_atomic_init(erts_smp_atomic_t *var, erts_aint_t i) { #ifdef ERTS_SMP erts_atomic_init(var, i); @@ -621,7 +665,7 @@ erts_smp_atomic_init(erts_smp_atomic_t *var, long i) } ERTS_GLB_INLINE void -erts_smp_atomic_set(erts_smp_atomic_t *var, long i) +erts_smp_atomic_set(erts_smp_atomic_t *var, erts_aint_t i) { #ifdef ERTS_SMP erts_atomic_set(var, i); @@ -630,7 +674,7 @@ erts_smp_atomic_set(erts_smp_atomic_t *var, long i) #endif } -ERTS_GLB_INLINE long +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_read(erts_smp_atomic_t *var) { #ifdef ERTS_SMP @@ -640,7 +684,7 @@ erts_smp_atomic_read(erts_smp_atomic_t *var) #endif } -ERTS_GLB_INLINE long +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_inctest(erts_smp_atomic_t *incp) { #ifdef ERTS_SMP @@ -650,7 +694,7 @@ erts_smp_atomic_inctest(erts_smp_atomic_t *incp) #endif } -ERTS_GLB_INLINE long +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_dectest(erts_smp_atomic_t *decp) { #ifdef ERTS_SMP @@ -680,8 +724,8 @@ erts_smp_atomic_dec(erts_smp_atomic_t *decp) #endif } -ERTS_GLB_INLINE long -erts_smp_atomic_addtest(erts_smp_atomic_t *addp, long i) +ERTS_GLB_INLINE erts_aint_t +erts_smp_atomic_addtest(erts_smp_atomic_t *addp, erts_aint_t i) { #ifdef ERTS_SMP return erts_atomic_addtest(addp, i); @@ -691,7 +735,7 @@ erts_smp_atomic_addtest(erts_smp_atomic_t *addp, long i) } ERTS_GLB_INLINE void -erts_smp_atomic_add(erts_smp_atomic_t *addp, long i) +erts_smp_atomic_add(erts_smp_atomic_t *addp, erts_aint_t i) { #ifdef ERTS_SMP erts_atomic_add(addp, i); @@ -700,59 +744,61 @@ erts_smp_atomic_add(erts_smp_atomic_t *addp, long i) #endif } -ERTS_GLB_INLINE long -erts_smp_atomic_xchg(erts_smp_atomic_t *xchgp, long new) +ERTS_GLB_INLINE erts_aint_t +erts_smp_atomic_xchg(erts_smp_atomic_t *xchgp, erts_aint_t new) { #ifdef ERTS_SMP return erts_atomic_xchg(xchgp, new); #else - long old; + erts_aint_t old; old = *xchgp; *xchgp = new; return old; #endif } -ERTS_GLB_INLINE long -erts_smp_atomic_cmpxchg(erts_smp_atomic_t *xchgp, long new, long expected) +ERTS_GLB_INLINE erts_aint_t +erts_smp_atomic_cmpxchg(erts_smp_atomic_t *xchgp, + erts_aint_t new, + erts_aint_t expected) { #ifdef ERTS_SMP return erts_atomic_cmpxchg(xchgp, new, expected); #else - long old = *xchgp; + erts_aint_t old = *xchgp; if (old == expected) *xchgp = new; return old; #endif } -ERTS_GLB_INLINE long -erts_smp_atomic_bor(erts_smp_atomic_t *var, long mask) +ERTS_GLB_INLINE erts_aint_t +erts_smp_atomic_bor(erts_smp_atomic_t *var, erts_aint_t mask) { #ifdef ERTS_SMP return erts_atomic_bor(var, mask); #else - long old; + erts_aint_t old; old = *var; *var |= mask; return old; #endif } -ERTS_GLB_INLINE long -erts_smp_atomic_band(erts_smp_atomic_t *var, long mask) +ERTS_GLB_INLINE erts_aint_t +erts_smp_atomic_band(erts_smp_atomic_t *var, erts_aint_t mask) { #ifdef ERTS_SMP return erts_atomic_band(var, mask); #else - long old; + erts_aint_t old; old = *var; *var &= mask; return old; #endif } -ERTS_GLB_INLINE long +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_read_acqb(erts_smp_atomic_t *var) { #ifdef ERTS_SMP @@ -763,7 +809,7 @@ erts_smp_atomic_read_acqb(erts_smp_atomic_t *var) } ERTS_GLB_INLINE void -erts_smp_atomic_set_relb(erts_smp_atomic_t *var, long i) +erts_smp_atomic_set_relb(erts_smp_atomic_t *var, erts_aint_t i) { #ifdef ERTS_SMP erts_atomic_set_relb(var, i); @@ -772,7 +818,8 @@ erts_smp_atomic_set_relb(erts_smp_atomic_t *var, long i) #endif } -ERTS_GLB_INLINE void erts_smp_atomic_dec_relb(erts_smp_atomic_t *decp) +ERTS_GLB_INLINE void +erts_smp_atomic_dec_relb(erts_smp_atomic_t *decp) { #ifdef ERTS_SMP erts_atomic_dec_relb(decp); @@ -781,7 +828,7 @@ ERTS_GLB_INLINE void erts_smp_atomic_dec_relb(erts_smp_atomic_t *decp) #endif } -ERTS_GLB_INLINE long +ERTS_GLB_INLINE erts_aint_t erts_smp_atomic_dectest_relb(erts_smp_atomic_t *decp) { #ifdef ERTS_SMP @@ -791,28 +838,244 @@ erts_smp_atomic_dectest_relb(erts_smp_atomic_t *decp) #endif } -ERTS_GLB_INLINE long erts_smp_atomic_cmpxchg_acqb(erts_smp_atomic_t *xchgp, - long new, - long exp) +ERTS_GLB_INLINE erts_aint_t +erts_smp_atomic_cmpxchg_acqb(erts_smp_atomic_t *xchgp, + erts_aint_t new, + erts_aint_t exp) { #ifdef ERTS_SMP return erts_atomic_cmpxchg_acqb(xchgp, new, exp); #else - long old = *xchgp; + erts_aint_t old = *xchgp; if (old == exp) *xchgp = new; return old; #endif } -ERTS_GLB_INLINE long erts_smp_atomic_cmpxchg_relb(erts_smp_atomic_t *xchgp, - long new, - long exp) +ERTS_GLB_INLINE erts_aint_t +erts_smp_atomic_cmpxchg_relb(erts_smp_atomic_t *xchgp, + erts_aint_t new, + erts_aint_t exp) { #ifdef ERTS_SMP return erts_atomic_cmpxchg_relb(xchgp, new, exp); #else - long old = *xchgp; + erts_aint_t old = *xchgp; + if (old == exp) + *xchgp = new; + return old; +#endif +} + +ERTS_GLB_INLINE void +erts_smp_atomic32_init(erts_smp_atomic32_t *var, erts_aint32_t i) +{ +#ifdef ERTS_SMP + erts_atomic32_init(var, i); +#else + *var = i; +#endif +} + +ERTS_GLB_INLINE void +erts_smp_atomic32_set(erts_smp_atomic32_t *var, erts_aint32_t i) +{ +#ifdef ERTS_SMP + erts_atomic32_set(var, i); +#else + *var = i; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_read(erts_smp_atomic32_t *var) +{ +#ifdef ERTS_SMP + return erts_atomic32_read(var); +#else + return *var; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_inctest(erts_smp_atomic32_t *incp) +{ +#ifdef ERTS_SMP + return erts_atomic32_inctest(incp); +#else + return ++(*incp); +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_dectest(erts_smp_atomic32_t *decp) +{ +#ifdef ERTS_SMP + return erts_atomic32_dectest(decp); +#else + return --(*decp); +#endif +} + +ERTS_GLB_INLINE void +erts_smp_atomic32_inc(erts_smp_atomic32_t *incp) +{ +#ifdef ERTS_SMP + erts_atomic32_inc(incp); +#else + ++(*incp); +#endif +} + +ERTS_GLB_INLINE void +erts_smp_atomic32_dec(erts_smp_atomic32_t *decp) +{ +#ifdef ERTS_SMP + erts_atomic32_dec(decp); +#else + --(*decp); +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_addtest(erts_smp_atomic32_t *addp, erts_aint32_t i) +{ +#ifdef ERTS_SMP + return erts_atomic32_addtest(addp, i); +#else + return *addp += i; +#endif +} + +ERTS_GLB_INLINE void +erts_smp_atomic32_add(erts_smp_atomic32_t *addp, erts_aint32_t i) +{ +#ifdef ERTS_SMP + erts_atomic32_add(addp, i); +#else + *addp += i; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_xchg(erts_smp_atomic32_t *xchgp, erts_aint32_t new) +{ +#ifdef ERTS_SMP + return erts_atomic32_xchg(xchgp, new); +#else + erts_aint32_t old; + old = *xchgp; + *xchgp = new; + return old; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_cmpxchg(erts_smp_atomic32_t *xchgp, + erts_aint32_t new, + erts_aint32_t expected) +{ +#ifdef ERTS_SMP + return erts_atomic32_cmpxchg(xchgp, new, expected); +#else + erts_aint32_t old = *xchgp; + if (old == expected) + *xchgp = new; + return old; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_bor(erts_smp_atomic32_t *var, erts_aint32_t mask) +{ +#ifdef ERTS_SMP + return erts_atomic32_bor(var, mask); +#else + erts_aint32_t old; + old = *var; + *var |= mask; + return old; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_band(erts_smp_atomic32_t *var, erts_aint32_t mask) +{ +#ifdef ERTS_SMP + return erts_atomic32_band(var, mask); +#else + erts_aint32_t old; + old = *var; + *var &= mask; + return old; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_read_acqb(erts_smp_atomic32_t *var) +{ +#ifdef ERTS_SMP + return erts_atomic32_read_acqb(var); +#else + return *var; +#endif +} + +ERTS_GLB_INLINE void +erts_smp_atomic32_set_relb(erts_smp_atomic32_t *var, erts_aint32_t i) +{ +#ifdef ERTS_SMP + erts_atomic32_set_relb(var, i); +#else + *var = i; +#endif +} + +ERTS_GLB_INLINE void +erts_smp_atomic32_dec_relb(erts_smp_atomic32_t *decp) +{ +#ifdef ERTS_SMP + erts_atomic32_dec_relb(decp); +#else + --(*decp); +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_dectest_relb(erts_smp_atomic32_t *decp) +{ +#ifdef ERTS_SMP + return erts_atomic32_dectest_relb(decp); +#else + return --(*decp); +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_cmpxchg_acqb(erts_smp_atomic32_t *xchgp, + erts_aint32_t new, + erts_aint32_t exp) +{ +#ifdef ERTS_SMP + return erts_atomic32_cmpxchg_acqb(xchgp, new, exp); +#else + erts_aint32_t old = *xchgp; + if (old == exp) + *xchgp = new; + return old; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_smp_atomic32_cmpxchg_relb(erts_smp_atomic32_t *xchgp, + erts_aint32_t new, + erts_aint32_t exp) +{ +#ifdef ERTS_SMP + return erts_atomic32_cmpxchg_relb(xchgp, new, exp); +#else + erts_aint32_t old = *xchgp; if (old == exp) *xchgp = new; return old; @@ -988,14 +1251,6 @@ erts_smp_lc_rwlock_is_rwlocked(erts_smp_rwlock_t *lock) } ERTS_GLB_INLINE void -erts_smp_thr_time_now(erts_smp_thr_timeval_t *time) -{ -#ifdef ERTS_SMP - erts_thr_time_now(time); -#endif -} - -ERTS_GLB_INLINE void erts_smp_tsd_key_create(erts_smp_tsd_key_t *keyp) { #ifdef ERTS_SMP diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index b8e4473141..815cc1beae 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -193,7 +193,7 @@ struct erl_node_; /* Declared in erl_node_tables.h */ #endif #define _is_aligned(x) (((Uint)(x) & 0x3) == 0) #define _unchecked_make_boxed(x) ((Uint) COMPRESS_POINTER(x) + TAG_PRIMARY_BOXED) -_ET_DECLARE_CHECKED(Eterm,make_boxed,Eterm*); +_ET_DECLARE_CHECKED(Eterm,make_boxed,Eterm*) #define make_boxed(x) _ET_APPLY(make_boxed,(x)) #if 1 #define _is_not_boxed(x) ((x) & (_TAG_PRIMARY_MASK-TAG_PRIMARY_BOXED)) @@ -204,12 +204,12 @@ _ET_DECLARE_CHECKED(int,is_boxed,Eterm) #define is_boxed(x) (((x) & _TAG_PRIMARY_MASK) == TAG_PRIMARY_BOXED) #endif #define _unchecked_boxed_val(x) ((Eterm*) EXPAND_POINTER(((x) - TAG_PRIMARY_BOXED))) -_ET_DECLARE_CHECKED(Eterm*,boxed_val,Eterm); +_ET_DECLARE_CHECKED(Eterm*,boxed_val,Eterm) #define boxed_val(x) _ET_APPLY(boxed_val,(x)) /* cons cell ("list") access methods */ #define _unchecked_make_list(x) ((Uint) COMPRESS_POINTER(x) + TAG_PRIMARY_LIST) -_ET_DECLARE_CHECKED(Eterm,make_list,Eterm*); +_ET_DECLARE_CHECKED(Eterm,make_list,Eterm*) #define make_list(x) _ET_APPLY(make_list,(x)) #if 1 #define _unchecked_is_not_list(x) ((x) & (_TAG_PRIMARY_MASK-TAG_PRIMARY_LIST)) @@ -226,7 +226,7 @@ _ET_DECLARE_CHECKED(int,is_not_list,Eterm) #define _list_precond(x) (is_list(x)) #endif #define _unchecked_list_val(x) ((Eterm*) EXPAND_POINTER((x) - TAG_PRIMARY_LIST)) -_ET_DECLARE_CHECKED(Eterm*,list_val,Eterm); +_ET_DECLARE_CHECKED(Eterm*,list_val,Eterm) #define list_val(x) _ET_APPLY(list_val,(x)) #define CONS(hp, car, cdr) \ @@ -995,14 +995,14 @@ _ET_DECLARE_CHECKED(struct erl_node_*,external_ref_node,Eterm) #endif #define _unchecked_make_cp(x) ((Eterm) COMPRESS_POINTER(x)) -_ET_DECLARE_CHECKED(Eterm,make_cp,BeamInstr*); +_ET_DECLARE_CHECKED(Eterm,make_cp,BeamInstr*) #define make_cp(x) _ET_APPLY(make_cp,(x)) #define is_not_CP(x) ((x) & _CPMASK) #define is_CP(x) (!is_not_CP(x)) #define _unchecked_cp_val(x) ((BeamInstr*) EXPAND_POINTER(x)) -_ET_DECLARE_CHECKED(BeamInstr*,cp_val,Eterm); +_ET_DECLARE_CHECKED(BeamInstr*,cp_val,Eterm) #define cp_val(x) _ET_APPLY(cp_val,(x)) #define make_catch(x) (((x) << _TAG_IMMED2_SIZE) | _TAG_IMMED2_CATCH) diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h index a74cf79b8c..84a20b51f2 100644 --- a/erts/emulator/beam/erl_threads.h +++ b/erts/emulator/beam/erl_threads.h @@ -89,7 +89,10 @@ typedef ethr_rwmutex_opt erts_rwmtx_opt_t; typedef ethr_tsd_key erts_tsd_key_t; typedef ethr_ts_event erts_tse_t; +typedef ethr_sint_t erts_aint_t; typedef ethr_atomic_t erts_atomic_t; +typedef ethr_sint32_t erts_aint32_t; +typedef ethr_atomic32_t erts_atomic32_t; /* spinlock */ typedef struct { @@ -113,7 +116,6 @@ typedef struct { #endif } erts_rwlock_t; -typedef ethr_timeval erts_thr_timeval_t; __decl_noreturn void __noreturn erts_thr_fatal_error(int, char *); /* implemented in erl_init.c */ @@ -152,7 +154,10 @@ typedef struct { typedef int erts_rwmtx_t; typedef int erts_tsd_key_t; typedef int erts_tse_t; -typedef long erts_atomic_t; +typedef SWord erts_aint_t; +typedef SWord erts_atomic_t; +typedef SWord erts_aint32_t; +typedef SWord erts_atomic32_t; #if __GNUC__ > 2 typedef struct { } erts_spinlock_t; typedef struct { } erts_rwlock_t; @@ -160,10 +165,6 @@ typedef struct { } erts_rwlock_t; typedef struct { int gcc_is_buggy; } erts_spinlock_t; typedef struct { int gcc_is_buggy; } erts_rwlock_t; #endif -typedef struct { - long tv_sec; - long tv_nsec; -} erts_thr_timeval_t; #define ERTS_MTX_INITER 0 #define ERTS_CND_INITER 0 @@ -173,6 +174,8 @@ typedef struct { #endif /* #ifdef USE_THREADS */ +#define ERTS_AINT_T_MAX (~(((erts_aint_t) 1) << (sizeof(erts_aint_t)*8-1))) + ERTS_GLB_INLINE void erts_thr_init(erts_thr_init_data_t *id); ERTS_GLB_INLINE void erts_thr_late_init(erts_thr_late_init_data_t *id); ERTS_GLB_INLINE void erts_thr_create(erts_tid_t *tid, void * (*func)(void *), @@ -231,33 +234,65 @@ ERTS_GLB_INLINE int erts_rwmtx_tryrwlock(erts_rwmtx_t *rwmtx); ERTS_GLB_INLINE void erts_rwmtx_rwunlock(erts_rwmtx_t *rwmtx); ERTS_GLB_INLINE int erts_lc_rwmtx_is_rlocked(erts_rwmtx_t *mtx); ERTS_GLB_INLINE int erts_lc_rwmtx_is_rwlocked(erts_rwmtx_t *mtx); -ERTS_GLB_INLINE void erts_atomic_init(erts_atomic_t *var, long i); -ERTS_GLB_INLINE void erts_atomic_set(erts_atomic_t *var, long i); -ERTS_GLB_INLINE long erts_atomic_read(erts_atomic_t *var); -ERTS_GLB_INLINE long erts_atomic_inctest(erts_atomic_t *incp); -ERTS_GLB_INLINE long erts_atomic_dectest(erts_atomic_t *decp); +ERTS_GLB_INLINE void erts_atomic_init(erts_atomic_t *var, erts_aint_t i); +ERTS_GLB_INLINE void erts_atomic_set(erts_atomic_t *var, erts_aint_t i); +ERTS_GLB_INLINE erts_aint_t erts_atomic_read(erts_atomic_t *var); +ERTS_GLB_INLINE erts_aint_t erts_atomic_inctest(erts_atomic_t *incp); +ERTS_GLB_INLINE erts_aint_t erts_atomic_dectest(erts_atomic_t *decp); ERTS_GLB_INLINE void erts_atomic_inc(erts_atomic_t *incp); ERTS_GLB_INLINE void erts_atomic_dec(erts_atomic_t *decp); -ERTS_GLB_INLINE long erts_atomic_addtest(erts_atomic_t *addp, - long i); -ERTS_GLB_INLINE void erts_atomic_add(erts_atomic_t *addp, long i); -ERTS_GLB_INLINE long erts_atomic_xchg(erts_atomic_t *xchgp, - long new); -ERTS_GLB_INLINE long erts_atomic_cmpxchg(erts_atomic_t *xchgp, - long new, - long expected); -ERTS_GLB_INLINE long erts_atomic_bor(erts_atomic_t *var, long mask); -ERTS_GLB_INLINE long erts_atomic_band(erts_atomic_t *var, long mask); -ERTS_GLB_INLINE long erts_atomic_read_acqb(erts_atomic_t *var); -ERTS_GLB_INLINE void erts_atomic_set_relb(erts_atomic_t *var, long i); +ERTS_GLB_INLINE erts_aint_t erts_atomic_addtest(erts_atomic_t *addp, + erts_aint_t i); +ERTS_GLB_INLINE void erts_atomic_add(erts_atomic_t *addp, erts_aint_t i); +ERTS_GLB_INLINE erts_aint_t erts_atomic_xchg(erts_atomic_t *xchgp, + erts_aint_t new); +ERTS_GLB_INLINE erts_aint_t erts_atomic_cmpxchg(erts_atomic_t *xchgp, + erts_aint_t new, + erts_aint_t expected); +ERTS_GLB_INLINE erts_aint_t erts_atomic_bor(erts_atomic_t *var, + erts_aint_t mask); +ERTS_GLB_INLINE erts_aint_t erts_atomic_band(erts_atomic_t *var, + erts_aint_t mask); +ERTS_GLB_INLINE erts_aint_t erts_atomic_read_acqb(erts_atomic_t *var); +ERTS_GLB_INLINE void erts_atomic_set_relb(erts_atomic_t *var, erts_aint_t i); ERTS_GLB_INLINE void erts_atomic_dec_relb(erts_atomic_t *decp); -ERTS_GLB_INLINE long erts_atomic_dectest_relb(erts_atomic_t *decp); -ERTS_GLB_INLINE long erts_atomic_cmpxchg_acqb(erts_atomic_t *xchgp, - long new, - long exp); -ERTS_GLB_INLINE long erts_atomic_cmpxchg_relb(erts_atomic_t *xchgp, - long new, - long exp); +ERTS_GLB_INLINE erts_aint_t erts_atomic_dectest_relb(erts_atomic_t *decp); +ERTS_GLB_INLINE erts_aint_t erts_atomic_cmpxchg_acqb(erts_atomic_t *xchgp, + erts_aint_t new, + erts_aint_t exp); +ERTS_GLB_INLINE erts_aint_t erts_atomic_cmpxchg_relb(erts_atomic_t *xchgp, + erts_aint_t new, + erts_aint_t exp); +ERTS_GLB_INLINE void erts_atomic32_init(erts_atomic32_t *var, erts_aint32_t i); +ERTS_GLB_INLINE void erts_atomic32_set(erts_atomic32_t *var, erts_aint32_t i); +ERTS_GLB_INLINE erts_aint32_t erts_atomic32_read(erts_atomic32_t *var); +ERTS_GLB_INLINE erts_aint32_t erts_atomic32_inctest(erts_atomic32_t *incp); +ERTS_GLB_INLINE erts_aint32_t erts_atomic32_dectest(erts_atomic32_t *decp); +ERTS_GLB_INLINE void erts_atomic32_inc(erts_atomic32_t *incp); +ERTS_GLB_INLINE void erts_atomic32_dec(erts_atomic32_t *decp); +ERTS_GLB_INLINE erts_aint32_t erts_atomic32_addtest(erts_atomic32_t *addp, + erts_aint32_t i); +ERTS_GLB_INLINE void erts_atomic32_add(erts_atomic32_t *addp, erts_aint32_t i); +ERTS_GLB_INLINE erts_aint32_t erts_atomic32_xchg(erts_atomic32_t *xchgp, + erts_aint32_t new); +ERTS_GLB_INLINE erts_aint32_t erts_atomic32_cmpxchg(erts_atomic32_t *xchgp, + erts_aint32_t new, + erts_aint32_t expected); +ERTS_GLB_INLINE erts_aint32_t erts_atomic32_bor(erts_atomic32_t *var, + erts_aint32_t mask); +ERTS_GLB_INLINE erts_aint32_t erts_atomic32_band(erts_atomic32_t *var, + erts_aint32_t mask); +ERTS_GLB_INLINE erts_aint32_t erts_atomic32_read_acqb(erts_atomic32_t *var); +ERTS_GLB_INLINE void erts_atomic32_set_relb(erts_atomic32_t *var, + erts_aint32_t i); +ERTS_GLB_INLINE void erts_atomic32_dec_relb(erts_atomic32_t *decp); +ERTS_GLB_INLINE erts_aint32_t erts_atomic32_dectest_relb(erts_atomic32_t *decp); +ERTS_GLB_INLINE erts_aint32_t erts_atomic32_cmpxchg_acqb(erts_atomic32_t *xchgp, + erts_aint32_t new, + erts_aint32_t exp); +ERTS_GLB_INLINE erts_aint32_t erts_atomic32_cmpxchg_relb(erts_atomic32_t *xchgp, + erts_aint32_t new, + erts_aint32_t exp); ERTS_GLB_INLINE void erts_spinlock_init_x_opt(erts_spinlock_t *lock, char *name, Eterm extra, @@ -292,7 +327,6 @@ ERTS_GLB_INLINE void erts_write_lock(erts_rwlock_t *lock); ERTS_GLB_INLINE void erts_write_unlock(erts_rwlock_t *lock); ERTS_GLB_INLINE int erts_lc_rwlock_is_rlocked(erts_rwlock_t *lock); ERTS_GLB_INLINE int erts_lc_rwlock_is_rwlocked(erts_rwlock_t *lock); -ERTS_GLB_INLINE void erts_thr_time_now(erts_thr_timeval_t *time); ERTS_GLB_INLINE void erts_tsd_key_create(erts_tsd_key_t *keyp); ERTS_GLB_INLINE void erts_tsd_key_delete(erts_tsd_key_t key); ERTS_GLB_INLINE void erts_tsd_set(erts_tsd_key_t key, void *value); @@ -925,7 +959,7 @@ erts_lc_rwmtx_is_rwlocked(erts_rwmtx_t *mtx) } ERTS_GLB_INLINE void -erts_atomic_init(erts_atomic_t *var, long i) +erts_atomic_init(erts_atomic_t *var, erts_aint_t i) { #ifdef USE_THREADS ethr_atomic_init(var, i); @@ -935,7 +969,7 @@ erts_atomic_init(erts_atomic_t *var, long i) } ERTS_GLB_INLINE void -erts_atomic_set(erts_atomic_t *var, long i) +erts_atomic_set(erts_atomic_t *var, erts_aint_t i) { #ifdef USE_THREADS ethr_atomic_set(var, i); @@ -944,7 +978,7 @@ erts_atomic_set(erts_atomic_t *var, long i) #endif } -ERTS_GLB_INLINE long +ERTS_GLB_INLINE erts_aint_t erts_atomic_read(erts_atomic_t *var) { #ifdef USE_THREADS @@ -954,7 +988,7 @@ erts_atomic_read(erts_atomic_t *var) #endif } -ERTS_GLB_INLINE long +ERTS_GLB_INLINE erts_aint_t erts_atomic_inctest(erts_atomic_t *incp) { #ifdef USE_THREADS @@ -964,7 +998,7 @@ erts_atomic_inctest(erts_atomic_t *incp) #endif } -ERTS_GLB_INLINE long +ERTS_GLB_INLINE erts_aint_t erts_atomic_dectest(erts_atomic_t *decp) { #ifdef USE_THREADS @@ -994,8 +1028,8 @@ erts_atomic_dec(erts_atomic_t *decp) #endif } -ERTS_GLB_INLINE long -erts_atomic_addtest(erts_atomic_t *addp, long i) +ERTS_GLB_INLINE erts_aint_t +erts_atomic_addtest(erts_atomic_t *addp, erts_aint_t i) { #ifdef USE_THREADS return ethr_atomic_add_read(addp, i); @@ -1005,7 +1039,7 @@ erts_atomic_addtest(erts_atomic_t *addp, long i) } ERTS_GLB_INLINE void -erts_atomic_add(erts_atomic_t *addp, long i) +erts_atomic_add(erts_atomic_t *addp, erts_aint_t i) { #ifdef USE_THREADS ethr_atomic_add(addp, i); @@ -1014,59 +1048,58 @@ erts_atomic_add(erts_atomic_t *addp, long i) #endif } -ERTS_GLB_INLINE long -erts_atomic_xchg(erts_atomic_t *xchgp, long new) +ERTS_GLB_INLINE erts_aint_t +erts_atomic_xchg(erts_atomic_t *xchgp, erts_aint_t new) { - long old; #ifdef USE_THREADS return ethr_atomic_xchg(xchgp, new); #else - old = *xchgp; + erts_aint_t old = *xchgp; *xchgp = new; -#endif return old; +#endif } -ERTS_GLB_INLINE long -erts_atomic_cmpxchg(erts_atomic_t *xchgp, long new, long expected) +ERTS_GLB_INLINE erts_aint_t +erts_atomic_cmpxchg(erts_atomic_t *xchgp, erts_aint_t new, erts_aint_t expected) { #ifdef USE_THREADS return ethr_atomic_cmpxchg(xchgp, new, expected); #else - long old = *xchgp; + erts_aint_t old = *xchgp; if (old == expected) *xchgp = new; return old; #endif } -ERTS_GLB_INLINE long -erts_atomic_bor(erts_atomic_t *var, long mask) +ERTS_GLB_INLINE erts_aint_t +erts_atomic_bor(erts_atomic_t *var, erts_aint_t mask) { #ifdef USE_THREADS return ethr_atomic_read_bor(var, mask); #else - long old; + erts_aint_t old; old = *var; *var |= mask; return old; #endif } -ERTS_GLB_INLINE long -erts_atomic_band(erts_atomic_t *var, long mask) +ERTS_GLB_INLINE erts_aint_t +erts_atomic_band(erts_atomic_t *var, erts_aint_t mask) { #ifdef USE_THREADS return ethr_atomic_read_band(var, mask); #else - long old; + erts_aint_t old; old = *var; *var &= mask; return old; #endif } -ERTS_GLB_INLINE long +ERTS_GLB_INLINE erts_aint_t erts_atomic_read_acqb(erts_atomic_t *var) { #ifdef USE_THREADS @@ -1077,7 +1110,7 @@ erts_atomic_read_acqb(erts_atomic_t *var) } ERTS_GLB_INLINE void -erts_atomic_set_relb(erts_atomic_t *var, long i) +erts_atomic_set_relb(erts_atomic_t *var, erts_aint_t i) { #ifdef USE_THREADS ethr_atomic_set_relb(var, i); @@ -1096,7 +1129,7 @@ erts_atomic_dec_relb(erts_atomic_t *decp) #endif } -ERTS_GLB_INLINE long +ERTS_GLB_INLINE erts_aint_t erts_atomic_dectest_relb(erts_atomic_t *decp) { #ifdef USE_THREADS @@ -1106,28 +1139,243 @@ erts_atomic_dectest_relb(erts_atomic_t *decp) #endif } -ERTS_GLB_INLINE long erts_atomic_cmpxchg_acqb(erts_atomic_t *xchgp, - long new, - long exp) +ERTS_GLB_INLINE erts_aint_t erts_atomic_cmpxchg_acqb(erts_atomic_t *xchgp, + erts_aint_t new, + erts_aint_t exp) { #ifdef USE_THREADS return ethr_atomic_cmpxchg_acqb(xchgp, new, exp); #else - long old = *xchgp; + erts_aint_t old = *xchgp; if (old == exp) *xchgp = new; return old; #endif } -ERTS_GLB_INLINE long erts_atomic_cmpxchg_relb(erts_atomic_t *xchgp, - long new, - long exp) +ERTS_GLB_INLINE erts_aint_t erts_atomic_cmpxchg_relb(erts_atomic_t *xchgp, + erts_aint_t new, + erts_aint_t exp) { #ifdef USE_THREADS return ethr_atomic_cmpxchg_relb(xchgp, new, exp); #else - long old = *xchgp; + erts_aint_t old = *xchgp; + if (old == exp) + *xchgp = new; + return old; +#endif +} + +/* atomic32 */ + +ERTS_GLB_INLINE void +erts_atomic32_init(erts_atomic32_t *var, erts_aint32_t i) +{ +#ifdef USE_THREADS + ethr_atomic32_init(var, i); +#else + *var = i; +#endif +} + +ERTS_GLB_INLINE void +erts_atomic32_set(erts_atomic32_t *var, erts_aint32_t i) +{ +#ifdef USE_THREADS + ethr_atomic32_set(var, i); +#else + *var = i; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_atomic32_read(erts_atomic32_t *var) +{ +#ifdef USE_THREADS + return ethr_atomic32_read(var); +#else + return *var; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_atomic32_inctest(erts_atomic32_t *incp) +{ +#ifdef USE_THREADS + return ethr_atomic32_inc_read(incp); +#else + return ++(*incp); +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_atomic32_dectest(erts_atomic32_t *decp) +{ +#ifdef USE_THREADS + return ethr_atomic32_dec_read(decp); +#else + return --(*decp); +#endif +} + +ERTS_GLB_INLINE void +erts_atomic32_inc(erts_atomic32_t *incp) +{ +#ifdef USE_THREADS + ethr_atomic32_inc(incp); +#else + ++(*incp); +#endif +} + +ERTS_GLB_INLINE void +erts_atomic32_dec(erts_atomic32_t *decp) +{ +#ifdef USE_THREADS + ethr_atomic32_dec(decp); +#else + --(*decp); +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_atomic32_addtest(erts_atomic32_t *addp, erts_aint32_t i) +{ +#ifdef USE_THREADS + return ethr_atomic32_add_read(addp, i); +#else + return *addp += i; +#endif +} + +ERTS_GLB_INLINE void +erts_atomic32_add(erts_atomic32_t *addp, erts_aint32_t i) +{ +#ifdef USE_THREADS + ethr_atomic32_add(addp, i); +#else + *addp += i; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_atomic32_xchg(erts_atomic32_t *xchgp, erts_aint32_t new) +{ +#ifdef USE_THREADS + return ethr_atomic32_xchg(xchgp, new); +#else + erts_aint32_t old = *xchgp; + *xchgp = new; + return old; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_atomic32_cmpxchg(erts_atomic32_t *xchgp, + erts_aint32_t new, + erts_aint32_t expected) +{ +#ifdef USE_THREADS + return ethr_atomic32_cmpxchg(xchgp, new, expected); +#else + erts_aint32_t old = *xchgp; + if (old == expected) + *xchgp = new; + return old; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_atomic32_bor(erts_atomic32_t *var, erts_aint32_t mask) +{ +#ifdef USE_THREADS + return ethr_atomic32_read_bor(var, mask); +#else + erts_aint32_t old; + old = *var; + *var |= mask; + return old; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_atomic32_band(erts_atomic32_t *var, erts_aint32_t mask) +{ +#ifdef USE_THREADS + return ethr_atomic32_read_band(var, mask); +#else + erts_aint32_t old; + old = *var; + *var &= mask; + return old; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_atomic32_read_acqb(erts_atomic32_t *var) +{ +#ifdef USE_THREADS + return ethr_atomic32_read_acqb(var); +#else + return *var; +#endif +} + +ERTS_GLB_INLINE void +erts_atomic32_set_relb(erts_atomic32_t *var, erts_aint32_t i) +{ +#ifdef USE_THREADS + ethr_atomic32_set_relb(var, i); +#else + *var = i; +#endif +} + +ERTS_GLB_INLINE void +erts_atomic32_dec_relb(erts_atomic32_t *decp) +{ +#ifdef USE_THREADS + ethr_atomic32_dec_relb(decp); +#else + --(*decp); +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_atomic32_dectest_relb(erts_atomic32_t *decp) +{ +#ifdef USE_THREADS + return ethr_atomic32_dec_read_relb(decp); +#else + return --(*decp); +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_atomic32_cmpxchg_acqb(erts_atomic32_t *xchgp, + erts_aint32_t new, + erts_aint32_t exp) +{ +#ifdef USE_THREADS + return ethr_atomic32_cmpxchg_acqb(xchgp, new, exp); +#else + erts_aint32_t old = *xchgp; + if (old == exp) + *xchgp = new; + return old; +#endif +} + +ERTS_GLB_INLINE erts_aint32_t +erts_atomic32_cmpxchg_relb(erts_atomic32_t *xchgp, + erts_aint32_t new, + erts_aint32_t exp) +{ +#ifdef USE_THREADS + return ethr_atomic32_cmpxchg_relb(xchgp, new, exp); +#else + erts_aint32_t old = *xchgp; if (old == exp) *xchgp = new; return old; @@ -1428,16 +1676,6 @@ erts_lc_rwlock_is_rwlocked(erts_rwlock_t *lock) } ERTS_GLB_INLINE void -erts_thr_time_now(erts_thr_timeval_t *time) -{ -#ifdef USE_THREADS - int res = ethr_time_now(time); - if (res) - erts_thr_fatal_error(res, "get current time"); -#endif -} - -ERTS_GLB_INLINE void erts_tsd_key_create(erts_tsd_key_t *keyp) { #ifdef USE_THREADS diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index 6f6b971d34..93d8ea4cb4 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -20,11 +20,15 @@ #ifndef ERL_TIME_H__ #define ERL_TIME_H__ +extern erts_smp_atomic_t do_time; /* set at clock interrupt */ +extern SysTimeval erts_first_emu_time; + /* ** Timer entry: */ typedef struct erl_timer { struct erl_timer* next; /* next entry tiw slot or chain */ + struct erl_timer* prev; /* prev entry tiw slot or chain */ Uint slot; /* slot in timer wheel */ Uint count; /* number of loops remaining */ int active; /* 1=activated, 0=deactivated */ @@ -39,7 +43,6 @@ typedef void (*ErlTimeoutProc)(void*); typedef void (*ErlCancelProc)(void*); #ifdef ERTS_SMP - /* * Process and port timer */ @@ -61,7 +64,66 @@ void erts_create_smp_ptimer(ErtsSmpPTimer **timer_ref, ErlTimeoutProc timeout_func, Uint timeout); void erts_cancel_smp_ptimer(ErtsSmpPTimer *ptimer); +#endif + +/* timer-wheel api */ +void erts_init_time(void); +void erts_set_timer(ErlTimer*, ErlTimeoutProc, ErlCancelProc, void*, Uint); +void erts_cancel_timer(ErlTimer*); +void erts_bump_timer(erts_aint_t); +Uint erts_timer_wheel_memory_size(void); +Uint erts_time_left(ErlTimer *); +erts_aint_t erts_next_time(void); + +#ifdef DEBUG +void erts_p_slpq(void); #endif +ERTS_GLB_INLINE erts_aint_t erts_do_time_read_and_reset(void); +ERTS_GLB_INLINE void erts_do_time_add(long); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE erts_aint_t erts_do_time_read_and_reset(void) { return erts_smp_atomic_xchg(&do_time, 0L); } +ERTS_GLB_INLINE void erts_do_time_add(long elapsed) { erts_smp_atomic_add(&do_time, elapsed); } + +#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ + + +/* time_sup */ + +#if (defined(HAVE_GETHRVTIME) || defined(HAVE_CLOCK_GETTIME)) +# ifndef HAVE_ERTS_NOW_CPU +# define HAVE_ERTS_NOW_CPU +# ifdef HAVE_GETHRVTIME +# define erts_start_now_cpu() sys_start_hrvtime() +# define erts_stop_now_cpu() sys_stop_hrvtime() +# endif +# endif +void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec); #endif + +void erts_get_timeval(SysTimeval *tv); +long erts_get_time(void); +void erts_get_emu_time(SysTimeval *); + +ERTS_GLB_INLINE int erts_cmp_timeval(SysTimeval *t1p, SysTimeval *t2p); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE int +erts_cmp_timeval(SysTimeval *t1p, SysTimeval *t2p) +{ + if (t1p->tv_sec == t2p->tv_sec) { + if (t1p->tv_usec < t2p->tv_usec) + return -1; + else if (t1p->tv_usec > t2p->tv_usec) + return 1; + return 0; + } + return t1p->tv_sec < t2p->tv_sec ? -1 : 1; +} + +#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ +#endif /* ERL_TIME_H__ */ diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 7b8706ea13..ca4b54188e 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -358,10 +358,6 @@ static int clock_resolution; ** instead of something like select. */ -#if defined(ERTS_TIMER_THREAD) -static ERTS_INLINE void init_erts_deliver_time(const SysTimeval *inittv) { } -static ERTS_INLINE void do_erts_deliver_time(const SysTimeval *current) { } -#else static SysTimeval last_delivered; static void init_erts_deliver_time(const SysTimeval *inittv) @@ -389,11 +385,10 @@ static void do_erts_deliver_time(const SysTimeval *current) this by simply pretend as if the time stood still. :) */ if (elapsed > 0) { - do_time_add(elapsed); + erts_do_time_add(elapsed); last_delivered = cur_time; } } -#endif int erts_init_time_sup(void) @@ -786,7 +781,6 @@ get_sys_now(Uint* megasec, Uint* sec, Uint* microsec) to a struct timeval representing current time (to save a gettimeofday() where possible) or NULL */ -#if !defined(ERTS_TIMER_THREAD) void erts_deliver_time(void) { SysTimeval now; @@ -797,7 +791,6 @@ void erts_deliver_time(void) { erts_smp_mtx_unlock(&erts_timeofday_mtx); } -#endif /* get *real* time (not ticks) remaining until next timeout - if there isn't one, give a "long" time, that is guaranteed @@ -806,14 +799,12 @@ void erts_deliver_time(void) { void erts_time_remaining(SysTimeval *rem_time) { int ticks; -#if !defined(ERTS_TIMER_THREAD) SysTimeval cur_time; -#endif long elapsed; - /* next_time() returns no of ticks to next timeout or -1 if none */ + /* erts_next_time() returns no of ticks to next timeout or -1 if none */ - if ((ticks = next_time()) == -1) { + if ((ticks = erts_next_time()) == -1) { /* timer queue empty */ /* this will cause at most 100000000 ticks */ rem_time->tv_sec = 100000; @@ -822,9 +813,6 @@ void erts_time_remaining(SysTimeval *rem_time) /* next timeout after ticks ticks */ ticks *= CLOCK_RESOLUTION; -#if defined(ERTS_TIMER_THREAD) - elapsed = 0; -#else erts_smp_mtx_lock(&erts_timeofday_mtx); get_tolerant_timeofday(&cur_time); @@ -839,7 +827,6 @@ void erts_time_remaining(SysTimeval *rem_time) rem_time->tv_sec = rem_time->tv_usec = 0; return; } -#endif rem_time->tv_sec = (ticks - elapsed) / 1000; rem_time->tv_usec = 1000 * ((ticks - elapsed) % 1000); } diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c index 72207df621..545b345a71 100644 --- a/erts/emulator/beam/erl_unicode.c +++ b/erts/emulator/beam/erl_unicode.c @@ -2420,7 +2420,7 @@ void erts_copy_utf8_to_utf16_little(byte *target, byte *bytes, int num_chars) /* * This internal bif converts a filename to whatever format is suitable for the file driver - * It also adds zero termination so that prim_file neednt bother with the character encoding + * It also adds zero termination so that prim_file needn't bother with the character encoding * of the file driver */ BIF_RETTYPE prim_file_internal_name2native_1(BIF_ALIST_1) diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index 48fa99934e..5b42a2ce11 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2010. 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 @@ -47,7 +47,7 @@ #define SEQ_TRACE 1 #define CONTEXT_REDS 2000 /* Swap process out after this number */ -#define MAX_ARG 256 /* Max number of arguments allowed */ +#define MAX_ARG 255 /* Max number of arguments allowed */ #define MAX_REG 1024 /* Max number of x(N) registers used */ /* Scheduler stores data for temporary heaps if diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 524db2a2eb..e8a9d5f32f 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -544,7 +544,7 @@ ERTS_GLB_INLINE void erts_may_save_closed_port(Port *prt) if (prt->snapshot != erts_smp_atomic_read(&erts_ports_snapshot)) { /* Dead ports are added from the end of the snapshot buffer */ Eterm* tombstone = (Eterm*) erts_smp_atomic_addtest(&erts_dead_ports_ptr, - -(long)sizeof(Eterm)); + -(erts_aint_t)sizeof(Eterm)); ASSERT(tombstone+1 != NULL); ASSERT(prt->snapshot == (Uint32) erts_smp_atomic_read(&erts_ports_snapshot) - 1); *tombstone = prt->id; @@ -563,7 +563,7 @@ extern Uint display_items; /* no of items to display in traces etc */ extern Uint display_loads; /* print info about loaded modules */ extern int erts_backtrace_depth; -extern erts_smp_atomic_t erts_max_gen_gcs; +extern erts_smp_atomic32_t erts_max_gen_gcs; extern int erts_disable_tolerant_timeofday; @@ -1206,7 +1206,7 @@ ERTS_GLB_INLINE void erts_smp_port_unlock(Port *prt) { #ifdef ERTS_SMP - long refc; + erts_aint_t refc; erts_smp_mtx_unlock(prt->lock); refc = erts_smp_atomic_dectest(&prt->refc); ASSERT(refc >= 0); @@ -1425,84 +1425,6 @@ void erl_drv_thr_init(void); /* time.c */ -ERTS_GLB_INLINE long do_time_read_and_reset(void); -#ifdef ERTS_TIMER_THREAD -ERTS_GLB_INLINE int next_time(void); -ERTS_GLB_INLINE void bump_timer(long); -#else -int next_time(void); -void bump_timer(long); -extern erts_smp_atomic_t do_time; /* set at clock interrupt */ -ERTS_GLB_INLINE void do_time_add(long); -#endif - -#if ERTS_GLB_INLINE_INCL_FUNC_DEF - -#ifdef ERTS_TIMER_THREAD -ERTS_GLB_INLINE long do_time_read_and_reset(void) { return 0; } -ERTS_GLB_INLINE int next_time(void) { return -1; } -ERTS_GLB_INLINE void bump_timer(long ignore) { } -#else -ERTS_GLB_INLINE long do_time_read_and_reset(void) -{ - return erts_smp_atomic_xchg(&do_time, 0L); -} -ERTS_GLB_INLINE void do_time_add(long elapsed) -{ - erts_smp_atomic_add(&do_time, elapsed); -} -#endif - -#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ - -void init_time(void); -void erl_set_timer(ErlTimer*, ErlTimeoutProc, ErlCancelProc, void*, Uint); -void erl_cancel_timer(ErlTimer*); -Uint time_left(ErlTimer *); - -Uint erts_timer_wheel_memory_size(void); - -#if (defined(HAVE_GETHRVTIME) || defined(HAVE_CLOCK_GETTIME)) -# ifndef HAVE_ERTS_NOW_CPU -# define HAVE_ERTS_NOW_CPU -# ifdef HAVE_GETHRVTIME -# define erts_start_now_cpu() sys_start_hrvtime() -# define erts_stop_now_cpu() sys_stop_hrvtime() -# endif -# endif -void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec); -#endif - -void erts_get_timeval(SysTimeval *tv); -long erts_get_time(void); - -extern SysTimeval erts_first_emu_time; - -void erts_get_emu_time(SysTimeval *); - -ERTS_GLB_INLINE int erts_cmp_timeval(SysTimeval *t1p, SysTimeval *t2p); - -#if ERTS_GLB_INLINE_INCL_FUNC_DEF - -ERTS_GLB_INLINE int -erts_cmp_timeval(SysTimeval *t1p, SysTimeval *t2p) -{ - if (t1p->tv_sec == t2p->tv_sec) { - if (t1p->tv_usec < t2p->tv_usec) - return -1; - else if (t1p->tv_usec > t2p->tv_usec) - return 1; - return 0; - } - return t1p->tv_sec < t2p->tv_sec ? -1 : 1; -} - -#endif - -#ifdef DEBUG -void p_slpq(void); -#endif - /* utils.c */ /* diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 9ed92bbe03..f21a96c754 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -428,7 +428,7 @@ setup_port(Port* prt, Eterm pid, erts_driver_t *driver, old_name = prt->name; prt->name = new_name; #ifdef ERTS_SMP - erts_smp_atomic_set(&prt->run_queue, (long) runq); + erts_smp_atomic_set(&prt->run_queue, (erts_aint_t) runq); #endif ASSERT(!prt->drv_ptr); prt->drv_ptr = driver; @@ -670,7 +670,7 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ #ifdef ERTS_SMP erts_cancel_smp_ptimer(port->ptimer); #else - erl_cancel_timer(&(port->tm)); + erts_cancel_timer(&(port->tm)); #endif stopq(port); kill_port(port); @@ -1297,7 +1297,7 @@ void init_io(void) erts_port[i].port_data_lock = NULL; } - erts_smp_atomic_init(&erts_ports_snapshot, (long) 0); + erts_smp_atomic_init(&erts_ports_snapshot, (erts_aint_t) 0); last_port_num = 0; erts_smp_spinlock_init(&get_free_port_lck, "get_free_port"); @@ -1839,7 +1839,7 @@ terminate_port(Port *prt) #ifdef ERTS_SMP erts_cancel_smp_ptimer(prt->ptimer); #else - erl_cancel_timer(&prt->tm); + erts_cancel_timer(&prt->tm); #endif drv = prt->drv_ptr; @@ -3252,7 +3252,7 @@ int driver_output_binary(ErlDrvPort ix, char* hbuf, int hlen, return 0; prt->bytes_in += (hlen + len); - erts_smp_atomic_add(&erts_bytes_in, (long) (hlen + len)); + erts_smp_atomic_add(&erts_bytes_in, (erts_aint_t) (hlen + len)); if (prt->status & ERTS_PORT_SFLG_DISTRIBUTION) { return erts_net_message(prt, prt->dist_entry, @@ -3287,7 +3287,7 @@ int driver_output2(ErlDrvPort ix, char* hbuf, int hlen, char* buf, int len) return 0; prt->bytes_in += (hlen + len); - erts_smp_atomic_add(&erts_bytes_in, (long) (hlen + len)); + erts_smp_atomic_add(&erts_bytes_in, (erts_aint_t) (hlen + len)); if (prt->status & ERTS_PORT_SFLG_DISTRIBUTION) { if (len == 0) return erts_net_message(prt, @@ -3364,7 +3364,7 @@ int driver_outputv(ErlDrvPort ix, char* hbuf, int hlen, ErlIOVec* vec, int skip) /* XXX handle distribution !!! */ prt->bytes_in += (hlen + size); - erts_smp_atomic_add(&erts_bytes_in, (long) (hlen + size)); + erts_smp_atomic_add(&erts_bytes_in, (erts_aint_t) (hlen + size)); deliver_vec_message(prt, prt->connected, hbuf, hlen, binv, iov, n, size); return 0; } @@ -3408,25 +3408,25 @@ int len; * reference count on driver binaries... */ -long +ErlDrvSInt driver_binary_get_refc(ErlDrvBinary *dbp) { Binary* bp = ErlDrvBinary2Binary(dbp); - return erts_refc_read(&bp->refc, 1); + return (ErlDrvSInt) erts_refc_read(&bp->refc, 1); } -long +ErlDrvSInt driver_binary_inc_refc(ErlDrvBinary *dbp) { Binary* bp = ErlDrvBinary2Binary(dbp); - return erts_refc_inctest(&bp->refc, 2); + return (ErlDrvSInt) erts_refc_inctest(&bp->refc, 2); } -long +ErlDrvSInt driver_binary_dec_refc(ErlDrvBinary *dbp) { Binary* bp = ErlDrvBinary2Binary(dbp); - return erts_refc_dectest(&bp->refc, 1); + return (ErlDrvSInt) erts_refc_dectest(&bp->refc, 1); } @@ -3541,12 +3541,12 @@ pdl_init_refc(ErlDrvPDL pdl) erts_atomic_init(&pdl->refc, 1); } -static ERTS_INLINE long +static ERTS_INLINE ErlDrvSInt pdl_read_refc(ErlDrvPDL pdl) { - long refc = erts_atomic_read(&pdl->refc); + erts_aint_t refc = erts_atomic_read(&pdl->refc); ERTS_LC_ASSERT(refc >= 0); - return refc; + return (ErlDrvSInt) refc; } static ERTS_INLINE void @@ -3556,12 +3556,12 @@ pdl_inc_refc(ErlDrvPDL pdl) ERTS_LC_ASSERT(driver_pdl_get_refc(pdl) > 1); } -static ERTS_INLINE long +static ERTS_INLINE ErlDrvSInt pdl_inctest_refc(ErlDrvPDL pdl) { - long refc = erts_atomic_inctest(&pdl->refc); + erts_aint_t refc = erts_atomic_inctest(&pdl->refc); ERTS_LC_ASSERT(refc > 1); - return refc; + return (ErlDrvSInt) refc; } #if 0 /* unused */ @@ -3573,12 +3573,12 @@ pdl_dec_refc(ErlDrvPDL pdl) } #endif -static ERTS_INLINE long +static ERTS_INLINE ErlDrvSInt pdl_dectest_refc(ErlDrvPDL pdl) { - long refc = erts_atomic_dectest(&pdl->refc); + erts_aint_t refc = erts_atomic_dectest(&pdl->refc); ERTS_LC_ASSERT(refc >= 0); - return refc; + return (ErlDrvSInt) refc; } static ERTS_INLINE void pdl_destroy(ErlDrvPDL pdl) @@ -3649,7 +3649,7 @@ driver_pdl_lock(ErlDrvPDL pdl) void driver_pdl_unlock(ErlDrvPDL pdl) { - long refc; + ErlDrvSInt refc; #ifdef HARDDEBUG erts_fprintf(stderr, "driver_pdl_unlock(0x%08X)\r\n",(unsigned) pdl); #endif @@ -3659,28 +3659,30 @@ driver_pdl_unlock(ErlDrvPDL pdl) pdl_destroy(pdl); } -long +ErlDrvSInt driver_pdl_get_refc(ErlDrvPDL pdl) { return pdl_read_refc(pdl); } -long +ErlDrvSInt driver_pdl_inc_refc(ErlDrvPDL pdl) { - long refc = pdl_inctest_refc(pdl); + ErlDrvSInt refc = pdl_inctest_refc(pdl); #ifdef HARDDEBUG - erts_fprintf(stderr, "driver_pdl_inc_refc(0x%08X) -> %ld\r\n",(unsigned) pdl, refc); + erts_fprintf(stderr, "driver_pdl_inc_refc(%p) -> %bpd\r\n", + pdl, refc); #endif return refc; } -long +ErlDrvSInt driver_pdl_dec_refc(ErlDrvPDL pdl) { - long refc = pdl_dectest_refc(pdl); + ErlDrvSInt refc = pdl_dectest_refc(pdl); #ifdef HARDDEBUG - erts_fprintf(stderr, "driver_pdl_dec_refc(0x%08X) -> %ld\r\n",(unsigned) pdl, refc); + erts_fprintf(stderr, "driver_pdl_dec_refc(%p) -> %bpd\r\n", + pdl, refc); #endif if (!refc) pdl_destroy(pdl); @@ -4066,7 +4068,7 @@ drv_cancel_timer(Port *prt) #ifdef ERTS_SMP erts_cancel_smp_ptimer(prt->ptimer); #else - erl_cancel_timer(&prt->tm); + erts_cancel_timer(&prt->tm); #endif if (erts_port_task_is_scheduled(&prt->timeout_task)) erts_port_task_abort(prt->id, &prt->timeout_task); @@ -4090,7 +4092,7 @@ int driver_set_timer(ErlDrvPort ix, UWord t) (ErlTimeoutProc) schedule_port_timeout, t); #else - erl_set_timer(&prt->tm, + erts_set_timer(&prt->tm, (ErlTimeoutProc) schedule_port_timeout, NULL, prt, @@ -4121,9 +4123,9 @@ driver_read_timer(ErlDrvPort ix, unsigned long* t) return -1; ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); #ifdef ERTS_SMP - *t = prt->ptimer ? time_left(&prt->ptimer->timer.tm) : 0; + *t = prt->ptimer ? erts_time_left(&prt->ptimer->timer.tm) : 0; #else - *t = time_left(&prt->tm); + *t = erts_time_left(&prt->tm); #endif return 0; } diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index a2439d5582..e861f97e7a 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -101,16 +101,16 @@ return %macro: test_heap TestHeap -pack allocate t t -allocate_heap I I I +allocate_heap t I t deallocate I init y allocate_zero t t -allocate_heap_zero I I I +allocate_heap_zero t I t trim N Remaining => i_trim N i_trim I -test_heap I I +test_heap I t allocate_heap S u==0 R => allocate S R allocate_heap_zero S u==0 R => allocate_zero S R @@ -124,7 +124,7 @@ init Y1 | init Y2 => init2 Y1 Y2 # Selecting values -select_val S=q Fail=f Size=u Rest=* => const_select_val(S, Fail, Size, Rest) +select_val S=aiq Fail=f Size=u Rest=* => const_select_val(S, Fail, Size, Rest) select_val S=s Fail=f Size=u Rest=* | use_jump_tab(Size, Rest) => \ gen_jump_tab(S, Fail, Size, Rest) @@ -132,34 +132,59 @@ select_val S=s Fail=f Size=u Rest=* | use_jump_tab(Size, Rest) => \ is_integer Fail=f S | select_val S=s Fail=f Size=u Rest=* | use_jump_tab(Size, Rest) => \ gen_jump_tab(S, Fail, Size, Rest) +is_integer TypeFail=f S | select_val S=s Fail=f Size=u Rest=* | \ + mixed_types(Size, Rest) => \ + gen_split_values(S, TypeFail, Fail, Size, Rest) + select_val S=s Fail=f Size=u Rest=* | mixed_types(Size, Rest) => \ - gen_split_values(S, Fail, Size, Rest) + gen_split_values(S, Fail, Fail, Size, Rest) -is_integer Fail=f S | select_val S=s Fail=f Size=u Rest=* | \ +is_integer Fail=f S | select_val S=d Fail=f Size=u Rest=* | \ fixed_size_values(Size, Rest) => gen_select_val(S, Fail, Size, Rest) -is_atom Fail=f S | select_val S=s Fail=f Size=u Rest=* | \ +is_atom Fail=f S | select_val S=d Fail=f Size=u Rest=* | \ fixed_size_values(Size, Rest) => gen_select_val(S, Fail, Size, Rest) -select_val S=s Fail=f Size=u Rest=* | fixed_size_values(Size, Rest) => \ - gen_select_val(S, Fail, Size, Rest) +select_val S=s Fail=f Size=u Rest=* | floats_or_bignums(Size, Rest) => \ + gen_select_literals(S, Fail, Size, Rest) -select_val S=s Fail=f Size=u Rest=* | all_values_are_big(Size, Rest) => \ - gen_select_big(S, Fail, Size, Rest) +select_val S=d Fail=f Size=u Rest=* | fixed_size_values(Size, Rest) => \ + gen_select_val(S, Fail, Size, Rest) -is_tuple Fail=f S | select_tuple_arity S=s Fail=f Size=u Rest=* => \ +is_tuple Fail=f S | select_tuple_arity S=d Fail=f Size=u Rest=* => \ gen_select_tuple_arity(S, Fail, Size, Rest) -select_tuple_arity S=s Fail=f Size=u Rest=* => \ +select_tuple_arity S=d Fail=f Size=u Rest=* => \ gen_select_tuple_arity(S, Fail, Size, Rest) -i_select_val s f I -i_select_tuple_arity s f I -i_select_big s f -i_select_float s f I +i_select_val r f I +i_select_val x f I +i_select_val y f I + +i_select_val2 r f c f c f +i_select_val2 x f c f c f +i_select_val2 y f c f c f + +i_select_tuple_arity2 r f A f A f +i_select_tuple_arity2 x f A f A f +i_select_tuple_arity2 y f A f A f + +i_select_tuple_arity r f I +i_select_tuple_arity x f I +i_select_tuple_arity y f I + +i_jump_on_val_zero r f I +i_jump_on_val_zero x f I +i_jump_on_val_zero y f I + +i_jump_on_val r f I I +i_jump_on_val x f I I +i_jump_on_val y f I I -i_jump_on_val_zero s f I -i_jump_on_val s f I I +jump Target | label Lbl | same_label(Target, Lbl) => label Lbl + +is_ne_exact L1 S1 S2 | jump Fail | label L2 | same_label(L1, L2) => \ + is_eq_exact Fail S1 S2 | label L2 %macro: get_list GetList -pack get_list x x x @@ -234,11 +259,17 @@ is_number Fail Literal=q => move Literal x | is_number Fail x jump f -case_end Literal=q => move Literal x | case_end x -badmatch Literal=q => move Literal x | badmatch x +case_end Literal=cq => move Literal x | case_end x +badmatch Literal=cq => move Literal x | badmatch x + +case_end r +case_end x +case_end y + +badmatch r +badmatch x +badmatch y -case_end s -badmatch s if_end raise s s @@ -248,12 +279,33 @@ system_limit j move R R => +move C=cxy r | jump Lbl => move_jump Lbl C + +%macro: move_jump MoveJump -nonext +move_jump f n +move_jump f c +move_jump f x +move_jump f y + move X1=x Y1=y | move X2=x Y2=y => move2 X1 Y1 X2 Y2 move Y1=y X1=x | move Y2=y X2=x => move2 Y1 X1 Y2 X2 +move X1=x X2=x | move X3=x X4=x => move2 X1 X2 X3 X4 + +move C=aiq X=x==1 => move_x1 C +move C=aiq X=x==2 => move_x2 C + +move_x1 c +move_x2 c %macro: move2 Move2 -pack move2 x y x y move2 y x y x +move2 x x x x + +# The compiler almost never generates a "move Literal y(Y)" instruction, +# so let's cheat if we encounter one. +move S=n D=y => init D +move S=c D=y => move S x | move x D %macro:move Move -pack -gen_dest move x x @@ -265,15 +317,10 @@ move r x move r y move c r move c x -move c y move n x move n r move y y -%cold -move s d -%hot - # Receive operations. loop_rec Fail Src | smp_mark_target_label(Fail) => i_loop_rec Fail Src @@ -306,55 +353,78 @@ i_wait_error_locked send # -# Comparisions. +# Optimized comparisons with one immediate/literal operand. +# + +is_eq_exact Lbl R=rxy C=ian => i_is_eq_exact_immed Lbl R C +is_eq_exact Lbl R=rxy C=q => i_is_eq_exact_literal R Lbl C + +is_ne_exact Lbl R=rxy C=ian => i_is_ne_exact_immed Lbl R C +is_ne_exact Lbl R=rxy C=q => i_is_ne_exact_literal R Lbl C + +%macro: i_is_eq_exact_immed EqualImmed -fail_action +i_is_eq_exact_immed f r c +i_is_eq_exact_immed f x c +i_is_eq_exact_immed f y c + +i_is_eq_exact_literal r f c +i_is_eq_exact_literal x f c +i_is_eq_exact_literal y f c + +%macro: i_is_ne_exact_immed NotEqualImmed -fail_action +i_is_ne_exact_immed f r c +i_is_ne_exact_immed f x c +i_is_ne_exact_immed f y c + +i_is_ne_exact_literal r f c +i_is_ne_exact_literal x f c +i_is_ne_exact_literal y f c + +# +# All other comparisons. # -is_eq_exact Lbl=f R=rxy C=ian => i_is_eq_immed Lbl R C -is_eq Lbl=f R=rxy C=an => i_is_eq_immed Lbl R C +is_eq_exact Lbl S1 S2 => i_fetch S1 S2 | i_is_eq_exact Lbl +is_ne_exact Lbl S1 S2 => i_fetch S1 S2 | i_is_ne_exact Lbl is_ge Lbl S1 S2 => i_fetch S1 S2 | i_is_ge Lbl is_lt Lbl S1 S2 => i_fetch S1 S2 | i_is_lt Lbl is_eq Lbl S1 S2 => i_fetch S1 S2 | i_is_eq Lbl is_ne Lbl S1 S2 => i_fetch S1 S2 | i_is_ne Lbl -is_eq_exact Lbl=f S1 S2 => i_fetch S1 S2 | i_is_eq_exact Lbl -is_ne_exact Lbl S1 S2 => i_fetch S1 S2 | i_is_ne_exact Lbl - +i_is_eq_exact f +i_is_ne_exact f i_is_lt f i_is_ge f i_is_eq f i_is_ne f -i_is_eq_exact f -i_is_ne_exact f - -%macro: i_is_eq_immed EqualImmed -fail_action -i_is_eq_immed f r c -i_is_eq_immed f x c -i_is_eq_immed f y c # # Putting things. # -put_tuple Arity Dst | put V => i_put_tuple Arity V Dst +put_tuple Arity Dst => i_put_tuple Dst u -%macro: i_put_tuple PutTuple -pack -i_put_tuple A x x -i_put_tuple A y x -i_put_tuple A r x -i_put_tuple A n x -i_put_tuple A c x -i_put_tuple A x y -i_put_tuple A x r -i_put_tuple A y r -i_put_tuple A n r -i_put_tuple A c r +i_put_tuple Dst Arity Puts=* | put S1 | put S2 | \ + put S3 | put S4 | put S5 => \ + tuple_append_put5(Arity, Dst, Puts, S1, S2, S3, S4, S5) -%cold -i_put_tuple A r y -i_put_tuple A y y -i_put_tuple A c y -%hot +i_put_tuple Dst Arity Puts=* | put S => \ + tuple_append_put(Arity, Dst, Puts, S) + +i_put_tuple/2 + +%macro:i_put_tuple PutTuple -pack -goto:do_put_tuple +i_put_tuple r I +i_put_tuple x I +i_put_tuple y I + +# +# The instruction "put_list Const [] Dst" will not be generated by +# the current BEAM compiler. But until R15A, play it safe by handling +# that instruction with the following transformation. +# +put_list Const=c n Dst => move Const x | put_list x n Dst %macro:put_list PutList -pack -gen_dest @@ -362,10 +432,8 @@ put_list x n x put_list y n x put_list x x x put_list y x x -put_list c n x put_list x x r put_list y r r -put_list c n r put_list y y x put_list x y x @@ -376,6 +444,13 @@ put_list y y r put_list y r x put_list r n x +put_list x r x +put_list x y r +put_list y x r +put_list y x x + +put_list x r r + # put_list SrcReg Constant Dst put_list r c r put_list r c x @@ -403,17 +478,9 @@ put_list c y x put_list c y y %cold -put_list x r r put_list s s d %hot -%macro: put Put -put x -put r -put y -put c -put n - %macro: i_fetch FetchArgs -pack i_fetch c c i_fetch c r @@ -464,19 +531,20 @@ move_return n r move S r | deallocate D | return => move_deallocate_return S r D -%macro: move_deallocate_return MoveDeallocateReturn -nonext -move_deallocate_return x r P -move_deallocate_return y r P -move_deallocate_return c r P -move_deallocate_return n r P +%macro: move_deallocate_return MoveDeallocateReturn -pack -nonext +move_deallocate_return x r Q +move_deallocate_return y r Q +move_deallocate_return c r Q +move_deallocate_return n r Q deallocate D | return => deallocate_return D %macro: deallocate_return DeallocateReturn -nonext -deallocate_return P +deallocate_return Q test_heap Need u==1 | put_list Y=y r r => test_heap_1_put_list Need Y +%macro: test_heap_1_put_list TestHeapPutList -pack test_heap_1_put_list I y # Test tuple & arity (head) @@ -576,14 +644,14 @@ is_list f y is_nonempty_list Fail=f S=rx | allocate Need Rs => is_nonempty_list_allocate Fail S Need Rs -%macro:is_nonempty_list_allocate IsNonemptyListAllocate -fail_action -is_nonempty_list_allocate f x I I -is_nonempty_list_allocate f r I I +%macro:is_nonempty_list_allocate IsNonemptyListAllocate -fail_action -pack +is_nonempty_list_allocate f x I t +is_nonempty_list_allocate f r I t is_nonempty_list F=f r | test_heap I1 I2 => is_non_empty_list_test_heap F r I1 I2 -%macro: is_non_empty_list_test_heap IsNonemptyListTestHeap -fail_action -is_non_empty_list_test_heap f r I I +%macro: is_non_empty_list_test_heap IsNonemptyListTestHeap -fail_action -pack +is_non_empty_list_test_heap f r I t %macro: is_nonempty_list IsNonemptyList -fail_action is_nonempty_list f x @@ -912,8 +980,13 @@ node x node y %hot -i_fast_element j I s d -i_element j s s d +i_fast_element r j I d +i_fast_element x j I d +i_fast_element y j I d + +i_element r j s d +i_element x j s d +i_element y j s d bif1 f b s d bif1_body b s d @@ -940,11 +1013,11 @@ move S r | call_last Ar P=f D => move_call_last S r P D i_move_call_last f P c r -%macro:move_call_last MoveCallLast -arg_f -nonext +%macro:move_call_last MoveCallLast -arg_f -nonext -pack move_call_last/4 -move_call_last x r f P -move_call_last y r f P +move_call_last x r f Q +move_call_last y r f Q move S=c r | call_only Ar P=f => i_move_call_only P S r move S=x r | call_only Ar P=f => move_call_only S r P @@ -1307,6 +1380,8 @@ fconv Arg=iqan Dst=l => move Arg x | fconv x Dst fmove q l fmove d l +fmove l d + fconv d l i_fadd l l l @@ -1322,12 +1397,6 @@ fcheckerror p => i_fcheckerror i_fcheckerror fclearerror -fmove FR=l Dst=d | new_float_allocation() => fmove_new FR Dst - -# The new instruction for moving a float out of a floating point register. -# (No allocation.) -fmove_new l d - # # New apply instructions in R10B. # @@ -1336,7 +1405,21 @@ apply I apply_last I P # -# New GCing arithmetic instructions. +# Optimize addition and subtraction of small literals using +# the i_increment/4 instruction (in bodies, not in guards). +# + +gc_bif2 p Live u$bif:erlang:splus/2 Int=i Reg=d Dst => \ + gen_increment(Reg, Int, Live, Dst) +gc_bif2 p Live u$bif:erlang:splus/2 Reg=d Int=i Dst => \ + gen_increment(Reg, Int, Live, Dst) + +gc_bif2 p Live u$bif:erlang:sminus/2 Reg=d Int=i Dst | \ + negation_is_small(Int) => \ + gen_increment_from_minus(Reg, Int, Live, Dst) + +# +# GCing arithmetic instructions. # gc_bif2 Fail I u$bif:erlang:splus/2 S1 S2 Dst=d => i_fetch S1 S2 | i_plus Fail I Dst @@ -1359,6 +1442,10 @@ gc_bif1 Fail I u$bif:erlang:bnot/1 Src Dst=d => i_int_bnot Fail Src I Dst gc_bif1 Fail I u$bif:erlang:sminus/1 Src Dst=d => i_fetch i Src | i_minus Fail I Dst gc_bif1 Fail I u$bif:erlang:splus/1 Src Dst=d => i_fetch i Src | i_plus Fail I Dst +i_increment r I I d +i_increment x I I d +i_increment y I I d + i_plus j I d i_minus j I d i_times j I d diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 27c5f99320..dff2dc37a2 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -39,13 +39,6 @@ #define ENABLE_CHILD_WAITER_THREAD 1 #endif -/* The ERTS_TIMER_TREAD #define must be visible to the - erl_${OS}_sys.h #include files: it controls whether - certain optional facilities should be defined or not. */ -#if defined(ERTS_SMP) && 0 -#define ERTS_TIMER_THREAD -#endif - #if defined (__WIN32__) # include "erl_win_sys.h" #elif defined (VXWORKS) @@ -562,11 +555,7 @@ extern char *erts_default_arg0; extern char os_type[]; extern int sys_init_time(void); -#if defined(ERTS_TIMER_THREAD) -#define erts_deliver_time() -#else extern void erts_deliver_time(void); -#endif extern void erts_time_remaining(SysTimeval *); extern int erts_init_time_sup(void); extern void erts_sys_init_float(void); @@ -728,11 +717,11 @@ typedef enum { } erts_activity_error_t; typedef struct { - erts_smp_atomic_t do_block; + erts_smp_atomic32_t do_block; struct { - erts_smp_atomic_t wait; - erts_smp_atomic_t gc; - erts_smp_atomic_t io; + erts_smp_atomic32_t wait; + erts_smp_atomic32_t gc; + erts_smp_atomic32_t io; } in_activity; } erts_system_block_state_t; @@ -883,7 +872,7 @@ ERTS_GLB_INLINE int erts_smp_pending_system_block(void) { #ifdef ERTS_SMP - return erts_smp_atomic_read(&erts_system_block_state.do_block); + return (int) erts_smp_atomic32_read(&erts_system_block_state.do_block); #else return 0; #endif @@ -919,7 +908,7 @@ erts_smp_set_activity(erts_activity_t old_activity, case ERTS_ACTIVITY_UNDEFINED: break; case ERTS_ACTIVITY_WAIT: - erts_smp_atomic_dec(&erts_system_block_state.in_activity.wait); + erts_smp_atomic32_dec(&erts_system_block_state.in_activity.wait); if (locked) { /* You are not allowed to leave activity waiting * without supplying the possibility to block @@ -930,10 +919,10 @@ erts_smp_set_activity(erts_activity_t old_activity, } break; case ERTS_ACTIVITY_GC: - erts_smp_atomic_dec(&erts_system_block_state.in_activity.gc); + erts_smp_atomic32_dec(&erts_system_block_state.in_activity.gc); break; case ERTS_ACTIVITY_IO: - erts_smp_atomic_dec(&erts_system_block_state.in_activity.io); + erts_smp_atomic32_dec(&erts_system_block_state.in_activity.io); break; default: erts_set_activity_error(ERTS_ACT_ERR_LEAVE_UNKNOWN_ACTIVITY, @@ -949,13 +938,13 @@ erts_smp_set_activity(erts_activity_t old_activity, case ERTS_ACTIVITY_UNDEFINED: break; case ERTS_ACTIVITY_WAIT: - erts_smp_atomic_inc(&erts_system_block_state.in_activity.wait); + erts_smp_atomic32_inc(&erts_system_block_state.in_activity.wait); break; case ERTS_ACTIVITY_GC: - erts_smp_atomic_inc(&erts_system_block_state.in_activity.gc); + erts_smp_atomic32_inc(&erts_system_block_state.in_activity.gc); break; case ERTS_ACTIVITY_IO: - erts_smp_atomic_inc(&erts_system_block_state.in_activity.io); + erts_smp_atomic32_inc(&erts_system_block_state.in_activity.io); break; default: erts_set_activity_error(ERTS_ACT_ERR_ENTER_UNKNOWN_ACTIVITY, @@ -990,27 +979,31 @@ erts_smp_set_activity(erts_activity_t old_activity, typedef erts_smp_atomic_t erts_refc_t; -ERTS_GLB_INLINE void erts_refc_init(erts_refc_t *refcp, long val); -ERTS_GLB_INLINE void erts_refc_inc(erts_refc_t *refcp, long min_val); -ERTS_GLB_INLINE long erts_refc_inctest(erts_refc_t *refcp, long min_val); -ERTS_GLB_INLINE void erts_refc_dec(erts_refc_t *refcp, long min_val); -ERTS_GLB_INLINE long erts_refc_dectest(erts_refc_t *refcp, long min_val); -ERTS_GLB_INLINE void erts_refc_add(erts_refc_t *refcp, long diff, long min_val); -ERTS_GLB_INLINE long erts_refc_read(erts_refc_t *refcp, long min_val); +ERTS_GLB_INLINE void erts_refc_init(erts_refc_t *refcp, erts_aint_t val); +ERTS_GLB_INLINE void erts_refc_inc(erts_refc_t *refcp, erts_aint_t min_val); +ERTS_GLB_INLINE erts_aint_t erts_refc_inctest(erts_refc_t *refcp, + erts_aint_t min_val); +ERTS_GLB_INLINE void erts_refc_dec(erts_refc_t *refcp, erts_aint_t min_val); +ERTS_GLB_INLINE erts_aint_t erts_refc_dectest(erts_refc_t *refcp, + erts_aint_t min_val); +ERTS_GLB_INLINE void erts_refc_add(erts_refc_t *refcp, erts_aint_t diff, + erts_aint_t min_val); +ERTS_GLB_INLINE erts_aint_t erts_refc_read(erts_refc_t *refcp, + erts_aint_t min_val); #if ERTS_GLB_INLINE_INCL_FUNC_DEF ERTS_GLB_INLINE void -erts_refc_init(erts_refc_t *refcp, long val) +erts_refc_init(erts_refc_t *refcp, erts_aint_t val) { erts_smp_atomic_init((erts_smp_atomic_t *) refcp, val); } ERTS_GLB_INLINE void -erts_refc_inc(erts_refc_t *refcp, long min_val) +erts_refc_inc(erts_refc_t *refcp, erts_aint_t min_val) { #ifdef ERTS_REFC_DEBUG - long val = erts_smp_atomic_inctest((erts_smp_atomic_t *) refcp); + erts_aint_t val = erts_smp_atomic_inctest((erts_smp_atomic_t *) refcp); if (val < min_val) erl_exit(ERTS_ABORT_EXIT, "erts_refc_inc(): Bad refc found (refc=%ld < %ld)!\n", @@ -1020,10 +1013,10 @@ erts_refc_inc(erts_refc_t *refcp, long min_val) #endif } -ERTS_GLB_INLINE long -erts_refc_inctest(erts_refc_t *refcp, long min_val) +ERTS_GLB_INLINE erts_aint_t +erts_refc_inctest(erts_refc_t *refcp, erts_aint_t min_val) { - long val = erts_smp_atomic_inctest((erts_smp_atomic_t *) refcp); + erts_aint_t val = erts_smp_atomic_inctest((erts_smp_atomic_t *) refcp); #ifdef ERTS_REFC_DEBUG if (val < min_val) erl_exit(ERTS_ABORT_EXIT, @@ -1034,10 +1027,10 @@ erts_refc_inctest(erts_refc_t *refcp, long min_val) } ERTS_GLB_INLINE void -erts_refc_dec(erts_refc_t *refcp, long min_val) +erts_refc_dec(erts_refc_t *refcp, erts_aint_t min_val) { #ifdef ERTS_REFC_DEBUG - long val = erts_smp_atomic_dectest((erts_smp_atomic_t *) refcp); + erts_aint_t val = erts_smp_atomic_dectest((erts_smp_atomic_t *) refcp); if (val < min_val) erl_exit(ERTS_ABORT_EXIT, "erts_refc_dec(): Bad refc found (refc=%ld < %ld)!\n", @@ -1047,10 +1040,10 @@ erts_refc_dec(erts_refc_t *refcp, long min_val) #endif } -ERTS_GLB_INLINE long -erts_refc_dectest(erts_refc_t *refcp, long min_val) +ERTS_GLB_INLINE erts_aint_t +erts_refc_dectest(erts_refc_t *refcp, erts_aint_t min_val) { - long val = erts_smp_atomic_dectest((erts_smp_atomic_t *) refcp); + erts_aint_t val = erts_smp_atomic_dectest((erts_smp_atomic_t *) refcp); #ifdef ERTS_REFC_DEBUG if (val < min_val) erl_exit(ERTS_ABORT_EXIT, @@ -1061,10 +1054,10 @@ erts_refc_dectest(erts_refc_t *refcp, long min_val) } ERTS_GLB_INLINE void -erts_refc_add(erts_refc_t *refcp, long diff, long min_val) +erts_refc_add(erts_refc_t *refcp, erts_aint_t diff, erts_aint_t min_val) { #ifdef ERTS_REFC_DEBUG - long val = erts_smp_atomic_addtest((erts_smp_atomic_t *) refcp, diff); + erts_aint_t val = erts_smp_atomic_addtest((erts_smp_atomic_t *) refcp, diff); if (val < min_val) erl_exit(ERTS_ABORT_EXIT, "erts_refc_add(%ld): Bad refc found (refc=%ld < %ld)!\n", @@ -1074,10 +1067,10 @@ erts_refc_add(erts_refc_t *refcp, long diff, long min_val) #endif } -ERTS_GLB_INLINE long -erts_refc_read(erts_refc_t *refcp, long min_val) +ERTS_GLB_INLINE erts_aint_t +erts_refc_read(erts_refc_t *refcp, erts_aint_t min_val) { - long val = erts_smp_atomic_read((erts_smp_atomic_t *) refcp); + erts_aint_t val = erts_smp_atomic_read((erts_smp_atomic_t *) refcp); #ifdef ERTS_REFC_DEBUG if (val < min_val) erl_exit(ERTS_ABORT_EXIT, diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c index 53d39aef0e..c65cc37fc6 100644 --- a/erts/emulator/beam/time.c +++ b/erts/emulator/beam/time.c @@ -99,80 +99,37 @@ static erts_smp_mtx_t tiw_lock; static ErlTimer** tiw; /* the timing wheel, allocated in init_time() */ static Uint tiw_pos; /* current position in wheel */ static Uint tiw_nto; /* number of timeouts in wheel */ +static Uint tiw_min; +static ErlTimer *tiw_min_ptr; /* END tiw_lock protected variables */ /* Actual interval time chosen by sys_init_time() */ static int itime; /* Constant after init */ -#if defined(ERTS_TIMER_THREAD) -static SysTimeval time_start; /* start of current time interval */ -static long ticks_end; /* time_start+ticks_end == time_wakeup */ -static long ticks_latest; /* delta from time_start at latest time update*/ - -static ERTS_INLINE long time_gettimeofday(SysTimeval *now) -{ - long elapsed; - - erts_get_timeval(now); - now->tv_usec = 1000 * (now->tv_usec / 1000); /* ms resolution */ - elapsed = (1000 * (now->tv_sec - time_start.tv_sec) + - (now->tv_usec - time_start.tv_usec) / 1000); - // elapsed /= CLOCK_RESOLUTION; - return elapsed; -} - -static long do_time_update(void) -{ - SysTimeval now; - long elapsed; - - elapsed = time_gettimeofday(&now); - ticks_latest = elapsed; - return elapsed; -} - -static ERTS_INLINE long do_time_read(void) -{ - return ticks_latest; -} - -static long do_time_reset(void) -{ - SysTimeval now; - long elapsed; - - elapsed = time_gettimeofday(&now); - time_start = now; - ticks_end = LONG_MAX; - ticks_latest = 0; - return elapsed; -} - -static ERTS_INLINE void do_time_init(void) -{ - (void)do_time_reset(); -} - -#else erts_smp_atomic_t do_time; /* set at clock interrupt */ -static ERTS_INLINE long do_time_read(void) { return erts_smp_atomic_read(&do_time); } -static ERTS_INLINE long do_time_update(void) { return do_time_read(); } +static ERTS_INLINE erts_aint_t do_time_read(void) { return erts_smp_atomic_read(&do_time); } +static ERTS_INLINE erts_aint_t do_time_update(void) { return do_time_read(); } static ERTS_INLINE void do_time_init(void) { erts_smp_atomic_init(&do_time, 0L); } -#endif /* get the time (in units of itime) to the next timeout, or -1 if there are no timeouts */ -static int next_time_internal(void) /* PRE: tiw_lock taken by caller */ +static erts_aint_t next_time_internal(void) /* PRE: tiw_lock taken by caller */ { int i, tm, nto; unsigned int min; ErlTimer* p; - long dt; + erts_aint_t dt; if (tiw_nto == 0) return -1; /* no timeouts in wheel */ + + if (tiw_min_ptr) { + min = tiw_min; + dt = do_time_read(); + return ((min >= dt) ? (min - dt) : 0); + } /* start going through wheel to find next timeout */ tm = nto = 0; @@ -185,11 +142,17 @@ static int next_time_internal(void) /* PRE: tiw_lock taken by caller */ if (p->count == 0) { /* found next timeout */ dt = do_time_read(); + /* p->count is zero */ + tiw_min_ptr = p; + tiw_min = tm; return ((tm >= dt) ? (tm - dt) : 0); } else { /* keep shortest time in 'min' */ - if (tm + p->count*TIW_SIZE < min) + if (tm + p->count*TIW_SIZE < min) { min = tm + p->count*TIW_SIZE; + tiw_min_ptr = p; + tiw_min = min; + } } p = p->next; } @@ -202,11 +165,35 @@ static int next_time_internal(void) /* PRE: tiw_lock taken by caller */ return ((min >= dt) ? (min - dt) : 0); } -#if !defined(ERTS_TIMER_THREAD) +static void remove_timer(ErlTimer *p) { + /* first */ + if (!p->prev) { + tiw[p->slot] = p->next; + if(p->next) + p->next->prev = NULL; + } else { + p->prev->next = p->next; + } + + /* last */ + if (!p->next) { + if (p->prev) + p->prev->next = NULL; + } else { + p->next->prev = p->prev; + } + + p->next = NULL; + p->prev = NULL; + /* Make sure cancel callback isn't called */ + p->active = 0; + tiw_nto--; +} + /* Private export to erl_time_sup.c */ -int next_time(void) +erts_aint_t erts_next_time(void) { - int ret; + erts_aint_t ret; erts_smp_mtx_lock(&tiw_lock); (void)do_time_update(); @@ -214,14 +201,13 @@ int next_time(void) erts_smp_mtx_unlock(&tiw_lock); return ret; } -#endif -static ERTS_INLINE void bump_timer_internal(long dt) /* PRE: tiw_lock is write-locked */ +static ERTS_INLINE void bump_timer_internal(erts_aint_t dt) /* PRE: tiw_lock is write-locked */ { Uint keep_pos; Uint count; ErlTimer *p, **prev, *timeout_head, **timeout_tail; - Uint dtime = (unsigned long)dt; + Uint dtime = (Uint) dt; /* no need to bump the position if there aren't any timeouts */ if (tiw_nto == 0) { @@ -242,12 +228,16 @@ static ERTS_INLINE void bump_timer_internal(long dt) /* PRE: tiw_lock is write-l if (tiw_pos == keep_pos) count--; prev = &tiw[tiw_pos]; while ((p = *prev) != NULL) { + ASSERT( p != p->next); if (p->count < count) { /* we have a timeout */ - *prev = p->next; /* Remove from list */ - tiw_nto--; - p->next = NULL; - p->active = 0; /* Make sure cancel callback - isn't called */ + /* remove min time */ + if (tiw_min_ptr == p) { + tiw_min_ptr = NULL; + tiw_min = 0; + } + + /* Remove from list */ + remove_timer(p); *timeout_tail = p; /* Insert in timeout queue */ timeout_tail = &p->next; } @@ -261,6 +251,8 @@ static ERTS_INLINE void bump_timer_internal(long dt) /* PRE: tiw_lock is write-l dtime--; } tiw_pos = keep_pos; + if (tiw_min_ptr) + tiw_min -= dt; erts_smp_mtx_unlock(&tiw_lock); @@ -275,24 +267,17 @@ static ERTS_INLINE void bump_timer_internal(long dt) /* PRE: tiw_lock is write-l * callback is called. */ p->next = NULL; + p->prev = NULL; p->slot = 0; (*p->timeout)(p->arg); } } -#if defined(ERTS_TIMER_THREAD) -static void timer_thread_bump_timer(void) -{ - erts_smp_mtx_lock(&tiw_lock); - bump_timer_internal(do_time_reset()); -} -#else -void bump_timer(long dt) /* dt is value from do_time */ +void erts_bump_timer(erts_aint_t dt) /* dt is value from do_time */ { erts_smp_mtx_lock(&tiw_lock); bump_timer_internal(dt); } -#endif Uint erts_timer_wheel_memory_size(void) @@ -300,82 +285,10 @@ erts_timer_wheel_memory_size(void) return (Uint) TIW_SIZE * sizeof(ErlTimer*); } -#if defined(ERTS_TIMER_THREAD) -static struct erts_iwait *timer_thread_iwait; - -static int timer_thread_setup_delay(SysTimeval *rem_time) -{ - long elapsed; - int ticks; - - erts_smp_mtx_lock(&tiw_lock); - elapsed = do_time_update(); - ticks = next_time_internal(); - if (ticks == -1) /* timer queue empty */ - ticks = 100*1000*1000; - if (elapsed > ticks) - elapsed = ticks; - ticks -= elapsed; - //ticks *= CLOCK_RESOLUTION; - rem_time->tv_sec = ticks / 1000; - rem_time->tv_usec = 1000 * (ticks % 1000); - ticks_end = ticks; - erts_smp_mtx_unlock(&tiw_lock); - return ticks; -} - -static void *timer_thread_start(void *ignore) -{ - SysTimeval delay; - -#ifdef ERTS_ENABLE_LOCK_CHECK - erts_lc_set_thread_name("timer"); -#endif - erts_register_blockable_thread(); - - for(;;) { - if (timer_thread_setup_delay(&delay)) { - erts_smp_activity_begin(ERTS_ACTIVITY_WAIT, NULL, NULL, NULL); - ASSERT_NO_LOCKED_LOCKS; - erts_iwait_wait(timer_thread_iwait, &delay); - ASSERT_NO_LOCKED_LOCKS; - erts_smp_activity_end(ERTS_ACTIVITY_WAIT, NULL, NULL, NULL); - } - else - erts_smp_chk_system_block(NULL, NULL, NULL); - timer_thread_bump_timer(); - ASSERT_NO_LOCKED_LOCKS; - } - /*NOTREACHED*/ - return NULL; -} - -static ERTS_INLINE void timer_thread_post_insert(Uint ticks) -{ - if ((Sint)ticks < ticks_end) - erts_iwait_interrupt(timer_thread_iwait); -} - -static void timer_thread_init(void) -{ - erts_thr_opts_t opts = ERTS_THR_OPTS_DEFAULT_INITER; - erts_tid_t tid; - - opts->detached = 1; - - timer_thread_iwait = erts_iwait_init(); - erts_thr_create(&tid, timer_thread_start, NULL, &opts); -} - -#else -static ERTS_INLINE void timer_thread_post_insert(Uint ticks) { } -static ERTS_INLINE void timer_thread_init(void) { } -#endif - /* this routine links the time cells into a free list at the start and sets the time queue as empty */ void -init_time(void) +erts_init_time(void) { int i; @@ -391,10 +304,13 @@ init_time(void) tiw[i] = NULL; do_time_init(); tiw_pos = tiw_nto = 0; - - timer_thread_init(); + tiw_min_ptr = NULL; + tiw_min = 0; } + + + /* ** Insert a process into the time queue, with a timeout 't' */ @@ -424,16 +340,31 @@ insert_timer(ErlTimer* p, Uint t) /* insert at head of list at slot */ p->next = tiw[tm]; + p->prev = NULL; + if (p->next != NULL) + p->next->prev = p; tiw[tm] = p; - tiw_nto++; - timer_thread_post_insert(ticks); + + /* insert min time */ + if ((tiw_nto == 0) || ((tiw_min_ptr != NULL) && (ticks < tiw_min))) { + tiw_min = ticks; + tiw_min_ptr = p; + } + if ((tiw_min_ptr == p) && (ticks > tiw_min)) { + /* some other timer might be 'min' now */ + tiw_min = 0; + tiw_min_ptr = NULL; + } + + tiw_nto++; } void -erl_set_timer(ErlTimer* p, ErlTimeoutProc timeout, ErlCancelProc cancel, +erts_set_timer(ErlTimer* p, ErlTimeoutProc timeout, ErlCancelProc cancel, void* arg, Uint t) { + erts_deliver_time(); erts_smp_mtx_lock(&tiw_lock); if (p->active) { /* XXX assert ? */ @@ -446,42 +377,34 @@ erl_set_timer(ErlTimer* p, ErlTimeoutProc timeout, ErlCancelProc cancel, p->active = 1; insert_timer(p, t); erts_smp_mtx_unlock(&tiw_lock); -#if defined(ERTS_SMP) && !defined(ERTS_TIMER_THREAD) +#if defined(ERTS_SMP) if (t <= (Uint) LONG_MAX) erts_sys_schedule_interrupt_timed(1, (long) t); #endif } void -erl_cancel_timer(ErlTimer* p) +erts_cancel_timer(ErlTimer* p) { - ErlTimer *tp; - ErlTimer **prev; - erts_smp_mtx_lock(&tiw_lock); if (!p->active) { /* allow repeated cancel (drivers) */ erts_smp_mtx_unlock(&tiw_lock); return; } - /* find p in linked list at slot p->slot and remove it */ - prev = &tiw[p->slot]; - while ((tp = *prev) != NULL) { - if (tp == p) { - *prev = p->next; /* Remove from list */ - tiw_nto--; - p->next = NULL; - p->slot = p->count = 0; - p->active = 0; - if (p->cancel != NULL) { - erts_smp_mtx_unlock(&tiw_lock); - (*p->cancel)(p->arg); - } else { - erts_smp_mtx_unlock(&tiw_lock); - } - return; - } else { - prev = &tp->next; - } + + /* is it the 'min' timer, remove min */ + if (p == tiw_min_ptr) { + tiw_min_ptr = NULL; + tiw_min = 0; + } + + remove_timer(p); + p->slot = p->count = 0; + + if (p->cancel != NULL) { + erts_smp_mtx_unlock(&tiw_lock); + (*p->cancel)(p->arg); + return; } erts_smp_mtx_unlock(&tiw_lock); } @@ -493,10 +416,10 @@ erl_cancel_timer(ErlTimer* p) immediately if it hadn't been cancelled). */ Uint -time_left(ErlTimer *p) +erts_time_left(ErlTimer *p) { Uint left; - long dt; + erts_aint_t dt; erts_smp_mtx_lock(&tiw_lock); @@ -517,12 +440,11 @@ time_left(ErlTimer *p) erts_smp_mtx_unlock(&tiw_lock); - return left * itime; + return (Uint) left * itime; } #ifdef DEBUG - -void p_slpq() +void erts_p_slpq() { int i; ErlTimer* p; @@ -551,5 +473,4 @@ void p_slpq() erts_smp_mtx_unlock(&tiw_lock); } - #endif /* DEBUG */ diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index ab5e8b5d4a..1d60b54d21 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -59,13 +59,6 @@ /* profile_scheduler mini message queue */ -#ifdef ERTS_TIMER_THREAD -/* A timer thread is not welcomed with this lock violation work around. - * - Bj�rn-Egil - */ -#error Timer thread may not be enabled due to lock violation. -#endif - typedef struct { Uint scheduler_id; Uint no_schedulers; @@ -3183,7 +3176,7 @@ erts_create_smp_ptimer(ErtsSmpPTimer **timer_ref, *timer_ref = res; - erl_set_timer(&res->timer.tm, + erts_set_timer(&res->timer.tm, (ErlTimeoutProc) ptimer_timeout, (ErlCancelProc) ptimer_cancelled, (void*) res, @@ -3197,7 +3190,7 @@ erts_cancel_smp_ptimer(ErtsSmpPTimer *ptimer) ASSERT(*ptimer->timer.timer_ref == ptimer); *ptimer->timer.timer_ref = NULL; ptimer->timer.flags |= ERTS_PTMR_FLG_CANCELLED; - erl_cancel_timer(&ptimer->timer.tm); + erts_cancel_timer(&ptimer->timer.tm); } } @@ -3637,19 +3630,19 @@ erts_set_activity_error(erts_activity_error_t error, char *file, int line) } -static ERTS_INLINE int +static ERTS_INLINE erts_aint32_t threads_not_under_control(void) { - int res = system_block_state.threads_to_block; + erts_aint32_t res = system_block_state.threads_to_block; /* Waiting is always an allowed activity... */ - res -= erts_smp_atomic_read(&erts_system_block_state.in_activity.wait); + res -= erts_smp_atomic32_read(&erts_system_block_state.in_activity.wait); if (system_block_state.allowed_activities & ERTS_BS_FLG_ALLOW_GC) - res -= erts_smp_atomic_read(&erts_system_block_state.in_activity.gc); + res -= erts_smp_atomic32_read(&erts_system_block_state.in_activity.gc); if (system_block_state.allowed_activities & ERTS_BS_FLG_ALLOW_IO) - res -= erts_smp_atomic_read(&erts_system_block_state.in_activity.io); + res -= erts_smp_atomic32_read(&erts_system_block_state.in_activity.io); if (res < 0) { ASSERT(0); @@ -3709,7 +3702,7 @@ erts_block_system(Uint32 allowed_activities) } else { - erts_smp_atomic_inc(&erts_system_block_state.do_block); + erts_smp_atomic32_inc(&erts_system_block_state.do_block); /* Someone else might be waiting for us to block... */ if (do_block) { @@ -3761,11 +3754,11 @@ erts_emergency_block_system(long timeout, Uint32 allowed_activities) another_blocker = erts_smp_pending_system_block(); system_block_state.emergency = 1; - erts_smp_atomic_inc(&erts_system_block_state.do_block); + erts_smp_atomic32_inc(&erts_system_block_state.do_block); if (another_blocker) { if (is_blocker()) { - erts_smp_atomic_dec(&erts_system_block_state.do_block); + erts_smp_atomic32_dec(&erts_system_block_state.do_block); res = 0; goto done; } @@ -3822,7 +3815,7 @@ erts_release_system(void) if (system_block_state.recursive_block) system_block_state.recursive_block--; else { - do_block = erts_smp_atomic_dectest(&erts_system_block_state.do_block); + do_block = erts_smp_atomic32_dectest(&erts_system_block_state.do_block); system_block_state.have_blocker = 0; if (is_blockable_thread()) system_block_state.threads_to_block++; @@ -3957,10 +3950,10 @@ erts_system_block_init(void) /* Global state... */ - erts_smp_atomic_init(&erts_system_block_state.do_block, 0L); - erts_smp_atomic_init(&erts_system_block_state.in_activity.wait, 0L); - erts_smp_atomic_init(&erts_system_block_state.in_activity.gc, 0L); - erts_smp_atomic_init(&erts_system_block_state.in_activity.io, 0L); + erts_smp_atomic32_init(&erts_system_block_state.do_block, 0); + erts_smp_atomic32_init(&erts_system_block_state.in_activity.wait, 0); + erts_smp_atomic32_init(&erts_system_block_state.in_activity.gc, 0); + erts_smp_atomic32_init(&erts_system_block_state.in_activity.io, 0); /* Make sure blockable threads unregister when exiting... */ erts_smp_install_exit_handler(erts_unregister_blockable_thread); diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 786fa7da77..6449c6f506 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -385,7 +385,6 @@ struct t_data ErlDrvBinary *binp; int size; int offset; - char name[1]; } read_file; struct { struct t_readdir_buf *first_buf; @@ -1117,7 +1116,7 @@ static void invoke_read_file(void *data) Sint64 size; if (! (d->result_ok = - efile_openfile(&d->errInfo, d->c.read_file.name, + efile_openfile(&d->errInfo, d->b, EFILE_MODE_READ, &fd, &size))) { goto done; } @@ -3071,7 +3070,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { d->command = command; d->reply = !0; /* Copy name */ - FILENAME_COPY(d->c.read_file.name, filename); + FILENAME_COPY(d->b, filename); d->c.read_file.binp = NULL; d->invoke = invoke_read_file; d->free = free_read_file; diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 1382d1dfe4..818bc6334e 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -4945,39 +4945,45 @@ static int inet_ctl_getifaddrs(inet_descriptor* desc_p, *buf_p++ = '\0'; *buf_p++ = INET_IFOPT_FLAGS; put_int32(IFGET_FLAGS(ifa_p->ifa_flags), buf_p); buf_p += 4; - if (ifa_p->ifa_addr->sa_family == AF_INET + if (ifa_p->ifa_addr) { + if (ifa_p->ifa_addr->sa_family == AF_INET #if defined(AF_INET6) - || ifa_p->ifa_addr->sa_family == AF_INET6 + || ifa_p->ifa_addr->sa_family == AF_INET6 #endif - ) { - SOCKADDR_TO_BUF(INET_IFOPT_ADDR, ifa_p->ifa_addr); - BUF_ENSURE(1); - SOCKADDR_TO_BUF(INET_IFOPT_NETMASK, ifa_p->ifa_netmask); - if (ifa_p->ifa_flags & IFF_POINTOPOINT) { - BUF_ENSURE(1); - SOCKADDR_TO_BUF(INET_IFOPT_DSTADDR, ifa_p->ifa_dstaddr); - } else if (ifa_p->ifa_flags & IFF_BROADCAST) { - BUF_ENSURE(1); - SOCKADDR_TO_BUF(INET_IFOPT_BROADADDR, ifa_p->ifa_broadaddr); + ) { + SOCKADDR_TO_BUF(INET_IFOPT_ADDR, ifa_p->ifa_addr); + if (ifa_p->ifa_netmask) { + BUF_ENSURE(1); + SOCKADDR_TO_BUF(INET_IFOPT_NETMASK, ifa_p->ifa_netmask); + } + if (ifa_p->ifa_dstaddr && + (ifa_p->ifa_flags & IFF_POINTOPOINT)) { + BUF_ENSURE(1); + SOCKADDR_TO_BUF(INET_IFOPT_DSTADDR, ifa_p->ifa_dstaddr); + } else if (ifa_p->ifa_broadaddr && + (ifa_p->ifa_flags & IFF_BROADCAST)) { + BUF_ENSURE(1); + SOCKADDR_TO_BUF(INET_IFOPT_BROADADDR, ifa_p->ifa_broadaddr); + } } - } #if defined(AF_LINK) || defined(AF_PACKET) - else if ( + else if ( #if defined(AF_LINK) - ifa_p->ifa_addr->sa_family == AF_LINK + ifa_p->ifa_addr->sa_family == AF_LINK #else - 0 + 0 #endif #if defined(AF_PACKET) - || ifa_p->ifa_addr->sa_family == AF_PACKET + || ifa_p->ifa_addr->sa_family == AF_PACKET #endif - ) { - char *bp = buf_p; - BUF_ENSURE(1); - SOCKADDR_TO_BUF(INET_IFOPT_HWADDR, ifa_p->ifa_addr); - if (buf_p - bp < 4) buf_p = bp; /* Empty hwaddr */ - } + ) { + char *bp = buf_p; + BUF_ENSURE(1); + SOCKADDR_TO_BUF(INET_IFOPT_HWADDR, ifa_p->ifa_addr); + if (buf_p - bp < 4) buf_p = bp; /* Empty hwaddr */ + } #endif + } BUF_ENSURE(1); *buf_p++ = '\0'; } diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index 6297ccb8bc..4b3934657c 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -620,7 +620,7 @@ efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ if (IS_DOT_OR_DOTDOT(dirp->d_name)) continue; buffer[0] = '\0'; - strncat(buffer, dirp->d_name, size-1); + strncat(buffer, dirp->d_name, (*size)-1); *size = strlen(dirp->d_name); return 1; } diff --git a/erts/emulator/drivers/win32/win_con.c b/erts/emulator/drivers/win32/win_con.c index 2202ca655f..14f7941643 100644 --- a/erts/emulator/drivers/win32/win_con.c +++ b/erts/emulator/drivers/win32/win_con.c @@ -704,6 +704,18 @@ FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) } write_inbuf(&c, 1); return 0; + case WM_MOUSEWHEEL: + { + int delta = GET_WHEEL_DELTA_WPARAM(wParam); + if (delta < 0) { + PostMessage(hClientWnd, WM_VSCROLL, MAKELONG(SB_THUMBTRACK, + (iVscrollPos + 5)),0); + } else { + WORD pos = ((iVscrollPos - 5) < 0) ? 0 : (iVscrollPos - 5); + PostMessage(hClientWnd, WM_VSCROLL, MAKELONG(SB_THUMBTRACK,pos),0); + } + return 0; + } case WM_CHAR: c = (TCHAR)wParam; write_inbuf(&c,1); diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index 4ec9579529..101853736a 100755 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -1016,8 +1016,8 @@ efile_write_info(Efile_error* errInfo, } \ } - MKTIME(ModifyFileTime, pInfo->accessTime, mtime); - MKTIME(AccessFileTime, pInfo->modifyTime, atime); + MKTIME(ModifyFileTime, pInfo->modifyTime, mtime); + MKTIME(AccessFileTime, pInfo->accessTime, atime); MKTIME(CreationFileTime, pInfo->cTime, ctime); #undef MKTIME diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index b1ee165489..010d60eb63 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -35,6 +35,7 @@ #include "global.h" #include "erl_threads.h" #include "erl_mtrace.h" +#include "erl_time.h" #include "big.h" #if HAVE_ERTS_MSEG @@ -271,7 +272,7 @@ schedule_cache_check(void) #endif { cache_check_timer.active = 0; - erl_set_timer(&cache_check_timer, + erts_set_timer(&cache_check_timer, check_cache, NULL, NULL, diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index c17806d96c..4d0ca97889 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -124,9 +124,9 @@ erts_smp_mtx_unlock(&(PS)->mtx) #define ERTS_POLLSET_SET_POLLED_CHK(PS) \ - ((int) erts_smp_atomic_xchg(&(PS)->polled, (long) 1)) + ((int) erts_smp_atomic_xchg(&(PS)->polled, (erts_aint_t) 1)) #define ERTS_POLLSET_UNSET_POLLED(PS) \ - erts_smp_atomic_set(&(PS)->polled, (long) 0) + erts_smp_atomic_set(&(PS)->polled, (erts_aint_t) 0) #define ERTS_POLLSET_IS_POLLED(PS) \ ((int) erts_smp_atomic_read(&(PS)->polled)) @@ -134,11 +134,11 @@ #define ERTS_POLLSET_SET_POLLER_WOKEN(PS) \ do { \ ERTS_THR_MEMORY_BARRIER; \ - erts_smp_atomic_set(&(PS)->woken, (long) 1); \ + erts_smp_atomic_set(&(PS)->woken, (erts_aint_t) 1); \ } while (0) #define ERTS_POLLSET_UNSET_POLLER_WOKEN(PS) \ do { \ - erts_smp_atomic_set(&(PS)->woken, (long) 0); \ + erts_smp_atomic_set(&(PS)->woken, (erts_aint_t) 0); \ ERTS_THR_MEMORY_BARRIER; \ } while (0) #define ERTS_POLLSET_IS_POLLER_WOKEN(PS) \ @@ -179,9 +179,9 @@ do { \ #if ERTS_POLL_USE_UPDATE_REQUESTS_QUEUE #define ERTS_POLLSET_SET_HAVE_UPDATE_REQUESTS(PS) \ - erts_smp_atomic_set(&(PS)->have_update_requests, (long) 1) + erts_smp_atomic_set(&(PS)->have_update_requests, (erts_aint_t) 1) #define ERTS_POLLSET_UNSET_HAVE_UPDATE_REQUESTS(PS) \ - erts_smp_atomic_set(&(PS)->have_update_requests, (long) 0) + erts_smp_atomic_set(&(PS)->have_update_requests, (erts_aint_t) 0) #define ERTS_POLLSET_HAVE_UPDATE_REQUESTS(PS) \ ((int) erts_smp_atomic_read(&(PS)->have_update_requests)) #else @@ -202,13 +202,13 @@ do { \ #define ERTS_POLLSET_UNSET_INTERRUPTED_CHK(PS) unset_interrupted_chk((PS)) #define ERTS_POLLSET_UNSET_INTERRUPTED(PS) \ do { \ - erts_smp_atomic_set(&(PS)->interrupt, (long) 0); \ + erts_smp_atomic_set(&(PS)->interrupt, (erts_aint_t) 0); \ ERTS_THR_MEMORY_BARRIER; \ } while (0) #define ERTS_POLLSET_SET_INTERRUPTED(PS) \ do { \ ERTS_THR_MEMORY_BARRIER; \ - erts_smp_atomic_set(&(PS)->interrupt, (long) 1); \ + erts_smp_atomic_set(&(PS)->interrupt, (erts_aint_t) 1); \ } while (0) #define ERTS_POLLSET_IS_INTERRUPTED(PS) \ ((int) erts_smp_atomic_read(&(PS)->interrupt)) @@ -356,7 +356,7 @@ unset_interrupted_chk(ErtsPollSet ps) res = ps->interrupt; ps->interrupt = 0; #else - res = (int) erts_smp_atomic_xchg(&ps->interrupt, (long) 0); + res = (int) erts_smp_atomic_xchg(&ps->interrupt, (erts_aint_t) 0); ERTS_THR_MEMORY_BARRIER; #endif return res; @@ -369,7 +369,7 @@ static ERTS_INLINE int set_poller_woken_chk(ErtsPollSet ps) { ERTS_THR_MEMORY_BARRIER; - return (int) erts_smp_atomic_xchg(&ps->woken, (long) 1); + return (int) erts_smp_atomic_xchg(&ps->woken, (erts_aint_t) 1); } #endif @@ -1918,7 +1918,7 @@ check_fd_events(ErtsPollSet ps, SysTimeval *tv, int max_res, int *ps_locked) return 0; } else { - long timeout = tv->tv_sec*1000 + tv->tv_usec/1000; + erts_aint_t timeout = tv->tv_sec*1000 + tv->tv_usec/1000; ASSERT(timeout >= 0); erts_smp_atomic_set(&ps->timeout, timeout); #if ERTS_POLL_USE_FALLBACK @@ -2112,7 +2112,7 @@ ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet ps, #endif done: - erts_smp_atomic_set(&ps->timeout, LONG_MAX); + erts_smp_atomic_set(&ps->timeout, ERTS_AINT_T_MAX); #ifdef ERTS_POLL_DEBUG_PRINT erts_printf("Leaving %s = erts_poll_wait()\n", res == 0 ? "0" : erl_errno_id(res)); @@ -2150,10 +2150,12 @@ ERTS_POLL_EXPORT(erts_poll_interrupt)(ErtsPollSet ps, int set) * is not guaranteed that it will timeout before 'msec' milli seconds. */ void -ERTS_POLL_EXPORT(erts_poll_interrupt_timed)(ErtsPollSet ps, int set, long msec) +ERTS_POLL_EXPORT(erts_poll_interrupt_timed)(ErtsPollSet ps, + int set, + long msec) { if (set) { - if (erts_smp_atomic_read(&ps->timeout) > msec) { + if (erts_smp_atomic_read(&ps->timeout) > (erts_aint_t) msec) { ERTS_POLLSET_SET_INTERRUPTED(ps); #if ERTS_POLL_ASYNC_INTERRUPT_SUPPORT || defined(ERTS_SMP) wake_poller(ps); @@ -2315,7 +2317,7 @@ ERTS_POLL_EXPORT(erts_poll_create_pollset)(void) #else erts_smp_atomic_init(&ps->interrupt, 0); #endif - erts_smp_atomic_init(&ps->timeout, LONG_MAX); + erts_smp_atomic_init(&ps->timeout, ERTS_AINT_T_MAX); #ifdef ERTS_POLL_COUNT_AVOIDED_WAKEUPS erts_smp_atomic_init(&ps->no_avoided_wakeups, 0); erts_smp_atomic_init(&ps->no_avoided_interrupts, 0); diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 2d5ef882f6..824678a0bb 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -329,11 +329,4 @@ extern int exit_async(void); #define ERTS_EXIT_AFTER_DUMP _exit -#ifdef ERTS_TIMER_THREAD -struct erts_iwait; /* opaque for clients */ -extern struct erts_iwait *erts_iwait_init(void); -extern void erts_iwait_wait(struct erts_iwait *iwait, struct timeval *delay); -extern void erts_iwait_interrupt(struct erts_iwait *iwait); -#endif /* ERTS_TIMER_THREAD */ - #endif /* #ifndef _ERL_UNIX_SYS_H */ diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c index 01ba773688..bfc04faa45 100644 --- a/erts/emulator/sys/unix/sys.c +++ b/erts/emulator/sys/unix/sys.c @@ -237,9 +237,9 @@ static int max_files = -1; #ifdef ERTS_SMP erts_smp_atomic_t erts_break_requested; #define ERTS_SET_BREAK_REQUESTED \ - erts_smp_atomic_set(&erts_break_requested, (long) 1) + erts_smp_atomic_set(&erts_break_requested, (erts_aint_t) 1) #define ERTS_UNSET_BREAK_REQUESTED \ - erts_smp_atomic_set(&erts_break_requested, (long) 0) + erts_smp_atomic_set(&erts_break_requested, (erts_aint_t) 0) #else volatile int erts_break_requested = 0; #define ERTS_SET_BREAK_REQUESTED (erts_break_requested = 1) @@ -3109,226 +3109,3 @@ erl_sys_args(int* argc, char** argv) } *argc = j; } - -#ifdef ERTS_TIMER_THREAD - -/* - * Interruptible-wait facility: low-level synchronisation state - * and methods that are implementation dependent. - * - * Constraint: Every implementation must define 'struct erts_iwait' - * with a field 'erts_smp_atomic_t state;'. - */ - -/* values for struct erts_iwait's state field */ -#define IWAIT_WAITING 0 -#define IWAIT_AWAKE 1 -#define IWAIT_INTERRUPT 2 - -#if 0 /* XXX: needs feature test in erts/configure.in */ - -/* - * This is an implementation of the interruptible wait facility on - * top of Linux-specific futexes. - */ -#include <asm/unistd.h> -#define FUTEX_WAIT 0 -#define FUTEX_WAKE 1 -static int sys_futex(void *futex, int op, int val, const struct timespec *timeout) -{ - return syscall(__NR_futex, futex, op, val, timeout); -} - -struct erts_iwait { - erts_smp_atomic_t state; /* &state.counter is our futex */ -}; - -static void iwait_lowlevel_init(struct erts_iwait *iwait) { /* empty */ } - -static void iwait_lowlevel_wait(struct erts_iwait *iwait, struct timeval *delay) -{ - struct timespec timeout; - int res; - - timeout.tv_sec = delay->tv_sec; - timeout.tv_nsec = delay->tv_usec * 1000; - res = sys_futex((void*)&iwait->state.counter, FUTEX_WAIT, IWAIT_WAITING, &timeout); - if (res < 0 && errno != ETIMEDOUT && errno != EWOULDBLOCK && errno != EINTR) - perror("FUTEX_WAIT"); -} - -static void iwait_lowlevel_interrupt(struct erts_iwait *iwait) -{ - int res = sys_futex((void*)&iwait->state.counter, FUTEX_WAKE, 1, NULL); - if (res < 0) - perror("FUTEX_WAKE"); -} - -#else /* using poll() or select() */ - -/* - * This is an implementation of the interruptible wait facility on - * top of pipe(), poll() or select(), read(), and write(). - */ -struct erts_iwait { - erts_smp_atomic_t state; - int read_fd; /* wait polls and reads this fd */ - int write_fd; /* interrupt writes this fd */ -}; - -static void iwait_lowlevel_init(struct erts_iwait *iwait) -{ - int fds[2]; - - if (pipe(fds) < 0) { - perror("pipe()"); - exit(1); - } - iwait->read_fd = fds[0]; - iwait->write_fd = fds[1]; -} - -#if defined(ERTS_USE_POLL) - -#include <sys/poll.h> -#define PERROR_POLL "poll()" - -static int iwait_lowlevel_poll(int read_fd, struct timeval *delay) -{ - struct pollfd pollfd; - int timeout; - - pollfd.fd = read_fd; - pollfd.events = POLLIN; - pollfd.revents = 0; - timeout = delay->tv_sec * 1000 + delay->tv_usec / 1000; - return poll(&pollfd, 1, timeout); -} - -#else /* !ERTS_USE_POLL */ - -#include <sys/select.h> -#define PERROR_POLL "select()" - -static int iwait_lowlevel_poll(int read_fd, struct timeval *delay) -{ - fd_set readfds; - - FD_ZERO(&readfds); - FD_SET(read_fd, &readfds); - return select(read_fd + 1, &readfds, NULL, NULL, delay); -} - -#endif /* !ERTS_USE_POLL */ - -static void iwait_lowlevel_wait(struct erts_iwait *iwait, struct timeval *delay) -{ - int res; - char buf[64]; - - res = iwait_lowlevel_poll(iwait->read_fd, delay); - if (res > 0) - (void)read(iwait->read_fd, buf, sizeof buf); - else if (res < 0 && errno != EINTR) - perror(PERROR_POLL); -} - -static void iwait_lowlevel_interrupt(struct erts_iwait *iwait) -{ - int res = write(iwait->write_fd, "!", 1); - if (res < 0) - perror("write()"); -} - -#endif /* using poll() or select() */ - -#if 0 /* not using poll() or select() */ -/* - * This is an implementation of the interruptible wait facility on - * top of pthread_cond_timedwait(). This has two problems: - * 1. pthread_cond_timedwait() requires an absolute time point, - * so the relative delay must be converted to absolute time. - * Worse, this breaks if the machine's time is adjusted while - * we're preparing to wait. - * 2. Each cond operation requires additional mutex lock/unlock operations. - * - * Problem 2 is probably not too bad on Linux (they'll just become - * relatively cheap futex operations), but problem 1 is the real killer. - * Only use this implementation if no better alternatives are available! - */ -struct erts_iwait { - erts_smp_atomic_t state; - pthread_cond_t cond; - pthread_mutex_t mutex; -}; - -static void iwait_lowlevel_init(struct erts_iwait *iwait) -{ - iwait->cond = (pthread_cond_t) PTHREAD_COND_INITIALIZER; - iwait->mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER; -} - -static void iwait_lowlevel_wait(struct erts_iwait *iwait, struct timeval *delay) -{ - struct timeval tmp; - struct timespec timeout; - - /* Due to pthread_cond_timedwait()'s use of absolute - time, this must be the real gettimeofday(), _not_ - the "smoothed" one beam/erl_time_sup.c implements. */ - gettimeofday(&tmp, NULL); - - tmp.tv_sec += delay->tv_sec; - tmp.tv_usec += delay->tv_usec; - if (tmp.tv_usec >= 1000*1000) { - tmp.tv_usec -= 1000*1000; - tmp.tv_sec += 1; - } - timeout.tv_sec = tmp.tv_sec; - timeout.tv_nsec = tmp.tv_usec * 1000; - pthread_mutex_lock(&iwait->mutex); - pthread_cond_timedwait(&iwait->cond, &iwait->mutex, &timeout); - pthread_mutex_unlock(&iwait->mutex); -} - -static void iwait_lowlevel_interrupt(struct erts_iwait *iwait) -{ - pthread_mutex_lock(&iwait->mutex); - pthread_cond_signal(&iwait->cond); - pthread_mutex_unlock(&iwait->mutex); -} - -#endif /* not using POLL */ - -/* - * Interruptible-wait facility. This is just a wrapper around the - * low-level synchronisation code, where we maintain our logical - * state in order to suppress some state transitions. - */ - -struct erts_iwait *erts_iwait_init(void) -{ - struct erts_iwait *iwait = malloc(sizeof *iwait); - if (!iwait) { - perror("malloc"); - exit(1); - } - iwait_lowlevel_init(iwait); - erts_smp_atomic_init(&iwait->state, IWAIT_AWAKE); - return iwait; -} - -void erts_iwait_wait(struct erts_iwait *iwait, struct timeval *delay) -{ - if (erts_smp_atomic_xchg(&iwait->state, IWAIT_WAITING) != IWAIT_INTERRUPT) - iwait_lowlevel_wait(iwait, delay); - erts_smp_atomic_set(&iwait->state, IWAIT_AWAKE); -} - -void erts_iwait_interrupt(struct erts_iwait *iwait) -{ - if (erts_smp_atomic_xchg(&iwait->state, IWAIT_INTERRUPT) == IWAIT_WAITING) - iwait_lowlevel_interrupt(iwait); -} - -#endif /* ERTS_TIMER_THREAD */ diff --git a/erts/emulator/sys/vxworks/sys.c b/erts/emulator/sys/vxworks/sys.c index 411b4b37cf..c6e7b65f32 100644 --- a/erts/emulator/sys/vxworks/sys.c +++ b/erts/emulator/sys/vxworks/sys.c @@ -85,7 +85,7 @@ EXTERN_FUNCTION(void, erl_exit, (int n, char*, _DOTS_)); EXTERN_FUNCTION(void, erl_error, (char*, va_list)); EXTERN_FUNCTION(int, driver_interrupt, (int, int)); EXTERN_FUNCTION(void, increment_time, (int)); -EXTERN_FUNCTION(int, next_time, (_VOID_)); +EXTERN_FUNCTION(int, erts_next_time, (_VOID_)); EXTERN_FUNCTION(void, set_reclaim_free_function, (FreeFunction)); EXTERN_FUNCTION(int, erl_mem_info_get, (MEM_PART_STATS *)); EXTERN_FUNCTION(void, erl_crash_dump, (char* file, int line, char* fmt, ...)); diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c index a766fe9575..d84ae2ede2 100644 --- a/erts/emulator/sys/win32/erl_poll.c +++ b/erts/emulator/sys/win32/erl_poll.c @@ -297,11 +297,11 @@ struct ErtsPollSet_ { #define ERTS_POLLSET_UNLOCK(PS) \ erts_smp_mtx_unlock(&(PS)->mtx) #define ERTS_POLLSET_SET_POLLED_CHK(PS) \ - ((int) erts_smp_atomic_xchg(&(PS)->polled, (long) 1)) + ((int) erts_smp_atomic_xchg(&(PS)->polled, (erts_aint_t) 1)) #define ERTS_POLLSET_SET_POLLED(PS) \ - erts_smp_atomic_set(&(PS)->polled, (long) 1) + erts_smp_atomic_set(&(PS)->polled, (erts_aint_t) 1) #define ERTS_POLLSET_UNSET_POLLED(PS) \ - erts_smp_atomic_set(&(PS)->polled, (long) 0) + erts_smp_atomic_set(&(PS)->polled, (erts_aint_t) 0) #define ERTS_POLLSET_IS_POLLED(PS) \ ((int) erts_smp_atomic_read(&(PS)->polled)) @@ -309,11 +309,11 @@ struct ErtsPollSet_ { #define ERTS_POLLSET_SET_POLLER_WOKEN(PS) \ do { \ ERTS_THR_MEMORY_BARRIER; \ - erts_smp_atomic_set(&(PS)->woken, (long) 1); \ + erts_smp_atomic_set(&(PS)->woken, (erts_aint_t) 1); \ } while (0) #define ERTS_POLLSET_UNSET_POLLER_WOKEN(PS) \ do { \ - erts_smp_atomic_set(&(PS)->woken, (long) 0); \ + erts_smp_atomic_set(&(PS)->woken, (erts_aint_t) 0); \ ERTS_THR_MEMORY_BARRIER; \ } while (0) #define ERTS_POLLSET_IS_POLLER_WOKEN(PS) \ @@ -322,13 +322,13 @@ do { \ #define ERTS_POLLSET_UNSET_INTERRUPTED_CHK(PS) unset_interrupted_chk((PS)) #define ERTS_POLLSET_UNSET_INTERRUPTED(PS) \ do { \ - erts_smp_atomic_set(&(PS)->interrupt, (long) 0); \ + erts_smp_atomic_set(&(PS)->interrupt, (erts_aint_t) 0); \ ERTS_THR_MEMORY_BARRIER; \ } while (0) #define ERTS_POLLSET_SET_INTERRUPTED(PS) \ do { \ ERTS_THR_MEMORY_BARRIER; \ - erts_smp_atomic_set(&(PS)->interrupt, (long) 1); \ + erts_smp_atomic_set(&(PS)->interrupt, (erts_aint_t) 1); \ } while (0) #define ERTS_POLLSET_IS_INTERRUPTED(PS) \ ((int) erts_smp_atomic_read(&(PS)->interrupt)) @@ -336,7 +336,7 @@ do { \ static ERTS_INLINE int unset_interrupted_chk(ErtsPollSet ps) { - int res = (int) erts_smp_atomic_xchg(&ps->interrupt, (long) 0); + int res = (int) erts_smp_atomic_xchg(&ps->interrupt, (erts_aint_t) 0); ERTS_THR_MEMORY_BARRIER; return res; @@ -346,7 +346,7 @@ static ERTS_INLINE int set_poller_woken_chk(ErtsPollSet ps) { ERTS_THR_MEMORY_BARRIER; - return (int) erts_smp_atomic_xchg(&ps->woken, (long) 1); + return (int) erts_smp_atomic_xchg(&ps->woken, (erts_aint_t) 1); } #else @@ -413,9 +413,9 @@ set_poller_woken_chk(ErtsPollSet ps) #ifdef ERTS_SMP extern erts_smp_atomic_t erts_break_requested; #define ERTS_SET_BREAK_REQUESTED \ - erts_smp_atomic_set(&erts_break_requested, (long) 1) + erts_smp_atomic_set(&erts_break_requested, (erts_aint_t) 1) #define ERTS_UNSET_BREAK_REQUESTED \ - erts_smp_atomic_set(&erts_break_requested, (long) 0) + erts_smp_atomic_set(&erts_break_requested, (erts_aint_t) 0) #else extern volatile int erts_break_requested; #define ERTS_SET_BREAK_REQUESTED (erts_break_requested = 1) @@ -986,7 +986,7 @@ void erts_poll_interrupt_timed(ErtsPollSet ps, HARDTRACEF(("In erts_poll_interrupt_timed(%d,%ld)",set,msec)); #ifdef ERTS_SMP if (set) { - if (erts_smp_atomic_read(&ps->timeout) > msec) { + if (erts_smp_atomic_read(&ps->timeout) > (erts_aint_t) msec) { ERTS_POLLSET_SET_INTERRUPTED(ps); wake_poller(ps); } @@ -1228,7 +1228,7 @@ int erts_poll_wait(ErtsPollSet ps, erts_mtx_unlock(&w->mtx); } done: - erts_smp_atomic_set(&ps->timeout, LONG_MAX); + erts_smp_atomic_set(&ps->timeout, ERTS_AINT_T_MAX); *len = num; ERTS_POLLSET_UNLOCK(ps); HARDTRACEF(("Out erts_poll_wait")); @@ -1314,7 +1314,7 @@ ErtsPollSet erts_poll_create_pollset(void) erts_smp_mtx_init(&ps->mtx, "pollset"); erts_smp_atomic_init(&ps->interrupt, 0); #endif - erts_smp_atomic_init(&ps->timeout, LONG_MAX); + erts_smp_atomic_init(&ps->timeout, ERTS_AINT_T_MAX); HARDTRACEF(("Out erts_poll_create_pollset")); return ps; diff --git a/erts/emulator/sys/win32/erl_win_dyn_driver.h b/erts/emulator/sys/win32/erl_win_dyn_driver.h index 4949998abc..1347eead91 100644 --- a/erts/emulator/sys/win32/erl_win_dyn_driver.h +++ b/erts/emulator/sys/win32/erl_win_dyn_driver.h @@ -87,15 +87,15 @@ WDD_TYPEDEF(unsigned long, erts_alc_test, (unsigned long, unsigned long, unsigned long, unsigned long)); -WDD_TYPEDEF(long, driver_binary_get_refc, (ErlDrvBinary *dbp)); -WDD_TYPEDEF(long, driver_binary_inc_refc, (ErlDrvBinary *dbp)); -WDD_TYPEDEF(long, driver_binary_dec_refc, (ErlDrvBinary *dbp)); +WDD_TYPEDEF(ErlDrvSInt, driver_binary_get_refc, (ErlDrvBinary *dbp)); +WDD_TYPEDEF(ErlDrvSInt, driver_binary_inc_refc, (ErlDrvBinary *dbp)); +WDD_TYPEDEF(ErlDrvSInt, driver_binary_dec_refc, (ErlDrvBinary *dbp)); WDD_TYPEDEF(ErlDrvPDL, driver_pdl_create, (ErlDrvPort)); WDD_TYPEDEF(void, driver_pdl_lock, (ErlDrvPDL)); WDD_TYPEDEF(void, driver_pdl_unlock, (ErlDrvPDL)); -WDD_TYPEDEF(long, driver_pdl_get_refc, (ErlDrvPDL)); -WDD_TYPEDEF(long, driver_pdl_inc_refc, (ErlDrvPDL)); -WDD_TYPEDEF(long, driver_pdl_dec_refc, (ErlDrvPDL)); +WDD_TYPEDEF(ErlDrvSInt, driver_pdl_get_refc, (ErlDrvPDL)); +WDD_TYPEDEF(ErlDrvSInt, driver_pdl_inc_refc, (ErlDrvPDL)); +WDD_TYPEDEF(ErlDrvSInt, driver_pdl_dec_refc, (ErlDrvPDL)); WDD_TYPEDEF(void, driver_system_info, (ErlDrvSysInfo *, size_t)); WDD_TYPEDEF(int, driver_get_now, (ErlDrvNowData *)); WDD_TYPEDEF(int, driver_monitor_process, (ErlDrvPort port, diff --git a/erts/emulator/sys/win32/sys_interrupt.c b/erts/emulator/sys/win32/sys_interrupt.c index d2449a1bdb..262f84babc 100644 --- a/erts/emulator/sys/win32/sys_interrupt.c +++ b/erts/emulator/sys/win32/sys_interrupt.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * Copyright Ericsson AB 1997-2010. 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 @@ -33,9 +33,9 @@ #ifdef ERTS_SMP erts_smp_atomic_t erts_break_requested; #define ERTS_SET_BREAK_REQUESTED \ - erts_smp_atomic_set(&erts_break_requested, (long) 1) + erts_smp_atomic_set(&erts_break_requested, (erts_aint_t) 1) #define ERTS_UNSET_BREAK_REQUESTED \ - erts_smp_atomic_set(&erts_break_requested, (long) 0) + erts_smp_atomic_set(&erts_break_requested, (erts_aint_t) 0) #else volatile int erts_break_requested = 0; #define ERTS_SET_BREAK_REQUESTED (erts_break_requested = 1) diff --git a/erts/emulator/test/beam_SUITE.erl b/erts/emulator/test/beam_SUITE.erl index 228ff15341..32ac07cb2d 100644 --- a/erts/emulator/test/beam_SUITE.erl +++ b/erts/emulator/test/beam_SUITE.erl @@ -20,7 +20,8 @@ -module(beam_SUITE). -export([all/1, packed_registers/1, apply_last/1, apply_last_bif/1, - buildo_mucho/1, heap_sizes/1, big_lists/1, fconv/1]). + buildo_mucho/1, heap_sizes/1, big_lists/1, fconv/1, + select_val/1]). -export([applied/2]). @@ -28,7 +29,7 @@ all(suite) -> [packed_registers, apply_last, apply_last_bif, buildo_mucho, - heap_sizes, big_lists]. + heap_sizes, big_lists, select_val]. %% Verify that apply(M, F, A) is really tail recursive. @@ -302,3 +303,19 @@ do_fconv(nil, Float) when is_float(Float) -> Float + []; do_fconv(tuple_literal, Float) when is_float(Float) -> Float + {a,b}. + +select_val(Config) when is_list(Config) -> + ?line zero = do_select_val(0), + ?line big = do_select_val(1 bsl 64), + ?line integer = do_select_val(42), + ok. + +do_select_val(X) -> + case X of + 0 -> + zero; + 1 bsl 64 -> + big; + Int when is_integer(Int) -> + integer + end. diff --git a/erts/emulator/test/beam_literals_SUITE.erl b/erts/emulator/test/beam_literals_SUITE.erl index 75841adbfc..1eda939cf8 100644 --- a/erts/emulator/test/beam_literals_SUITE.erl +++ b/erts/emulator/test/beam_literals_SUITE.erl @@ -23,7 +23,8 @@ matching_bigs/1, matching_more_bigs/1, matching_bigs_and_smalls/1, badmatch/1, case_clause/1, receiving/1, literal_type_tests/1, - put_list/1, fconv/1, literal_case_expression/1]). + put_list/1, fconv/1, literal_case_expression/1, + increment/1]). -include("test_server.hrl"). @@ -32,7 +33,7 @@ all(suite) -> matching_bigs, matching_more_bigs, matching_bigs_and_smalls, badmatch, case_clause, receiving, literal_type_tests, - put_list, fconv, literal_case_expression]. + put_list, fconv, literal_case_expression, increment]. putting(doc) -> "Test creating lists and tuples containing big number literals."; putting(Config) when is_list(Config) -> @@ -48,6 +49,7 @@ matching_bigs(doc) -> "Test matching of a few big number literals (in Beam," matching_bigs(Config) when is_list(Config) -> a = matching1(3972907842873739), b = matching1(-389789298378939783333333333333333333784), + other = matching1(3141699999999999999999999999999999999), other = matching1(42). matching_smalls(doc) -> "Test matching small numbers (both positive and negative)."; @@ -405,14 +407,51 @@ fconv_2(F) when is_float(F) -> literal_case_expression(Config) when is_list(Config) -> ?line DataDir = ?config(data_dir, Config), ?line Src = filename:join(DataDir, "literal_case_expression"), - ?line {ok,literal_case_expression=Mod,Code} = compile:file(Src, [from_asm,binary]), + ?line {ok,literal_case_expression=Mod,Code} = + compile:file(Src, [from_asm,binary]), ?line {module,Mod} = code:load_binary(Mod, Src, Code), ?line ok = Mod:x(), ?line ok = Mod:y(), + ?line ok = Mod:zi1(), + ?line ok = Mod:zi2(), + ?line ok = Mod:za1(), + ?line ok = Mod:za2(), ?line true = code:delete(Mod), ?line code:purge(Mod), ok. +%% Test the i_increment instruction. +increment(Config) when is_list(Config) -> + %% In the 32-bit emulator, Neg32 can be represented as a small, + %% but -Neg32 cannot. Therefore the i_increment instruction must + %% not be used in the subtraction that follows (since i_increment + %% cannot handle a bignum literal). + Neg32 = -(1 bsl 27), + Big32 = id(1 bsl 32), + Result32 = (1 bsl 32) + (1 bsl 27), + ?line Result32 = Big32 + (1 bsl 27), + ?line Result32 = Big32 - Neg32, + + %% Same thing, but for the 64-bit emulator. + Neg64 = -(1 bsl 59), + Big64 = id(1 bsl 64), + Result64 = (1 bsl 64) + (1 bsl 59), + ?line Result64 = Big64 + (1 bsl 59), + ?line Result64 = Big64 - Neg64, + + %% Test error handling for the i_increment instruction. + Bad = id(bad), + ?line {'EXIT',{badarith,_}} = (catch Bad + 42), + + %% Small operands, but a big result. + Res32 = 1 bsl 27, + Small32 = id(Res32-1), + ?line Res32 = Small32 + 1, + Res64 = 1 bsl 59, + Small64 = id(Res64-1), + ?line Res64 = Small64 + 1, + ok. + %% Help functions. chksum(Term) -> diff --git a/erts/emulator/test/beam_literals_SUITE_data/literal_case_expression.S b/erts/emulator/test/beam_literals_SUITE_data/literal_case_expression.S index c0ffe9ab53..bfdfc079dc 100644 --- a/erts/emulator/test/beam_literals_SUITE_data/literal_case_expression.S +++ b/erts/emulator/test/beam_literals_SUITE_data/literal_case_expression.S @@ -1,10 +1,11 @@ {module, literal_case_expression}. %% version = 0 -{exports, [{module_info,0},{module_info,1},{x,0},{y,0}]}. +{exports, [{module_info,0},{module_info,1},{x,0},{y,0}, + {zi1,0},{zi2,0},{za1,0},{za2,0}]}. {attributes, []}. -{labels, 15}. +{labels, 32}. {function, x, 0, 2}. @@ -52,6 +53,81 @@ {label,10}. {case_end,{float,34.0000}}. +{function, zi1, 0, 16}. + {label,15}. + {func_info,{atom,literal_case_expression},{atom,zi1},0}. + {label,16}. + {test,is_integer,{f,19},[{integer,42}]}. + {select_val,{integer,42}, + {f,18}, + {list,[{integer,42}, + {f,17}, + {integer,1000}, + {f,18}]}}. + {label,17}. + {move,{atom,ok},{x,0}}. + return. + {label,18}. + {move,{atom,error},{x,0}}. + return. + {label,19}. + {case_end,{integer,42}}. + +{function, zi2, 0, 16}. + {label,20}. + {func_info,{atom,literal_case_expression},{atom,zi2},0}. + {label,21}. + {test,is_integer,{f,23},[{integer,42}]}. + {select_val,{integer,42}, + {f,23}, + {list,[{integer,42}, + {f,22}, + {integer,1000}, + {f,23}]}}. + {label,22}. + {move,{atom,ok},{x,0}}. + return. + {label,23}. + {move,{atom,error},{x,0}}. + return. + +{function, za1, 0, 25}. + {label,24}. + {func_info,{atom,literal_case_expression},{atom,za1},0}. + {label,25}. + {test,is_atom,{f,28},[{atom,x}]}. + {select_val,{atom,x}, + {f,27}, + {list,[{atom,a}, + {f,27}, + {atom,x}, + {f,26}]}}. + {label,26}. + {move,{atom,ok},{x,0}}. + return. + {label,27}. + {move,{atom,error},{x,0}}. + return. + {label,28}. + {case_end,{atom,x}}. + +{function, za2, 0, 30}. + {label,29}. + {func_info,{atom,literal_case_expression},{atom,za2},0}. + {label,30}. + {test,is_atom,{f,32},[{atom,x}]}. + {select_val,{atom,x}, + {f,32}, + {list,[{atom,a}, + {f,32}, + {atom,x}, + {f,31}]}}. + {label,31}. + {move,{atom,ok},{x,0}}. + return. + {label,32}. + {move,{atom,error},{x,0}}. + return. {function, module_info, 0, 12}. {label,11}. diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 3d9b51d278..138a19a626 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2010. 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 @@ -27,7 +27,7 @@ mem_leak/1, coerce_to_float/1, bjorn/1, huge_float_field/1, huge_binary/1, system_limit/1, badarg/1, copy_writable_binary/1, kostis/1, dynamic/1, bs_add/1, - otp_7422/1]). + otp_7422/1, zero_width/1]). -include("test_server.hrl"). @@ -36,7 +36,7 @@ all(suite) -> not_used, in_guard, mem_leak, coerce_to_float, bjorn, huge_float_field, huge_binary, system_limit, badarg, copy_writable_binary, kostis, dynamic, bs_add, - otp_7422]. + otp_7422, zero_width]. big(1) -> 57285702734876389752897683. @@ -786,5 +786,20 @@ otp_7422_bin(N) when N < 512 -> end), otp_7422_bin(N+1); otp_7422_bin(_) -> ok. + +zero_width(Config) when is_list(Config) -> + ?line Z = id(0), + Small = id(42), + Big = id(1 bsl 128), + ?line <<>> = <<Small:Z>>, + ?line <<>> = <<Small:0>>, + ?line <<>> = <<Big:Z>>, + ?line <<>> = <<Big:0>>, + + ?line {'EXIT',{badarg,_}} = (catch <<not_a_number:0>>), + ?line {'EXIT',{badarg,_}} = (catch <<(id(not_a_number)):Z>>), + ?line {'EXIT',{badarg,_}} = (catch <<(id(not_a_number)):0>>), + + ok. id(I) -> I. diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index 33351a3cc9..5707de30d3 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-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 @@ -320,6 +320,9 @@ make_stub(Config) when is_list(Config) -> (catch code:make_stub_module(my_code_test, bit_sized_binary(Code), {[],[]})), + ?line {'EXIT',{badarg,_}} = + (catch code:make_stub_module(my_code_test_with_wrong_name, + Code, {[],[]})), ok. make_stub_many_funs(Config) when is_list(Config) -> diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 79252d0593..f26455e6da 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -39,6 +39,7 @@ atom_roundtrip/1, atom_roundtrip_r12b/1, contended_atom_cache_entry/1, + bad_dist_structure/1, bad_dist_ext/1, bad_dist_ext_receive/1, bad_dist_ext_process_info/1, @@ -61,6 +62,7 @@ all(suite) -> [ stop_dist, trap_bif, dist_auto_connect, dist_parallel_send, atom_roundtrip, atom_roundtrip_r12b, contended_atom_cache_entry, + bad_dist_structure, bad_dist_ext ]. @@ -174,7 +176,7 @@ bulk_sendsend2(Terms, BinSize, BusyBufSize) -> ?line {ok, NodeRecv} = start_node(bulk_receiver), ?line Recv = spawn(NodeRecv, erlang, apply, [fun receiver/2, [0, 0]]), ?line Bin = list_to_binary(lists:duplicate(BinSize*1024, 253)), - ?line Size = Terms*size(Bin), + %%?line Size = Terms*size(Bin), %% SLF LEFT OFF HERE. %% When the caller uses small hunks, like 4k via @@ -187,7 +189,7 @@ bulk_sendsend2(Terms, BinSize, BusyBufSize) -> ?line {ok, NodeSend} = start_node(bulk_sender, "+zdbbl " ++ integer_to_list(BusyBufSize)), ?line _Send = spawn(NodeSend, erlang, apply, [fun sendersender/4, [self(), Recv, Bin, Terms]]), - ?line {Elapsed, {TermsN, SizeN}, MonitorCount} = + ?line {Elapsed, {_TermsN, SizeN}, MonitorCount} = receive {sendersender, BigRes} -> BigRes end, @@ -227,7 +229,7 @@ sendersender3(To, _Bin, 0, SendDone, MonitorCount) -> ok end, receive - {monitor, _Pid, _Type, _Info} = M -> + {monitor, _Pid, _Type, _Info} -> sendersender3(To, _Bin, 0, SendDone, MonitorCount + 1) after 0 -> if SendDone -> @@ -522,7 +524,7 @@ sink1() -> lost_exit(doc) -> "Test that EXIT and DOWN messages send to another node are not lost if " - "if the distribution port is busy."; + "the distribution port is busy."; lost_exit(Config) when is_list(Config) -> ?line {ok, Node} = start_node(lost_exit), @@ -1143,8 +1145,7 @@ contended_atom_cache_entry(Config) when is_list(Config) -> ?line {ok, SNode} = start_node(Config), ?line {ok, RNode} = start_node(Config), ?line Success = make_ref(), - ?line Mstr - = spawn_link( + ?line spawn_link( SNode, fun () -> erts_debug:set_internal_state(available_internal_state, @@ -1201,13 +1202,13 @@ contended_atom_cache_entry(Config) when is_list(Config) -> ?line stop_node(RNode), ?line ok. -send_ref_atom(To, Ref, Atom, 0) -> +send_ref_atom(_To, _Ref, _Atom, 0) -> ok; send_ref_atom(To, Ref, Atom, N) -> To ! {Ref, Atom}, send_ref_atom(To, Ref, Atom, N-1). -receive_ref_atom(Ref, Atom, 0) -> +receive_ref_atom(_Ref, _Atom, 0) -> ok; receive_ref_atom(Ref, Atom, N) -> receive @@ -1242,7 +1243,7 @@ unwanted_cixs() -> nodes()). -get_conflicting_atoms(CIX, 0) -> +get_conflicting_atoms(_CIX, 0) -> []; get_conflicting_atoms(CIX, N) -> {A, B, C} = now(), @@ -1256,6 +1257,186 @@ get_conflicting_atoms(CIX, N) -> get_conflicting_atoms(CIX, N) end. +-define(COOKIE, ''). +-define(DOP_LINK, 1). +-define(DOP_SEND, 2). +-define(DOP_EXIT, 3). +-define(DOP_UNLINK, 4). +-define(DOP_REG_SEND, 6). +-define(DOP_GROUP_LEADER, 7). +-define(DOP_EXIT2, 8). + +-define(DOP_SEND_TT, 12). +-define(DOP_EXIT_TT, 13). +-define(DOP_REG_SEND_TT, 16). +-define(DOP_EXIT2_TT, 18). + +-define(DOP_MONITOR_P, 19). +-define(DOP_DEMONITOR_P, 20). +-define(DOP_MONITOR_P_EXIT, 21). + +start_monitor(Offender,P) -> + ?line Parent = self(), + ?line Q = spawn(Offender, + fun () -> + Ref = erlang:monitor(process,P), + Parent ! {self(),ref,Ref}, + receive + just_stay_alive -> ok + end + end), + ?line Ref = receive + {Q,ref,R} -> + R + after 5000 -> + error + end, + io:format("Ref is ~p~n",[Ref]), + ok. +start_link(Offender,P) -> + ?line Parent = self(), + ?line Q = spawn(Offender, + fun () -> + process_flag(trap_exit,true), + link(P), + Parent ! {self(),ref,P}, + receive + just_stay_alive -> ok + end + end), + ?line Ref = receive + {Q,ref,R} -> + R + after 5000 -> + error + end, + io:format("Ref is ~p~n",[Ref]), + ok. + +bad_dist_structure(suite) -> + []; +bad_dist_structure(doc) -> + ["Test dist messages with valid structure (binary to term ok) but malformed" + "control content"]; +bad_dist_structure(Config) when is_list(Config) -> + %process_flag(trap_exit,true), + ODog = ?config(watchdog, Config), + ?t:timetrap_cancel(ODog), + Dog = ?t:timetrap(?t:seconds(15)), + + ?line {ok, Offender} = start_node(bad_dist_structure_offender), + ?line {ok, Victim} = start_node(bad_dist_structure_victim), + ?line start_node_monitors([Offender,Victim]), + ?line Parent = self(), + ?line P = spawn(Victim, + fun () -> + process_flag(trap_exit,true), + Parent ! {self(), started}, + receive check_msgs -> ok end, + bad_dist_struct_check_msgs([one, + two]), + Parent ! {self(), messages_checked}, + receive done -> ok end + end), + ?line receive {P, started} -> ok end, + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line verify_up(Offender, Victim), + ?line true = lists:member(Offender, rpc:call(Victim, erlang, nodes, [])), + ?line start_monitor(Offender,P), + ?line P ! one, + ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal},2), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line start_monitor(Offender,P), + ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P_EXIT,'replace',P,normal,normal},2), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line start_link(Offender,P), + ?line send_bad_structure(Offender, P,{?DOP_LINK},0), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line start_link(Offender,P), + ?line send_bad_structure(Offender, P,{?DOP_UNLINK,'replace'},2), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line start_link(Offender,P), + ?line send_bad_structure(Offender, P,{?DOP_UNLINK,'replace',make_ref()},2), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line start_link(Offender,P), + ?line send_bad_structure(Offender, P,{?DOP_UNLINK,make_ref(),P},0), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line start_link(Offender,P), + ?line send_bad_structure(Offender, P,{?DOP_UNLINK,normal,normal},0), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line start_monitor(Offender,P), + ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P},2), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line start_monitor(Offender,P), + ?line send_bad_structure(Offender, P,{?DOP_MONITOR_P,'replace',P,normal},2), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line start_monitor(Offender,P), + ?line send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P},2), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line start_monitor(Offender,P), + ?line send_bad_structure(Offender, P,{?DOP_DEMONITOR_P,'replace',P,normal},2), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_EXIT,'replace',P},2), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_EXIT,make_ref(),normal,normal},0), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_EXIT_TT,'replace',token,P},2), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_EXIT_TT,make_ref(),token,normal,normal},0), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_EXIT2,'replace',P},2), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_EXIT2,make_ref(),normal,normal},0), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_EXIT2_TT,'replace',token,P},2), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_EXIT2_TT,make_ref(),token,normal,normal},0), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace'},2), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace','atomic'},2), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_GROUP_LEADER,'replace',P},0), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name},2,{message}), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_REG_SEND_TT,'replace','',name,token},0,{message}), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace',''},2,{message}), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',P},0,{message}), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name},0,{message}), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_REG_SEND,'replace','',name,{token}},2,{message}), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_SEND_TT,'',P},0,{message}), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_SEND_TT,'',name,token},0,{message}), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_SEND,''},0,{message}), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_SEND,'',name},0,{message}), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line send_bad_structure(Offender, P,{?DOP_SEND,'',P,{token}},0,{message}), + ?line pong = rpc:call(Victim, net_adm, ping, [Offender]), + ?line P ! two, + ?line P ! check_msgs, + ?line receive + {P, messages_checked} -> ok + after 5000 -> + exit(victim_is_dead) + end, + + ?line {message_queue_len, 0} + = rpc:call(Victim, erlang, process_info, [P, message_queue_len]), + + ?line unlink(P), + ?line P ! done, + ?line stop_node(Offender), + ?line stop_node(Victim), + ?t:timetrap_cancel(Dog), + ok. bad_dist_ext(doc) -> []; bad_dist_ext(suite) -> @@ -1483,6 +1664,22 @@ bad_dist_ext_connection_id(Config) when is_list(Config) -> ?line stop_node(Victim). +bad_dist_struct_check_msgs([]) -> + receive + Msg -> + exit({unexpected_message, Msg}) + after 0 -> + ok + end; +bad_dist_struct_check_msgs([M|Ms]) -> + receive + {'EXIT',_,_} = EM -> + io:format("Ignoring exit message: ~p~n",[EM]), + bad_dist_struct_check_msgs([M|Ms]); + Msg -> + M = Msg, + bad_dist_struct_check_msgs(Ms) + end. bad_dist_ext_check_msgs([]) -> receive Msg -> @@ -1497,24 +1694,6 @@ bad_dist_ext_check_msgs([M|Ms]) -> bad_dist_ext_check_msgs(Ms) end. --define(COOKIE, ''). --define(DOP_LINK, 1). --define(DOP_SEND, 2). --define(DOP_EXIT, 3). --define(DOP_UNLINK, 4). --define(DOP_NODE_LINK, 5). --define(DOP_REG_SEND, 6). --define(DOP_GROUP_LEADER, 7). --define(DOP_EXIT2, 8). - --define(DOP_SEND_TT, 12). --define(DOP_EXIT_TT, 13). --define(DOP_REG_SEND_TT, 16). --define(DOP_EXIT2_TT, 18). - --define(DOP_MONITOR_P, 19). --define(DOP_DEMONITOR_P, 20). --define(DOP_MONITOR_P_EXIT, 21). dport_reg_send(Node, Name, Msg) -> DPrt = case dport(Node) of @@ -1546,6 +1725,39 @@ dport_send(To, Msg) -> ?COOKIE, To}), dmsg_ext(Msg)]). +send_bad_structure(Offender,Victim,Bad,WhereToPutSelf) -> + send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,[]). +send_bad_structure(Offender,Victim,Bad,WhereToPutSelf,PayLoad) -> + Parent = self(), + Done = make_ref(), + spawn(Offender, + fun () -> + Node = node(Victim), + pong = net_adm:ping(Node), + DPrt = dport(Node), + Bad1 = case WhereToPutSelf of + 0 -> + Bad; + N when N > 0 -> + setelement(N,Bad,self()) + end, + DData = [dmsg_hdr(), + dmsg_ext(Bad1)] ++ + case PayLoad of + [] -> []; + _Other -> [dmsg_ext(PayLoad)] + end, + port_command(DPrt, DData), + Parent ! {DData,Done} + end), + receive + {WhatSent,Done} -> + io:format("Offender sent ~p~n",[WhatSent]), + ok + after 5000 -> + exit(unable_to_send) + end. + %% send_bad_msgs(): %% Send a valid distribution header and control message @@ -1629,10 +1841,10 @@ dmsg_bad_hdr() -> 255]. % 255 atom references -dmsg_fake_hdr1() -> - A = <<"fake header atom 1">>, - [131, % Version Magic - $D, 1, 16#8, 0, size(A), A]. % Fake header +%% dmsg_fake_hdr1() -> +%% A = <<"fake header atom 1">>, +%% [131, % Version Magic +%% $D, 1, 16#8, 0, size(A), A]. % Fake header dmsg_fake_hdr2() -> A1 = <<"fake header atom 1">>, @@ -1817,7 +2029,7 @@ flush_node_changes() -> node_monitor_loop(Master) -> receive - {nodeup, Node, InfoList} = Msg -> + {nodeup, Node, _InfoList} = Msg -> Master ! {nodeup, node(), Node}, ?t:format("~p ~p: ~p~n", [node(), erlang:now(), Msg]), node_monitor_loop(Master); @@ -1854,9 +2066,9 @@ verify_no_down(A, B) -> ok end. -verify_down(A, B) -> - receive {nodedown, A, B, _} -> ok end, - receive {nodedown, B, A, _} -> ok end. +%% verify_down(A, B) -> +%% receive {nodedown, A, B, _} -> ok end, +%% receive {nodedown, B, A, _} -> ok end. verify_down(A, ReasonA, B, ReasonB) -> receive @@ -1876,11 +2088,11 @@ from(H, [H | T]) -> T; from(H, [_ | T]) -> from(H, T); from(_, []) -> []. -fun_spawn(Fun) -> - fun_spawn(Fun, []). +%% fun_spawn(Fun) -> +%% fun_spawn(Fun, []). -fun_spawn(Fun, Args) -> - spawn_link(erlang, apply, [Fun, Args]). +%% fun_spawn(Fun, Args) -> +%% spawn_link(erlang, apply, [Fun, Args]). long_or_short() -> diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl index 542c8dffbe..11a7a61586 100644 --- a/erts/emulator/test/erl_link_SUITE.erl +++ b/erts/emulator/test/erl_link_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2010. 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 @@ -1050,7 +1050,6 @@ stop_node(Node) -> -define(DOP_SEND, 2). -define(DOP_EXIT, 3). -define(DOP_UNLINK, 4). --define(DOP_NODE_LINK, 5). -define(DOP_REG_SEND, 6). -define(DOP_GROUP_LEADER, 7). -define(DOP_EXIT2, 8). diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl index e60a999df1..934a1b10a4 100644 --- a/erts/emulator/test/erts_debug_SUITE.erl +++ b/erts/emulator/test/erts_debug_SUITE.erl @@ -21,10 +21,10 @@ -include("test_server.hrl"). -export([all/1,init_per_testcase/2,fin_per_testcase/2, - flat_size/1,flat_size_big/1,df/1]). + flat_size/1,flat_size_big/1,df/1,instructions/1]). all(suite) -> - [flat_size,flat_size_big,df]. + [flat_size,flat_size_big,df,instructions]. init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> Dog=?t:timetrap(?t:minutes(2)), @@ -70,3 +70,8 @@ df(Config) when is_list(Config) -> pps() -> {erlang:ports()}. + +instructions(Config) when is_list(Config) -> + ?line Is = erts_debug:instructions(), + ?line _ = [list_to_atom(I) || I <- Is], + ok. diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index de19a2e35b..e7c57142c0 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -27,6 +27,7 @@ my $outdir = "."; # Directory for output files. my $verbose = 0; my $hot = 1; my $num_file_opcodes = 0; +my $wordsize = 32; # This is shift counts and mask for the packer. my $WHOLE_WORD = ''; @@ -36,12 +37,20 @@ my @pack_mask; $pack_instr[2] = ['6', 'i']; $pack_instr[3] = ['0', '0', 'i']; +$pack_instr[4] = ['6', '6', '6', 'i']; # Only for 64 bit wordsize $pack_shift[2] = ['0', 'BEAM_LOOSE_SHIFT']; $pack_shift[3] = ['0', 'BEAM_TIGHT_SHIFT', '(2*BEAM_TIGHT_SHIFT)']; +$pack_shift[4] = ['0', 'BEAM_LOOSE_SHIFT', # Only for 64 bit wordsize + '(2*BEAM_LOOSE_SHIFT)', + '(3*BEAM_LOOSE_SHIFT)']; $pack_mask[2] = ['BEAM_LOOSE_MASK', $WHOLE_WORD]; $pack_mask[3] = ['BEAM_TIGHT_MASK', 'BEAM_TIGHT_MASK', 'BEAM_TIGHT_MASK']; +$pack_mask[4] = ['BEAM_LOOSE_MASK', # Only for 64 bit wordsize + 'BEAM_LOOSE_MASK', + 'BEAM_LOOSE_MASK', + $WHOLE_WORD]; # There are two types of instructions: generic and specific. # The generic instructions are those generated by the Beam compiler. @@ -80,6 +89,8 @@ my %cold_code; my @unnumbered_generic; my %unnumbered; +my %is_transformed; + # # Code transformations. # @@ -118,7 +129,8 @@ my %arg_size = ('r' => 0, # x(0) - x register zero 't' => 1, # untagged integer -- can be packed 'b' => 1, # pointer to bif 'A' => 1, # arity value - 'P' => 1, # byte offset into tuple + 'P' => 1, # byte offset into tuple or stack + 'Q' => 1, # like 'P', but packable 'h' => 1, # character 'l' => 1, # float reg 'q' => 1, # literal term @@ -157,6 +169,7 @@ my @tag_type; $type_bit{'U'} = $type_bit{'u'}; $type_bit{'e'} = $type_bit{'u'}; $type_bit{'P'} = $type_bit{'u'}; + $type_bit{'Q'} = $type_bit{'u'}; } # @@ -169,6 +182,7 @@ while (@ARGV && $ARGV[0] =~ /^-(.*)/) { ($target = \&emulator_output), next if /^emulator/; ($target = \&compiler_output), next if /^compiler/; ($outdir = shift), next if /^outdir/; + ($wordsize = shift), next if /^wordsize/; ($verbose = 1), next if /^v/; die "$0: Bad option: -$_\n"; } @@ -474,8 +488,9 @@ sub emulator_output { $gen_transform_offset{$key} : -1; my($spec_op) = $gen_to_spec{$key}; my($num_specific) = $num_specific{$key}; - defined $spec_op or $tr != -1 or + defined $spec_op or $obsolete[$gen_opnum{$name,$arity}] or + $is_transformed{$name,$arity} or error("instruction $key has no specific instruction"); $spec_op = -1 unless defined $spec_op; &init_item($name, $arity, $spec_op, $num_specific, $tr, $min_window{$key}); @@ -498,12 +513,14 @@ sub emulator_output { print "#define NUM_SPECIFIC_OPS ", scalar(@op_to_name), "\n"; print "\n"; print "#ifdef ARCH_64\n"; + print "# define BEAM_WIDE_MASK 0xFFFFUL\n"; print "# define BEAM_LOOSE_MASK 0x1FFFUL\n"; print "#if HALFWORD_HEAP\n"; print "# define BEAM_TIGHT_MASK 0x1FFCUL\n"; print "#else\n"; print "# define BEAM_TIGHT_MASK 0x1FF8UL\n"; print "#endif\n"; + print "# define BEAM_WIDE_SHIFT 32\n"; print "# define BEAM_LOOSE_SHIFT 16\n"; print "# define BEAM_TIGHT_SHIFT 16\n"; print "#else\n"; @@ -796,6 +813,7 @@ sub basic_generator { 'I' => 1, 't' => 1, 'P' => 1, + 'Q' => 1, ); # Pick up the macro to use and its flags (if any). @@ -916,7 +934,18 @@ sub basic_generator { $var_decls .= "BeamInstr tmp_packed2;" if $macro_code =~ /tmp_packed2/; if ($flags =~ /-nonext/) { - $code = "$macro_code\n"; + $code = join("\n", + "{ $var_decls", + $macro_code, + "}"); + } elsif ($flags =~ /-goto:(\S*)/) { + my $goto = $1; + $code = join("\n", + "{ $var_decls", + $macro_code, + "I += $size + 1;", + "goto $goto;", + "}"); } else { $code = join("\n", "{ $var_decls", @@ -935,18 +964,31 @@ sub basic_generator { sub do_pack { my(@args) = @_; - my($i); my($packable_args) = 0; + my @is_packable; # Packability (boolean) for each argument. + my $wide_packing = 0; # # Count the number of packable arguments. If we encounter any 's' or 'd' # arguments, packing is not possible. # - for ($i = 0; $i < @args; $i++) { - if ($args[$i] =~ /[xyt]/) { + my $packable_types = "xytQ"; + foreach my $arg (@args) { + if ($arg =~ /^[$packable_types]/) { $packable_args++; - } elsif ($args[$i] =~ /[sd]/) { + push @is_packable, 1; + } elsif ($arg =~ /^I/ and $wordsize == 64 and $packable_args < 2) { + $wide_packing = 1; + push @is_packable, 1; + if (++$packable_args == 2) { + # We can only pack two arguments. Turn off packing + # for the rest of the arguments. + $packable_types = "\xFF"; + } + } elsif ($arg =~ /^[sd]/) { return ('', '', @args); + } else { + push @is_packable, 0; } } @@ -962,10 +1004,27 @@ sub do_pack { # beginning). my($up) = ''; # Pack commands (storing back while # moving forward). - my($args_per_word) = $packable_args < 4 ? $packable_args : 2; - my(@shift) = @{$pack_shift[$args_per_word]}; - my(@mask) = @{$pack_mask[$args_per_word]}; - my(@pack_instr) = @{$pack_instr[$args_per_word]}; + my $args_per_word; + if ($packable_args < 4 or $wordsize == 64) { + $args_per_word = $packable_args; + } else { + # 4 packable argument, 32 bit wordsize. Need 2 words. + $args_per_word = 2; + } + + my @shift; + my @mask; + my @instr; + + if ($wide_packing) { + @shift = ('0', 'BEAM_WIDE_SHIFT'); + @mask = ('BEAM_WIDE_MASK', $WHOLE_WORD); + @instr = ('w', 'i'); + } else { + @shift = @{$pack_shift[$args_per_word]}; + @mask = @{$pack_mask[$args_per_word]}; + @instr = @{$pack_instr[$args_per_word]}; + } # # Now generate the packing instructions. One complication is that @@ -979,10 +1038,10 @@ sub do_pack { my($ap) = 0; # Argument number within word. my($tmpnum) = 1; # Number of temporary variable. my($expr) = ''; - for ($i = 0; $i < @args; $i++) { + for (my $i = 0; $i < @args; $i++) { my($reg) = $args[$i]; my($this_size) = $arg_size{$reg}; - if ($reg =~ /[xyt]/) { + if ($is_packable[$i]) { $this_size = 0; $did_some_packing = 1; @@ -993,7 +1052,7 @@ sub do_pack { $this_size = 1; } - $down = "$pack_instr[$ap]$down"; + $down = "$instr[$ap]$down"; my($unpack) = &make_unpack($tmpnum, $shift[$ap], $mask[$ap]); $args[$i] = "pack:$this_size:$reg" . "b($unpack)"; @@ -1103,6 +1162,10 @@ sub compile_transform { if ($obsolete[$gen_opnum{$name,$arity}]) { error("obsolete function must not be used in transformations"); } + + if ($src) { + $is_transformed{$name,$arity} = 1; + } [$name,$arity,@ops]; } @@ -1291,13 +1354,28 @@ sub tr_gen_from { my($var, $type, $type_val, $cond, $val) = @$op; if ($type ne '' && $type ne '*') { - my($types) = ''; - my($type_mask) = 0; - foreach (split('', $type)) { - $types .= "$_ "; - $type_mask |= $type_bit{$_}; + # + # The is_bif, is_not_bif, and is_func instructions have + # their own built-in type test and don't need to + # be guarded with a type test instruction. + # + unless ($cond eq 'is_bif' or + $cond eq 'is_not_bif' or + $cond eq 'is_func') { + my($types) = ''; + my($type_mask) = 0; + foreach (split('', $type)) { + $types .= "$_ "; + $type_mask |= $type_bit{$_}; + } + if ($cond ne 'is_eq') { + push(@code, &make_op($types, 'is_type', $type_mask)); + } else { + $cond = ''; + push(@code, &make_op($types, 'is_type_eq', + $type_mask, $val)); + } } - push(@code, &make_op($types, 'is_type', $type_mask)); } if ($cond eq 'is_func') { diff --git a/erts/emulator/utils/count b/erts/emulator/utils/count new file mode 100755 index 0000000000..617f5c25e8 --- /dev/null +++ b/erts/emulator/utils/count @@ -0,0 +1,127 @@ +%% -*- erlang -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2010. 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 +%% 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 online 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. +%% +%% %CopyrightEnd% +%% + +-mode(compile). + +main(_) -> + DisDir = "./dis", + ok = filelib:ensure_dir(filename:join(DisDir, "dummy")), + io:format("Dissambling to ~s\n", [DisDir]), + ok = file:set_cwd(DisDir), + Path = code:get_path() -- ["."], + Beams0 = [filelib:wildcard(filename:join(Dir, "*.beam")) || + Dir <- Path], + Beams = lists:append(Beams0), + Mods0 = [list_to_atom(filename:rootname(filename:basename(F))) || + F <- Beams], + Mods = lists:usort(Mods0), + start_sem(), + Ps = [begin + {_,Ref} = spawn_monitor(fun() -> count(M) end), + Ref + end || M <- Mods], + [put(list_to_atom(I), 0) || I <- erts_debug:instructions()], + Res = wait_for_all(Ps, 1), + OutFile = "count", + {ok,Out} = file:open(OutFile, [write]), + [io:format(Out, "~s ~p\n", [I,C]) || {I,C} <- Res], + ok = file:close(Out), + io:format("\nResult written to ~s\n", + [filename:join(DisDir, OutFile)]), + ok. + +wait_for_all([], _) -> + lists:reverse(lists:keysort(2, get())); +wait_for_all([_|_]=Ps, I) -> + receive + {'DOWN',Ref,process,_,Result} -> + io:format("\r~p", [I]), + [increment(Key, Count) || {Key,Count} <- Result], + wait_for_all(Ps -- [Ref], I+1) + end. + +count(M) -> + down(), + erts_debug:df(M), + {ok,Fd} = file:open(atom_to_list(M) ++ ".dis", [read,raw]), + count_is(Fd), + ok = file:close(Fd), + exit(get()). + +count_is(Fd) -> + case file:read_line(Fd) of + {ok,Line} -> + count_instr(Line), + count_is(Fd); + eof -> + ok + end. + +count_instr([$\s|T]) -> + count_instr_1(T, []); +count_instr([_|T]) -> + count_instr(T); +count_instr([]) -> + %% Empty line. + ok. + +count_instr_1([$\s|_], Acc) -> + Instr = list_to_atom(lists:reverse(Acc)), + increment(Instr, 1); +count_instr_1([H|T], Acc) -> + count_instr_1(T, [H|Acc]). + +increment(Key, Inc) -> + case get(Key) of + undefined -> + put(Key, Inc); + Count -> + put(Key, Count+Inc) + end. + +%%% +%%% Counting sempahore to limit the number of processes that +%%% can run concurrently. +%%% + +down() -> + sem ! {down,self()}, + receive + sem_taken -> ok + end. + +start_sem() -> + spawn(fun() -> + register(sem, self()), + process_flag(trap_exit, true), + do_sem(erlang:system_info(schedulers)+1) end). + +do_sem(0) -> + receive + {'EXIT',_,_} -> + do_sem(1) + end; +do_sem(C) -> + receive + {down,Pid} -> + link(Pid), + Pid ! sem_taken, + do_sem(C-1) + end. diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index ef471a473a..3499ab2934 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -2,7 +2,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2010. All Rights Reserved. + * Copyright Ericsson AB 1998-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 @@ -157,8 +157,10 @@ void run(EpmdVars *g) dbg_printf(g,2,"starting"); - listen(listensock, SOMAXCONN); - + if(listen(listensock, SOMAXCONN) < 0) { + dbg_perror(g,"failed to listen on socket"); + epmd_cleanup_exit(g,1); + } FD_ZERO(&g->orig_read_mask); FD_SET(listensock,&g->orig_read_mask); diff --git a/erts/include/internal/ethr_atomics.h b/erts/include/internal/ethr_atomics.h new file mode 100644 index 0000000000..1caf4d0567 --- /dev/null +++ b/erts/include/internal/ethr_atomics.h @@ -0,0 +1,726 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010. 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 + * 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 online 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. + * + * %CopyrightEnd% + */ + +/* + * Description: The ethread atomic API + * Author: Rickard Green + */ + +#ifndef ETHR_ATOMIC_H__ +#define ETHR_ATOMIC_H__ + +#if !defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_ATOMIC_IMPL__) +# define ETHR_NEED_ATOMIC_PROTOTYPES__ +#endif + +#ifndef ETHR_HAVE_NATIVE_ATOMICS +/* + * No native atomic implementation available. :( + * Use fallback... + */ +typedef ethr_sint32_t ethr_atomic32_t; +typedef ethr_sint_t ethr_atomic_t; +#else +/* + * Map ethread native atomics to ethread API atomics. + * + * We do at least have a native atomic implementation that + * can handle integers of a size larger than or equal to + * the size of pointers. + */ + +/* -- Pointer size atomics -- */ + +#undef ETHR_NAINT_T__ +#undef ETHR_NATMC_FUNC__ +#undef ETHR_NATMC_ADDR_FUNC__ +#if ETHR_SIZEOF_PTR == 8 +# if defined(ETHR_HAVE_NATIVE_ATOMIC64) +# define ETHR_NATMC_ADDR_FUNC__ ethr_native_atomic64_addr +typedef ethr_native_atomic64_t ethr_atomic_t; +# define ETHR_NAINT_T__ ethr_sint64_t +# define ETHR_NATMC_FUNC__(X) ethr_native_atomic64_ ## X +# else +# error "Missing native atomic implementation" +# endif +#elif ETHR_SIZEOF_PTR == 4 +# define ETHR_NATMC_ADDR_FUNC__ ethr_native_atomic32_addr +# ifdef ETHR_HAVE_NATIVE_ATOMIC32 +typedef ethr_native_atomic32_t ethr_atomic_t; +# define ETHR_NAINT_T__ ethr_sint32_t +# define ETHR_NATMC_FUNC__(X) ethr_native_atomic32_ ## X +# elif defined(ETHR_HAVE_NATIVE_ATOMIC64) +typedef ethr_native_atomic64_t ethr_atomic_t; +# define ETHR_NATMC_T__ ethr_native_atomic64_t +# define ETHR_NAINT_T__ ethr_sint64_t +# define ETHR_NATMC_FUNC__(X) ethr_native_atomic64_ ## X +# else +# error "Missing native atomic implementation" +# endif +#endif + +/* -- 32-bit atomics -- */ + +#undef ETHR_NAINT32_T__ +#undef ETHR_NATMC32_FUNC__ +#if defined(ETHR_HAVE_NATIVE_ATOMIC32) +typedef ethr_native_atomic32_t ethr_atomic32_t; +# define ETHR_NAINT32_T__ ethr_sint32_t +# define ETHR_NATMC32_FUNC__(X) ethr_native_atomic32_ ## X +#elif defined(ETHR_HAVE_NATIVE_ATOMIC64) +typedef ethr_native_atomic64_t ethr_atomic32_t; +# define ETHR_NAINT32_T__ ethr_sint64_t +# define ETHR_NATMC32_FUNC__(X) ethr_native_atomic64_ ## X +#else +# error "Missing native atomic implementation" +#endif + +#endif + +#ifdef ETHR_NEED_ATOMIC_PROTOTYPES__ +ethr_sint_t *ethr_atomic_addr(ethr_atomic_t *); +void ethr_atomic_init(ethr_atomic_t *, ethr_sint_t); +void ethr_atomic_set(ethr_atomic_t *, ethr_sint_t); +ethr_sint_t ethr_atomic_read(ethr_atomic_t *); +ethr_sint_t ethr_atomic_inc_read(ethr_atomic_t *); +ethr_sint_t ethr_atomic_dec_read(ethr_atomic_t *); +void ethr_atomic_inc(ethr_atomic_t *); +void ethr_atomic_dec(ethr_atomic_t *); +ethr_sint_t ethr_atomic_add_read(ethr_atomic_t *, ethr_sint_t); +void ethr_atomic_add(ethr_atomic_t *, ethr_sint_t); +ethr_sint_t ethr_atomic_read_band(ethr_atomic_t *, ethr_sint_t); +ethr_sint_t ethr_atomic_read_bor(ethr_atomic_t *, ethr_sint_t); +ethr_sint_t ethr_atomic_xchg(ethr_atomic_t *, ethr_sint_t); +ethr_sint_t ethr_atomic_cmpxchg(ethr_atomic_t *, ethr_sint_t, ethr_sint_t); +ethr_sint_t ethr_atomic_read_acqb(ethr_atomic_t *); +ethr_sint_t ethr_atomic_inc_read_acqb(ethr_atomic_t *); +void ethr_atomic_set_relb(ethr_atomic_t *, ethr_sint_t); +void ethr_atomic_dec_relb(ethr_atomic_t *); +ethr_sint_t ethr_atomic_dec_read_relb(ethr_atomic_t *); +ethr_sint_t ethr_atomic_cmpxchg_acqb(ethr_atomic_t *, ethr_sint_t, ethr_sint_t); +ethr_sint_t ethr_atomic_cmpxchg_relb(ethr_atomic_t *, ethr_sint_t, ethr_sint_t); + +ethr_sint32_t *ethr_atomic32_addr(ethr_atomic32_t *); +void ethr_atomic32_init(ethr_atomic32_t *, ethr_sint32_t); +void ethr_atomic32_set(ethr_atomic32_t *, ethr_sint32_t); +ethr_sint32_t ethr_atomic32_read(ethr_atomic32_t *); +ethr_sint32_t ethr_atomic32_inc_read(ethr_atomic32_t *); +ethr_sint32_t ethr_atomic32_dec_read(ethr_atomic32_t *); +void ethr_atomic32_inc(ethr_atomic32_t *); +void ethr_atomic32_dec(ethr_atomic32_t *); +ethr_sint32_t ethr_atomic32_add_read(ethr_atomic32_t *, ethr_sint32_t); +void ethr_atomic32_add(ethr_atomic32_t *, ethr_sint32_t); +ethr_sint32_t ethr_atomic32_read_band(ethr_atomic32_t *, ethr_sint32_t); +ethr_sint32_t ethr_atomic32_read_bor(ethr_atomic32_t *, ethr_sint32_t); +ethr_sint32_t ethr_atomic32_xchg(ethr_atomic32_t *, ethr_sint32_t); +ethr_sint32_t ethr_atomic32_cmpxchg(ethr_atomic32_t *, + ethr_sint32_t, + ethr_sint32_t); +ethr_sint32_t ethr_atomic32_read_acqb(ethr_atomic32_t *); +ethr_sint32_t ethr_atomic32_inc_read_acqb(ethr_atomic32_t *); +void ethr_atomic32_set_relb(ethr_atomic32_t *, ethr_sint32_t); +void ethr_atomic32_dec_relb(ethr_atomic32_t *); +ethr_sint32_t ethr_atomic32_dec_read_relb(ethr_atomic32_t *); +ethr_sint32_t ethr_atomic32_cmpxchg_acqb(ethr_atomic32_t *, + ethr_sint32_t, + ethr_sint32_t); +ethr_sint32_t ethr_atomic32_cmpxchg_relb(ethr_atomic32_t *, + ethr_sint32_t, + ethr_sint32_t); +#endif + +int ethr_init_atomics(void); + +#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_ATOMIC_IMPL__) + +#ifndef ETHR_HAVE_NATIVE_ATOMICS +/* + * Fallbacks for atomics used in absence of a native implementation. + */ + +#define ETHR_ATOMIC_ADDR_BITS 10 +#define ETHR_ATOMIC_ADDR_SHIFT 6 + +typedef struct { + union { + ethr_spinlock_t lck; + char buf[ETHR_CACHE_LINE_SIZE]; + } u; +} ethr_atomic_protection_t; + +extern ethr_atomic_protection_t ethr_atomic_protection__[1 << ETHR_ATOMIC_ADDR_BITS]; + +#define ETHR_ATOMIC_PTR2LCK__(PTR) \ +(ðr_atomic_protection__[((((ethr_uint_t) (PTR)) >> ETHR_ATOMIC_ADDR_SHIFT) \ + & ((1 << ETHR_ATOMIC_ADDR_BITS) - 1))].u.lck) + + +#define ETHR_ATOMIC_OP_FALLBACK_IMPL__(AP, EXPS) \ +do { \ + ethr_spinlock_t *slp__ = ETHR_ATOMIC_PTR2LCK__((AP)); \ + ethr_spin_lock(slp__); \ + { EXPS; } \ + ethr_spin_unlock(slp__); \ +} while (0) + +#endif + +/* + * --- Pointer size atomics --------------------------------------------------- + */ + +static ETHR_INLINE ethr_sint_t * +ETHR_INLINE_FUNC_NAME_(ethr_atomic_addr)(ethr_atomic_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint_t *) ETHR_NATMC_ADDR_FUNC__(var); +#else + return (ethr_sint_t *) var; +#endif +} + +static ETHR_INLINE void +ETHR_INLINE_FUNC_NAME_(ethr_atomic_init)(ethr_atomic_t *var, ethr_sint_t i) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + ETHR_NATMC_FUNC__(init)(var, (ETHR_NAINT_T__) i); +#else + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, *var = i); +#endif +} + +static ETHR_INLINE void +ETHR_INLINE_FUNC_NAME_(ethr_atomic_set)(ethr_atomic_t *var, ethr_sint_t i) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + ETHR_NATMC_FUNC__(set)(var, (ETHR_NAINT_T__) i); +#else + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, *var = i); +#endif +} + +static ETHR_INLINE ethr_sint_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic_read)(ethr_atomic_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint_t) ETHR_NATMC_FUNC__(read)(var); +#else + ethr_sint_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = *var); + return res; +#endif +} + +static ETHR_INLINE void +ETHR_INLINE_FUNC_NAME_(ethr_atomic_add)(ethr_atomic_t *var, ethr_sint_t incr) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + ETHR_NATMC_FUNC__(add)(var, (ETHR_NAINT_T__) incr); +#else + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, *var += incr); +#endif +} + +static ETHR_INLINE ethr_sint_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic_add_read)(ethr_atomic_t *var, ethr_sint_t i) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint_t) ETHR_NATMC_FUNC__(add_return)(var, (ETHR_NAINT_T__) i); +#else + ethr_sint_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, *var += i; res = *var); + return res; +#endif +} + +static ETHR_INLINE void +ETHR_INLINE_FUNC_NAME_(ethr_atomic_inc)(ethr_atomic_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + ETHR_NATMC_FUNC__(inc)(var); +#else + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, ++(*var)); +#endif +} + +static ETHR_INLINE void +ETHR_INLINE_FUNC_NAME_(ethr_atomic_dec)(ethr_atomic_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + ETHR_NATMC_FUNC__(dec)(var); +#else + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, --(*var)); +#endif +} + +static ETHR_INLINE ethr_sint_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic_inc_read)(ethr_atomic_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint_t) ETHR_NATMC_FUNC__(inc_return)(var); +#else + ethr_sint_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = ++(*var)); + return res; +#endif +} + +static ETHR_INLINE ethr_sint_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic_dec_read)(ethr_atomic_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint_t) ETHR_NATMC_FUNC__(dec_return)(var); +#else + ethr_sint_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = --(*var)); + return res; +#endif +} + +static ETHR_INLINE ethr_sint_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic_read_band)(ethr_atomic_t *var, + ethr_sint_t mask) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint_t) ETHR_NATMC_FUNC__(and_retold)(var, + (ETHR_NAINT_T__) mask); +#else + ethr_sint_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = *var; *var &= mask); + return res; +#endif +} + +static ETHR_INLINE ethr_sint_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic_read_bor)(ethr_atomic_t *var, + ethr_sint_t mask) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint_t) ETHR_NATMC_FUNC__(or_retold)(var, + (ETHR_NAINT_T__) mask); +#else + ethr_sint_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = *var; *var |= mask); + return res; +#endif +} + +static ETHR_INLINE ethr_sint_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic_xchg)(ethr_atomic_t *var, ethr_sint_t new) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint_t) ETHR_NATMC_FUNC__(xchg)(var, + (ETHR_NAINT_T__) new); +#else + ethr_sint_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = *var; *var = new); + return res; +#endif +} + +static ETHR_INLINE ethr_sint_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic_cmpxchg)(ethr_atomic_t *var, + ethr_sint_t new, + ethr_sint_t exp) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint_t) ETHR_NATMC_FUNC__(cmpxchg)(var, + (ETHR_NAINT_T__) new, + (ETHR_NAINT_T__) exp); +#else + ethr_sint_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, + { + res = *var; + if (__builtin_expect(res == exp, 1)) + *var = new; + }); + return res; +#endif +} + +/* + * Important memory barrier requirements. + * + * The following atomic operations *must* supply a memory barrier of + * at least the type specified by its suffix: + * _acqb = acquire barrier + * _relb = release barrier + */ + +static ETHR_INLINE ethr_sint_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic_read_acqb)(ethr_atomic_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint_t) ETHR_NATMC_FUNC__(read_acqb)(var); +#else + return ETHR_INLINE_FUNC_NAME_(ethr_atomic_read)(var); +#endif +} + +static ETHR_INLINE ethr_sint_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic_inc_read_acqb)(ethr_atomic_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint_t) ETHR_NATMC_FUNC__(inc_return_acqb)(var); +#else + return ETHR_INLINE_FUNC_NAME_(ethr_atomic_inc_read)(var); +#endif +} + +static ETHR_INLINE void +ETHR_INLINE_FUNC_NAME_(ethr_atomic_set_relb)(ethr_atomic_t *var, + ethr_sint_t val) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + ETHR_NATMC_FUNC__(set_relb)(var, (ETHR_NAINT_T__) val); +#else + ETHR_INLINE_FUNC_NAME_(ethr_atomic_set)(var, val); +#endif +} + +static ETHR_INLINE void +ETHR_INLINE_FUNC_NAME_(ethr_atomic_dec_relb)(ethr_atomic_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + ETHR_NATMC_FUNC__(dec_relb)(var); +#else + ETHR_INLINE_FUNC_NAME_(ethr_atomic_dec)(var); +#endif +} + +static ETHR_INLINE ethr_sint_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic_dec_read_relb)(ethr_atomic_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint_t) ETHR_NATMC_FUNC__(dec_return_relb)(var); +#else + return ETHR_INLINE_FUNC_NAME_(ethr_atomic_dec_read)(var); +#endif +} + +static ETHR_INLINE ethr_sint_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic_cmpxchg_acqb)(ethr_atomic_t *var, + ethr_sint_t new, + ethr_sint_t exp) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint_t) ETHR_NATMC_FUNC__(cmpxchg_acqb)(var, + (ETHR_NAINT_T__) new, + (ETHR_NAINT_T__) exp); +#else + return ETHR_INLINE_FUNC_NAME_(ethr_atomic_cmpxchg)(var, new, exp); +#endif +} + +static ETHR_INLINE ethr_sint_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic_cmpxchg_relb)(ethr_atomic_t *var, + ethr_sint_t new, + ethr_sint_t exp) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint_t) ETHR_NATMC_FUNC__(cmpxchg_relb)(var, + (ETHR_NAINT_T__) new, + (ETHR_NAINT_T__) exp); +#else + return ETHR_INLINE_FUNC_NAME_(ethr_atomic_cmpxchg)(var, new, exp); +#endif +} + +/* + * --- 32-bit atomics --------------------------------------------------------- + */ + +static ETHR_INLINE ethr_sint32_t * +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_addr)(ethr_atomic32_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return ethr_native_atomic32_addr(var); +#else + return (ethr_sint32_t *) var; +#endif +} + +static ETHR_INLINE void +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_init)(ethr_atomic32_t *var, + ethr_sint32_t i) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + ETHR_NATMC32_FUNC__(init)(var, (ETHR_NAINT32_T__) i); +#else + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, *var = i); +#endif +} + +static ETHR_INLINE void +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_set)(ethr_atomic32_t *var, ethr_sint32_t i) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + ETHR_NATMC32_FUNC__(set)(var, (ETHR_NAINT32_T__) i); +#else + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, *var = i); +#endif +} + +static ETHR_INLINE ethr_sint32_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_read)(ethr_atomic32_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint32_t) ETHR_NATMC32_FUNC__(read)(var); +#else + ethr_sint32_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = *var); + return res; +#endif +} + +static ETHR_INLINE void +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_add)(ethr_atomic32_t *var, + ethr_sint32_t incr) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + ETHR_NATMC32_FUNC__(add)(var, (ETHR_NAINT32_T__) incr); +#else + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, *var += incr); +#endif +} + +static ETHR_INLINE ethr_sint32_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_add_read)(ethr_atomic32_t *var, + ethr_sint32_t i) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint32_t) + ETHR_NATMC32_FUNC__(add_return)(var, (ETHR_NAINT32_T__) i); +#else + ethr_sint32_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, *var += i; res = *var); + return res; +#endif +} + +static ETHR_INLINE void +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_inc)(ethr_atomic32_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + ETHR_NATMC32_FUNC__(inc)(var); +#else + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, ++(*var)); +#endif +} + +static ETHR_INLINE void +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_dec)(ethr_atomic32_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + ETHR_NATMC32_FUNC__(dec)(var); +#else + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, --(*var)); +#endif +} + +static ETHR_INLINE ethr_sint32_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_inc_read)(ethr_atomic32_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint32_t) ETHR_NATMC32_FUNC__(inc_return)(var); +#else + ethr_sint32_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = ++(*var)); + return res; +#endif +} + +static ETHR_INLINE ethr_sint32_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_dec_read)(ethr_atomic32_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint32_t) ETHR_NATMC32_FUNC__(dec_return)(var); +#else + ethr_sint32_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = --(*var)); + return res; +#endif +} + +static ETHR_INLINE ethr_sint32_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_read_band)(ethr_atomic32_t *var, + ethr_sint32_t mask) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint32_t) + ETHR_NATMC32_FUNC__(and_retold)(var, (ETHR_NAINT32_T__) mask); +#else + ethr_sint32_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = *var; *var &= mask); + return res; +#endif +} + +static ETHR_INLINE ethr_sint32_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_read_bor)(ethr_atomic32_t *var, + ethr_sint32_t mask) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return + (ethr_sint32_t) ETHR_NATMC32_FUNC__(or_retold)(var, + (ETHR_NAINT32_T__) mask); +#else + ethr_sint32_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = *var; *var |= mask); + return res; +#endif +} + +static ETHR_INLINE ethr_sint32_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_xchg)(ethr_atomic32_t *var, + ethr_sint32_t new) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint32_t) ETHR_NATMC32_FUNC__(xchg)(var, + (ETHR_NAINT32_T__) new); +#else + ethr_sint32_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = *var; *var = new); + return res; +#endif +} + +static ETHR_INLINE ethr_sint32_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_cmpxchg)(ethr_atomic32_t *var, + ethr_sint32_t new, + ethr_sint32_t exp) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint32_t) ETHR_NATMC32_FUNC__(cmpxchg)(var, + (ETHR_NAINT32_T__) new, + (ETHR_NAINT32_T__) exp); +#else + ethr_sint32_t res; + ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, + { + res = *var; + if (__builtin_expect(res == exp, 1)) + *var = new; + }); + return res; +#endif +} + +/* + * Important memory barrier requirements. + * + * The following atomic operations *must* supply a memory barrier of + * at least the type specified by its suffix: + * _acqb = acquire barrier + * _relb = release barrier + */ + +static ETHR_INLINE ethr_sint32_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_read_acqb)(ethr_atomic32_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint32_t) ETHR_NATMC32_FUNC__(read_acqb)(var); +#else + return ETHR_INLINE_FUNC_NAME_(ethr_atomic32_read)(var); +#endif +} + +static ETHR_INLINE ethr_sint32_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_inc_read_acqb)(ethr_atomic32_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint32_t) ETHR_NATMC32_FUNC__(inc_return_acqb)(var); +#else + return ETHR_INLINE_FUNC_NAME_(ethr_atomic32_inc_read)(var); +#endif +} + +static ETHR_INLINE void +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_set_relb)(ethr_atomic32_t *var, + ethr_sint32_t val) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + ETHR_NATMC32_FUNC__(set_relb)(var, (ETHR_NAINT32_T__) val); +#else + ETHR_INLINE_FUNC_NAME_(ethr_atomic32_set)(var, val); +#endif +} + +static ETHR_INLINE void +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_dec_relb)(ethr_atomic32_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + ETHR_NATMC32_FUNC__(dec_relb)(var); +#else + ETHR_INLINE_FUNC_NAME_(ethr_atomic32_dec)(var); +#endif +} + +static ETHR_INLINE ethr_sint32_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_dec_read_relb)(ethr_atomic32_t *var) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint32_t) ETHR_NATMC32_FUNC__(dec_return_relb)(var); +#else + return ETHR_INLINE_FUNC_NAME_(ethr_atomic32_dec_read)(var); +#endif +} + +static ETHR_INLINE ethr_sint32_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_cmpxchg_acqb)(ethr_atomic32_t *var, + ethr_sint32_t new, + ethr_sint32_t exp) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint32_t) + ETHR_NATMC32_FUNC__(cmpxchg_acqb)(var, + (ETHR_NAINT32_T__) new, + (ETHR_NAINT32_T__) exp); +#else + return ETHR_INLINE_FUNC_NAME_(ethr_atomic32_cmpxchg)(var, new, exp); +#endif +} + +static ETHR_INLINE ethr_sint32_t +ETHR_INLINE_FUNC_NAME_(ethr_atomic32_cmpxchg_relb)(ethr_atomic32_t *var, + ethr_sint32_t new, + ethr_sint32_t exp) +{ +#ifdef ETHR_HAVE_NATIVE_ATOMICS + return (ethr_sint32_t) + ETHR_NATMC32_FUNC__(cmpxchg_relb)(var, + (ETHR_NAINT32_T__) new, + (ETHR_NAINT32_T__) exp); +#else + return ETHR_INLINE_FUNC_NAME_(ethr_atomic32_cmpxchg)(var, new, exp); +#endif +} + + +#endif /* ETHR_TRY_INLINE_FUNCS */ + +#undef ETHR_NAINT_T__ +#undef ETHR_NATMC_FUNC__ +#undef ETHR_NATMC_ADDR_FUNC__ + +#undef ETHR_NAINT32_T__ +#undef ETHR_NATMC32_FUNC__ + +#endif diff --git a/erts/include/internal/ethr_mutex.h b/erts/include/internal/ethr_mutex.h index 01855864e3..fadaf1e2a4 100644 --- a/erts/include/internal/ethr_mutex.h +++ b/erts/include/internal/ethr_mutex.h @@ -78,13 +78,13 @@ # error Need a qlock implementation #endif -#define ETHR_RWMTX_W_FLG__ (((long) 1) << 31) -#define ETHR_RWMTX_W_WAIT_FLG__ (((long) 1) << 30) -#define ETHR_RWMTX_R_WAIT_FLG__ (((long) 1) << 29) +#define ETHR_RWMTX_W_FLG__ (((ethr_sint32_t) 1) << 31) +#define ETHR_RWMTX_W_WAIT_FLG__ (((ethr_sint32_t) 1) << 30) +#define ETHR_RWMTX_R_WAIT_FLG__ (((ethr_sint32_t) 1) << 29) /* frequent read kind */ -#define ETHR_RWMTX_R_FLG__ (((long) 1) << 28) -#define ETHR_RWMTX_R_ABRT_UNLCK_FLG__ (((long) 1) << 27) +#define ETHR_RWMTX_R_FLG__ (((ethr_sint32_t) 1) << 28) +#define ETHR_RWMTX_R_ABRT_UNLCK_FLG__ (((ethr_sint32_t) 1) << 27) #define ETHR_RWMTX_R_PEND_UNLCK_MASK__ (ETHR_RWMTX_R_ABRT_UNLCK_FLG__ - 1) /* normal kind */ @@ -106,28 +106,28 @@ #endif #define ETHR_MTX_DBG_CHK_UNUSED_FLG_BITS(MTX) \ - ETHR_DBG_CHK_UNUSED_FLG_BITS(ethr_atomic_read(&(MTX)->mtxb.flgs)) + ETHR_DBG_CHK_UNUSED_FLG_BITS(ethr_atomic32_read(&(MTX)->mtxb.flgs)) struct ethr_mutex_base_ { #ifdef ETHR_MTX_HARD_DEBUG_FENCE long pre_fence; #endif - ethr_atomic_t flgs; - ETHR_MTX_QLOCK_TYPE__ qlck; - ethr_ts_event *q; + ethr_atomic32_t flgs; short aux_scnt; short main_scnt; + ETHR_MTX_QLOCK_TYPE__ qlck; + ethr_ts_event *q; #ifdef ETHR_MTX_HARD_DEBUG_WSQ int ws; #endif #ifdef ETHR_MTX_CHK_EXCL - ethr_atomic_t exclusive; + ethr_atomic32_t exclusive; #endif #ifdef ETHR_MTX_CHK_NON_EXCL - ethr_atomic_t non_exclusive; + ethr_atomic32_t non_exclusive; #endif #ifdef ETHR_MTX_HARD_DEBUG_LFS - ethr_atomic_t hdbg_lfs; + ethr_atomic32_t hdbg_lfs; #endif }; @@ -236,7 +236,7 @@ typedef struct { typedef union { struct { - ethr_atomic_t readers; + ethr_atomic32_t readers; int waiting_readers; int byte_offset; ethr_rwmutex_lived lived; @@ -298,13 +298,13 @@ void ethr_rwmutex_rwunlock(ethr_rwmutex *); #ifdef ETHR_MTX_HARD_DEBUG_LFS # define ETHR_MTX_HARD_DEBUG_LFS_INIT(MTXB) \ do { \ - ethr_atomic_init(&(MTXB)->hdbg_lfs, 0); \ + ethr_atomic32_init(&(MTXB)->hdbg_lfs, 0); \ } while (0) # define ETHR_MTX_HARD_DEBUG_LFS_RLOCK(MTXB) \ do { \ - long val__; \ + ethr_sint32_t val__; \ ETHR_COMPILER_BARRIER; \ - val__ = ethr_atomic_inc_read(&(MTXB)->hdbg_lfs); \ + val__ = ethr_atomic32_inc_read(&(MTXB)->hdbg_lfs); \ ETHR_MTX_HARD_ASSERT(val__ > 0); \ } while (0) # define ETHR_MTX_HARD_DEBUG_LFS_TRYRLOCK(MTXB, RES) \ @@ -317,15 +317,15 @@ do { \ } while (0) # define ETHR_MTX_HARD_DEBUG_LFS_RUNLOCK(MTXB) \ do { \ - long val__ = ethr_atomic_dec_read(&(MTXB)->hdbg_lfs); \ + ethr_sint32_t val__ = ethr_atomic32_dec_read(&(MTXB)->hdbg_lfs); \ ETHR_MTX_HARD_ASSERT(val__ >= 0); \ ETHR_COMPILER_BARRIER; \ } while (0) # define ETHR_MTX_HARD_DEBUG_LFS_RWLOCK(MTXB) \ do { \ - long val__; \ + ethr_sint32_t val__; \ ETHR_COMPILER_BARRIER; \ - val__ = ethr_atomic_dec_read(&(MTXB)->hdbg_lfs); \ + val__ = ethr_atomic32_dec_read(&(MTXB)->hdbg_lfs); \ ETHR_MTX_HARD_ASSERT(val__ == -1); \ } while (0) # define ETHR_MTX_HARD_DEBUG_LFS_TRYRWLOCK(MTXB, RES) \ @@ -338,7 +338,7 @@ do { \ } while (0) # define ETHR_MTX_HARD_DEBUG_LFS_RWUNLOCK(MTXB) \ do { \ - long val__ = ethr_atomic_inctest(&(MTXB)->hdbg_lfs); \ + ethr_sint32_t val__ = ethr_atomic32_inctest(&(MTXB)->hdbg_lfs); \ ETHR_MTX_HARD_ASSERT(val__ == 0); \ ETHR_COMPILER_BARRIER; \ } while (0) @@ -386,12 +386,12 @@ do { \ #endif # define ETHR_MTX_CHK_EXCL_INIT__(MTXB) \ - ethr_atomic_init(&(MTXB)->exclusive, 0) + ethr_atomic32_init(&(MTXB)->exclusive, 0) # define ETHR_MTX_CHK_EXCL_IS_EXCL(MTXB) \ do { \ ETHR_COMPILER_BARRIER; \ - if (!ethr_atomic_read(&(MTXB)->exclusive)) \ + if (!ethr_atomic32_read(&(MTXB)->exclusive)) \ ethr_assert_failed(__FILE__, __LINE__, __func__,\ "is exclusive"); \ ETHR_COMPILER_BARRIER; \ @@ -399,7 +399,7 @@ do { \ # define ETHR_MTX_CHK_EXCL_IS_NOT_EXCL(MTXB) \ do { \ ETHR_COMPILER_BARRIER; \ - if (ethr_atomic_read(&(MTXB)->exclusive)) \ + if (ethr_atomic32_read(&(MTXB)->exclusive)) \ ethr_assert_failed(__FILE__, __LINE__, __func__,\ "is not exclusive"); \ ETHR_COMPILER_BARRIER; \ @@ -407,13 +407,13 @@ do { \ # define ETHR_MTX_CHK_EXCL_SET_EXCL(MTXB) \ do { \ ETHR_MTX_CHK_EXCL_IS_NOT_EXCL((MTXB)); \ - ethr_atomic_set(&(MTXB)->exclusive, 1); \ + ethr_atomic32_set(&(MTXB)->exclusive, 1); \ ETHR_COMPILER_BARRIER; \ } while (0) # define ETHR_MTX_CHK_EXCL_UNSET_EXCL(MTXB) \ do { \ ETHR_MTX_CHK_EXCL_IS_EXCL((MTXB)); \ - ethr_atomic_set(&(MTXB)->exclusive, 0); \ + ethr_atomic32_set(&(MTXB)->exclusive, 0); \ ETHR_COMPILER_BARRIER; \ } while (0) @@ -424,11 +424,11 @@ do { \ #endif # define ETHR_MTX_CHK_NON_EXCL_INIT__(MTXB) \ - ethr_atomic_init(&(MTXB)->non_exclusive, 0) + ethr_atomic32_init(&(MTXB)->non_exclusive, 0) # define ETHR_MTX_CHK_EXCL_IS_NON_EXCL(MTXB) \ do { \ ETHR_COMPILER_BARRIER; \ - if (!ethr_atomic_read(&(MTXB)->non_exclusive)) \ + if (!ethr_atomic32_read(&(MTXB)->non_exclusive)) \ ethr_assert_failed(__FILE__, __LINE__, __func__,\ "is non-exclusive"); \ ETHR_COMPILER_BARRIER; \ @@ -436,7 +436,7 @@ do { \ # define ETHR_MTX_CHK_EXCL_IS_NOT_NON_EXCL(MTXB) \ do { \ ETHR_COMPILER_BARRIER; \ - if (ethr_atomic_read(&(MTXB)->non_exclusive)) \ + if (ethr_atomic32_read(&(MTXB)->non_exclusive)) \ ethr_assert_failed(__FILE__, __LINE__, __func__,\ "is not non-exclusive"); \ ETHR_COMPILER_BARRIER; \ @@ -444,19 +444,19 @@ do { \ # define ETHR_MTX_CHK_EXCL_SET_NON_EXCL(MTXB) \ do { \ ETHR_COMPILER_BARRIER; \ - ethr_atomic_inc(&(MTXB)->non_exclusive); \ + ethr_atomic32_inc(&(MTXB)->non_exclusive); \ ETHR_COMPILER_BARRIER; \ } while (0) # define ETHR_MTX_CHK_EXCL_SET_NON_EXCL_NO(MTXB, NO) \ do { \ ETHR_COMPILER_BARRIER; \ - ethr_atomic_add(&(MTXB)->non_exclusive, (NO)); \ + ethr_atomic32_add(&(MTXB)->non_exclusive, (NO)); \ ETHR_COMPILER_BARRIER; \ } while (0) # define ETHR_MTX_CHK_EXCL_UNSET_NON_EXCL(MTXB) \ do { \ ETHR_COMPILER_BARRIER; \ - ethr_atomic_dec(&(MTXB)->non_exclusive); \ + ethr_atomic32_dec(&(MTXB)->non_exclusive); \ ETHR_COMPILER_BARRIER; \ } while (0) #else @@ -501,18 +501,18 @@ do { \ #if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_MUTEX_IMPL__) -void ethr_mutex_lock_wait__(ethr_mutex *, long); -void ethr_mutex_unlock_wake__(ethr_mutex *, long); +void ethr_mutex_lock_wait__(ethr_mutex *, ethr_sint32_t); +void ethr_mutex_unlock_wake__(ethr_mutex *, ethr_sint32_t); static ETHR_INLINE int ETHR_INLINE_FUNC_NAME_(ethr_mutex_trylock)(ethr_mutex *mtx) { - long act; + ethr_sint32_t act; int res; ETHR_MTX_HARD_DEBUG_FENCE_CHK(mtx); ETHR_MTX_DBG_CHK_UNUSED_FLG_BITS(mtx); - act = ethr_atomic_cmpxchg_acqb(&mtx->mtxb.flgs, ETHR_RWMTX_W_FLG__, 0); + act = ethr_atomic32_cmpxchg_acqb(&mtx->mtxb.flgs, ETHR_RWMTX_W_FLG__, 0); res = (act == 0) ? 0 : EBUSY; #ifdef ETHR_MTX_CHK_EXCL @@ -531,11 +531,11 @@ ETHR_INLINE_FUNC_NAME_(ethr_mutex_trylock)(ethr_mutex *mtx) static ETHR_INLINE void ETHR_INLINE_FUNC_NAME_(ethr_mutex_lock)(ethr_mutex *mtx) { - long act; + ethr_sint32_t act; ETHR_MTX_HARD_DEBUG_FENCE_CHK(mtx); ETHR_MTX_DBG_CHK_UNUSED_FLG_BITS(mtx); - act = ethr_atomic_cmpxchg_acqb(&mtx->mtxb.flgs, ETHR_RWMTX_W_FLG__, 0); + act = ethr_atomic32_cmpxchg_acqb(&mtx->mtxb.flgs, ETHR_RWMTX_W_FLG__, 0); if (act != 0) ethr_mutex_lock_wait__(mtx, act); @@ -551,7 +551,7 @@ ETHR_INLINE_FUNC_NAME_(ethr_mutex_lock)(ethr_mutex *mtx) static ETHR_INLINE void ETHR_INLINE_FUNC_NAME_(ethr_mutex_unlock)(ethr_mutex *mtx) { - long act; + ethr_sint32_t act; ETHR_COMPILER_BARRIER; ETHR_MTX_HARD_DEBUG_FENCE_CHK(mtx); ETHR_MTX_HARD_DEBUG_LFS_RWUNLOCK(&mtx->mtxb); @@ -559,7 +559,7 @@ ETHR_INLINE_FUNC_NAME_(ethr_mutex_unlock)(ethr_mutex *mtx) ETHR_MTX_CHK_EXCL_UNSET_EXCL(&mtx->mtxb); - act = ethr_atomic_cmpxchg_relb(&mtx->mtxb.flgs, 0, ETHR_RWMTX_W_FLG__); + act = ethr_atomic32_cmpxchg_relb(&mtx->mtxb.flgs, 0, ETHR_RWMTX_W_FLG__); if (act != ETHR_RWMTX_W_FLG__) ethr_mutex_unlock_wake__(mtx, act); diff --git a/erts/include/internal/ethr_optimized_fallbacks.h b/erts/include/internal/ethr_optimized_fallbacks.h index 2f9f987d0b..8e04692856 100644 --- a/erts/include/internal/ethr_optimized_fallbacks.h +++ b/erts/include/internal/ethr_optimized_fallbacks.h @@ -71,36 +71,46 @@ ethr_opt_spin_lock(ethr_opt_spinlock_t *lock) #define ETHR_HAVE_NATIVE_SPINLOCKS 1 #define ETHR_HAVE_OPTIMIZED_SPINLOCKS 1 -typedef ethr_native_atomic_t ethr_native_spinlock_t; +#if defined(ETHR_HAVE_NATIVE_ATOMIC32) +typedef ethr_native_atomic32_t ethr_native_spinlock_t; +# define ETHR_NATMC_FUNC__(X) ethr_native_atomic32_ ## X +#elif defined(ETHR_HAVE_NATIVE_ATOMIC64) +typedef ethr_native_atomic64_t ethr_native_spinlock_t; +# define ETHR_NATMC_FUNC__(X) ethr_native_atomic64_ ## X +#else +# error "Missing native atomic implementation" +#endif #if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_AUX_IMPL__) static ETHR_INLINE void ethr_native_spinlock_init(ethr_native_spinlock_t *lock) { - ethr_native_atomic_init((ethr_native_atomic_t *) lock, 0); + ETHR_NATMC_FUNC__(init)(lock, 0); } static ETHR_INLINE void ethr_native_spin_unlock(ethr_native_spinlock_t *lock) { ETHR_COMPILER_BARRIER; - ETHR_ASSERT(ethr_native_atomic_read((ethr_native_atomic_t *) lock) == 1); - ethr_native_atomic_set_relb((ethr_native_atomic_t *) lock, 0); + ETHR_ASSERT(ETHR_NATMC_FUNC__(read)(lock) == 1); + ETHR_NATMC_FUNC__(set_relb)(lock, 0); } static ETHR_INLINE void ethr_native_spin_lock(ethr_native_spinlock_t *lock) { - while (ethr_native_atomic_cmpxchg_acqb((ethr_native_atomic_t *) lock, - (long) 1, (long) 0) != 0) { - ETHR_SPIN_BODY; + while (ETHR_NATMC_FUNC__(cmpxchg_acqb)(lock, 1, 0) != 0) { + while (ETHR_NATMC_FUNC__(read)(lock) != 0) + ETHR_SPIN_BODY; } ETHR_COMPILER_BARRIER; } #endif +#undef ETHR_NATMC_FUNC__ + #endif @@ -111,16 +121,26 @@ ethr_native_spin_lock(ethr_native_spinlock_t *lock) #define ETHR_HAVE_NATIVE_RWSPINLOCKS 1 #define ETHR_HAVE_OPTIMIZED_RWSPINLOCKS 1 -typedef ethr_native_atomic_t ethr_native_rwlock_t; +#if defined(ETHR_HAVE_NATIVE_ATOMIC32) +typedef ethr_native_atomic32_t ethr_native_rwlock_t; +# define ETHR_NAINT_T__ ethr_sint32_t +# define ETHR_WLOCK_FLAG__ (((ethr_sint32_t) 1) << 30) +# define ETHR_NATMC_FUNC__(X) ethr_native_atomic32_ ## X +#elif defined(ETHR_HAVE_NATIVE_ATOMIC64) +typedef ethr_native_atomic64_t ethr_native_rwlock_t; +# define ETHR_NAINT_T__ ethr_sint64_t +# define ETHR_WLOCK_FLAG__ (((ethr_sint64_t) 1) << 62) +# define ETHR_NATMC_FUNC__(X) ethr_native_atomic64_ ## X +#else +# error "Missing native atomic implementation" +#endif #if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_AUX_IMPL__) -#define ETHR_WLOCK_FLAG__ (((long) 1) << 30) - static ETHR_INLINE void ethr_native_rwlock_init(ethr_native_rwlock_t *lock) { - ethr_native_atomic_init((ethr_native_atomic_t *) lock, 0); + ETHR_NATMC_FUNC__(init)(lock, 0); } static ETHR_INLINE void @@ -128,22 +148,24 @@ ethr_native_read_unlock(ethr_native_rwlock_t *lock) { ETHR_COMPILER_BARRIER; #ifdef DEBUG - ETHR_ASSERT(ethr_native_atomic_read((ethr_native_atomic_t *) lock) >= 0); + ETHR_ASSERT(ETHR_NATMC_FUNC__(read)(lock) >= 0); #endif - ethr_native_atomic_dec_relb((ethr_native_atomic_t *) lock); + ETHR_NATMC_FUNC__(dec_relb)(lock); } static ETHR_INLINE void ethr_native_read_lock(ethr_native_rwlock_t *lock) { - long act, exp = 0; + ETHR_NAINT_T__ act, exp = 0; while (1) { - act = ethr_native_atomic_cmpxchg_acqb((ethr_native_atomic_t *) lock, - exp+1, exp); + act = ETHR_NATMC_FUNC__(cmpxchg_acqb)(lock, exp+1, exp); if (act == exp) break; - ETHR_SPIN_BODY; - exp = (act & ETHR_WLOCK_FLAG__) ? 0 : act; + while (act & ETHR_WLOCK_FLAG__) { + ETHR_SPIN_BODY; + act = ETHR_NATMC_FUNC__(read)(lock); + } + exp = act; } ETHR_COMPILER_BARRIER; } @@ -152,18 +174,16 @@ static ETHR_INLINE void ethr_native_write_unlock(ethr_native_rwlock_t *lock) { ETHR_COMPILER_BARRIER; - ETHR_ASSERT(ethr_native_atomic_read((ethr_native_atomic_t *) lock) - == ETHR_WLOCK_FLAG__); - ethr_native_atomic_set_relb((ethr_native_atomic_t *) lock, 0); + ETHR_ASSERT(ETHR_NATMC_FUNC__(read)(lock) == ETHR_WLOCK_FLAG__); + ETHR_NATMC_FUNC__(set_relb)(lock, 0); } static ETHR_INLINE void ethr_native_write_lock(ethr_native_rwlock_t *lock) { - long act, exp = 0; + ETHR_NAINT_T__ act, exp = 0; while (1) { - act = ethr_native_atomic_cmpxchg_acqb((ethr_native_atomic_t *) lock, - exp|ETHR_WLOCK_FLAG__, exp); + act = ETHR_NATMC_FUNC__(cmpxchg_acqb)(lock, exp|ETHR_WLOCK_FLAG__, exp); if (act == exp) break; ETHR_SPIN_BODY; @@ -173,13 +193,17 @@ ethr_native_write_lock(ethr_native_rwlock_t *lock) /* Wait for readers to leave */ while (act != ETHR_WLOCK_FLAG__) { ETHR_SPIN_BODY; - act = ethr_native_atomic_read_acqb((ethr_native_atomic_t *) lock); + act = ETHR_NATMC_FUNC__(read_acqb)(lock); } ETHR_COMPILER_BARRIER; } #endif +#undef ETHR_NAINT_T__ +#undef ETHR_NATMC_FUNC__ +#undef ETHR_WLOCK_FLAG__ + #endif #endif diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 53fa1acdc2..4cd95faf6a 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -37,11 +37,6 @@ #undef ETHR_HAVE_OPTIMIZED_SPINLOCK #undef ETHR_HAVE_OPTIMIZED_RWSPINLOCK -typedef struct { - long tv_sec; - long tv_nsec; -} ethr_timeval; - #if defined(DEBUG) # define ETHR_DEBUG #endif @@ -73,7 +68,7 @@ typedef struct { #endif /* Assume 64-byte cache line size */ -#define ETHR_CACHE_LINE_SIZE 64L +#define ETHR_CACHE_LINE_SIZE ((ethr_uint_t) 64) #define ETHR_CACHE_LINE_MASK (ETHR_CACHE_LINE_SIZE - 1) #define ETHR_CACHE_LINE_ALIGN_SIZE(SZ) \ @@ -171,6 +166,22 @@ typedef pthread_key_t ethr_tsd_key; # undef WIN32_LEAN_AND_MEAN #endif +#if defined(_MSC_VER) + +#if ETHR_SIZEOF_LONG == 4 +#define ETHR_HAVE_INT32_T 1 +typedef long ethr_sint32_t; +typedef unsigned long ethr_uint32_t; +#endif + +#if ETHR_SIZEOF___INT64 == 8 +#define ETHR_HAVE_INT64_T 1 +typedef __int64 ethr_sint64_t; +typedef unsigned __int64 ethr_uint64_t; +#endif + +#endif + struct ethr_join_data_; /* Types */ @@ -198,12 +209,48 @@ typedef DWORD ethr_tsd_key; #endif -#ifdef SIZEOF_LONG -#if SIZEOF_LONG < ETHR_SIZEOF_PTR -#error size of long currently needs to be at least the same as size of void * +#ifndef ETHR_HAVE_INT32_T +#if ETHR_SIZEOF_INT == 4 +#define ETHR_HAVE_INT32_T 1 +typedef int ethr_sint32_t; +typedef unsigned int ethr_uint32_t; +#elif ETHR_SIZEOF_LONG == 4 +#define ETHR_HAVE_INT32_T 1 +typedef long ethr_sint32_t; +typedef unsigned long ethr_uint32_t; #endif #endif +#ifndef ETHR_HAVE_INT64_T +#if ETHR_SIZEOF_INT == 8 +#define ETHR_HAVE_INT64_T 1 +typedef int ethr_sint64_t; +typedef unsigned int ethr_uint64_t; +#elif ETHR_SIZEOF_LONG == 8 +#define ETHR_HAVE_INT64_T 1 +typedef long ethr_sint64_t; +typedef unsigned long ethr_uint64_t; +#elif ETHR_SIZEOF_LONG_LONG == 8 +#define ETHR_HAVE_INT64_T 1 +typedef long long ethr_sint64_t; +typedef unsigned long long ethr_uint64_t; +#endif +#endif + +#if ETHR_SIZEOF_PTR == 4 +#ifndef ETHR_HAVE_INT32_T +#error "No 32-bit integer type found" +#endif +typedef ethr_sint32_t ethr_sint_t; +typedef ethr_uint32_t ethr_uint_t; +#elif ETHR_SIZEOF_PTR == 8 +#ifndef ETHR_HAVE_INT64_T +#error "No 64-bit integer type found" +#endif +typedef ethr_sint64_t ethr_sint_t; +typedef ethr_uint64_t ethr_uint_t; +#endif + /* __builtin_expect() is needed by both native atomics code * and the fallback code */ #if !defined(__GNUC__) || (__GNUC__ < 2) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96) @@ -386,7 +433,6 @@ typedef struct { #if !defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_AUX_IMPL__) # define ETHR_NEED_SPINLOCK_PROTOTYPES__ # define ETHR_NEED_RWSPINLOCK_PROTOTYPES__ -# define ETHR_NEED_ATOMIC_PROTOTYPES__ #endif int ethr_init(ethr_init_data *); @@ -399,7 +445,6 @@ void ethr_thr_exit(void *); ethr_tid ethr_self(void); int ethr_equal_tids(ethr_tid, ethr_tid); -int ethr_time_now(ethr_timeval *); int ethr_tsd_key_create(ethr_tsd_key *); int ethr_tsd_key_delete(ethr_tsd_key); int ethr_tsd_set(ethr_tsd_key, void *); @@ -502,312 +547,7 @@ ETHR_INLINE_FUNC_NAME_(ethr_spin_lock)(ethr_spinlock_t *lock) #endif /* ETHR_TRY_INLINE_FUNCS */ -#ifdef ETHR_HAVE_NATIVE_ATOMICS -/* - * Map ethread native atomics to ethread API atomics. - */ -typedef ethr_native_atomic_t ethr_atomic_t; -#else -typedef long ethr_atomic_t; -#endif - -#ifdef ETHR_NEED_ATOMIC_PROTOTYPES__ -void ethr_atomic_init(ethr_atomic_t *, long); -void ethr_atomic_set(ethr_atomic_t *, long); -long ethr_atomic_read(ethr_atomic_t *); -long ethr_atomic_inc_read(ethr_atomic_t *); -long ethr_atomic_dec_read(ethr_atomic_t *); -void ethr_atomic_inc(ethr_atomic_t *); -void ethr_atomic_dec(ethr_atomic_t *); -long ethr_atomic_add_read(ethr_atomic_t *, long); -void ethr_atomic_add(ethr_atomic_t *, long); -long ethr_atomic_read_band(ethr_atomic_t *, long); -long ethr_atomic_read_bor(ethr_atomic_t *, long); -long ethr_atomic_xchg(ethr_atomic_t *, long); -long ethr_atomic_cmpxchg(ethr_atomic_t *, long, long); -long ethr_atomic_read_acqb(ethr_atomic_t *); -long ethr_atomic_inc_read_acqb(ethr_atomic_t *); -void ethr_atomic_set_relb(ethr_atomic_t *, long); -void ethr_atomic_dec_relb(ethr_atomic_t *); -long ethr_atomic_dec_read_relb(ethr_atomic_t *); -long ethr_atomic_cmpxchg_acqb(ethr_atomic_t *, long, long); -long ethr_atomic_cmpxchg_relb(ethr_atomic_t *, long, long); -#endif - -#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_AUX_IMPL__) - -#ifndef ETHR_HAVE_NATIVE_ATOMICS -/* - * Fallbacks for atomics used in absence of a native implementation. - */ - -#define ETHR_ATOMIC_ADDR_BITS 10 -#define ETHR_ATOMIC_ADDR_SHIFT 6 - -typedef struct { - union { - ethr_spinlock_t lck; - char buf[ETHR_CACHE_LINE_SIZE]; - } u; -} ethr_atomic_protection_t; - -extern ethr_atomic_protection_t ethr_atomic_protection__[1 << ETHR_ATOMIC_ADDR_BITS]; - -#define ETHR_ATOMIC_PTR2LCK__(PTR) \ -(ðr_atomic_protection__[((((unsigned long) (PTR)) >> ETHR_ATOMIC_ADDR_SHIFT) \ - & ((1 << ETHR_ATOMIC_ADDR_BITS) - 1))].u.lck) - - -#define ETHR_ATOMIC_OP_FALLBACK_IMPL__(AP, EXPS) \ -do { \ - ethr_spinlock_t *slp__ = ETHR_ATOMIC_PTR2LCK__((AP)); \ - ethr_spin_lock(slp__); \ - { EXPS; } \ - ethr_spin_unlock(slp__); \ -} while (0) - -#endif - -static ETHR_INLINE void -ETHR_INLINE_FUNC_NAME_(ethr_atomic_init)(ethr_atomic_t *var, long i) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - ethr_native_atomic_init(var, i); -#else - ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, *var = i); -#endif -} - -static ETHR_INLINE void -ETHR_INLINE_FUNC_NAME_(ethr_atomic_set)(ethr_atomic_t *var, long i) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - ethr_native_atomic_set(var, i); -#else - ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, *var = i); -#endif -} - -static ETHR_INLINE long -ETHR_INLINE_FUNC_NAME_(ethr_atomic_read)(ethr_atomic_t *var) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - return ethr_native_atomic_read(var); -#else - long res; - ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = (long) *var); - return res; -#endif -} - -static ETHR_INLINE void -ETHR_INLINE_FUNC_NAME_(ethr_atomic_add)(ethr_atomic_t *var, long incr) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - ethr_native_atomic_add(var, incr); -#else - ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, *var += incr); -#endif -} - -static ETHR_INLINE long -ETHR_INLINE_FUNC_NAME_(ethr_atomic_add_read)(ethr_atomic_t *var, long i) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - return ethr_native_atomic_add_return(var, i); -#else - long res; - ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, *var += i; res = *var); - return res; -#endif -} - -static ETHR_INLINE void -ETHR_INLINE_FUNC_NAME_(ethr_atomic_inc)(ethr_atomic_t *var) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - ethr_native_atomic_inc(var); -#else - ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, ++(*var)); -#endif -} - -static ETHR_INLINE void -ETHR_INLINE_FUNC_NAME_(ethr_atomic_dec)(ethr_atomic_t *var) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - ethr_native_atomic_dec(var); -#else - ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, --(*var)); -#endif -} - -static ETHR_INLINE long -ETHR_INLINE_FUNC_NAME_(ethr_atomic_inc_read)(ethr_atomic_t *var) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - return ethr_native_atomic_inc_return(var); -#else - long res; - ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = (long) ++(*var)); - return res; -#endif -} - -static ETHR_INLINE long -ETHR_INLINE_FUNC_NAME_(ethr_atomic_dec_read)(ethr_atomic_t *var) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - return ethr_native_atomic_dec_return(var); -#else - long res; - ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = (long) --(*var)); - return res; -#endif -} - -static ETHR_INLINE long -ETHR_INLINE_FUNC_NAME_(ethr_atomic_read_band)(ethr_atomic_t *var, - long mask) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - return ethr_native_atomic_and_retold(var, mask); -#else - long res; - ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = *var; *var &= mask); - return res; -#endif -} - -static ETHR_INLINE long -ETHR_INLINE_FUNC_NAME_(ethr_atomic_read_bor)(ethr_atomic_t *var, - long mask) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - return ethr_native_atomic_or_retold(var, mask); -#else - long res; - ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = *var; *var |= mask); - return res; -#endif -} - -static ETHR_INLINE long -ETHR_INLINE_FUNC_NAME_(ethr_atomic_xchg)(ethr_atomic_t *var, - long new) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - return ethr_native_atomic_xchg(var, new); -#else - long res; - ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, res = *var; *var = new); - return res; -#endif -} - -static ETHR_INLINE long -ETHR_INLINE_FUNC_NAME_(ethr_atomic_cmpxchg)(ethr_atomic_t *var, - long new, - long exp) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - return ethr_native_atomic_cmpxchg(var, new, exp); -#else - long res; - ETHR_ATOMIC_OP_FALLBACK_IMPL__(var, - { - res = *var; - if (__builtin_expect(res == exp, 1)) - *var = new; - }); - return res; -#endif -} - -/* - * Important memory barrier requirements. - * - * The following atomic operations *must* supply a memory barrier of - * at least the type specified by its suffix: - * _acqb = acquire barrier - * _relb = release barrier - */ - -static ETHR_INLINE long -ETHR_INLINE_FUNC_NAME_(ethr_atomic_read_acqb)(ethr_atomic_t *var) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - return ethr_native_atomic_read_acqb(var); -#else - return ETHR_INLINE_FUNC_NAME_(ethr_atomic_read)(var); -#endif -} - -static ETHR_INLINE long -ETHR_INLINE_FUNC_NAME_(ethr_atomic_inc_read_acqb)(ethr_atomic_t *var) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - return ethr_native_atomic_inc_return_acqb(var); -#else - return ETHR_INLINE_FUNC_NAME_(ethr_atomic_inc_read)(var); -#endif -} - -static ETHR_INLINE void -ETHR_INLINE_FUNC_NAME_(ethr_atomic_set_relb)(ethr_atomic_t *var, long val) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - ethr_native_atomic_set_relb(var, val); -#else - ETHR_INLINE_FUNC_NAME_(ethr_atomic_set)(var, val); -#endif -} - -static ETHR_INLINE void -ETHR_INLINE_FUNC_NAME_(ethr_atomic_dec_relb)(ethr_atomic_t *var) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - ethr_native_atomic_dec_relb(var); -#else - ETHR_INLINE_FUNC_NAME_(ethr_atomic_dec)(var); -#endif -} - -static ETHR_INLINE long -ETHR_INLINE_FUNC_NAME_(ethr_atomic_dec_read_relb)(ethr_atomic_t *var) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - return ethr_native_atomic_dec_return_relb(var); -#else - return ETHR_INLINE_FUNC_NAME_(ethr_atomic_dec_read)(var); -#endif -} - -static ETHR_INLINE long -ETHR_INLINE_FUNC_NAME_(ethr_atomic_cmpxchg_acqb)(ethr_atomic_t *var, - long new, - long exp) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - return ethr_native_atomic_cmpxchg_acqb(var, new, exp); -#else - return ETHR_INLINE_FUNC_NAME_(ethr_atomic_cmpxchg)(var, new, exp); -#endif -} - -static ETHR_INLINE long -ETHR_INLINE_FUNC_NAME_(ethr_atomic_cmpxchg_relb)(ethr_atomic_t *var, - long new, - long exp) -{ -#ifdef ETHR_HAVE_NATIVE_ATOMICS - return ethr_native_atomic_cmpxchg_relb(var, new, exp); -#else - return ETHR_INLINE_FUNC_NAME_(ethr_atomic_cmpxchg)(var, new, exp); -#endif -} - -#endif /* ETHR_TRY_INLINE_FUNCS */ +#include "ethr_atomics.h" typedef struct ethr_ts_event_ ethr_ts_event; /* Needed by ethr_mutex.h */ @@ -825,7 +565,7 @@ struct ethr_ts_event_ { ethr_ts_event *prev; ethr_event event; void *udata; - ethr_atomic_t uaflgs; + ethr_atomic32_t uaflgs; unsigned uflgs; unsigned iflgs; /* for ethr lib only */ short rgix; /* for ethr lib only */ diff --git a/erts/include/internal/ethread_header_config.h.in b/erts/include/internal/ethread_header_config.h.in index 5debb44756..f394d790d2 100644 --- a/erts/include/internal/ethread_header_config.h.in +++ b/erts/include/internal/ethread_header_config.h.in @@ -20,6 +20,21 @@ /* Define to the size of pointers */ #undef ETHR_SIZEOF_PTR +/* Define to the size of int */ +#undef ETHR_SIZEOF_INT + +/* Define to the size of long */ +#undef ETHR_SIZEOF_LONG + +/* Define to the size of long long */ +#undef ETHR_SIZEOF_LONG_LONG + +/* Define to the size of __int64 */ +#undef ETHR_SIZEOF___INT64 + +/* Define if bigendian */ +#undef ETHR_BIGENDIAN + /* Define if you want to disable native ethread implementations */ #undef ETHR_DISABLE_NATIVE_IMPLS @@ -100,6 +115,27 @@ /* Define to the size of AO_t if libatomic_ops is used */ #undef ETHR_SIZEOF_AO_T +/* Define if you have _InterlockedCompareExchange64() */ +#undef ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE64 + +/* Define if you have _InterlockedDecrement64() */ +#undef ETHR_HAVE__INTERLOCKEDDECREMENT64 + +/* Define if you have _InterlockedIncrement64() */ +#undef ETHR_HAVE__INTERLOCKEDINCREMENT64 + +/* Define if you have _InterlockedExchangeAdd64() */ +#undef ETHR_HAVE__INTERLOCKEDEXCHANGEADD64 + +/* Define if you have _InterlockedExchange64() */ +#undef ETHR_HAVE__INTERLOCKEDEXCHANGE64 + +/* Define if you have _InterlockedAnd64() */ +#undef ETHR_HAVE__INTERLOCKEDAND64 + +/* Define if you have _InterlockedOr64() */ +#undef ETHR_HAVE__INTERLOCKEDOR64 + /* Define if you want to turn on extra sanity checking in the ethread library */ #undef ETHR_XCHK diff --git a/erts/include/internal/gcc/ethr_atomic.h b/erts/include/internal/gcc/ethr_atomic.h index e8e529dd48..16935084b1 100644 --- a/erts/include/internal/gcc/ethr_atomic.h +++ b/erts/include/internal/gcc/ethr_atomic.h @@ -22,24 +22,35 @@ * Author: Rickard Green */ -#ifndef ETHR_GCC_ATOMIC_H__ -#define ETHR_GCC_ATOMIC_H__ +#undef ETHR_INCLUDE_ATOMIC_IMPL__ +#if !defined(ETHR_GCC_ATOMIC32_H__) && defined(ETHR_ATOMIC_WANT_32BIT_IMPL__) +#define ETHR_GCC_ATOMIC32_H__ +#define ETHR_INCLUDE_ATOMIC_IMPL__ 4 +#undef ETHR_ATOMIC_WANT_32BIT_IMPL__ +#elif !defined(ETHR_GCC_ATOMIC64_H__) && defined(ETHR_ATOMIC_WANT_64BIT_IMPL__) +#define ETHR_GCC_ATOMIC64_H__ +#define ETHR_INCLUDE_ATOMIC_IMPL__ 8 +#undef ETHR_ATOMIC_WANT_64BIT_IMPL__ +#endif + +#ifdef ETHR_INCLUDE_ATOMIC_IMPL__ -#if !defined(ETHR_HAVE_NATIVE_ATOMICS) && defined(ETHR_HAVE_GCC_ATOMIC_OPS) -#define ETHR_HAVE_NATIVE_ATOMICS 1 +#ifndef ETHR_GCC_ATOMIC_COMMON__ +#define ETHR_GCC_ATOMIC_COMMON__ -#define ETHR_IMMED_ATOMIC_SET_GET_SAFE__ 0 -/* Enable immediate read/write on platforms where we know it is safe */ +#define ETHR_READ_AND_SET_WITHOUT_SYNC_OP__ 0 #if defined(__i386__) || defined(__x86_64__) || defined(__sparc__) \ || defined(__powerpc__) || defined(__ppc__) || defined(__mips__) -# undef ETHR_IMMED_ATOMIC_SET_GET_SAFE__ -# define ETHR_IMMED_ATOMIC_SET_GET_SAFE__ 1 +# undef ETHR_READ_AND_SET_WITHOUT_SYNC_OP__ +# define ETHR_READ_AND_SET_WITHOUT_SYNC_OP__ 1 #endif -typedef struct { - volatile long counter; -} ethr_native_atomic_t; - +#if defined(__x86_64__) || (defined(__i386__) \ + && !defined(ETHR_PRE_PENTIUM4_COMPAT)) +# define ETHR_READ_ACQB_AND_SET_RELB_COMPILER_BARRIER_ONLY__ 1 +#else +# define ETHR_READ_ACQB_AND_SET_RELB_COMPILER_BARRIER_ONLY__ 0 +#endif /* * According to the documentation this is what we want: @@ -47,34 +58,73 @@ typedef struct { * However, __sync_synchronize() is known to erroneously be * a noop on at least some platforms with some gcc versions. * This has suposedly been fixed in some gcc version, but we - * don't know from which version. Therefore, we use the - * workaround implemented below on all gcc versions except - * for gcc 4.2 or above for MIPS, where it's been verified. + * don't know from which version. Therefore, we only use + * it when it has been verified to work. Otherwise + * we use a workaround. */ #if defined(__mips__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)) +/* __sync_synchronize() has been verified to work here */ #define ETHR_MEMORY_BARRIER __sync_synchronize() +#define ETHR_READ_DEPEND_MEMORY_BARRIER __sync_synchronize() +#elif defined(__x86_64__) || (defined(__i386__) \ + && !defined(ETHR_PRE_PENTIUM4_COMPAT)) +/* Use fence instructions directly instead of workaround */ +#define ETHR_MEMORY_BARRIER __asm__ __volatile__("mfence" : : : "memory") +#define ETHR_WRITE_MEMORY_BARRIER __asm__ __volatile__("sfence" : : : "memory") +#define ETHR_READ_MEMORY_BARRIER __asm__ __volatile__("lfence" : : : "memory") +#define ETHR_READ_DEPEND_MEMORY_BARRIER __asm__ __volatile__("" : : : "memory") #else +/* Workaround */ #define ETHR_MEMORY_BARRIER \ do { \ - volatile long x___ = 0; \ - (void) __sync_val_compare_and_swap(&x___, (long) 0, (long) 1); \ + volatile ethr_sint32_t x___ = 0; \ + (void) __sync_val_compare_and_swap(&x___, (ethr_sint32_t) 0, (ethr_sint32_t) 1); \ } while (0) -#endif #define ETHR_READ_DEPEND_MEMORY_BARRIER ETHR_MEMORY_BARRIER +#endif + +#define ETHR_COMPILER_BARRIER __asm__ __volatile__("" : : : "memory") -#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_AUX_IMPL__) +#endif /* ETHR_GCC_ATOMIC_COMMON__ */ + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +#define ETHR_HAVE_NATIVE_ATOMIC32 1 +#define ETHR_NATMC_FUNC__(X) ethr_native_atomic32_ ## X +#define ETHR_ATMC_T__ ethr_native_atomic32_t +#define ETHR_AINT_T__ ethr_sint32_t +#elif ETHR_INCLUDE_ATOMIC_IMPL__ == 8 +#define ETHR_HAVE_NATIVE_ATOMIC64 1 +#define ETHR_NATMC_FUNC__(X) ethr_native_atomic64_ ## X +#define ETHR_ATMC_T__ ethr_native_atomic64_t +#define ETHR_AINT_T__ ethr_sint64_t +#else +#error "Unsupported integer size" +#endif + +typedef struct { + volatile ETHR_AINT_T__ counter; +} ETHR_ATMC_T__; + + +#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_ATOMIC_IMPL__) + +static ETHR_INLINE ETHR_AINT_T__ * +ETHR_NATMC_FUNC__(addr)(ETHR_ATMC_T__ *var) +{ + return (ETHR_AINT_T__ *) &var->counter; +} static ETHR_INLINE void -ethr_native_atomic_set(ethr_native_atomic_t *var, long value) +ETHR_NATMC_FUNC__(set)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ value) { -#if ETHR_IMMED_ATOMIC_SET_GET_SAFE__ +#if ETHR_READ_AND_SET_WITHOUT_SYNC_OP__ var->counter = value; #else /* * Unfortunately no __sync_store() or similar exist in the gcc atomic * op interface. We therefore have to simulate it this way... */ - long act = 0, exp; + ETHR_AINT_T__ act = 0, exp; do { exp = act; act = __sync_val_compare_and_swap(&var->counter, exp, value); @@ -82,80 +132,86 @@ ethr_native_atomic_set(ethr_native_atomic_t *var, long value) #endif } -#define ethr_native_atomic_init ethr_native_atomic_set +static ETHR_INLINE void +ETHR_NATMC_FUNC__(init)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ value) +{ + ETHR_NATMC_FUNC__(set)(var, value); +} -static ETHR_INLINE long -ethr_native_atomic_read(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(read)(ETHR_ATMC_T__ *var) { -#if ETHR_IMMED_ATOMIC_SET_GET_SAFE__ +#if ETHR_READ_AND_SET_WITHOUT_SYNC_OP__ return var->counter; #else /* * Unfortunately no __sync_fetch() or similar exist in the gcc atomic * op interface. We therefore have to simulate it this way... */ - return __sync_add_and_fetch(&var->counter, (long) 0); + return __sync_add_and_fetch(&var->counter, (ETHR_AINT_T__) 0); #endif } static ETHR_INLINE void -ethr_native_atomic_add(ethr_native_atomic_t *var, long incr) +ETHR_NATMC_FUNC__(add)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) { (void) __sync_add_and_fetch(&var->counter, incr); } -static ETHR_INLINE long -ethr_native_atomic_add_return(ethr_native_atomic_t *var, long incr) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(add_return)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) { return __sync_add_and_fetch(&var->counter, incr); } static ETHR_INLINE void -ethr_native_atomic_inc(ethr_native_atomic_t *var) +ETHR_NATMC_FUNC__(inc)(ETHR_ATMC_T__ *var) { - (void) __sync_add_and_fetch(&var->counter, (long) 1); + (void) __sync_add_and_fetch(&var->counter, (ETHR_AINT_T__) 1); } static ETHR_INLINE void -ethr_native_atomic_dec(ethr_native_atomic_t *var) +ETHR_NATMC_FUNC__(dec)(ETHR_ATMC_T__ *var) { - (void) __sync_sub_and_fetch(&var->counter, (long) 1); + (void) __sync_sub_and_fetch(&var->counter, (ETHR_AINT_T__) 1); } -static ETHR_INLINE long -ethr_native_atomic_inc_return(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(inc_return)(ETHR_ATMC_T__ *var) { - return __sync_add_and_fetch(&var->counter, (long) 1); + return __sync_add_and_fetch(&var->counter, (ETHR_AINT_T__) 1); } -static ETHR_INLINE long -ethr_native_atomic_dec_return(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(dec_return)(ETHR_ATMC_T__ *var) { - return __sync_sub_and_fetch(&var->counter, (long) 1); + return __sync_sub_and_fetch(&var->counter, (ETHR_AINT_T__) 1); } -static ETHR_INLINE long -ethr_native_atomic_and_retold(ethr_native_atomic_t *var, long mask) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(and_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { return __sync_fetch_and_and(&var->counter, mask); } -static ETHR_INLINE long -ethr_native_atomic_or_retold(ethr_native_atomic_t *var, long mask) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(or_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { - return (long) __sync_fetch_and_or(&var->counter, mask); + return (ETHR_AINT_T__) __sync_fetch_and_or(&var->counter, mask); } -static ETHR_INLINE long -ethr_native_atomic_cmpxchg(ethr_native_atomic_t *var, long new, long old) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ old) { return __sync_val_compare_and_swap(&var->counter, old, new); } -static ETHR_INLINE long -ethr_native_atomic_xchg(ethr_native_atomic_t *var, long new) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(xchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new) { - long exp, act = 0; + ETHR_AINT_T__ exp, act = 0; do { exp = act; act = __sync_val_compare_and_swap(&var->counter, exp, new); @@ -167,22 +223,68 @@ ethr_native_atomic_xchg(ethr_native_atomic_t *var, long new) * Atomic ops with at least specified barriers. */ -static ETHR_INLINE long -ethr_native_atomic_read_acqb(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(read_acqb)(ETHR_ATMC_T__ *var) { - return __sync_add_and_fetch(&var->counter, (long) 0); +#if ETHR_READ_ACQB_AND_SET_RELB_COMPILER_BARRIER_ONLY__ + ETHR_AINT_T__ val = var->counter; + ETHR_COMPILER_BARRIER; + return val; +#else + return __sync_add_and_fetch(&var->counter, (ETHR_AINT_T__) 0); +#endif } -#define ethr_native_atomic_inc_return_acqb ethr_native_atomic_inc_return -#define ethr_native_atomic_set_relb ethr_native_atomic_xchg -#define ethr_native_atomic_dec_relb ethr_native_atomic_dec_return -#define ethr_native_atomic_dec_return_relb ethr_native_atomic_dec_return +static ETHR_INLINE void +ETHR_NATMC_FUNC__(set_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ i) +{ +#if ETHR_READ_ACQB_AND_SET_RELB_COMPILER_BARRIER_ONLY__ + ETHR_COMPILER_BARRIER; + var->counter = i; +#else + (void) ETHR_NATMC_FUNC__(xchg)(var, i); +#endif +} -#define ethr_native_atomic_cmpxchg_acqb ethr_native_atomic_cmpxchg -#define ethr_native_atomic_cmpxchg_relb ethr_native_atomic_cmpxchg +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(inc_return_acqb)(ETHR_ATMC_T__ *var) +{ + return ETHR_NATMC_FUNC__(inc_return)(var); +} -#endif +static ETHR_INLINE void +ETHR_NATMC_FUNC__(dec_relb)(ETHR_ATMC_T__ *var) +{ + ETHR_NATMC_FUNC__(dec)(var); +} + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(dec_return_relb)(ETHR_ATMC_T__ *var) +{ + return ETHR_NATMC_FUNC__(dec_return)(var); +} + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg_acqb)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ old) +{ + return ETHR_NATMC_FUNC__(cmpxchg)(var, new, old); +} + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg_relb)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ old) +{ + return ETHR_NATMC_FUNC__(cmpxchg)(var, new, old); +} #endif +#undef ETHR_NATMC_FUNC__ +#undef ETHR_ATMC_T__ +#undef ETHR_AINT_T__ +#undef ETHR_AINT_SUFFIX__ + #endif diff --git a/erts/include/internal/gcc/ethread.h b/erts/include/internal/gcc/ethread.h index bb378e31e0..392a1aa2b2 100644 --- a/erts/include/internal/gcc/ethread.h +++ b/erts/include/internal/gcc/ethread.h @@ -25,6 +25,16 @@ #ifndef ETHREAD_GCC_H__ #define ETHREAD_GCC_H__ +#if !defined(ETHR_HAVE_NATIVE_ATOMICS) && defined(ETHR_HAVE_GCC_ATOMIC_OPS) +#define ETHR_HAVE_NATIVE_ATOMICS 1 + +#define ETHR_ATOMIC_WANT_32BIT_IMPL__ #include "ethr_atomic.h" +#if ETHR_SIZEOF_PTR == 8 +# define ETHR_ATOMIC_WANT_64BIT_IMPL__ +# include "ethr_atomic.h" +#endif + +#endif #endif diff --git a/erts/include/internal/i386/atomic.h b/erts/include/internal/i386/atomic.h index 52d01aab32..4e402f261a 100644 --- a/erts/include/internal/i386/atomic.h +++ b/erts/include/internal/i386/atomic.h @@ -23,14 +23,24 @@ * * This code requires a 486 or newer processor. */ -#ifndef ETHREAD_I386_ATOMIC_H -#define ETHREAD_I386_ATOMIC_H -/* An atomic is an aligned long accessed via locked operations. - */ -typedef struct { - volatile long counter; -} ethr_native_atomic_t; +#undef ETHR_INCLUDE_ATOMIC_IMPL__ +#if !defined(ETHR_X86_ATOMIC32_H__) && defined(ETHR_ATOMIC_WANT_32BIT_IMPL__) +#define ETHR_X86_ATOMIC32_H__ +#define ETHR_INCLUDE_ATOMIC_IMPL__ 4 +#undef ETHR_ATOMIC_WANT_32BIT_IMPL__ +#elif !defined(ETHR_X86_ATOMIC64_H__) && defined(ETHR_ATOMIC_WANT_64BIT_IMPL__) +#define ETHR_X86_ATOMIC64_H__ +#define ETHR_INCLUDE_ATOMIC_IMPL__ 8 +#undef ETHR_ATOMIC_WANT_64BIT_IMPL__ +#endif + +#ifdef ETHR_INCLUDE_ATOMIC_IMPL__ + +#ifndef ETHR_X86_ATOMIC_COMMON__ +#define ETHR_X86_ATOMIC_COMMON__ + +#define ETHR_ATOMIC_HAVE_INC_DEC_INSTRUCTIONS 1 #if defined(__x86_64__) || !defined(ETHR_PRE_PENTIUM4_COMPAT) #define ETHR_MEMORY_BARRIER __asm__ __volatile__("mfence" : : : "memory") @@ -40,123 +50,161 @@ typedef struct { #else #define ETHR_MEMORY_BARRIER \ do { \ - volatile long x___ = 0; \ + volatile ethr_sint32_t x___ = 0; \ __asm__ __volatile__("lock; incl %0" : "=m"(x___) : "m"(x___) : "memory"); \ } while (0) #endif -#define ETHR_ATOMIC_HAVE_INC_DEC_INSTRUCTIONS 1 - -#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_AUX_IMPL__) +#endif /* ETHR_X86_ATOMIC_COMMON__ */ -#ifdef __x86_64__ -#define LONG_SUFFIX "q" +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +#define ETHR_HAVE_NATIVE_ATOMIC32 1 +#define ETHR_NATMC_FUNC__(X) ethr_native_atomic32_ ## X +#define ETHR_ATMC_T__ ethr_native_atomic32_t +#define ETHR_AINT_T__ ethr_sint32_t +#define ETHR_AINT_SUFFIX__ "l" +#elif ETHR_INCLUDE_ATOMIC_IMPL__ == 8 +#define ETHR_HAVE_NATIVE_ATOMIC64 1 +#define ETHR_NATMC_FUNC__(X) ethr_native_atomic64_ ## X +#define ETHR_ATMC_T__ ethr_native_atomic64_t +#define ETHR_AINT_T__ ethr_sint64_t +#define ETHR_AINT_SUFFIX__ "q" #else -#define LONG_SUFFIX "l" +#error "Unsupported integer size" #endif +/* An atomic is an aligned ETHR_AINT_T__ accessed via locked operations. + */ +typedef struct { + volatile ETHR_AINT_T__ counter; +} ETHR_ATMC_T__; + +#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_ATOMIC_IMPL__) + +static ETHR_INLINE ETHR_AINT_T__ * +ETHR_NATMC_FUNC__(addr)(ETHR_ATMC_T__ *var) +{ + return (ETHR_AINT_T__ *) &var->counter; +} + static ETHR_INLINE void -ethr_native_atomic_init(ethr_native_atomic_t *var, long i) +ETHR_NATMC_FUNC__(init)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ i) { var->counter = i; } -#define ethr_native_atomic_set(v, i) ethr_native_atomic_init((v), (i)) -static ETHR_INLINE long -ethr_native_atomic_read(ethr_native_atomic_t *var) +static ETHR_INLINE void +ETHR_NATMC_FUNC__(set)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ i) +{ + var->counter = i; +} + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(read)(ETHR_ATMC_T__ *var) { return var->counter; } static ETHR_INLINE void -ethr_native_atomic_add(ethr_native_atomic_t *var, long incr) +ETHR_NATMC_FUNC__(add)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) { __asm__ __volatile__( - "lock; add" LONG_SUFFIX " %1, %0" + "lock; add" ETHR_AINT_SUFFIX__ " %1, %0" : "=m"(var->counter) : "ir"(incr), "m"(var->counter)); } static ETHR_INLINE void -ethr_native_atomic_inc(ethr_native_atomic_t *var) +ETHR_NATMC_FUNC__(inc)(ETHR_ATMC_T__ *var) { __asm__ __volatile__( - "lock; inc" LONG_SUFFIX " %0" + "lock; inc" ETHR_AINT_SUFFIX__ " %0" : "=m"(var->counter) : "m"(var->counter)); } static ETHR_INLINE void -ethr_native_atomic_dec(ethr_native_atomic_t *var) +ETHR_NATMC_FUNC__(dec)(ETHR_ATMC_T__ *var) { __asm__ __volatile__( - "lock; dec" LONG_SUFFIX " %0" + "lock; dec" ETHR_AINT_SUFFIX__ " %0" : "=m"(var->counter) : "m"(var->counter)); } -static ETHR_INLINE long -ethr_native_atomic_add_return(ethr_native_atomic_t *var, long incr) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(add_return)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) { - long tmp; + ETHR_AINT_T__ tmp; tmp = incr; __asm__ __volatile__( - "lock; xadd" LONG_SUFFIX " %0, %1" /* xadd didn't exist prior to the 486 */ + "lock; xadd" ETHR_AINT_SUFFIX__ " %0, %1" /* xadd didn't exist prior to the 486 */ : "=r"(tmp) : "m"(var->counter), "0"(tmp)); /* now tmp is the atomic's previous value */ return tmp + incr; } -#define ethr_native_atomic_inc_return(var) ethr_native_atomic_add_return((var), 1) -#define ethr_native_atomic_dec_return(var) ethr_native_atomic_add_return((var), -1) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(inc_return)(ETHR_ATMC_T__ *var) +{ + return ETHR_NATMC_FUNC__(add_return)(var, (ETHR_AINT_T__) 1); +} -static ETHR_INLINE long -ethr_native_atomic_cmpxchg(ethr_native_atomic_t *var, long new, long old) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(dec_return)(ETHR_ATMC_T__ *var) +{ + return ETHR_NATMC_FUNC__(add_return)(var, (ETHR_AINT_T__) -1); +} + +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ old) { __asm__ __volatile__( - "lock; cmpxchg" LONG_SUFFIX " %2, %3" + "lock; cmpxchg" ETHR_AINT_SUFFIX__ " %2, %3" : "=a"(old), "=m"(var->counter) : "r"(new), "m"(var->counter), "0"(old) : "cc", "memory"); /* full memory clobber to make this a compiler barrier */ return old; } -static ETHR_INLINE long -ethr_native_atomic_and_retold(ethr_native_atomic_t *var, long mask) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(and_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { - long tmp, old; + ETHR_AINT_T__ tmp, old; tmp = var->counter; do { old = tmp; - tmp = ethr_native_atomic_cmpxchg(var, tmp & mask, tmp); + tmp = ETHR_NATMC_FUNC__(cmpxchg)(var, tmp & mask, tmp); } while (__builtin_expect(tmp != old, 0)); /* now tmp is the atomic's previous value */ return tmp; } -static ETHR_INLINE long -ethr_native_atomic_or_retold(ethr_native_atomic_t *var, long mask) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(or_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { - long tmp, old; + ETHR_AINT_T__ tmp, old; tmp = var->counter; do { old = tmp; - tmp = ethr_native_atomic_cmpxchg(var, tmp | mask, tmp); + tmp = ETHR_NATMC_FUNC__(cmpxchg)(var, tmp | mask, tmp); } while (__builtin_expect(tmp != old, 0)); /* now tmp is the atomic's previous value */ return tmp; } -static ETHR_INLINE long -ethr_native_atomic_xchg(ethr_native_atomic_t *var, long val) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(xchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ val) { - long tmp = val; + ETHR_AINT_T__ tmp = val; __asm__ __volatile__( - "xchg" LONG_SUFFIX " %0, %1" + "xchg" ETHR_AINT_SUFFIX__ " %0, %1" : "=r"(tmp) : "m"(var->counter), "0"(tmp)); /* now tmp is the atomic's previous value */ @@ -167,57 +215,73 @@ ethr_native_atomic_xchg(ethr_native_atomic_t *var, long val) * Atomic ops with at least specified barriers. */ -static ETHR_INLINE long -ethr_native_atomic_read_acqb(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(read_acqb)(ETHR_ATMC_T__ *var) { - long val; + ETHR_AINT_T__ val; #if defined(__x86_64__) || !defined(ETHR_PRE_PENTIUM4_COMPAT) val = var->counter; #else - val = ethr_native_atomic_add_return(var, 0); + val = ETHR_NATMC_FUNC__(add_return)(var, 0); #endif __asm__ __volatile__("" : : : "memory"); return val; } static ETHR_INLINE void -ethr_native_atomic_set_relb(ethr_native_atomic_t *var, long i) +ETHR_NATMC_FUNC__(set_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ i) { __asm__ __volatile__("" : : : "memory"); #if defined(__x86_64__) || !defined(ETHR_PRE_PENTIUM4_COMPAT) var->counter = i; #else - (void) ethr_native_atomic_xchg(var, i); + (void) ETHR_NATMC_FUNC__(xchg)(var, i); #endif } -static ETHR_INLINE long -ethr_native_atomic_inc_return_acqb(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(inc_return_acqb)(ETHR_ATMC_T__ *var) { - long res = ethr_native_atomic_inc_return(var); + ETHR_AINT_T__ res = ETHR_NATMC_FUNC__(inc_return)(var); __asm__ __volatile__("" : : : "memory"); return res; } static ETHR_INLINE void -ethr_native_atomic_dec_relb(ethr_native_atomic_t *var) +ETHR_NATMC_FUNC__(dec_relb)(ETHR_ATMC_T__ *var) { __asm__ __volatile__("" : : : "memory"); - ethr_native_atomic_dec(var); + ETHR_NATMC_FUNC__(dec)(var); } -static ETHR_INLINE long -ethr_native_atomic_dec_return_relb(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(dec_return_relb)(ETHR_ATMC_T__ *var) { __asm__ __volatile__("" : : : "memory"); - return ethr_native_atomic_dec_return(var); + return ETHR_NATMC_FUNC__(dec_return)(var); } -#define ethr_native_atomic_cmpxchg_acqb ethr_native_atomic_cmpxchg -#define ethr_native_atomic_cmpxchg_relb ethr_native_atomic_cmpxchg +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg_acqb)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ old) +{ + return ETHR_NATMC_FUNC__(cmpxchg)(var, new, old); +} -#undef LONG_SUFFIX +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg_relb)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ old) +{ + return ETHR_NATMC_FUNC__(cmpxchg)(var, new, old); +} #endif /* ETHR_TRY_INLINE_FUNCS */ -#endif /* ETHREAD_I386_ATOMIC_H */ +#undef ETHR_NATMC_FUNC__ +#undef ETHR_ATMC_T__ +#undef ETHR_AINT_T__ +#undef ETHR_AINT_SUFFIX__ + +#endif /* ETHR_INCLUDE_ATOMIC_IMPL__ */ diff --git a/erts/include/internal/i386/ethread.h b/erts/include/internal/i386/ethread.h index ed43e77279..b5a17caefb 100644 --- a/erts/include/internal/i386/ethread.h +++ b/erts/include/internal/i386/ethread.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2009. All Rights Reserved. + * Copyright Ericsson AB 2005-2010. 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 @@ -24,7 +24,12 @@ #ifndef ETHREAD_I386_ETHREAD_H #define ETHREAD_I386_ETHREAD_H +#define ETHR_ATOMIC_WANT_32BIT_IMPL__ #include "atomic.h" +#if ETHR_SIZEOF_PTR == 8 +# define ETHR_ATOMIC_WANT_64BIT_IMPL__ +# include "atomic.h" +#endif #include "spinlock.h" #include "rwlock.h" diff --git a/erts/include/internal/libatomic_ops/ethr_atomic.h b/erts/include/internal/libatomic_ops/ethr_atomic.h index a6eb43a0bd..d56693dbf8 100644 --- a/erts/include/internal/libatomic_ops/ethr_atomic.h +++ b/erts/include/internal/libatomic_ops/ethr_atomic.h @@ -46,17 +46,39 @@ * - AO_store() * - AO_compare_and_swap() * - * The `AO_t' type also have to be at least as large as - * `void *' and `long' types. + * The `AO_t' type also have to be at least as large as the `void *' type. */ #if ETHR_SIZEOF_AO_T < ETHR_SIZEOF_PTR #error The AO_t type is too small #endif +#if ETHR_SIZEOF_AO_T == 4 +#define ETHR_HAVE_NATIVE_ATOMIC32 1 +#define ETHR_NATMC_FUNC__(X) ethr_native_atomic32_ ## X +#define ETHR_ATMC_T__ ethr_native_atomic32_t +#define ETHR_AINT_T__ ethr_sint32_t +#define ETHR_AINT_SUFFIX__ "l" +#elif ETHR_SIZEOF_AO_T == 8 +#define ETHR_HAVE_NATIVE_ATOMIC64 1 +#define ETHR_NATMC_FUNC__(X) ethr_native_atomic64_ ## X +#define ETHR_ATMC_T__ ethr_native_atomic64_t +#define ETHR_AINT_T__ ethr_sint64_t +#define ETHR_AINT_SUFFIX__ "q" +#else +#error "Unsupported integer size" +#endif + +#if ETHR_SIZEOF_AO_T == 8 +typedef union { + volatile AO_t counter; + ethr_sint32_t sint32[2]; +} ETHR_ATMC_T__; +#else typedef struct { volatile AO_t counter; -} ethr_native_atomic_t; +} ETHR_ATMC_T__; +#endif #define ETHR_MEMORY_BARRIER AO_nop_full() #ifdef AO_HAVE_nop_write @@ -72,123 +94,151 @@ typedef struct { #ifdef AO_NO_DD_ORDERING # define ETHR_READ_DEPEND_MEMORY_BARRIER ETHR_READ_MEMORY_BARRIER #else -# define ETHR_READ_DEPEND_MEMORY_BARRIER __asm__ __volatile__("":::"memory") +# define ETHR_READ_DEPEND_MEMORY_BARRIER AO_compiler_barrier() +#endif + +#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_ATOMIC_IMPL__) + +static ETHR_INLINE ETHR_AINT_T__ * +ETHR_NATMC_FUNC__(addr)(ETHR_ATMC_T__ *var) +{ + return (ETHR_AINT_T__ *) &var->counter; +} + +#if ETHR_SIZEOF_AO_T == 8 +/* + * We also need to provide an ethr_native_atomic32_addr(), since + * this 64-bit implementation will be used implementing 32-bit + * native atomics. + */ + +static ETHR_INLINE ethr_sint32_t * +ethr_native_atomic32_addr(ETHR_ATMC_T__ *var) +{ + ETHR_ASSERT(((void *) &var->sint32[0]) == ((void *) &var->counter)); +#ifdef ETHR_BIGENDIAN + return &var->sint32[1]; +#else + return &var->sint32[0]; #endif +} -#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_AUX_IMPL__) +#endif /* ETHR_SIZEOF_AO_T == 8 */ static ETHR_INLINE void -ethr_native_atomic_set(ethr_native_atomic_t *var, long value) +ETHR_NATMC_FUNC__(set)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ value) { AO_store(&var->counter, (AO_t) value); } static ETHR_INLINE void -ethr_native_atomic_init(ethr_native_atomic_t *var, long value) +ETHR_NATMC_FUNC__(init)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ value) { - ethr_native_atomic_set(var, value); + ETHR_NATMC_FUNC__(set)(var, value); } -static ETHR_INLINE long -ethr_native_atomic_read(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(read)(ETHR_ATMC_T__ *var) { - return (long) AO_load(&var->counter); + return (ETHR_AINT_T__) AO_load(&var->counter); } -static ETHR_INLINE long -ethr_native_atomic_add_return(ethr_native_atomic_t *var, long incr) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(add_return)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) { #ifdef AO_HAVE_fetch_and_add - return ((long) AO_fetch_and_add(&var->counter, (AO_t) incr)) + incr; + return ((ETHR_AINT_T__) AO_fetch_and_add(&var->counter, (AO_t) incr)) + incr; #else while (1) { AO_t exp = AO_load(&var->counter); AO_t new = exp + (AO_t) incr; if (AO_compare_and_swap(&var->counter, exp, new)) - return (long) new; + return (ETHR_AINT_T__) new; } #endif } static ETHR_INLINE void -ethr_native_atomic_add(ethr_native_atomic_t *var, long incr) +ETHR_NATMC_FUNC__(add)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) { - (void) ethr_native_atomic_add_return(var, incr); + (void) ETHR_NATMC_FUNC__(add_return)(var, incr); } -static ETHR_INLINE long -ethr_native_atomic_inc_return(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(inc_return)(ETHR_ATMC_T__ *var) { #ifdef AO_HAVE_fetch_and_add1 - return ((long) AO_fetch_and_add1(&var->counter)) + 1; + return ((ETHR_AINT_T__) AO_fetch_and_add1(&var->counter)) + 1; #else - return ethr_native_atomic_add_return(var, 1); + return ETHR_NATMC_FUNC__(add_return)(var, 1); #endif } static ETHR_INLINE void -ethr_native_atomic_inc(ethr_native_atomic_t *var) +ETHR_NATMC_FUNC__(inc)(ETHR_ATMC_T__ *var) { - (void) ethr_native_atomic_inc_return(var); + (void) ETHR_NATMC_FUNC__(inc_return)(var); } -static ETHR_INLINE long -ethr_native_atomic_dec_return(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(dec_return)(ETHR_ATMC_T__ *var) { #ifdef AO_HAVE_fetch_and_sub1 - return ((long) AO_fetch_and_sub1(&var->counter)) - 1; + return ((ETHR_AINT_T__) AO_fetch_and_sub1(&var->counter)) - 1; #else - return ethr_native_atomic_add_return(var, -1); + return ETHR_NATMC_FUNC__(add_return)(var, -1); #endif } static ETHR_INLINE void -ethr_native_atomic_dec(ethr_native_atomic_t *var) +ETHR_NATMC_FUNC__(dec)(ETHR_ATMC_T__ *var) { - (void) ethr_native_atomic_dec_return(var); + (void) ETHR_NATMC_FUNC__(dec_return)(var); } -static ETHR_INLINE long -ethr_native_atomic_and_retold(ethr_native_atomic_t *var, long mask) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(and_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { while (1) { AO_t exp = AO_load(&var->counter); AO_t new = exp & ((AO_t) mask); if (AO_compare_and_swap(&var->counter, exp, new)) - return (long) exp; + return (ETHR_AINT_T__) exp; } } -static ETHR_INLINE long -ethr_native_atomic_or_retold(ethr_native_atomic_t *var, long mask) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(or_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { while (1) { AO_t exp = AO_load(&var->counter); AO_t new = exp | ((AO_t) mask); if (AO_compare_and_swap(&var->counter, exp, new)) - return (long) exp; + return (ETHR_AINT_T__) exp; } } -static ETHR_INLINE long -ethr_native_atomic_cmpxchg(ethr_native_atomic_t *var, long new, long exp) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ exp) { - long act; + ETHR_AINT_T__ act; do { if (AO_compare_and_swap(&var->counter, (AO_t) exp, (AO_t) new)) return exp; - act = (long) AO_load(&var->counter); + act = (ETHR_AINT_T__) AO_load(&var->counter); } while (act == exp); return act; } -static ETHR_INLINE long -ethr_native_atomic_xchg(ethr_native_atomic_t *var, long new) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(xchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new) { while (1) { AO_t exp = AO_load(&var->counter); if (AO_compare_and_swap(&var->counter, exp, (AO_t) new)) - return (long) exp; + return (ETHR_AINT_T__) exp; } } @@ -196,97 +246,105 @@ ethr_native_atomic_xchg(ethr_native_atomic_t *var, long new) * Atomic ops with at least specified barriers. */ -static ETHR_INLINE long -ethr_native_atomic_read_acqb(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(read_acqb)(ETHR_ATMC_T__ *var) { #ifdef AO_HAVE_load_acquire - return (long) AO_load_acquire(&var->counter); + return (ETHR_AINT_T__) AO_load_acquire(&var->counter); #else - long res = ethr_native_atomic_read(var); + ETHR_AINT_T__ res = ETHR_NATMC_FUNC__(read)(var); ETHR_MEMORY_BARRIER; return res; #endif } -static ETHR_INLINE long -ethr_native_atomic_inc_return_acqb(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(inc_return_acqb)(ETHR_ATMC_T__ *var) { #ifdef AO_HAVE_fetch_and_add1_acquire - return ((long) AO_fetch_and_add1_acquire(&var->counter)) + 1; + return ((ETHR_AINT_T__) AO_fetch_and_add1_acquire(&var->counter)) + 1; #else - long res = ethr_native_atomic_add_return(var, 1); + ETHR_AINT_T__ res = ETHR_NATMC_FUNC__(add_return)(var, 1); ETHR_MEMORY_BARRIER; return res; #endif } static ETHR_INLINE void -ethr_native_atomic_set_relb(ethr_native_atomic_t *var, long value) +ETHR_NATMC_FUNC__(set_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ value) { #ifdef AO_HAVE_store_release AO_store_release(&var->counter, (AO_t) value); #else ETHR_MEMORY_BARRIER; - ethr_native_atomic_set(var, value); + ETHR_NATMC_FUNC__(set)(var, value); #endif } -static ETHR_INLINE long -ethr_native_atomic_dec_return_relb(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(dec_return_relb)(ETHR_ATMC_T__ *var) { #ifdef AO_HAVE_fetch_and_sub1_release - return ((long) AO_fetch_and_sub1_release(&var->counter)) - 1; + return ((ETHR_AINT_T__) AO_fetch_and_sub1_release(&var->counter)) - 1; #else ETHR_MEMORY_BARRIER; - return ethr_native_atomic_dec_return(var); + return ETHR_NATMC_FUNC__(dec_return)(var); #endif } static ETHR_INLINE void -ethr_native_atomic_dec_relb(ethr_native_atomic_t *var) +ETHR_NATMC_FUNC__(dec_relb)(ETHR_ATMC_T__ *var) { - (void) ethr_native_atomic_dec_return_relb(var); + (void) ETHR_NATMC_FUNC__(dec_return_relb)(var); } -static ETHR_INLINE long -ethr_native_atomic_cmpxchg_acqb(ethr_native_atomic_t *var, long new, long exp) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg_acqb)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ exp) { #ifdef AO_HAVE_compare_and_swap_acquire - long act; + ETHR_AINT_T__ act; do { if (AO_compare_and_swap_acquire(&var->counter, (AO_t) exp, (AO_t) new)) return exp; - act = (long) AO_load(&var->counter); + act = (ETHR_AINT_T__) AO_load(&var->counter); } while (act == exp); AO_nop_full(); return act; #else - long act = ethr_native_atomic_cmpxchg(var, new, exp); + ETHR_AINT_T__ act = ETHR_NATMC_FUNC__(cmpxchg)(var, new, exp); ETHR_MEMORY_BARRIER; return act; #endif } -static ETHR_INLINE long -ethr_native_atomic_cmpxchg_relb(ethr_native_atomic_t *var, long new, long exp) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg_relb)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ exp) { #ifdef AO_HAVE_compare_and_swap_release - long act; + ETHR_AINT_T__ act; do { if (AO_compare_and_swap_release(&var->counter, (AO_t) exp, (AO_t) new)) return exp; - act = (long) AO_load(&var->counter); + act = (ETHR_AINT_T__) AO_load(&var->counter); } while (act == exp); return act; #else ETHR_MEMORY_BARRIER; - return ethr_native_atomic_cmpxchg(var, new, exp); + return ETHR_NATMC_FUNC__(cmpxchg)(var, new, exp); #endif } -#endif +#endif /* ETHR_TRY_INLINE_FUNCS */ -#endif +#undef ETHR_NATMC_FUNC__ +#undef ETHR_ATMC_T__ +#undef ETHR_AINT_T__ -#endif +#endif /* !defined(ETHR_HAVE_NATIVE_ATOMICS) && defined(ETHR_HAVE_LIBATOMIC_OPS) */ + +#endif /* ETHR_LIBATOMIC_OPS_ATOMIC_H__ */ diff --git a/erts/include/internal/ppc32/atomic.h b/erts/include/internal/ppc32/atomic.h index f21f7c9588..522f433649 100644 --- a/erts/include/internal/ppc32/atomic.h +++ b/erts/include/internal/ppc32/atomic.h @@ -28,31 +28,39 @@ #ifndef ETHREAD_PPC_ATOMIC_H #define ETHREAD_PPC_ATOMIC_H +#define ETHR_HAVE_NATIVE_ATOMIC32 1 + typedef struct { - volatile int counter; -} ethr_native_atomic_t; + volatile ethr_sint32_t counter; +} ethr_native_atomic32_t; #define ETHR_MEMORY_BARRIER __asm__ __volatile__("sync" : : : "memory") -#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_AUX_IMPL__) +#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_ATOMIC_IMPL__) + +static ETHR_INLINE ethr_sint32_t * +ethr_native_atomic32_addr(ethr_native_atomic32_t *var) +{ + return (ethr_sint32_t *) &var->counter; +} static ETHR_INLINE void -ethr_native_atomic_init(ethr_native_atomic_t *var, int i) +ethr_native_atomic32_init(ethr_native_atomic32_t *var, ethr_sint32_t i) { var->counter = i; } -#define ethr_native_atomic_set(v, i) ethr_native_atomic_init((v), (i)) +#define ethr_native_atomic32_set(v, i) ethr_native_atomic32_init((v), (i)) -static ETHR_INLINE int -ethr_native_atomic_read(ethr_native_atomic_t *var) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_read(ethr_native_atomic32_t *var) { return var->counter; } -static ETHR_INLINE int -ethr_native_atomic_add_return(ethr_native_atomic_t *var, int incr) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_add_return(ethr_native_atomic32_t *var, ethr_sint32_t incr) { - int tmp; + ethr_sint32_t tmp; __asm__ __volatile__( "eieio\n\t" @@ -69,16 +77,16 @@ ethr_native_atomic_add_return(ethr_native_atomic_t *var, int incr) } static ETHR_INLINE void -ethr_native_atomic_add(ethr_native_atomic_t *var, int incr) +ethr_native_atomic32_add(ethr_native_atomic32_t *var, ethr_sint32_t incr) { /* XXX: could use weaker version here w/o eieio+isync */ - (void)ethr_native_atomic_add_return(var, incr); + (void)ethr_native_atomic32_add_return(var, incr); } -static ETHR_INLINE int -ethr_native_atomic_inc_return(ethr_native_atomic_t *var) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_inc_return(ethr_native_atomic32_t *var) { - int tmp; + ethr_sint32_t tmp; __asm__ __volatile__( "eieio\n\t" @@ -95,16 +103,16 @@ ethr_native_atomic_inc_return(ethr_native_atomic_t *var) } static ETHR_INLINE void -ethr_native_atomic_inc(ethr_native_atomic_t *var) +ethr_native_atomic32_inc(ethr_native_atomic32_t *var) { /* XXX: could use weaker version here w/o eieio+isync */ - (void)ethr_native_atomic_inc_return(var); + (void)ethr_native_atomic32_inc_return(var); } -static ETHR_INLINE int -ethr_native_atomic_dec_return(ethr_native_atomic_t *var) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_dec_return(ethr_native_atomic32_t *var) { - int tmp; + ethr_sint32_t tmp; __asm__ __volatile__( "eieio\n\t" @@ -121,16 +129,16 @@ ethr_native_atomic_dec_return(ethr_native_atomic_t *var) } static ETHR_INLINE void -ethr_native_atomic_dec(ethr_native_atomic_t *var) +ethr_native_atomic32_dec(ethr_native_atomic32_t *var) { /* XXX: could use weaker version here w/o eieio+isync */ - (void)ethr_native_atomic_dec_return(var); + (void)ethr_native_atomic32_dec_return(var); } -static ETHR_INLINE int -ethr_native_atomic_and_retold(ethr_native_atomic_t *var, int mask) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_and_retold(ethr_native_atomic32_t *var, ethr_sint32_t mask) { - int old, new; + ethr_sint32_t old, new; __asm__ __volatile__( "eieio\n\t" @@ -146,10 +154,10 @@ ethr_native_atomic_and_retold(ethr_native_atomic_t *var, int mask) return old; } -static ETHR_INLINE int -ethr_native_atomic_or_retold(ethr_native_atomic_t *var, int mask) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_or_retold(ethr_native_atomic32_t *var, ethr_sint32_t mask) { - int old, new; + ethr_sint32_t old, new; __asm__ __volatile__( "eieio\n\t" @@ -165,10 +173,10 @@ ethr_native_atomic_or_retold(ethr_native_atomic_t *var, int mask) return old; } -static ETHR_INLINE int -ethr_native_atomic_xchg(ethr_native_atomic_t *var, int val) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_xchg(ethr_native_atomic32_t *var, ethr_sint32_t val) { - int tmp; + ethr_sint32_t tmp; __asm__ __volatile__( "eieio\n\t" @@ -183,10 +191,12 @@ ethr_native_atomic_xchg(ethr_native_atomic_t *var, int val) return tmp; } -static ETHR_INLINE int -ethr_native_atomic_cmpxchg(ethr_native_atomic_t *var, int new, int expected) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_cmpxchg(ethr_native_atomic32_t *var, + ethr_sint32_t new, + ethr_sint32_t expected) { - int old; + ethr_sint32_t old; __asm__ __volatile__( "eieio\n\t" @@ -210,20 +220,20 @@ ethr_native_atomic_cmpxchg(ethr_native_atomic_t *var, int new, int expected) */ static ETHR_INLINE long -ethr_native_atomic_read_acqb(ethr_native_atomic_t *var) +ethr_native_atomic32_read_acqb(ethr_native_atomic32_t *var) { - long res = ethr_native_atomic_read(var); + long res = ethr_native_atomic32_read(var); ETHR_MEMORY_BARRIER; return res; } -#define ethr_native_atomic_set_relb ethr_native_atomic_xchg -#define ethr_native_atomic_inc_return_acqb ethr_native_atomic_inc_return -#define ethr_native_atomic_dec_relb ethr_native_atomic_dec_return -#define ethr_native_atomic_dec_return_relb ethr_native_atomic_dec_return +#define ethr_native_atomic32_set_relb ethr_native_atomic32_xchg +#define ethr_native_atomic32_inc_return_acqb ethr_native_atomic32_inc_return +#define ethr_native_atomic32_dec_relb ethr_native_atomic32_dec_return +#define ethr_native_atomic32_dec_return_relb ethr_native_atomic32_dec_return -#define ethr_native_atomic_cmpxchg_acqb ethr_native_atomic_cmpxchg -#define ethr_native_atomic_cmpxchg_relb ethr_native_atomic_cmpxchg +#define ethr_native_atomic32_cmpxchg_acqb ethr_native_atomic32_cmpxchg +#define ethr_native_atomic32_cmpxchg_relb ethr_native_atomic32_cmpxchg #endif /* ETHR_TRY_INLINE_FUNCS */ diff --git a/erts/include/internal/pthread/ethr_event.h b/erts/include/internal/pthread/ethr_event.h index 104ec287e0..93da8a0429 100644 --- a/erts/include/internal/pthread/ethr_event.h +++ b/erts/include/internal/pthread/ethr_event.h @@ -30,31 +30,9 @@ #include <linux/futex.h> #include <sys/time.h> -/* - * Note: Linux futexes operate on 32-bit integers, but - * ethr_native_atomic_t are 64-bits on 64-bit - * platforms. This has to be taken into account. - * Therefore, in each individual value used each - * byte look the same. - */ - -#if ETHR_SIZEOF_PTR == 8 - -#define ETHR_EVENT_OFF_WAITER__ 0xffffffffffffffffL -#define ETHR_EVENT_OFF__ 0x7777777777777777L -#define ETHR_EVENT_ON__ 0L - -#elif ETHR_SIZEOF_PTR == 4 - -#define ETHR_EVENT_OFF_WAITER__ 0xffffffffL -#define ETHR_EVENT_OFF__ 0x77777777L -#define ETHR_EVENT_ON__ 0L - -#else - -#error ehrm... - -#endif +#define ETHR_EVENT_OFF_WAITER__ ((ethr_sint32_t) -1) +#define ETHR_EVENT_OFF__ ((ethr_sint32_t) 1) +#define ETHR_EVENT_ON__ ((ethr_sint32_t) 0) #if defined(FUTEX_WAIT_PRIVATE) && defined(FUTEX_WAKE_PRIVATE) # define ETHR_FUTEX_WAIT__ FUTEX_WAIT_PRIVATE @@ -65,11 +43,17 @@ #endif typedef struct { - ethr_atomic_t futex; + ethr_atomic32_t futex; } ethr_event; -#define ETHR_FUTEX__(FTX, OP, VAL) \ - (-1 == syscall(__NR_futex, (void *) (FTX), (OP), (int) (VAL), NULL, NULL, 0)\ +#define ETHR_FUTEX__(FTX, OP, VAL) \ + (-1 == syscall(__NR_futex, \ + (void *) ethr_atomic32_addr((FTX)), \ + (OP), \ + (int) (VAL), \ + NULL, \ + NULL, \ + 0) \ ? errno : 0) #if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_EVENT_IMPL__) @@ -77,9 +61,9 @@ typedef struct { static void ETHR_INLINE ETHR_INLINE_FUNC_NAME_(ethr_event_set)(ethr_event *e) { - long val; + ethr_sint32_t val; ETHR_WRITE_MEMORY_BARRIER; - val = ethr_atomic_xchg(&e->futex, ETHR_EVENT_ON__); + val = ethr_atomic32_xchg(&e->futex, ETHR_EVENT_ON__); if (val == ETHR_EVENT_OFF_WAITER__) { int res = ETHR_FUTEX__(&e->futex, ETHR_FUTEX_WAKE__, 1); if (res != 0) @@ -90,7 +74,7 @@ ETHR_INLINE_FUNC_NAME_(ethr_event_set)(ethr_event *e) static void ETHR_INLINE ETHR_INLINE_FUNC_NAME_(ethr_event_reset)(ethr_event *e) { - ethr_atomic_set(&e->futex, ETHR_EVENT_OFF__); + ethr_atomic32_set(&e->futex, ETHR_EVENT_OFF__); ETHR_MEMORY_BARRIER; } @@ -100,7 +84,7 @@ ETHR_INLINE_FUNC_NAME_(ethr_event_reset)(ethr_event *e) /* --- Posix mutex/cond implementation of events ---------------------------- */ typedef struct { - ethr_atomic_t state; + ethr_atomic32_t state; pthread_mutex_t mtx; pthread_cond_t cnd; } ethr_event; @@ -114,9 +98,9 @@ typedef struct { static void ETHR_INLINE ETHR_INLINE_FUNC_NAME_(ethr_event_set)(ethr_event *e) { - long val; + ethr_sint32_t val; ETHR_WRITE_MEMORY_BARRIER; - val = ethr_atomic_xchg(&e->state, ETHR_EVENT_ON__); + val = ethr_atomic32_xchg(&e->state, ETHR_EVENT_ON__); if (val == ETHR_EVENT_OFF_WAITER__) { int res = pthread_mutex_lock(&e->mtx); if (res != 0) @@ -133,7 +117,7 @@ ETHR_INLINE_FUNC_NAME_(ethr_event_set)(ethr_event *e) static void ETHR_INLINE ETHR_INLINE_FUNC_NAME_(ethr_event_reset)(ethr_event *e) { - ethr_atomic_set(&e->state, ETHR_EVENT_OFF__); + ethr_atomic32_set(&e->state, ETHR_EVENT_OFF__); ETHR_MEMORY_BARRIER; } diff --git a/erts/include/internal/sparc32/atomic.h b/erts/include/internal/sparc32/atomic.h index 2da6472393..00380dbf07 100644 --- a/erts/include/internal/sparc32/atomic.h +++ b/erts/include/internal/sparc32/atomic.h @@ -21,49 +21,86 @@ * Native ethread atomics on SPARC V9. * Author: Mikael Pettersson. */ -#ifndef ETHR_SPARC32_ATOMIC_H -#define ETHR_SPARC32_ATOMIC_H -typedef struct { - volatile long counter; -} ethr_native_atomic_t; +#undef ETHR_INCLUDE_ATOMIC_IMPL__ +#if !defined(ETHR_SPARC_V9_ATOMIC32_H__) && defined(ETHR_ATOMIC_WANT_32BIT_IMPL__) +#define ETHR_SPARC_V9_ATOMIC32_H__ +#define ETHR_INCLUDE_ATOMIC_IMPL__ 4 +#undef ETHR_ATOMIC_WANT_32BIT_IMPL__ +#elif !defined(ETHR_SPARC_V9_ATOMIC64_H__) && defined(ETHR_ATOMIC_WANT_64BIT_IMPL__) +#define ETHR_SPARC_V9_ATOMIC64_H__ +#define ETHR_INCLUDE_ATOMIC_IMPL__ 8 +#undef ETHR_ATOMIC_WANT_64BIT_IMPL__ +#endif + +#ifdef ETHR_INCLUDE_ATOMIC_IMPL__ + +#ifndef ETHR_SPARC_V9_ATOMIC_COMMON__ +#define ETHR_SPARC_V9_ATOMIC_COMMON__ #define ETHR_MEMORY_BARRIER \ __asm__ __volatile__("membar #LoadLoad|#LoadStore|#StoreLoad|#StoreStore\n" \ : : : "memory") -#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_AUX_IMPL__) - -#if defined(__arch64__) -#define CASX "casx" +#endif /* ETHR_SPARC_V9_ATOMIC_COMMON__ */ + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 +#define ETHR_HAVE_NATIVE_ATOMIC32 1 +#define ETHR_NATMC_FUNC__(X) ethr_native_atomic32_ ## X +#define ETHR_ATMC_T__ ethr_native_atomic32_t +#define ETHR_AINT_T__ ethr_sint32_t +#define ETHR_CAS__ "cas" +#elif ETHR_INCLUDE_ATOMIC_IMPL__ == 8 +#define ETHR_HAVE_NATIVE_ATOMIC64 1 +#define ETHR_NATMC_FUNC__(X) ethr_native_atomic64_ ## X +#define ETHR_ATMC_T__ ethr_native_atomic64_t +#define ETHR_AINT_T__ ethr_sint64_t +#define ETHR_CAS__ "casx" #else -#define CASX "cas" +#error "Unsupported integer size" #endif +typedef struct { + volatile ETHR_AINT_T__ counter; +} ETHR_ATMC_T__; + +#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_ATOMIC_IMPL__) + +static ETHR_INLINE ETHR_AINT_T__ * +ETHR_NATMC_FUNC__(addr)(ETHR_ATMC_T__ *var) +{ + return (ETHR_AINT_T__ *) &var->counter; +} + +static ETHR_INLINE void +ETHR_NATMC_FUNC__(init)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ i) +{ + var->counter = i; +} + static ETHR_INLINE void -ethr_native_atomic_init(ethr_native_atomic_t *var, long i) +ETHR_NATMC_FUNC__(set)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ i) { var->counter = i; } -#define ethr_native_atomic_set(v, i) ethr_native_atomic_init((v), (i)) -static ETHR_INLINE long -ethr_native_atomic_read(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(read)(ETHR_ATMC_T__ *var) { return var->counter; } -static ETHR_INLINE long -ethr_native_atomic_add_return(ethr_native_atomic_t *var, long incr) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(add_return)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) { - long old, tmp; + ETHR_AINT_T__ old, tmp; __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n"); do { old = var->counter; tmp = old+incr; __asm__ __volatile__( - CASX " [%2], %1, %0" + ETHR_CAS__ " [%2], %1, %0" : "=&r"(tmp) : "r"(old), "r"(&var->counter), "0"(tmp) : "memory"); @@ -73,46 +110,46 @@ ethr_native_atomic_add_return(ethr_native_atomic_t *var, long incr) } static ETHR_INLINE void -ethr_native_atomic_add(ethr_native_atomic_t *var, long incr) +ETHR_NATMC_FUNC__(add)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) { - (void)ethr_native_atomic_add_return(var, incr); + (void)ETHR_NATMC_FUNC__(add_return)(var, incr); } -static ETHR_INLINE long -ethr_native_atomic_inc_return(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(inc_return)(ETHR_ATMC_T__ *var) { - return ethr_native_atomic_add_return(var, 1); + return ETHR_NATMC_FUNC__(add_return)(var, 1); } static ETHR_INLINE void -ethr_native_atomic_inc(ethr_native_atomic_t *var) +ETHR_NATMC_FUNC__(inc)(ETHR_ATMC_T__ *var) { - (void)ethr_native_atomic_add_return(var, 1); + (void)ETHR_NATMC_FUNC__(add_return)(var, 1); } -static ETHR_INLINE long -ethr_native_atomic_dec_return(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(dec_return)(ETHR_ATMC_T__ *var) { - return ethr_native_atomic_add_return(var, -1); + return ETHR_NATMC_FUNC__(add_return)(var, -1); } static ETHR_INLINE void -ethr_native_atomic_dec(ethr_native_atomic_t *var) +ETHR_NATMC_FUNC__(dec)(ETHR_ATMC_T__ *var) { - (void)ethr_native_atomic_add_return(var, -1); + (void)ETHR_NATMC_FUNC__(add_return)(var, -1); } -static ETHR_INLINE long -ethr_native_atomic_and_retold(ethr_native_atomic_t *var, long mask) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(and_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { - long old, tmp; + ETHR_AINT_T__ old, tmp; __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n"); do { old = var->counter; tmp = old & mask; __asm__ __volatile__( - CASX " [%2], %1, %0" + ETHR_CAS__ " [%2], %1, %0" : "=&r"(tmp) : "r"(old), "r"(&var->counter), "0"(tmp) : "memory"); @@ -121,17 +158,17 @@ ethr_native_atomic_and_retold(ethr_native_atomic_t *var, long mask) return old; } -static ETHR_INLINE long -ethr_native_atomic_or_retold(ethr_native_atomic_t *var, long mask) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(or_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { - long old, tmp; + ETHR_AINT_T__ old, tmp; __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n"); do { old = var->counter; tmp = old | mask; __asm__ __volatile__( - CASX " [%2], %1, %0" + ETHR_CAS__ " [%2], %1, %0" : "=&r"(tmp) : "r"(old), "r"(&var->counter), "0"(tmp) : "memory"); @@ -140,17 +177,17 @@ ethr_native_atomic_or_retold(ethr_native_atomic_t *var, long mask) return old; } -static ETHR_INLINE long -ethr_native_atomic_xchg(ethr_native_atomic_t *var, long val) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(xchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ val) { - long old, new; + ETHR_AINT_T__ old, new; __asm__ __volatile__("membar #LoadLoad|#StoreLoad"); do { old = var->counter; new = val; __asm__ __volatile__( - CASX " [%2], %1, %0" + ETHR_CAS__ " [%2], %1, %0" : "=&r"(new) : "r"(old), "r"(&var->counter), "0"(new) : "memory"); @@ -159,12 +196,12 @@ ethr_native_atomic_xchg(ethr_native_atomic_t *var, long val) return old; } -static ETHR_INLINE long -ethr_native_atomic_cmpxchg(ethr_native_atomic_t *var, long new, long old) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new, ETHR_AINT_T__ old) { __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n"); __asm__ __volatile__( - CASX " [%2], %1, %0" + ETHR_CAS__ " [%2], %1, %0" : "=&r"(new) : "r"(old), "r"(&var->counter), "0"(new) : "memory"); @@ -178,58 +215,63 @@ ethr_native_atomic_cmpxchg(ethr_native_atomic_t *var, long new, long old) /* TODO: relax acquire barriers */ -static ETHR_INLINE long -ethr_native_atomic_read_acqb(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(read_acqb)(ETHR_ATMC_T__ *var) { - long res = ethr_native_atomic_read(var); + ETHR_AINT_T__ res = ETHR_NATMC_FUNC__(read)(var); __asm__ __volatile__("membar #LoadLoad|#LoadStore|#StoreLoad|#StoreStore" : : : "memory"); return res; } static ETHR_INLINE void -ethr_native_atomic_set_relb(ethr_native_atomic_t *var, long i) +ETHR_NATMC_FUNC__(set_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ i) { __asm__ __volatile__("membar #LoadStore|#StoreStore" : : : "memory"); - ethr_native_atomic_set(var, i); + ETHR_NATMC_FUNC__(set)(var, i); } -static ETHR_INLINE long -ethr_native_atomic_inc_return_acqb(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(inc_return_acqb)(ETHR_ATMC_T__ *var) { - long res = ethr_native_atomic_inc_return(var); + ETHR_AINT_T__ res = ETHR_NATMC_FUNC__(inc_return)(var); __asm__ __volatile__("membar #LoadLoad|#LoadStore" : : : "memory"); return res; } static ETHR_INLINE void -ethr_native_atomic_dec_relb(ethr_native_atomic_t *var) +ETHR_NATMC_FUNC__(dec_relb)(ETHR_ATMC_T__ *var) { __asm__ __volatile__("membar #LoadStore|#StoreStore" : : : "memory"); - ethr_native_atomic_dec(var); + ETHR_NATMC_FUNC__(dec)(var); } -static ETHR_INLINE long -ethr_native_atomic_dec_return_relb(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(dec_return_relb)(ETHR_ATMC_T__ *var) { __asm__ __volatile__("membar #LoadStore|#StoreStore" : : : "memory"); - return ethr_native_atomic_dec_return(var); + return ETHR_NATMC_FUNC__(dec_return)(var); } -static ETHR_INLINE long -ethr_native_atomic_cmpxchg_acqb(ethr_native_atomic_t *var, long new, long old) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg_acqb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new, ETHR_AINT_T__ old) { - long res = ethr_native_atomic_cmpxchg(var, new, old); + ETHR_AINT_T__ res = ETHR_NATMC_FUNC__(cmpxchg)(var, new, old); __asm__ __volatile__("membar #LoadLoad|#LoadStore" : : : "memory"); return res; } -static ETHR_INLINE long -ethr_native_atomic_cmpxchg_relb(ethr_native_atomic_t *var, long new, long old) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new, ETHR_AINT_T__ old) { __asm__ __volatile__("membar #LoadStore|#StoreStore" : : : "memory"); - return ethr_native_atomic_cmpxchg(var, new, old); + return ETHR_NATMC_FUNC__(cmpxchg)(var, new, old); } #endif /* ETHR_TRY_INLINE_FUNCS */ -#endif /* ETHR_SPARC32_ATOMIC_H */ +#undef ETHR_NATMC_FUNC__ +#undef ETHR_ATMC_T__ +#undef ETHR_AINT_T__ +#undef ETHR_CAS__ + +#endif /* ETHR_INCLUDE_ATOMIC_IMPL__ */ diff --git a/erts/include/internal/sparc32/ethread.h b/erts/include/internal/sparc32/ethread.h index dca113b4d6..aea9794390 100644 --- a/erts/include/internal/sparc32/ethread.h +++ b/erts/include/internal/sparc32/ethread.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2009. All Rights Reserved. + * Copyright Ericsson AB 2005-2010. 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 @@ -24,7 +24,12 @@ #ifndef ETHREAD_SPARC32_ETHREAD_H #define ETHREAD_SPARC32_ETHREAD_H +#define ETHR_ATOMIC_WANT_32BIT_IMPL__ #include "atomic.h" +#if ETHR_SIZEOF_PTR == 8 +# define ETHR_ATOMIC_WANT_64BIT_IMPL__ +# include "atomic.h" +#endif #include "spinlock.h" #include "rwlock.h" diff --git a/erts/include/internal/tile/atomic.h b/erts/include/internal/tile/atomic.h index 69569d82d1..48e4c0c6c8 100644 --- a/erts/include/internal/tile/atomic.h +++ b/erts/include/internal/tile/atomic.h @@ -24,92 +24,102 @@ #ifndef ETHREAD_TILE_ATOMIC_H #define ETHREAD_TILE_ATOMIC_H +#define ETHR_HAVE_NATIVE_ATOMIC32 1 + #include <atomic.h> /* An atomic is an aligned int accessed via locked operations. */ typedef struct { - volatile long counter; -} ethr_native_atomic_t; + volatile ethr_sint32_t counter; +} ethr_native_atomic32_t; #define ETHR_MEMORY_BARRIER __insn_mf() -#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_AUX_IMPL__) +#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_ATOMIC_IMPL__) + +static ETHR_INLINE ethr_sint32_t * +ethr_native_atomic32_addr(ethr_native_atomic32_t *var) +{ + return (ethr_sint32_t *) &var->counter; +} static ETHR_INLINE void -ethr_native_atomic_init(ethr_native_atomic_t *var, long i) +ethr_native_atomic32_init(ethr_native_atomic32_t *var, ethr_sint32_t i) { var->counter = i; } static ETHR_INLINE void -ethr_native_atomic_set(ethr_native_atomic_t *var, long i) +ethr_native_atomic32_set(ethr_native_atomic32_t *var, ethr_sint32_t i) { atomic_exchange_acq(&var->counter, i); } -static ETHR_INLINE long -ethr_native_atomic_read(ethr_native_atomic_t *var) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_read(ethr_native_atomic32_t *var) { return var->counter; } static ETHR_INLINE void -ethr_native_atomic_add(ethr_native_atomic_t *var, long incr) +ethr_native_atomic32_add(ethr_native_atomic32_t *var, ethr_sint32_t incr) { atomic_add(&var->counter, incr); } static ETHR_INLINE void -ethr_native_atomic_inc(ethr_native_atomic_t *var) +ethr_native_atomic32_inc(ethr_native_atomic32_t *var) { atomic_increment(&var->counter); } static ETHR_INLINE void -ethr_native_atomic_dec(ethr_native_atomic_t *var) +ethr_native_atomic32_dec(ethr_native_atomic32_t *var) { atomic_decrement(&var->counter); } -static ETHR_INLINE long -ethr_native_atomic_add_return(ethr_native_atomic_t *var, long incr) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_add_return(ethr_native_atomic32_t *var, ethr_sint32_t incr) { return atomic_exchange_and_add(&var->counter, incr) + incr; } -static ETHR_INLINE long -ethr_native_atomic_inc_return(ethr_native_atomic_t *var) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_inc_return(ethr_native_atomic32_t *var) { - return ethr_native_atomic_add_return(var, 1); + return ethr_native_atomic32_add_return(var, 1); } -static ETHR_INLINE long -ethr_native_atomic_dec_return(ethr_native_atomic_t *var) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_dec_return(ethr_native_atomic32_t *var) { - return ethr_native_atomic_add_return(var, -1); + return ethr_native_atomic32_add_return(var, -1); } -static ETHR_INLINE long -ethr_native_atomic_and_retold(ethr_native_atomic_t *var, long mask) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_and_retold(ethr_native_atomic32_t *var, ethr_sint32_t mask) { return atomic_and_val(&var->counter, mask); } -static ETHR_INLINE long -ethr_native_atomic_or_retold(ethr_native_atomic_t *var, long mask) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_or_retold(ethr_native_atomic32_t *var, ethr_sint32_t mask) { return atomic_or_val(&var->counter, mask); } -static ETHR_INLINE long -ethr_native_atomic_xchg(ethr_native_atomic_t *var, long val) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_xchg(ethr_native_atomic32_t *var, ethr_sint32_t val) { return atomic_exchange_acq(&var->counter, val); } -static ETHR_INLINE long -ethr_native_atomic_cmpxchg(ethr_native_atomic_t *var, long new, long expected) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_cmpxchg(ethr_native_atomic32_t *var, + ethr_sint32_t new, + ethr_sint32_t expected) { return atomic_compare_and_exchange_val_acq(&var->counter, new, expected); } @@ -118,54 +128,58 @@ ethr_native_atomic_cmpxchg(ethr_native_atomic_t *var, long new, long expected) * Atomic ops with at least specified barriers. */ -static ETHR_INLINE long -ethr_native_atomic_read_acqb(ethr_native_atomic_t *var) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_read_acqb(ethr_native_atomic32_t *var) { - long res = ethr_native_atomic_read(var); + ethr_sint32_t res = ethr_native_atomic32_read(var); ETHR_MEMORY_BARRIER; return res; } -static ETHR_INLINE long -ethr_native_atomic_inc_return_acqb(ethr_native_atomic_t *var) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_inc_return_acqb(ethr_native_atomic32_t *var) { - long res = ethr_native_atomic_inc_return(var); + ethr_sint32_t res = ethr_native_atomic32_inc_return(var); ETHR_MEMORY_BARRIER; return res; } static ETHR_INLINE void -ethr_native_atomic_set_relb(ethr_native_atomic_t *var, long val) +ethr_native_atomic32_set_relb(ethr_native_atomic32_t *var, ethr_sint32_t val) { ETHR_MEMORY_BARRIER; - ethr_native_atomic_set(var, val); + ethr_native_atomic32_set(var, val); } static ETHR_INLINE void -ethr_native_atomic_dec_relb(ethr_native_atomic_t *var) +ethr_native_atomic32_dec_relb(ethr_native_atomic32_t *var) { ETHR_MEMORY_BARRIER; - ethr_native_atomic_dec(var); + ethr_native_atomic32_dec(var); } -static ETHR_INLINE long -ethr_native_atomic_dec_return_relb(ethr_native_atomic_t *var) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_dec_return_relb(ethr_native_atomic32_t *var) { ETHR_MEMORY_BARRIER; - return ethr_native_atomic_dec_return(var); + return ethr_native_atomic32_dec_return(var); } -static ETHR_INLINE long -ethr_native_atomic_cmpxchg_acqb(ethr_native_atomic_t *var, long new, long exp) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_cmpxchg_acqb(ethr_native_atomic32_t *var, + ethr_sint32_t new, + ethr_sint32_t exp) { - return ethr_native_atomic_cmpxchg(var, new, exp); + return ethr_native_atomic32_cmpxchg(var, new, exp); } -static ETHR_INLINE long -ethr_native_atomic_cmpxchg_relb(ethr_native_atomic_t *var, long new, long exp) +static ETHR_INLINE ethr_sint32_t +ethr_native_atomic32_cmpxchg_relb(ethr_native_atomic32_t *var, + ethr_sint32_t new, + ethr_sint32_t exp) { ETHR_MEMORY_BARRIER; - return ethr_native_atomic_cmpxchg(var, new, exp); + return ethr_native_atomic32_cmpxchg(var, new, exp); } #endif /* ETHR_TRY_INLINE_FUNCS */ diff --git a/erts/include/internal/win/ethr_atomic.h b/erts/include/internal/win/ethr_atomic.h index 500459dd6c..60def01a7e 100644 --- a/erts/include/internal/win/ethr_atomic.h +++ b/erts/include/internal/win/ethr_atomic.h @@ -22,223 +22,394 @@ * Author: Rickard Green */ -#ifndef ETHR_WIN_ATOMIC_H__ -#define ETHR_WIN_ATOMIC_H__ - -#ifdef _MSC_VER -# if _MSC_VER < 1300 -# define ETHR_IMMED_ATOMIC_SET_GET_SAFE__ 0 /* Dont trust really old compilers */ -# else -# if defined(_M_IX86) -# define ETHR_IMMED_ATOMIC_SET_GET_SAFE__ 1 -# else /* I.e. IA64 */ -# if _MSC_VER >= 1400 -# define ETHR_IMMED_ATOMIC_SET_GET_SAFE__ 1 -# else -# define ETHR_IMMED_ATOMIC_SET_GET_SAFE__ 0 -# endif -# endif -# endif -# if _MSC_VER >= 1400 -# include <intrin.h> -# undef ETHR_COMPILER_BARRIER -# define ETHR_COMPILER_BARRIER _ReadWriteBarrier() -# endif -#pragma intrinsic(_ReadWriteBarrier) -#pragma intrinsic(_InterlockedAnd) -#pragma intrinsic(_InterlockedOr) +#undef ETHR_INCLUDE_ATOMIC_IMPL__ +#if !defined(ETHR_WIN_ATOMIC32_H__) && defined(ETHR_ATOMIC_WANT_32BIT_IMPL__) +#define ETHR_WIN_ATOMIC32_H__ +#define ETHR_INCLUDE_ATOMIC_IMPL__ 4 +#undef ETHR_ATOMIC_WANT_32BIT_IMPL__ +#elif !defined(ETHR_WIN_ATOMIC64_H__) && defined(ETHR_ATOMIC_WANT_64BIT_IMPL__) +#define ETHR_WIN_ATOMIC64_H__ +#ifdef ETHR_HAVE__INTERLOCKEDCOMPAREEXCHANGE64 +/* _InterlockedCompareExchange64() required... */ +#define ETHR_INCLUDE_ATOMIC_IMPL__ 8 +#endif +#undef ETHR_ATOMIC_WANT_64BIT_IMPL__ +#endif + +#ifdef ETHR_INCLUDE_ATOMIC_IMPL__ + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + +#ifndef ETHR_WIN_ATOMIC_COMMON__ +#define ETHR_WIN_ATOMIC_COMMON__ + +#define ETHR_HAVE_NATIVE_ATOMICS 1 + +#if defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64) +# define ETHR_READ_AND_SET_WITHOUT_INTERLOCKED_OP__ 1 #else -# define ETHR_IMMED_ATOMIC_SET_GET_SAFE__ 0 +# define ETHR_READ_AND_SET_WITHOUT_INTERLOCKED_OP__ 0 #endif +#if defined(_M_AMD64) || (defined(_M_IX86) \ + && !defined(ETHR_PRE_PENTIUM4_COMPAT)) +# define ETHR_READ_ACQB_AND_SET_RELB_COMPILER_BARRIER_ONLY__ 1 +#else +# define ETHR_READ_ACQB_AND_SET_RELB_COMPILER_BARRIER_ONLY__ 0 +#endif /* - * No configure test checking for _Interlocked*_{acq,rel} and - * Interlocked*{Acquire,Release} have been written yet... + * No configure test checking for interlocked acquire/release + * versions have been written, yet. It should define + * ETHR_HAVE_INTERLOCKED_ACQUIRE_RELEASE_BARRIERS if, and + * only if, all used interlocked operations with barriers + * exists. * * Note, that these are pure optimizations for the itanium * processor. */ -#ifdef ETHR_HAVE_INTERLOCKEDCOMPAREEXCHANGE_ACQ -#pragma intrinsic(_InterlockedCompareExchange_acq) +#include <intrin.h> +#undef ETHR_COMPILER_BARRIER +#define ETHR_COMPILER_BARRIER _ReadWriteBarrier() +#pragma intrinsic(_ReadWriteBarrier) +#pragma intrinsic(_InterlockedCompareExchange) + +#if defined(_M_AMD64) || (defined(_M_IX86) \ + && !defined(ETHR_PRE_PENTIUM4_COMPAT)) +#include <emmintrin.h> +#include <mmintrin.h> +#pragma intrinsic(_mm_mfence) +#define ETHR_MEMORY_BARRIER _mm_mfence() +#pragma intrinsic(_mm_sfence) +#define ETHR_WRITE_MEMORY_BARRIER _mm_sfence() +#pragma intrinsic(_mm_lfence) +#define ETHR_READ_MEMORY_BARRIER _mm_lfence() +#define ETHR_READ_DEPEND_MEMORY_BARRIER ETHR_COMPILER_BARRIER + +#else + +#define ETHR_MEMORY_BARRIER \ +do { \ + volatile long x___ = 0; \ + _InterlockedCompareExchange(&x___, (long) 1, (long) 0); \ +} while (0) + #endif -#ifdef ETHR_HAVE_INTERLOCKEDCOMPAREEXCHANGE_REL + +#endif /* ETHR_WIN_ATOMIC_COMMON__ */ + +#if ETHR_INCLUDE_ATOMIC_IMPL__ == 4 + +#define ETHR_HAVE_NATIVE_ATOMIC32 1 + +/* + * All used operations available as 32-bit intrinsics + */ + +#pragma intrinsic(_InterlockedDecrement) +#pragma intrinsic(_InterlockedIncrement) +#pragma intrinsic(_InterlockedExchangeAdd) +#pragma intrinsic(_InterlockedExchange) +#pragma intrinsic(_InterlockedAnd) +#pragma intrinsic(_InterlockedOr) +#ifdef ETHR_HAVE_INTERLOCKED_ACQUIRE_RELEASE_BARRIERS +#pragma intrinsic(_InterlockedExchangeAdd_acq) +#pragma intrinsic(_InterlockedIncrement_acq) +#pragma intrinsic(_InterlockedDecrement_rel) +#pragma intrinsic(_InterlockedCompareExchange_acq) #pragma intrinsic(_InterlockedCompareExchange_rel) #endif +#define ETHR_ILCKD__(X) _Interlocked ## X +#ifdef ETHR_HAVE_INTERLOCKED_ACQUIRE_RELEASE_BARRIERS +#define ETHR_ILCKD_ACQ__(X) _Interlocked ## X ## _acq +#define ETHR_ILCKD_REL__(X) _Interlocked ## X ## _rel +#else +#define ETHR_ILCKD_ACQ__(X) _Interlocked ## X +#define ETHR_ILCKD_REL__(X) _Interlocked ## X +#endif + +#define ETHR_NATMC_FUNC__(X) ethr_native_atomic32_ ## X +#define ETHR_ATMC_T__ ethr_native_atomic32_t +#define ETHR_AINT_T__ ethr_sint32_t + +#elif ETHR_INCLUDE_ATOMIC_IMPL__ == 8 + +#define ETHR_HAVE_NATIVE_ATOMIC64 1 + +/* + * _InterlockedCompareExchange64() is required. The other may not + * be available, but if so, we can generate them. + */ +#pragma intrinsic(_InterlockedCompareExchange64) + +#if ETHR_READ_AND_SET_WITHOUT_INTERLOCKED_OP__ +#define ETHR_OWN_ILCKD_INIT_VAL__(PTR) *(PTR) +#else +#define ETHR_OWN_ILCKD_INIT_VAL__(PTR) (__int64) 0 +#endif + +#define ETHR_OWN_ILCKD_BODY_IMPL__(FUNC, PTR, NEW, ACT, EXP, OPS, RET) \ +{ \ + __int64 NEW, ACT, EXP; \ + ACT = ETHR_OWN_ILCKD_INIT_VAL__(PTR); \ + do { \ + EXP = ACT; \ + { OPS; } \ + ACT = _InterlockedCompareExchange64(PTR, NEW, EXP); \ + } while (ACT != EXP); \ + return RET; \ +} + +#define ETHR_OWN_ILCKD_1_IMPL__(FUNC, NEW, ACT, EXP, OPS, RET) \ +static __forceinline __int64 \ +FUNC(__int64 volatile *ptr) \ +ETHR_OWN_ILCKD_BODY_IMPL__(FUNC, ptr, NEW, ACT, EXP, OPS, RET) + +#define ETHR_OWN_ILCKD_2_IMPL__(FUNC, NEW, ACT, EXP, OPS, ARG, RET) \ +static __forceinline __int64 \ +FUNC(__int64 volatile *ptr, __int64 ARG) \ +ETHR_OWN_ILCKD_BODY_IMPL__(FUNC, ptr, NEW, ACT, EXP, OPS, RET) + + +#ifdef ETHR_HAVE__INTERLOCKEDDECREMENT64 +#pragma intrinsic(_InterlockedDecrement64) +#else +ETHR_OWN_ILCKD_1_IMPL__(_InterlockedDecrement64, new, act, exp, + new = act - 1, new) +#endif +#ifdef ETHR_HAVE__INTERLOCKEDINCREMENT64 +#pragma intrinsic(_InterlockedIncrement64) +#else +ETHR_OWN_ILCKD_1_IMPL__(_InterlockedIncrement64, new, act, exp, + new = act + 1, new) +#endif +#ifdef ETHR_HAVE__INTERLOCKEDEXCHANGEADD64 +#pragma intrinsic(_InterlockedExchangeAdd64) +#else +ETHR_OWN_ILCKD_2_IMPL__(_InterlockedExchangeAdd64, new, act, exp, + new = act + arg, arg, act) +#endif +#ifdef ETHR_HAVE__INTERLOCKEDEXCHANGE64 +#pragma intrinsic(_InterlockedExchange64) +#else +ETHR_OWN_ILCKD_2_IMPL__(_InterlockedExchange64, new, act, exp, + new = arg, arg, act) +#endif +#ifdef ETHR_HAVE__INTERLOCKEDAND64 +#pragma intrinsic(_InterlockedAnd64) +#else +ETHR_OWN_ILCKD_2_IMPL__(_InterlockedAnd64, new, act, exp, + new = act & arg, arg, act) +#endif +#ifdef ETHR_HAVE__INTERLOCKEDOR64 +#pragma intrinsic(_InterlockedOr64) +#else +ETHR_OWN_ILCKD_2_IMPL__(_InterlockedOr64, new, act, exp, + new = act | arg, arg, act) +#endif +#ifdef ETHR_HAVE_INTERLOCKED_ACQUIRE_RELEASE_BARRIERS +#pragma intrinsic(_InterlockedExchangeAdd64_acq) +#pragma intrinsic(_InterlockedIncrement64_acq) +#pragma intrinsic(_InterlockedDecrement64_rel) +#pragma intrinsic(_InterlockedCompareExchange64_acq) +#pragma intrinsic(_InterlockedCompareExchange64_rel) +#endif + +#define ETHR_ILCKD__(X) _Interlocked ## X ## 64 +#ifdef ETHR_HAVE_INTERLOCKED_ACQUIRE_RELEASE_BARRIERS +#define ETHR_ILCKD_ACQ__(X) _Interlocked ## X ## 64_acq +#define ETHR_ILCKD_REL__(X) _Interlocked ## X ## 64_rel +#else +#define ETHR_ILCKD_ACQ__(X) _Interlocked ## X ## 64 +#define ETHR_ILCKD_REL__(X) _Interlocked ## X ## 64 +#endif + +#define ETHR_NATMC_FUNC__(X) ethr_native_atomic64_ ## X +#define ETHR_ATMC_T__ ethr_native_atomic64_t +#define ETHR_AINT_T__ ethr_sint64_t + +#else +#error "Unsupported integer size" +#endif typedef struct { - volatile LONG value; -} ethr_native_atomic_t; + volatile ETHR_AINT_T__ value; +} ETHR_ATMC_T__; -#define ETHR_MEMORY_BARRIER \ -do { \ - volatile LONG x___ = 0; \ - _InterlockedCompareExchange(&x___, (LONG) 1, (LONG) 0); \ -} while (0) +#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_ATOMIC_IMPL__) -#if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_AUX_IMPL__) +static ETHR_INLINE ETHR_AINT_T__ * +ETHR_NATMC_FUNC__(addr)(ETHR_ATMC_T__ *var) +{ + return (ETHR_AINT_T__ *) &var->value; +} static ETHR_INLINE void -ethr_native_atomic_init(ethr_native_atomic_t *var, long i) +ETHR_NATMC_FUNC__(init)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ i) { - var->value = (LONG) i; +#if ETHR_READ_AND_SET_WITHOUT_INTERLOCKED_OP__ + var->value = i; +#else + (void) ETHR_ILCKD__(Exchange)(&var->value, i); +#endif } static ETHR_INLINE void -ethr_native_atomic_set(ethr_native_atomic_t *var, long i) +ETHR_NATMC_FUNC__(set)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ i) { -#if ETHR_IMMED_ATOMIC_SET_GET_SAFE__ - var->value = (LONG) i; +#if ETHR_READ_AND_SET_WITHOUT_INTERLOCKED_OP__ + var->value = i; #else - (void) InterlockedExchange(&var->value, (LONG) i); + (void) ETHR_ILCKD__(Exchange)(&var->value, i); #endif } -static ETHR_INLINE long -ethr_native_atomic_read(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(read)(ETHR_ATMC_T__ *var) { -#if ETHR_IMMED_ATOMIC_SET_GET_SAFE__ +#if ETHR_READ_AND_SET_WITHOUT_INTERLOCKED_OP__ return var->value; #else - return InterlockedExchangeAdd(&var->value, (LONG) 0); + return ETHR_ILCKD__(ExchangeAdd)(&var->value, (ETHR_AINT_T__) 0); #endif } static ETHR_INLINE void -ethr_native_atomic_add(ethr_native_atomic_t *var, long incr) +ETHR_NATMC_FUNC__(add)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) { - (void) InterlockedExchangeAdd(&var->value, (LONG) incr); + (void) ETHR_ILCKD__(ExchangeAdd)(&var->value, incr); } -static ETHR_INLINE long -ethr_native_atomic_add_return(ethr_native_atomic_t *var, long i) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(add_return)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ i) { - LONG tmp = InterlockedExchangeAdd(&var->value, (LONG) i); - return tmp + i; + return ETHR_ILCKD__(ExchangeAdd)(&var->value, i) + i; } static ETHR_INLINE void -ethr_native_atomic_inc(ethr_native_atomic_t *var) +ETHR_NATMC_FUNC__(inc)(ETHR_ATMC_T__ *var) { - (void) InterlockedIncrement(&var->value); + (void) ETHR_ILCKD__(Increment)(&var->value); } static ETHR_INLINE void -ethr_native_atomic_dec(ethr_native_atomic_t *var) +ETHR_NATMC_FUNC__(dec)(ETHR_ATMC_T__ *var) { - (void) InterlockedDecrement(&var->value); + (void) ETHR_ILCKD__(Decrement)(&var->value); } -static ETHR_INLINE long -ethr_native_atomic_inc_return(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(inc_return)(ETHR_ATMC_T__ *var) { - return (long) InterlockedIncrement(&var->value); + return ETHR_ILCKD__(Increment)(&var->value); } -static ETHR_INLINE long -ethr_native_atomic_dec_return(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(dec_return)(ETHR_ATMC_T__ *var) { - return (long) InterlockedDecrement(&var->value); + return ETHR_ILCKD__(Decrement)(&var->value); } -static ETHR_INLINE long -ethr_native_atomic_and_retold(ethr_native_atomic_t *var, long mask) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(and_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { - return (long) _InterlockedAnd(&var->value, mask); + return ETHR_ILCKD__(And)(&var->value, mask); } -static ETHR_INLINE long -ethr_native_atomic_or_retold(ethr_native_atomic_t *var, long mask) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(or_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { - return (long) _InterlockedOr(&var->value, mask); + return ETHR_ILCKD__(Or)(&var->value, mask); } - -static ETHR_INLINE long -ethr_native_atomic_cmpxchg(ethr_native_atomic_t *var, long new, long old) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ old) { - return (long) _InterlockedCompareExchange(&var->value, (LONG) new, (LONG) old); + return ETHR_ILCKD__(CompareExchange)(&var->value, new, old); } -static ETHR_INLINE long -ethr_native_atomic_xchg(ethr_native_atomic_t *var, long new) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(xchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new) { - return (long) InterlockedExchange(&var->value, (LONG) new); + return ETHR_ILCKD__(Exchange)(&var->value, new); } /* * Atomic ops with at least specified barriers. */ -static ETHR_INLINE long -ethr_native_atomic_read_acqb(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(read_acqb)(ETHR_ATMC_T__ *var) { -#ifdef ETHR_HAVE_INTERLOCKEDEXCHANGEADDACQUIRE - return (long) InterlockedExchangeAddAcquire(&var->value, (LONG) 0); +#if ETHR_READ_ACQB_AND_SET_RELB_COMPILER_BARRIER_ONLY__ + ETHR_AINT_T__ val = var->value; + ETHR_COMPILER_BARRIER; + return val; #else - return (long) InterlockedExchangeAdd(&var->value, (LONG) 0); + return ETHR_ILCKD_ACQ__(ExchangeAdd)(&var->value, (ETHR_AINT_T__) 0); #endif } -static ETHR_INLINE long -ethr_native_atomic_inc_return_acqb(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(inc_return_acqb)(ETHR_ATMC_T__ *var) { -#ifdef ETHR_HAVE_INTERLOCKEDINCREMENTACQUIRE - return (long) InterlockedIncrementAcquire(&var->value); -#else - return (long) InterlockedIncrement(&var->value); -#endif + return ETHR_ILCKD_ACQ__(Increment)(&var->value); } static ETHR_INLINE void -ethr_native_atomic_set_relb(ethr_native_atomic_t *var, long i) +ETHR_NATMC_FUNC__(set_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ i) { - (void) InterlockedExchange(&var->value, (LONG) i); +#if ETHR_READ_ACQB_AND_SET_RELB_COMPILER_BARRIER_ONLY__ + ETHR_COMPILER_BARRIER; + var->value = i; +#else + (void) ETHR_ILCKD_REL__(Exchange)(&var->value, i); +#endif } static ETHR_INLINE void -ethr_native_atomic_dec_relb(ethr_native_atomic_t *var) +ETHR_NATMC_FUNC__(dec_relb)(ETHR_ATMC_T__ *var) { -#ifdef ETHR_HAVE_INTERLOCKEDDECREMENTRELEASE - (void) InterlockedDecrementRelease(&var->value); -#else - (void) InterlockedDecrement(&var->value); -#endif + (void) ETHR_ILCKD_REL__(Decrement)(&var->value); } -static ETHR_INLINE long -ethr_native_atomic_dec_return_relb(ethr_native_atomic_t *var) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(dec_return_relb)(ETHR_ATMC_T__ *var) { -#ifdef ETHR_HAVE_INTERLOCKEDDECREMENTRELEASE - return (long) InterlockedDecrementRelease(&var->value); -#else - return (long) InterlockedDecrement(&var->value); -#endif + return ETHR_ILCKD_REL__(Decrement)(&var->value); } -static ETHR_INLINE long -ethr_native_atomic_cmpxchg_acqb(ethr_native_atomic_t *var, long new, long old) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg_acqb)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ old) { -#ifdef ETHR_HAVE_INTERLOCKEDCOMPAREEXCHANGE_ACQ - return (long) _InterlockedCompareExchange_acq(&var->value, (LONG) new, (LONG) old); -#else - return (long) _InterlockedCompareExchange(&var->value, (LONG) new, (LONG) old); -#endif + return ETHR_ILCKD_ACQ__(CompareExchange)(&var->value, new, old); } -static ETHR_INLINE long -ethr_native_atomic_cmpxchg_relb(ethr_native_atomic_t *var, long new, long old) +static ETHR_INLINE ETHR_AINT_T__ +ETHR_NATMC_FUNC__(cmpxchg_relb)(ETHR_ATMC_T__ *var, + ETHR_AINT_T__ new, + ETHR_AINT_T__ old) { - -#ifdef ETHR_HAVE_INTERLOCKEDCOMPAREEXCHANGE_REL - return (long) _InterlockedCompareExchange_rel(&var->value, (LONG) new, (LONG) old); -#else - return (long) _InterlockedCompareExchange(&var->value, (LONG) new, (LONG) old); -#endif + return ETHR_ILCKD_REL__(CompareExchange)(&var->value, new, old); } -#endif +#endif /* ETHR_TRY_INLINE_FUNCS */ -#endif +#undef ETHR_ILCKD__ +#undef ETHR_ILCKD_ACQ__ +#undef ETHR_ILCKD_REL__ +#undef ETHR_NATMC_FUNC__ +#undef ETHR_ATMC_T__ +#undef ETHR_AINT_T__ +#undef ETHR_READ_AND_SET_WITHOUT_INTERLOCKED_OP__ +#undef ETHR_READ_ACQB_AND_SET_RELB_COMPILER_BARRIER_ONLY__ + +#endif /* _MSC_VER */ + +#endif /* ETHR_INCLUDE_ATOMIC_IMPL__ */ diff --git a/erts/include/internal/win/ethr_event.h b/erts/include/internal/win/ethr_event.h index af57c20f91..598816b2c6 100644 --- a/erts/include/internal/win/ethr_event.h +++ b/erts/include/internal/win/ethr_event.h @@ -21,22 +21,24 @@ * Author: Rickard Green */ -#define ETHR_EVENT_OFF_WAITER__ ((LONG) -1) -#define ETHR_EVENT_OFF__ ((LONG) 1) -#define ETHR_EVENT_ON__ ((LONG) 0) +#define ETHR_EVENT_OFF_WAITER__ ((long) -1) +#define ETHR_EVENT_OFF__ ((long) 1) +#define ETHR_EVENT_ON__ ((long) 0) typedef struct { - volatile LONG state; + volatile long state; HANDLE handle; } ethr_event; #if defined(ETHR_TRY_INLINE_FUNCS) || defined(ETHR_EVENT_IMPL__) +#pragma intrinsic(_InterlockedExchange) + static ETHR_INLINE void ETHR_INLINE_FUNC_NAME_(ethr_event_set)(ethr_event *e) { - /* InterlockedExchange() imply a full memory barrier which is important */ - LONG state = InterlockedExchange(&e->state, ETHR_EVENT_ON__); + /* _InterlockedExchange() imply a full memory barrier which is important */ + long state = _InterlockedExchange(&e->state, ETHR_EVENT_ON__); if (state == ETHR_EVENT_OFF_WAITER__) { if (!SetEvent(e->handle)) ETHR_FATAL_ERROR__(ethr_win_get_errno__()); @@ -46,7 +48,7 @@ ETHR_INLINE_FUNC_NAME_(ethr_event_set)(ethr_event *e) static ETHR_INLINE void ETHR_INLINE_FUNC_NAME_(ethr_event_reset)(ethr_event *e) { - /* InterlockedExchange() imply a full memory barrier which is important */ + /* _InterlockedExchange() imply a full memory barrier which is important */ InterlockedExchange(&e->state, ETHR_EVENT_OFF__); } diff --git a/erts/include/internal/win/ethread.h b/erts/include/internal/win/ethread.h index b52710f6a3..c01b17cf14 100644 --- a/erts/include/internal/win/ethread.h +++ b/erts/include/internal/win/ethread.h @@ -25,7 +25,11 @@ #ifndef ETHREAD_WIN_H__ #define ETHREAD_WIN_H__ +#define ETHR_ATOMIC_WANT_32BIT_IMPL__ #include "ethr_atomic.h" -#define ETHR_HAVE_NATIVE_ATOMICS 1 +#if ETHR_SIZEOF_PTR == 8 +# define ETHR_ATOMIC_WANT_64BIT_IMPL__ +# include "ethr_atomic.h" +#endif #endif diff --git a/erts/lib_src/Makefile.in b/erts/lib_src/Makefile.in index 0d3181cace..757b3b24e2 100644 --- a/erts/lib_src/Makefile.in +++ b/erts/lib_src/Makefile.in @@ -283,6 +283,7 @@ endif ETHR_THR_LIB_BASE_DIR=@ETHR_THR_LIB_BASE_DIR@ ifneq ($(strip $(ETHR_LIB_NAME)),) ETHREAD_LIB_SRC=common/ethr_aux.c \ + common/ethr_atomics.c \ common/ethr_mutex.c \ common/ethr_cbf.c \ $(ETHR_THR_LIB_BASE_DIR)/ethread.c \ @@ -381,6 +382,11 @@ $(ERTS_LIB): $(ERTS_LIB_OBJS) # Object files # +ifeq ($(TYPE)-@GCC@,debug-yes) +$(r_OBJ_DIR)/ethr_aux.o: common/ethr_aux.c + $(CC) $(THR_DEFS) $(CFLAGS) -Wno-unused-function $(INCLUDES) -c $< -o $@ +endif + $(r_OBJ_DIR)/%.o: common/%.c $(CC) $(THR_DEFS) $(CFLAGS) $(INCLUDES) -c $< -o $@ @@ -445,6 +451,7 @@ INTERNAL_RELEASE_INCLUDES= \ $(ERTS_INCL_INT)/ethread.h \ $(ERTS_INCL_INT)/ethr_mutex.h \ $(ERTS_INCL_INT)/ethr_optimized_fallbacks.h \ + $(ERTS_INCL_INT)/ethr_atomics.h \ $(ERTS_INCL_INT)/$(TARGET)/ethread.mk \ $(ERTS_INCL_INT)/$(TARGET)/erts_internal.mk \ $(ERTS_INCL_INT)/$(TARGET)/ethread_header_config.h \ diff --git a/erts/lib_src/common/ethr_atomics.c b/erts/lib_src/common/ethr_atomics.c new file mode 100644 index 0000000000..94557d904a --- /dev/null +++ b/erts/lib_src/common/ethr_atomics.c @@ -0,0 +1,402 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010. 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 + * 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 online 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. + * + * %CopyrightEnd% + */ + +/* + * Description: The ethread atomic API + * Author: Rickard Green + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define ETHR_INLINE_FUNC_NAME_(X) X ## __ +#define ETHR_ATOMIC_IMPL__ + +#include "ethread.h" +#include "ethr_internal.h" + +#ifndef ETHR_HAVE_NATIVE_ATOMICS +ethr_atomic_protection_t ethr_atomic_protection__[1 << ETHR_ATOMIC_ADDR_BITS]; +#endif + +int +ethr_init_atomics(void) +{ +#ifndef ETHR_HAVE_NATIVE_ATOMICS + { + int i; + for (i = 0; i < (1 << ETHR_ATOMIC_ADDR_BITS); i++) { + int res = ethr_spinlock_init(ðr_atomic_protection__[i].u.lck); + if (res != 0) + return res; + } + } +#endif + return 0; +} + +/* + * --- Pointer size atomics --------------------------------------------------- + */ + +ethr_sint_t * +ethr_atomic_addr(ethr_atomic_t *var) +{ + ETHR_ASSERT(var); + return ethr_atomic_addr__(var); +} + +void +ethr_atomic_init(ethr_atomic_t *var, ethr_sint_t i) +{ + ETHR_ASSERT(var); + ethr_atomic_init__(var, i); +} + +void +ethr_atomic_set(ethr_atomic_t *var, ethr_sint_t i) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + ethr_atomic_set__(var, i); +} + +ethr_sint_t +ethr_atomic_read(ethr_atomic_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic_read__(var); +} + +ethr_sint_t +ethr_atomic_add_read(ethr_atomic_t *var, ethr_sint_t incr) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic_add_read__(var, incr); +} + +ethr_sint_t +ethr_atomic_inc_read(ethr_atomic_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic_inc_read__(var); +} + +ethr_sint_t +ethr_atomic_dec_read(ethr_atomic_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic_dec_read__(var); +} + +void +ethr_atomic_add(ethr_atomic_t *var, ethr_sint_t incr) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + ethr_atomic_add__(var, incr); +} + +void +ethr_atomic_inc(ethr_atomic_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + ethr_atomic_inc__(var); +} + +void +ethr_atomic_dec(ethr_atomic_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + ethr_atomic_dec__(var); +} + +ethr_sint_t +ethr_atomic_read_band(ethr_atomic_t *var, ethr_sint_t mask) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic_read_band__(var, mask); +} + +ethr_sint_t +ethr_atomic_read_bor(ethr_atomic_t *var, ethr_sint_t mask) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic_read_bor__(var, mask); +} + +ethr_sint_t +ethr_atomic_xchg(ethr_atomic_t *var, ethr_sint_t new) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic_xchg__(var, new); +} + +ethr_sint_t +ethr_atomic_cmpxchg(ethr_atomic_t *var, ethr_sint_t new, ethr_sint_t expected) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic_cmpxchg__(var, new, expected); +} + +ethr_sint_t +ethr_atomic_read_acqb(ethr_atomic_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic_read_acqb__(var); +} + +ethr_sint_t +ethr_atomic_inc_read_acqb(ethr_atomic_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic_inc_read_acqb__(var); +} + +void +ethr_atomic_set_relb(ethr_atomic_t *var, ethr_sint_t i) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + ethr_atomic_set_relb__(var, i); +} + +void +ethr_atomic_dec_relb(ethr_atomic_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + ethr_atomic_dec_relb__(var); +} + +ethr_sint_t +ethr_atomic_dec_read_relb(ethr_atomic_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic_dec_read_relb__(var); +} + +ethr_sint_t +ethr_atomic_cmpxchg_acqb(ethr_atomic_t *var, ethr_sint_t new, ethr_sint_t exp) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic_cmpxchg_acqb__(var, new, exp); +} + +ethr_sint_t +ethr_atomic_cmpxchg_relb(ethr_atomic_t *var, ethr_sint_t new, ethr_sint_t exp) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic_cmpxchg_relb__(var, new, exp); +} + + +/* + * --- 32-bit atomics --------------------------------------------------------- + */ + +ethr_sint32_t * +ethr_atomic32_addr(ethr_atomic32_t *var) +{ + ETHR_ASSERT(var); + return ethr_atomic32_addr__(var); +} + +void +ethr_atomic32_init(ethr_atomic32_t *var, ethr_sint32_t i) +{ + ETHR_ASSERT(var); + ethr_atomic32_init__(var, i); +} + +void +ethr_atomic32_set(ethr_atomic32_t *var, ethr_sint32_t i) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + ethr_atomic32_set__(var, i); +} + +ethr_sint32_t +ethr_atomic32_read(ethr_atomic32_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic32_read__(var); +} + + +ethr_sint32_t +ethr_atomic32_add_read(ethr_atomic32_t *var, ethr_sint32_t incr) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic32_add_read__(var, incr); +} + +ethr_sint32_t +ethr_atomic32_inc_read(ethr_atomic32_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic32_inc_read__(var); +} + +ethr_sint32_t +ethr_atomic32_dec_read(ethr_atomic32_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic32_dec_read__(var); +} + +void +ethr_atomic32_add(ethr_atomic32_t *var, ethr_sint32_t incr) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + ethr_atomic32_add__(var, incr); +} + +void +ethr_atomic32_inc(ethr_atomic32_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + ethr_atomic32_inc__(var); +} + +void +ethr_atomic32_dec(ethr_atomic32_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + ethr_atomic32_dec__(var); +} + +ethr_sint32_t +ethr_atomic32_read_band(ethr_atomic32_t *var, ethr_sint32_t mask) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic32_read_band__(var, mask); +} + +ethr_sint32_t +ethr_atomic32_read_bor(ethr_atomic32_t *var, ethr_sint32_t mask) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic32_read_bor__(var, mask); +} + +ethr_sint32_t +ethr_atomic32_xchg(ethr_atomic32_t *var, ethr_sint32_t new) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic32_xchg__(var, new); +} + +ethr_sint32_t +ethr_atomic32_cmpxchg(ethr_atomic32_t *var, + ethr_sint32_t new, + ethr_sint32_t expected) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic32_cmpxchg__(var, new, expected); +} + +ethr_sint32_t +ethr_atomic32_read_acqb(ethr_atomic32_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic32_read_acqb__(var); +} + +ethr_sint32_t +ethr_atomic32_inc_read_acqb(ethr_atomic32_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic32_inc_read_acqb__(var); +} + +void +ethr_atomic32_set_relb(ethr_atomic32_t *var, ethr_sint32_t i) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + ethr_atomic32_set_relb__(var, i); +} + +void +ethr_atomic32_dec_relb(ethr_atomic32_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + ethr_atomic32_dec_relb__(var); +} + +ethr_sint32_t +ethr_atomic32_dec_read_relb(ethr_atomic32_t *var) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic32_dec_read_relb__(var); +} + +ethr_sint32_t +ethr_atomic32_cmpxchg_acqb(ethr_atomic32_t *var, + ethr_sint32_t new, + ethr_sint32_t exp) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic32_cmpxchg_acqb__(var, new, exp); +} + +ethr_sint32_t +ethr_atomic32_cmpxchg_relb(ethr_atomic32_t *var, + ethr_sint32_t new, + ethr_sint32_t exp) +{ + ETHR_ASSERT(!ethr_not_inited__); + ETHR_ASSERT(var); + return ethr_atomic32_cmpxchg_relb__(var, new, exp); +} + diff --git a/erts/lib_src/common/ethr_aux.c b/erts/lib_src/common/ethr_aux.c index 4db4cffd3a..2c3e25a805 100644 --- a/erts/lib_src/common/ethr_aux.c +++ b/erts/lib_src/common/ethr_aux.c @@ -31,7 +31,10 @@ #define ETHR_INLINE_FUNC_NAME_(X) X ## __ #define ETHR_AUX_IMPL__ - +#define ETHR_ATOMIC_IMPL__ /* Needed in order to pull in + native atomic implementations + for optimized fallbacks of + spinlocks and rwspinlocks */ #include "ethread.h" #include "ethr_internal.h" #include <string.h> @@ -51,10 +54,6 @@ int ethr_not_inited__ = 1; ethr_memory_allocators ethr_mem__ = ETHR_MEM_ALLOCS_DEF_INITER__; -#ifndef ETHR_HAVE_OPTIMIZED_ATOMIC_OPS -ethr_atomic_protection_t ethr_atomic_protection__[1 << ETHR_ATOMIC_ADDR_BITS]; -#endif - void *(*ethr_thr_prepare_func__)(void) = NULL; void (*ethr_thr_parent_func__)(void *) = NULL; void (*ethr_thr_child_func__)(void *) = NULL; @@ -138,16 +137,9 @@ ethr_init_common__(ethr_init_data *id) #endif ethr_max_stack_size__ = ETHR_B2KW(ethr_max_stack_size__); -#ifndef ETHR_HAVE_OPTIMIZED_ATOMIC_OPS - { - int i; - for (i = 0; i < (1 << ETHR_ATOMIC_ADDR_BITS); i++) { - res = ethr_spinlock_init(ðr_atomic_protection__[i].u.lck); - if (res != 0) - return res; - } - } -#endif + res = ethr_init_atomics(); + if (res != 0) + return res; res = ethr_mutex_lib_init(erts_get_cpu_configured(ethr_cpu_info__)); if (res != 0) @@ -279,14 +271,6 @@ typedef union { static ethr_spinlock_t ts_ev_alloc_lock; static ethr_ts_event *free_ts_ev; -#if SIZEOF_VOID_P == SIZEOF_INT -typedef unsigned int EthrPtrSzUInt; -#elif SIZEOF_VOID_P == SIZEOF_LONG -typedef unsigned long EthrPtrSzUInt; -#else -#error No pointer sized integer type -#endif - static ethr_ts_event *ts_event_pool(int size, ethr_ts_event **endpp) { int i; @@ -295,16 +279,16 @@ static ethr_ts_event *ts_event_pool(int size, ethr_ts_event **endpp) + ETHR_CACHE_LINE_SIZE); if (!atsev) return NULL; - if ((((EthrPtrSzUInt) atsev) & ETHR_CACHE_LINE_MASK) == 0) + if ((((ethr_uint_t) atsev) & ETHR_CACHE_LINE_MASK) == 0) atsev = ((ethr_aligned_ts_event *) - ((((EthrPtrSzUInt) atsev) & ~ETHR_CACHE_LINE_MASK) + ((((ethr_uint_t) atsev) & ~ETHR_CACHE_LINE_MASK) + ETHR_CACHE_LINE_SIZE)); for (i = 1; i < size; i++) { atsev[i-1].ts_ev.next = &atsev[i].ts_ev; - ethr_atomic_init(&atsev[i-1].ts_ev.uaflgs, 0); + ethr_atomic32_init(&atsev[i-1].ts_ev.uaflgs, 0); atsev[i-1].ts_ev.iflgs = 0; } - ethr_atomic_init(&atsev[size-1].ts_ev.uaflgs, 0); + ethr_atomic32_init(&atsev[size-1].ts_ev.uaflgs, 0); atsev[size-1].ts_ev.iflgs = 0; atsev[size-1].ts_ev.next = NULL; if (endpp) @@ -466,170 +450,6 @@ int ethr_get_main_thr_status(int *on) return 0; } - -/* Atomics */ - -void -ethr_atomic_init(ethr_atomic_t *var, long i) -{ - ETHR_ASSERT(var); - ethr_atomic_init__(var, i); -} - -void -ethr_atomic_set(ethr_atomic_t *var, long i) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - ethr_atomic_set__(var, i); -} - -long -ethr_atomic_read(ethr_atomic_t *var) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - return ethr_atomic_read__(var); -} - - -long -ethr_atomic_add_read(ethr_atomic_t *var, long incr) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - return ethr_atomic_add_read__(var, incr); -} - -long -ethr_atomic_inc_read(ethr_atomic_t *var) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - return ethr_atomic_inc_read__(var); -} - -long -ethr_atomic_dec_read(ethr_atomic_t *var) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - return ethr_atomic_dec_read__(var); -} - -void -ethr_atomic_add(ethr_atomic_t *var, long incr) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - ethr_atomic_add__(var, incr); -} - -void -ethr_atomic_inc(ethr_atomic_t *var) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - ethr_atomic_inc__(var); -} - -void -ethr_atomic_dec(ethr_atomic_t *var) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - ethr_atomic_dec__(var); -} - -long -ethr_atomic_read_band(ethr_atomic_t *var, long mask) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - return ethr_atomic_read_band__(var, mask); -} - -long -ethr_atomic_read_bor(ethr_atomic_t *var, long mask) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - return ethr_atomic_read_bor__(var, mask); -} - -long -ethr_atomic_xchg(ethr_atomic_t *var, long new) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - return ethr_atomic_xchg__(var, new); -} - -long -ethr_atomic_cmpxchg(ethr_atomic_t *var, long new, long expected) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - return ethr_atomic_cmpxchg__(var, new, expected); -} - -long -ethr_atomic_read_acqb(ethr_atomic_t *var) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - return ethr_atomic_read_acqb__(var); -} - -long -ethr_atomic_inc_read_acqb(ethr_atomic_t *var) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - return ethr_atomic_inc_read_acqb__(var); -} - -void -ethr_atomic_set_relb(ethr_atomic_t *var, long i) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - ethr_atomic_set_relb__(var, i); -} - -void -ethr_atomic_dec_relb(ethr_atomic_t *var) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - ethr_atomic_dec_relb__(var); -} - -long -ethr_atomic_dec_read_relb(ethr_atomic_t *var) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - return ethr_atomic_dec_read_relb__(var); -} - -long -ethr_atomic_cmpxchg_acqb(ethr_atomic_t *var, long new, long exp) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - return ethr_atomic_cmpxchg_acqb__(var, new, exp); -} - -long -ethr_atomic_cmpxchg_relb(ethr_atomic_t *var, long new, long exp) -{ - ETHR_ASSERT(!ethr_not_inited__); - ETHR_ASSERT(var); - return ethr_atomic_cmpxchg_relb__(var, new, exp); -} - - /* Spinlocks and rwspinlocks */ int diff --git a/erts/lib_src/common/ethr_mutex.c b/erts/lib_src/common/ethr_mutex.c index 95fedc0ba2..2ddef32dfc 100644 --- a/erts/lib_src/common/ethr_mutex.c +++ b/erts/lib_src/common/ethr_mutex.c @@ -206,16 +206,16 @@ static void hard_debug_chk_q__(struct ethr_mutex_base_ *, int); #ifdef ETHR_USE_OWN_RWMTX_IMPL__ static void rwmutex_transfer_read_lock(ethr_rwmutex *rwmtx, - long initial, + ethr_sint32_t initial, int q_locked); static void rwmutex_unlock_wake(ethr_rwmutex *rwmtx, int have_w, - long initial, + ethr_sint32_t initial, int transfer_read_lock); static int rwmutex_try_complete_runlock(ethr_rwmutex *rwmtx, - long initial, + ethr_sint32_t initial, ethr_ts_event *tse, int start_next_ix, int check_before_try, @@ -242,12 +242,12 @@ rwmutex_freqread_rdrs_add(ethr_rwmutex *rwmtx, int inc) { if (type == ETHR_RWMUTEX_TYPE_FREQUENT_READ || ix == 0) - ethr_atomic_add(&rwmtx->tdata.ra[ix].data.readers, inc); + ethr_atomic32_add(&rwmtx->tdata.ra[ix].data.readers, inc); else { ETHR_ASSERT(type == ETHR_RWMUTEX_TYPE_EXTREMELY_FREQUENT_READ); - ETHR_ASSERT(ethr_atomic_read(&rwmtx->tdata.ra[ix].data.readers) == 0); + ETHR_ASSERT(ethr_atomic32_read(&rwmtx->tdata.ra[ix].data.readers) == 0); ETHR_ASSERT(inc == 1); - ethr_atomic_set(&rwmtx->tdata.ra[ix].data.readers, (long) 1); + ethr_atomic32_set(&rwmtx->tdata.ra[ix].data.readers, (ethr_sint32_t) 1); } } @@ -258,15 +258,15 @@ rwmutex_freqread_rdrs_inc(ethr_rwmutex *rwmtx, ethr_ts_event *tse) if (rwmtx->type == ETHR_RWMUTEX_TYPE_FREQUENT_READ) { ix = tse->rgix; atomic_inc: - ethr_atomic_inc(&rwmtx->tdata.ra[ix].data.readers); + ethr_atomic32_inc(&rwmtx->tdata.ra[ix].data.readers); } else { ix = tse->mtix; if (ix == 0) goto atomic_inc; ETHR_ASSERT(rwmtx->type == ETHR_RWMUTEX_TYPE_EXTREMELY_FREQUENT_READ); - ETHR_ASSERT(ethr_atomic_read(&rwmtx->tdata.ra[ix].data.readers) == 0); - ethr_atomic_set(&rwmtx->tdata.ra[ix].data.readers, (long) 1); + ETHR_ASSERT(ethr_atomic32_read(&rwmtx->tdata.ra[ix].data.readers) == 0); + ethr_atomic32_set(&rwmtx->tdata.ra[ix].data.readers, (ethr_sint32_t) 1); } } @@ -279,64 +279,65 @@ rwmutex_freqread_rdrs_dec(ethr_rwmutex *rwmtx, ethr_ts_event *tse) if (rwmtx->type == ETHR_RWMUTEX_TYPE_FREQUENT_READ) { ix = tse->rgix; atomic_dec: - ethr_atomic_dec(&rwmtx->tdata.ra[ix].data.readers); + ethr_atomic32_dec(&rwmtx->tdata.ra[ix].data.readers); } else { ix = tse->mtix; if (ix == 0) goto atomic_dec; ETHR_ASSERT(rwmtx->type == ETHR_RWMUTEX_TYPE_EXTREMELY_FREQUENT_READ); - ETHR_ASSERT(ethr_atomic_read(&rwmtx->tdata.ra[ix].data.readers) == 1); - ethr_atomic_set(&rwmtx->tdata.ra[ix].data.readers, (long) 0); + ETHR_ASSERT(ethr_atomic32_read(&rwmtx->tdata.ra[ix].data.readers) == 1); + ethr_atomic32_set(&rwmtx->tdata.ra[ix].data.readers, (ethr_sint32_t) 0); } } #endif -static ETHR_INLINE long +static ETHR_INLINE ethr_sint32_t rwmutex_freqread_rdrs_dec_read(ethr_rwmutex *rwmtx, ethr_ts_event *tse) { int ix; if (rwmtx->type == ETHR_RWMUTEX_TYPE_FREQUENT_READ) { ix = tse->rgix; atomic_dec_read: - return ethr_atomic_dec_read(&rwmtx->tdata.ra[ix].data.readers); + return ethr_atomic32_dec_read(&rwmtx->tdata.ra[ix].data.readers); } else { ix = tse->mtix; if (ix == 0) goto atomic_dec_read; ETHR_ASSERT(rwmtx->type == ETHR_RWMUTEX_TYPE_EXTREMELY_FREQUENT_READ); - ETHR_ASSERT(ethr_atomic_read(&rwmtx->tdata.ra[ix].data.readers) == 1); - ethr_atomic_set(&rwmtx->tdata.ra[ix].data.readers, (long) 0); - return (long) 0; + ETHR_ASSERT(ethr_atomic32_read(&rwmtx->tdata.ra[ix].data.readers) == 1); + ethr_atomic32_set(&rwmtx->tdata.ra[ix].data.readers, (ethr_sint32_t) 0); + return (ethr_sint32_t) 0; } } -static ETHR_INLINE long +static ETHR_INLINE ethr_sint32_t rwmutex_freqread_rdrs_dec_read_relb(ethr_rwmutex *rwmtx, ethr_ts_event *tse) { int ix; if (rwmtx->type == ETHR_RWMUTEX_TYPE_FREQUENT_READ) { ix = tse->rgix; atomic_dec_read: - return ethr_atomic_dec_read_relb(&rwmtx->tdata.ra[ix].data.readers); + return ethr_atomic32_dec_read_relb(&rwmtx->tdata.ra[ix].data.readers); } else { ix = tse->mtix; if (ix == 0) goto atomic_dec_read; ETHR_ASSERT(rwmtx->type == ETHR_RWMUTEX_TYPE_EXTREMELY_FREQUENT_READ); - ETHR_ASSERT(ethr_atomic_read(&rwmtx->tdata.ra[ix].data.readers) == 1); - ethr_atomic_set_relb(&rwmtx->tdata.ra[ix].data.readers, (long) 0); - return (long) 0; + ETHR_ASSERT(ethr_atomic32_read(&rwmtx->tdata.ra[ix].data.readers) == 1); + ethr_atomic32_set_relb(&rwmtx->tdata.ra[ix].data.readers, + (ethr_sint32_t) 0); + return (ethr_sint32_t) 0; } } -static ETHR_INLINE long +static ETHR_INLINE ethr_sint32_t rwmutex_freqread_rdrs_read(ethr_rwmutex *rwmtx, int ix) { - long res = ethr_atomic_read(&rwmtx->tdata.ra[ix].data.readers); + ethr_sint32_t res = ethr_atomic32_read(&rwmtx->tdata.ra[ix].data.readers); #ifdef ETHR_DEBUG switch (rwmtx->type) { case ETHR_RWMUTEX_TYPE_FREQUENT_READ: @@ -402,19 +403,19 @@ static void event_wait(struct ethr_mutex_base_ *mtxb, ethr_ts_event *tse, int spincount, - long type, + ethr_sint32_t type, int is_rwmtx, int is_freq_read) { int locked = 0; - long act; + ethr_sint32_t act; int need_try_complete_runlock = 0; int transfer_read_lock = 0; /* Need to enqueue and wait... */ tse->uflgs = type; - ethr_atomic_set(&tse->uaflgs, type); + ethr_atomic32_set(&tse->uaflgs, type); ETHR_MTX_Q_LOCK(&mtxb->qlck); locked = 1; @@ -423,7 +424,7 @@ event_wait(struct ethr_mutex_base_ *mtxb, hard_debug_chk_q__(mtxb, is_rwmtx); #endif - act = ethr_atomic_read(&mtxb->flgs); + act = ethr_atomic32_read(&mtxb->flgs); if (act & type) { @@ -453,7 +454,7 @@ event_wait(struct ethr_mutex_base_ *mtxb, /* Set wait bit */ while (1) { - long new, exp = act; + ethr_sint32_t new, exp = act; need_try_complete_runlock = 0; transfer_read_lock = 0; @@ -484,7 +485,7 @@ event_wait(struct ethr_mutex_base_ *mtxb, } } - act = ethr_atomic_cmpxchg_acqb(&mtxb->flgs, new, exp); + act = ethr_atomic32_cmpxchg_acqb(&mtxb->flgs, new, exp); if (exp == act) { if (new & type) { act = new; @@ -559,7 +560,7 @@ event_wait(struct ethr_mutex_base_ *mtxb, while (1) { ethr_event_reset(&tse->event); - act = ethr_atomic_read_acqb(&tse->uaflgs); + act = ethr_atomic32_read_acqb(&tse->uaflgs); if (!act) goto done; /* Got it */ @@ -567,7 +568,7 @@ event_wait(struct ethr_mutex_base_ *mtxb, ethr_event_swait(&tse->event, spincount); /* swait result: 0 || EINTR */ - act = ethr_atomic_read_acqb(&tse->uaflgs); + act = ethr_atomic32_read_acqb(&tse->uaflgs); if (!act) goto done; /* Got it */ } @@ -587,7 +588,7 @@ wake_writer(struct ethr_mutex_base_ *mtxb, int is_rwmtx) dequeue(&mtxb->q, tse, tse); ETHR_ASSERT(tse->uflgs == ETHR_RWMTX_W_WAIT_FLG__); - ETHR_ASSERT(ethr_atomic_read(&tse->uaflgs) == ETHR_RWMTX_W_WAIT_FLG__); + ETHR_ASSERT(ethr_atomic32_read(&tse->uaflgs) == ETHR_RWMTX_W_WAIT_FLG__); #ifdef ETHR_MTX_HARD_DEBUG_WSQ mtxb->ws--; #endif @@ -597,7 +598,7 @@ wake_writer(struct ethr_mutex_base_ *mtxb, int is_rwmtx) ETHR_MTX_Q_UNLOCK(&mtxb->qlck); - ethr_atomic_set(&tse->uaflgs, 0); + ethr_atomic32_set(&tse->uaflgs, 0); ethr_event_set(&tse->event); } @@ -649,11 +650,11 @@ int check_readers_array(ethr_rwmutex *rwmtx, static ETHR_INLINE void write_lock_wait(struct ethr_mutex_base_ *mtxb, - long initial, + ethr_sint32_t initial, int is_rwmtx, int is_freq_read) { - long act = initial; + ethr_sint32_t act = initial; int scnt, start_scnt; ethr_ts_event *tse = NULL; int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; @@ -706,13 +707,13 @@ write_lock_wait(struct ethr_mutex_base_ *mtxb, until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; ETHR_YIELD(); } - act = ethr_atomic_read(&mtxb->flgs); + act = ethr_atomic32_read(&mtxb->flgs); scnt--; } - act = ethr_atomic_cmpxchg_acqb(&mtxb->flgs, - ETHR_RWMTX_W_FLG__, - 0); + act = ethr_atomic32_cmpxchg_acqb(&mtxb->flgs, + ETHR_RWMTX_W_FLG__, + 0); if (act == 0) goto done; /* Got it */ } @@ -756,16 +757,16 @@ mtxb_init(struct ethr_mutex_base_ *mtxb, } mtxb->q = NULL; - ethr_atomic_init(&mtxb->flgs, 0); + ethr_atomic32_init(&mtxb->flgs, 0); return ETHR_MTX_QLOCK_INIT(&mtxb->qlck); } static int mtxb_destroy(struct ethr_mutex_base_ *mtxb) { - long act; + ethr_sint32_t act; ETHR_MTX_Q_LOCK(&mtxb->qlck); - act = ethr_atomic_read(&mtxb->flgs); + act = ethr_atomic32_read(&mtxb->flgs); ETHR_MTX_Q_UNLOCK(&mtxb->qlck); if (act != 0) return EINVAL; @@ -831,13 +832,13 @@ ethr_mutex_destroy(ethr_mutex *mtx) } void -ethr_mutex_lock_wait__(ethr_mutex *mtx, long initial) +ethr_mutex_lock_wait__(ethr_mutex *mtx, ethr_sint32_t initial) { write_lock_wait(&mtx->mtxb, initial, 0, 0); } void -ethr_mutex_unlock_wake__(ethr_mutex *mtx, long initial) +ethr_mutex_unlock_wake__(ethr_mutex *mtx, ethr_sint32_t initial) { ethr_ts_event *tse; @@ -845,7 +846,7 @@ ethr_mutex_unlock_wake__(ethr_mutex *mtx, long initial) tse = mtx->mtxb.q; ETHR_ASSERT(tse); - ETHR_ASSERT(ethr_atomic_read(&mtx->mtxb.flgs) + ETHR_ASSERT(ethr_atomic32_read(&mtx->mtxb.flgs) == (ETHR_RWMTX_W_FLG__|ETHR_RWMTX_W_WAIT_FLG__)); ETHR_ASSERT(initial & ETHR_RWMTX_W_WAIT_FLG__); ETHR_MTX_HARD_DEBUG_CHK_Q(mtx); @@ -855,7 +856,7 @@ ethr_mutex_unlock_wake__(ethr_mutex *mtx, long initial) * mtxb->flgs; otherwise, we need to clear the write wait bit... */ if (tse->next == mtx->mtxb.q) - ethr_atomic_set(&mtx->mtxb.flgs, ETHR_RWMTX_W_FLG__); + ethr_atomic32_set(&mtx->mtxb.flgs, ETHR_RWMTX_W_FLG__); wake_writer(&mtx->mtxb, 0); } @@ -865,7 +866,7 @@ ethr_mutex_unlock_wake__(ethr_mutex *mtx, long initial) static void enqueue_mtx(ethr_mutex *mtx, ethr_ts_event *tse_start, ethr_ts_event *tse_end) { - long act; + ethr_sint32_t act; /* * `ethr_cond_signal()' and `ethr_cond_broadcast()' end up here. If `mtx' @@ -894,7 +895,7 @@ enqueue_mtx(ethr_mutex *mtx, ethr_ts_event *tse_start, ethr_ts_event *tse_end) } #endif - act = ethr_atomic_read(&mtx->mtxb.flgs); + act = ethr_atomic32_read(&mtx->mtxb.flgs); ETHR_ASSERT(act == 0 || act == ETHR_RWMTX_W_FLG__ || act == (ETHR_RWMTX_W_FLG__|ETHR_RWMTX_W_WAIT_FLG__)); @@ -902,10 +903,10 @@ enqueue_mtx(ethr_mutex *mtx, ethr_ts_event *tse_start, ethr_ts_event *tse_end) /* The normal sane case */ if (!(act & ETHR_RWMTX_W_WAIT_FLG__)) { ETHR_ASSERT(!mtx->mtxb.q); - act = ethr_atomic_cmpxchg(&mtx->mtxb.flgs, - (ETHR_RWMTX_W_FLG__ - | ETHR_RWMTX_W_WAIT_FLG__), - ETHR_RWMTX_W_FLG__); + act = ethr_atomic32_cmpxchg(&mtx->mtxb.flgs, + (ETHR_RWMTX_W_FLG__ + | ETHR_RWMTX_W_WAIT_FLG__), + ETHR_RWMTX_W_FLG__); if (act != ETHR_RWMTX_W_FLG__) { /* * Sigh... this wasn't so sane after all since, the mutex was @@ -937,14 +938,14 @@ enqueue_mtx(ethr_mutex *mtx, ethr_ts_event *tse_start, ethr_ts_event *tse_end) multi = tse_start != tse_end; while (1) { - long new, exp = act; + ethr_sint32_t new, exp = act; if (multi || (act & ETHR_RWMTX_W_FLG__)) new = ETHR_RWMTX_W_FLG__|ETHR_RWMTX_W_WAIT_FLG__; else new = ETHR_RWMTX_W_FLG__; - act = ethr_atomic_cmpxchg(&mtx->mtxb.flgs, new, exp); + act = ethr_atomic32_cmpxchg(&mtx->mtxb.flgs, new, exp); if (exp == act) { ETHR_ASSERT(!mtx->mtxb.q); if (act & ETHR_RWMTX_W_FLG__) { @@ -972,7 +973,7 @@ enqueue_mtx(ethr_mutex *mtx, ethr_ts_event *tse_start, ethr_ts_event *tse_end) ETHR_MTX_HARD_DEBUG_CHK_Q(mtx); ETHR_MTX_Q_UNLOCK(&mtx->mtxb.qlck); - ethr_atomic_set(&tse_start->uaflgs, 0); + ethr_atomic32_set(&tse_start->uaflgs, 0); ethr_event_set(&tse_start->event); } break; @@ -1063,9 +1064,9 @@ ethr_cond_signal(ethr_cond *cnd) ETHR_MTX_HARD_DEBUG_FENCE_CHK(mtx); ETHR_ASSERT(tse->uflgs == ETHR_RWMTX_W_WAIT_FLG__); - ETHR_ASSERT(ethr_atomic_read(&tse->uaflgs) == ETHR_CND_WAIT_FLG__); + ETHR_ASSERT(ethr_atomic32_read(&tse->uaflgs) == ETHR_CND_WAIT_FLG__); - ethr_atomic_set(&tse->uaflgs, ETHR_RWMTX_W_WAIT_FLG__); + ethr_atomic32_set(&tse->uaflgs, ETHR_RWMTX_W_WAIT_FLG__); dequeue(&cnd->q, tse, tse); @@ -1116,10 +1117,11 @@ ethr_cond_broadcast(ethr_cond *cnd) /* The normal case */ ETHR_ASSERT(tse_tmp->uflgs == ETHR_RWMTX_W_WAIT_FLG__); - ETHR_ASSERT(ethr_atomic_read(&tse_tmp->uaflgs) + ETHR_ASSERT(ethr_atomic32_read(&tse_tmp->uaflgs) == ETHR_CND_WAIT_FLG__); - ethr_atomic_set(&tse_tmp->uaflgs, ETHR_RWMTX_W_WAIT_FLG__); + ethr_atomic32_set(&tse_tmp->uaflgs, + ETHR_RWMTX_W_WAIT_FLG__); } else { /* Should be very unusual */ @@ -1172,7 +1174,7 @@ ethr_cond_wait(ethr_cond *cnd, ethr_mutex *mtx) tse->udata = (void *) mtx; tse->uflgs = ETHR_RWMTX_W_WAIT_FLG__; /* Prep for mutex lock op */ - ethr_atomic_set(&tse->uaflgs, ETHR_CND_WAIT_FLG__); + ethr_atomic32_set(&tse->uaflgs, ETHR_CND_WAIT_FLG__); ETHR_MTX_Q_LOCK(&cnd->qlck); @@ -1185,11 +1187,11 @@ ethr_cond_wait(ethr_cond *cnd, ethr_mutex *mtx) /* Wait */ woken = 0; while (1) { - long act; + ethr_sint32_t act; ethr_event_reset(&tse->event); - act = ethr_atomic_read_acqb(&tse->uaflgs); + act = ethr_atomic32_read_acqb(&tse->uaflgs); if (!act) break; /* Mtx locked */ @@ -1205,7 +1207,7 @@ ethr_cond_wait(ethr_cond *cnd, ethr_mutex *mtx) */ if (act == ETHR_CND_WAIT_FLG__) { ETHR_MTX_Q_LOCK(&cnd->qlck); - act = ethr_atomic_read(&tse->uaflgs); + act = ethr_atomic32_read(&tse->uaflgs); ETHR_ASSERT(act == ETHR_CND_WAIT_FLG__ || act == ETHR_RWMTX_W_WAIT_FLG__); /* @@ -1407,7 +1409,7 @@ wake_readers(ethr_rwmutex *rwmtx, int rs) rwmtx->rq_end = NULL; ETHR_ASSERT(!rwmtx->mtxb.q - || (ethr_atomic_read(&rwmtx->mtxb.q->uaflgs) + || (ethr_atomic32_read(&rwmtx->mtxb.q->uaflgs) == ETHR_RWMTX_W_WAIT_FLG__)); ETHR_RWMTX_HARD_DEBUG_CHK_Q(rwmtx); @@ -1418,7 +1420,7 @@ wake_readers(ethr_rwmutex *rwmtx, int rs) #ifdef ETHR_DEBUG ETHR_ASSERT(tse->uflgs == ETHR_RWMTX_R_WAIT_FLG__); - ETHR_ASSERT(ethr_atomic_read(&tse->uaflgs) + ETHR_ASSERT(ethr_atomic32_read(&tse->uaflgs) == ETHR_RWMTX_R_WAIT_FLG__); drs++; #endif @@ -1426,7 +1428,7 @@ wake_readers(ethr_rwmutex *rwmtx, int rs) tse_next = tse->next; /* we aren't allowed to read tse->next after we have reset uaflgs */ - ethr_atomic_set(&tse->uaflgs, 0); + ethr_atomic32_set(&tse->uaflgs, 0); ethr_event_set(&tse->event); tse = tse_next; } @@ -1469,7 +1471,7 @@ int check_readers_array(ethr_rwmutex *rwmtx, ETHR_MEMORY_BARRIER; do { - long act = rwmutex_freqread_rdrs_read(rwmtx, ix); + ethr_sint32_t act = rwmutex_freqread_rdrs_read(rwmtx, ix); if (act != 0) return EBUSY; ix++; @@ -1483,9 +1485,9 @@ int check_readers_array(ethr_rwmutex *rwmtx, static void rwmutex_freqread_rdrs_dec_chk_wakeup(ethr_rwmutex *rwmtx, ethr_ts_event *tse, - long initial) + ethr_sint32_t initial) { - long act = initial; + ethr_sint32_t act = initial; if ((act & (ETHR_RWMTX_W_FLG__| ETHR_RWMTX_R_ABRT_UNLCK_FLG__)) == 0) { @@ -1515,7 +1517,7 @@ rwmutex_freqread_rdrs_dec_chk_wakeup(ethr_rwmutex *rwmtx, if (!rwmtx->mtxb.q) ETHR_MTX_Q_UNLOCK(&rwmtx->mtxb.qlck); else if (is_w_waiter(rwmtx->mtxb.q)) { - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); ETHR_MTX_Q_UNLOCK(&rwmtx->mtxb.qlck); if ((act & ETHR_RWMTX_W_FLG__) == 0) rwmutex_try_complete_runlock(rwmtx, act, tse, 1, 0, 0); @@ -1525,7 +1527,7 @@ rwmutex_freqread_rdrs_dec_chk_wakeup(ethr_rwmutex *rwmtx, * rwmutex_transfer_read_lock() will * unlock Q lock. */ - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); if (act & ETHR_RWMTX_W_FLG__) ETHR_MTX_Q_UNLOCK(&rwmtx->mtxb.qlck); else @@ -1539,7 +1541,7 @@ static void rwmutex_freqread_restore_failed_tryrlock(ethr_rwmutex *rwmtx, ethr_ts_event *tse) { - long act; + ethr_sint32_t act; /* * Restore failed increment */ @@ -1548,21 +1550,21 @@ rwmutex_freqread_restore_failed_tryrlock(ethr_rwmutex *rwmtx, ETHR_MEMORY_BARRIER; if (act == 0) { - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); rwmutex_freqread_rdrs_dec_chk_wakeup(rwmtx, tse, act); } } static int rwmutex_try_complete_runlock(ethr_rwmutex *rwmtx, - long initial, + ethr_sint32_t initial, ethr_ts_event *tse, int start_next_ix, int check_before_try, int try_write_lock) { ethr_ts_event *tse_tmp; - long act = initial; + ethr_sint32_t act = initial; int six, res, length; ETHR_ASSERT((act & ETHR_RWMTX_W_FLG__) == 0); @@ -1606,15 +1608,15 @@ rwmutex_try_complete_runlock(ethr_rwmutex *rwmtx, restart: while (1) { - long exp = act; - long new = act+1; + ethr_sint32_t exp = act; + ethr_sint32_t new = act+1; ETHR_ASSERT((act & ETHR_RWMTX_R_ABRT_UNLCK_FLG__) == 0); ETHR_ASSERT((act & ETHR_RWMTX_R_PEND_UNLCK_MASK__) < ETHR_RWMTX_R_PEND_UNLCK_MASK__); - act = ethr_atomic_cmpxchg(&rwmtx->mtxb.flgs, new, exp); + act = ethr_atomic32_cmpxchg(&rwmtx->mtxb.flgs, new, exp); if (exp == act) { act = new; break; @@ -1651,8 +1653,8 @@ rwmutex_try_complete_runlock(ethr_rwmutex *rwmtx, while (1) { int finished_abort = 0; - long exp = act; - long new = act; + ethr_sint32_t exp = act; + ethr_sint32_t new = act; new--; if (act & ETHR_RWMTX_R_ABRT_UNLCK_FLG__) { @@ -1668,7 +1670,7 @@ rwmutex_try_complete_runlock(ethr_rwmutex *rwmtx, ETHR_ASSERT(act & ETHR_RWMTX_R_PEND_UNLCK_MASK__); - act = ethr_atomic_cmpxchg(&rwmtx->mtxb.flgs, new, exp); + act = ethr_atomic32_cmpxchg(&rwmtx->mtxb.flgs, new, exp); if (exp == act) { act = new; if (act & ETHR_RWMTX_W_FLG__) @@ -1702,9 +1704,9 @@ rwmutex_try_complete_runlock(ethr_rwmutex *rwmtx, tryrwlock: /* Try to write lock it */ - act = ethr_atomic_cmpxchg_acqb(&rwmtx->mtxb.flgs, - ETHR_RWMTX_W_FLG__, - 0); + act = ethr_atomic32_cmpxchg_acqb(&rwmtx->mtxb.flgs, + ETHR_RWMTX_W_FLG__, + 0); return act == 0 ? 0 : EBUSY; } @@ -1713,11 +1715,11 @@ rwmutex_try_complete_runlock(ethr_rwmutex *rwmtx, static ETHR_INLINE void rwmutex_incdec_restore_failed_tryrlock(ethr_rwmutex *rwmtx) { - long act; + ethr_sint32_t act; /* * Restore failed increment */ - act = ethr_atomic_dec_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_dec_read(&rwmtx->mtxb.flgs); if ((act & ETHR_RWMTX_WAIT_FLGS__) && (act & ~ETHR_RWMTX_WAIT_FLGS__) == 0) { rwmutex_unlock_wake(rwmtx, 0, act, 0); @@ -1727,10 +1729,9 @@ rwmutex_incdec_restore_failed_tryrlock(ethr_rwmutex *rwmtx) #endif static void -rwmutex_normal_rlock_wait(ethr_rwmutex *rwmtx, - long initial) +rwmutex_normal_rlock_wait(ethr_rwmutex *rwmtx, ethr_sint32_t initial) { - long act = initial, exp; + ethr_sint32_t act = initial, exp; int scnt, start_scnt; ethr_ts_event *tse = NULL; int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; @@ -1746,7 +1747,7 @@ rwmutex_normal_rlock_wait(ethr_rwmutex *rwmtx, #ifdef ETHR_RLOCK_WITH_INC_DEC rwmutex_incdec_restore_failed_tryrlock(rwmtx); - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); #endif while (act & (ETHR_RWMTX_W_FLG__|ETHR_RWMTX_W_WAIT_FLG__)) { @@ -1763,17 +1764,17 @@ rwmutex_normal_rlock_wait(ethr_rwmutex *rwmtx, until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; ETHR_YIELD(); } - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); scnt--; } exp = act; #ifdef ETHR_RLOCK_WITH_INC_DEC - act = ethr_atomic_inc_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_inc_read(&rwmtx->mtxb.flgs); if ((act & (ETHR_RWMTX_W_FLG__|ETHR_RWMTX_W_WAIT_FLG__)) == 0) goto done; /* Got it */ #else - act = ethr_atomic_cmpxchg_acqb(&rwmtx->mtxb.flgs, exp+1, exp); + act = ethr_atomic32_cmpxchg_acqb(&rwmtx->mtxb.flgs, exp+1, exp); if (act == exp) goto done; /* Got it */ #endif @@ -1792,19 +1793,19 @@ static int rwmutex_freqread_rlock(ethr_rwmutex *rwmtx, ethr_ts_event *tse, int trylock) { int res = 0; - long act; + ethr_sint32_t act; rwmutex_freqread_rdrs_inc(rwmtx, tse); ETHR_MEMORY_BARRIER; - act = ethr_atomic_read_acqb(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read_acqb(&rwmtx->mtxb.flgs); if (act != ETHR_RWMTX_R_FLG__) { int wake_other_readers; while (1) { - long exp, new; + ethr_sint32_t exp, new; wake_other_readers = 0; @@ -1846,7 +1847,7 @@ rwmutex_freqread_rlock(ethr_rwmutex *rwmtx, ethr_ts_event *tse, int trylock) } exp = act; - act = ethr_atomic_cmpxchg_acqb(&rwmtx->mtxb.flgs, new, exp); + act = ethr_atomic32_cmpxchg_acqb(&rwmtx->mtxb.flgs, new, exp); if (act == exp) break; } @@ -1862,7 +1863,7 @@ static void rwmutex_freqread_rlock_wait(ethr_rwmutex *rwmtx, ethr_ts_event *tse) { - long act; + ethr_sint32_t act; int scnt, start_scnt; int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; @@ -1875,7 +1876,7 @@ rwmutex_freqread_rlock_wait(ethr_rwmutex *rwmtx, while (1) { - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); while (act & (ETHR_RWMTX_W_FLG__|ETHR_RWMTX_W_WAIT_FLG__)) { if (scnt <= 0) { @@ -1890,7 +1891,7 @@ rwmutex_freqread_rlock_wait(ethr_rwmutex *rwmtx, until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; ETHR_YIELD(); } - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); scnt--; } @@ -1900,21 +1901,23 @@ rwmutex_freqread_rlock_wait(ethr_rwmutex *rwmtx, } static void -rwmutex_normal_rwlock_wait(ethr_rwmutex *rwmtx, long initial) +rwmutex_normal_rwlock_wait(ethr_rwmutex *rwmtx, ethr_sint32_t initial) { write_lock_wait(&rwmtx->mtxb, initial, 1, 0); } static void -rwmutex_freqread_rwlock_wait(ethr_rwmutex *rwmtx, long initial) +rwmutex_freqread_rwlock_wait(ethr_rwmutex *rwmtx, ethr_sint32_t initial) { write_lock_wait(&rwmtx->mtxb, initial, 1, 1); } static ETHR_INLINE void -rwlock_wake_set_flags(ethr_rwmutex *rwmtx, long new_initial, long act_initial) +rwlock_wake_set_flags(ethr_rwmutex *rwmtx, + ethr_sint32_t new_initial, + ethr_sint32_t act_initial) { - long act, act_mask; + ethr_sint32_t act, act_mask; int chk_abrt_flg; ETHR_MEMORY_BARRIER; @@ -1935,18 +1938,18 @@ rwlock_wake_set_flags(ethr_rwmutex *rwmtx, long new_initial, long act_initial) #else /* rs mask always zero */ ETHR_ASSERT((act_initial & ETHR_RWMTX_RS_MASK__) == 0); - ethr_atomic_set(&rwmtx->mtxb.flgs, new_initial); + ethr_atomic32_set(&rwmtx->mtxb.flgs, new_initial); return; #endif } act = act_initial; while (1) { - long exp = act; - long new = new_initial + (act & act_mask); + ethr_sint32_t exp = act; + ethr_sint32_t new = new_initial + (act & act_mask); if (chk_abrt_flg && (act & act_mask)) new |= ETHR_RWMTX_R_ABRT_UNLCK_FLG__; - act = ethr_atomic_cmpxchg(&rwmtx->mtxb.flgs, new, exp); + act = ethr_atomic32_cmpxchg(&rwmtx->mtxb.flgs, new, exp); if (act == exp) break; exp = act; @@ -1960,7 +1963,7 @@ dbg_unlock_wake(ethr_rwmutex *rwmtx, int have_w, ethr_ts_event *tse) { - long exp, act, imask; + ethr_sint32_t exp, act, imask; exp = have_w ? ETHR_RWMTX_W_FLG__ : 0; @@ -1982,7 +1985,7 @@ dbg_unlock_wake(ethr_rwmutex *rwmtx, if (rwmtx->rq_end) { exp |= ETHR_RWMTX_R_WAIT_FLG__; } - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); ETHR_ASSERT((exp & ~imask) == (act & ~imask)); ETHR_RWMTX_HARD_DEBUG_CHK_Q(rwmtx); @@ -2001,7 +2004,7 @@ dbg_unlock_wake(ethr_rwmutex *rwmtx, imask |= ETHR_RWMTX_RS_MASK__; } } - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); ETHR_ASSERT((exp & ~imask) == (act & ~imask)); ETHR_RWMTX_HARD_DEBUG_CHK_Q(rwmtx); @@ -2012,9 +2015,11 @@ dbg_unlock_wake(ethr_rwmutex *rwmtx, #endif static void -rwmutex_transfer_read_lock(ethr_rwmutex *rwmtx, long initial, int q_locked) +rwmutex_transfer_read_lock(ethr_rwmutex *rwmtx, + ethr_sint32_t initial, + int q_locked) { - long act = initial; + ethr_sint32_t act = initial; if (!q_locked) { ethr_ts_event *tse; @@ -2022,7 +2027,7 @@ rwmutex_transfer_read_lock(ethr_rwmutex *rwmtx, long initial, int q_locked) ETHR_ASSERT((initial & ETHR_RWMTX_W_FLG__) == 0); ETHR_MTX_Q_LOCK(&rwmtx->mtxb.qlck); - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); tse = rwmtx->mtxb.q; if ((act & ETHR_RWMTX_W_FLG__) || !tse || is_w_waiter(tse)) { /* Someone else woke the readers up... */ @@ -2035,10 +2040,10 @@ rwmutex_transfer_read_lock(ethr_rwmutex *rwmtx, long initial, int q_locked) } static void -rwmutex_unlock_wake(ethr_rwmutex *rwmtx, int have_w, long initial, +rwmutex_unlock_wake(ethr_rwmutex *rwmtx, int have_w, ethr_sint32_t initial, int transfer_read_lock) { - long new, act = initial; + ethr_sint32_t new, act = initial; ethr_ts_event *tse; if (transfer_read_lock) { @@ -2060,9 +2065,9 @@ rwmutex_unlock_wake(ethr_rwmutex *rwmtx, int have_w, long initial, return; else { while ((act & ETHR_RWMTX_WAIT_FLGS__) == 0) { - long exp = act; + ethr_sint32_t exp = act; new = exp & ~ETHR_RWMTX_W_FLG__; - act = ethr_atomic_cmpxchg(&rwmtx->mtxb.flgs, new, exp); + act = ethr_atomic32_cmpxchg(&rwmtx->mtxb.flgs, new, exp); if (act == exp) return; } @@ -2075,12 +2080,12 @@ rwmutex_unlock_wake(ethr_rwmutex *rwmtx, int have_w, long initial, if (!have_w) { if (!tse) { #ifdef ETHR_DEBUG - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); ETHR_ASSERT((act & ETHR_RWMTX_WAIT_FLGS__) == 0); #endif goto already_served; } - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); if (act == (ETHR_RWMTX_R_WAIT_FLG__|ETHR_RWMTX_R_FLG__)) { ETHR_ASSERT(tse && !is_w_waiter(tse)); } @@ -2099,9 +2104,12 @@ rwmutex_unlock_wake(ethr_rwmutex *rwmtx, int have_w, long initial, if (is_w_waiter(tse)) { if (!have_w) { - act = ethr_atomic_read_bor(&rwmtx->mtxb.flgs, + act = ethr_atomic32_read_bor(&rwmtx->mtxb.flgs, ETHR_RWMTX_W_FLG__); - ETHR_ASSERT((act & ~ETHR_RWMTX_WAIT_FLGS__) == 0); + ETHR_ASSERT((act & ~(ETHR_RWMTX_WAIT_FLGS__ + | (rwmtx->type == ETHR_RWMUTEX_TYPE_NORMAL + ? 0 + : ETHR_RWMTX_R_PEND_UNLCK_MASK__))) == 0); ETHR_ASSERT(act & ETHR_RWMTX_W_WAIT_FLG__); act |= ETHR_RWMTX_W_FLG__; } @@ -2128,7 +2136,7 @@ rwmutex_unlock_wake(ethr_rwmutex *rwmtx, int have_w, long initial, if (rwmtx->type == ETHR_RWMUTEX_TYPE_NORMAL) { rs = rwmtx->tdata.rs; - new = (long) rs; + new = (ethr_sint32_t) rs; rwmtx->tdata.rs = 0; } else { @@ -2184,16 +2192,16 @@ alloc_readers_array(int length, ethr_rwmutex_lived lived) if (!mem) return NULL; - if ((((unsigned long) mem) & ETHR_CACHE_LINE_MASK) == 0) { + if ((((ethr_uint_t) mem) & ETHR_CACHE_LINE_MASK) == 0) { ra = (ethr_rwmtx_readers_array__ *) mem; ra->data.byte_offset = 0; } else { ra = ((ethr_rwmtx_readers_array__ *) - ((((unsigned long) mem) & ~ETHR_CACHE_LINE_MASK) + ((((ethr_uint_t) mem) & ~ETHR_CACHE_LINE_MASK) + ETHR_CACHE_LINE_SIZE)); - ra->data.byte_offset = (int) ((unsigned long) ra - - (unsigned long) mem); + ra->data.byte_offset = (int) ((ethr_uint_t) ra + - (ethr_uint_t) mem); } ra->data.lived = lived; return ra; @@ -2267,7 +2275,7 @@ ethr_rwmutex_init_opt(ethr_rwmutex *rwmtx, ethr_rwmutex_opt *opt) rwmtx->tdata.ra = ra; for (ix = 0; ix < length; ix++) { - ethr_atomic_init(&rwmtx->tdata.ra[ix].data.readers, 0); + ethr_atomic32_init(&rwmtx->tdata.ra[ix].data.readers, 0); rwmtx->tdata.ra[ix].data.waiting_readers = 0; } break; @@ -2321,7 +2329,7 @@ ethr_rwmutex_destroy(ethr_rwmutex *rwmtx) #endif ETHR_MTX_DBG_CHK_UNUSED_FLG_BITS(rwmtx); if (rwmtx->type != ETHR_RWMUTEX_TYPE_NORMAL) { - long act = ethr_atomic_read(&rwmtx->mtxb.flgs); + ethr_sint32_t act = ethr_atomic32_read(&rwmtx->mtxb.flgs); if (act == ETHR_RWMTX_R_FLG__) rwmutex_try_complete_runlock(rwmtx, act, NULL, 0, 0, 0); } @@ -2342,7 +2350,7 @@ int ethr_rwmutex_tryrlock(ethr_rwmutex *rwmtx) { int res = 0; - long act; + ethr_sint32_t act; ETHR_ASSERT(!ethr_not_inited__); ETHR_ASSERT(rwmtx); @@ -2355,22 +2363,22 @@ ethr_rwmutex_tryrlock(ethr_rwmutex *rwmtx) switch (rwmtx->type) { case ETHR_RWMUTEX_TYPE_NORMAL: { #ifdef ETHR_RLOCK_WITH_INC_DEC - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); if (act & (ETHR_RWMTX_W_FLG__|ETHR_RWMTX_W_WAIT_FLG__)) res = EBUSY; else { - act = ethr_atomic_inc_read_acqb(&rwmtx->mtxb.flgs); + act = ethr_atomic32_inc_read_acqb(&rwmtx->mtxb.flgs); if (act & (ETHR_RWMTX_W_FLG__|ETHR_RWMTX_W_WAIT_FLG__)) { rwmutex_incdec_restore_failed_tryrlock(rwmtx); res = EBUSY; } } #else - long exp = 0; + ethr_sint32_t exp = 0; int tries = 0; while (1) { - act = ethr_atomic_cmpxchg_acqb(&rwmtx->mtxb.flgs, exp+1, exp); + act = ethr_atomic32_cmpxchg_acqb(&rwmtx->mtxb.flgs, exp+1, exp); if (act == exp) { res = 0; break; @@ -2413,7 +2421,7 @@ ethr_rwmutex_tryrlock(ethr_rwmutex *rwmtx) void ethr_rwmutex_rlock(ethr_rwmutex *rwmtx) { - long act; + ethr_sint32_t act; ETHR_ASSERT(!ethr_not_inited__); ETHR_ASSERT(rwmtx); @@ -2426,14 +2434,14 @@ ethr_rwmutex_rlock(ethr_rwmutex *rwmtx) switch (rwmtx->type) { case ETHR_RWMUTEX_TYPE_NORMAL: { #ifdef ETHR_RLOCK_WITH_INC_DEC - act = ethr_atomic_inc_read_acqb(&rwmtx->mtxb.flgs); + act = ethr_atomic32_inc_read_acqb(&rwmtx->mtxb.flgs); if (act & (ETHR_RWMTX_W_FLG__|ETHR_RWMTX_W_WAIT_FLG__)) rwmutex_normal_rlock_wait(rwmtx, act); #else - long exp = 0; + ethr_sint32_t exp = 0; while (1) { - act = ethr_atomic_cmpxchg_acqb(&rwmtx->mtxb.flgs, exp+1, exp); + act = ethr_atomic32_cmpxchg_acqb(&rwmtx->mtxb.flgs, exp+1, exp); if (act == exp) break; @@ -2466,7 +2474,7 @@ ethr_rwmutex_rlock(ethr_rwmutex *rwmtx) void ethr_rwmutex_runlock(ethr_rwmutex *rwmtx) { - long act; + ethr_sint32_t act; ETHR_MTX_CHK_EXCL_IS_NOT_EXCL(&rwmtx->mtxb); ETHR_MTX_CHK_EXCL_UNSET_NON_EXCL(&rwmtx->mtxb); @@ -2481,7 +2489,7 @@ ethr_rwmutex_runlock(ethr_rwmutex *rwmtx) switch (rwmtx->type) { case ETHR_RWMUTEX_TYPE_NORMAL: - act = ethr_atomic_dec_read_relb(&rwmtx->mtxb.flgs); + act = ethr_atomic32_dec_read_relb(&rwmtx->mtxb.flgs); if ((act & ETHR_RWMTX_WAIT_FLGS__) && (act & ~ETHR_RWMTX_WAIT_FLGS__) == 0) { ETHR_ASSERT((act & ETHR_RWMTX_W_FLG__) == 0); @@ -2500,7 +2508,7 @@ ethr_rwmutex_runlock(ethr_rwmutex *rwmtx) ETHR_MEMORY_BARRIER; if (act == 0) { - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); if (act != ETHR_RWMTX_R_FLG__) rwmutex_freqread_rdrs_dec_chk_wakeup(rwmtx, tse, act); } @@ -2518,7 +2526,7 @@ int ethr_rwmutex_tryrwlock(ethr_rwmutex *rwmtx) { int res = 0; - long act; + ethr_sint32_t act; ETHR_ASSERT(!ethr_not_inited__); ETHR_ASSERT(rwmtx); @@ -2530,8 +2538,8 @@ ethr_rwmutex_tryrwlock(ethr_rwmutex *rwmtx) switch (rwmtx->type) { case ETHR_RWMUTEX_TYPE_NORMAL: - act = ethr_atomic_cmpxchg_acqb(&rwmtx->mtxb.flgs, - ETHR_RWMTX_W_FLG__, 0); + act = ethr_atomic32_cmpxchg_acqb(&rwmtx->mtxb.flgs, + ETHR_RWMTX_W_FLG__, 0); if (act != 0) res = EBUSY; break; @@ -2540,13 +2548,13 @@ ethr_rwmutex_tryrwlock(ethr_rwmutex *rwmtx) case ETHR_RWMUTEX_TYPE_EXTREMELY_FREQUENT_READ: res = 0; - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); do { if (act == 0) - act = ethr_atomic_cmpxchg_acqb(&rwmtx->mtxb.flgs, - ETHR_RWMTX_W_FLG__, 0); + act = ethr_atomic32_cmpxchg_acqb(&rwmtx->mtxb.flgs, + ETHR_RWMTX_W_FLG__, 0); else if (act == ETHR_RWMTX_R_FLG__) { res = rwmutex_try_complete_runlock(rwmtx, act, NULL, 0, 1, 1); @@ -2579,7 +2587,7 @@ ethr_rwmutex_tryrwlock(ethr_rwmutex *rwmtx) void ethr_rwmutex_rwlock(ethr_rwmutex *rwmtx) { - long act; + ethr_sint32_t act; ETHR_ASSERT(!ethr_not_inited__); ETHR_ASSERT(rwmtx); ETHR_ASSERT(rwmtx->initialized == ETHR_RWMUTEX_INITIALIZED); @@ -2590,8 +2598,8 @@ ethr_rwmutex_rwlock(ethr_rwmutex *rwmtx) switch (rwmtx->type) { case ETHR_RWMUTEX_TYPE_NORMAL: - act = ethr_atomic_cmpxchg_acqb(&rwmtx->mtxb.flgs, - ETHR_RWMTX_W_FLG__, 0); + act = ethr_atomic32_cmpxchg_acqb(&rwmtx->mtxb.flgs, + ETHR_RWMTX_W_FLG__, 0); if (act != 0) rwmutex_normal_rwlock_wait(rwmtx, act); break; @@ -2599,7 +2607,7 @@ ethr_rwmutex_rwlock(ethr_rwmutex *rwmtx) case ETHR_RWMUTEX_TYPE_FREQUENT_READ: case ETHR_RWMUTEX_TYPE_EXTREMELY_FREQUENT_READ: - act = ethr_atomic_read(&rwmtx->mtxb.flgs); + act = ethr_atomic32_read(&rwmtx->mtxb.flgs); do { @@ -2608,8 +2616,8 @@ ethr_rwmutex_rwlock(ethr_rwmutex *rwmtx) break; } - act = ethr_atomic_cmpxchg_acqb(&rwmtx->mtxb.flgs, - ETHR_RWMTX_W_FLG__, 0); + act = ethr_atomic32_cmpxchg_acqb(&rwmtx->mtxb.flgs, + ETHR_RWMTX_W_FLG__, 0); } while (act != 0); @@ -2627,7 +2635,7 @@ ethr_rwmutex_rwlock(ethr_rwmutex *rwmtx) void ethr_rwmutex_rwunlock(ethr_rwmutex *rwmtx) { - long act; + ethr_sint32_t act; ETHR_ASSERT(!ethr_not_inited__); ETHR_ASSERT(rwmtx); ETHR_ASSERT(rwmtx->initialized == ETHR_RWMUTEX_INITIALIZED); @@ -2642,16 +2650,16 @@ ethr_rwmutex_rwunlock(ethr_rwmutex *rwmtx) switch (rwmtx->type) { case ETHR_RWMUTEX_TYPE_NORMAL: - act = ethr_atomic_cmpxchg_relb(&rwmtx->mtxb.flgs, - 0, ETHR_RWMTX_W_FLG__); + act = ethr_atomic32_cmpxchg_relb(&rwmtx->mtxb.flgs, + 0, ETHR_RWMTX_W_FLG__); if (act != ETHR_RWMTX_W_FLG__) rwmutex_unlock_wake(rwmtx, 1, act, 0); break; case ETHR_RWMUTEX_TYPE_FREQUENT_READ: case ETHR_RWMUTEX_TYPE_EXTREMELY_FREQUENT_READ: - act = ethr_atomic_cmpxchg_relb(&rwmtx->mtxb.flgs, 0, - ETHR_RWMTX_W_FLG__); + act = ethr_atomic32_cmpxchg_relb(&rwmtx->mtxb.flgs, 0, + ETHR_RWMTX_W_FLG__); if (act != ETHR_RWMTX_W_FLG__) rwmutex_unlock_wake(rwmtx, 1, act, 0); break; @@ -2776,7 +2784,7 @@ static void hard_debug_chk_q__(struct ethr_mutex_base_ *mtxb, int is_rwmtx) { int res; - long flgs = ethr_atomic_read(&mtxb->flgs); + ethr_sint32_t flgs = ethr_atomic32_read(&mtxb->flgs); ETHR_MTX_HARD_ASSERT(res == 0); @@ -2799,12 +2807,12 @@ hard_debug_chk_q__(struct ethr_mutex_base_ *mtxb, int is_rwmtx) tse = mtxb->q; do { - long type; + ethr_sint32_t type; ETHR_MTX_HARD_ASSERT(tse->next->prev == tse); ETHR_MTX_HARD_ASSERT(tse->prev->next == tse); - type = ethr_atomic_read(&tse->uaflgs); + type = ethr_atomic32_read(&tse->uaflgs); ETHR_MTX_HARD_ASSERT(type == tse->uflgs); switch (type) { case ETHR_RWMTX_W_WAIT_FLG__: diff --git a/erts/lib_src/pthread/ethr_event.c b/erts/lib_src/pthread/ethr_event.c index 6731c0eb46..9434d60d0a 100644 --- a/erts/lib_src/pthread/ethr_event.c +++ b/erts/lib_src/pthread/ethr_event.c @@ -24,6 +24,10 @@ #define ETHR_INLINE_FUNC_NAME_(X) X ## __ #define ETHR_EVENT_IMPL__ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "ethread.h" #if defined(ETHR_LINUX_FUTEX_IMPL__) @@ -37,7 +41,7 @@ int ethr_event_init(ethr_event *e) { - ethr_atomic_init(&e->futex, ETHR_EVENT_OFF__); + ethr_atomic32_init(&e->futex, ETHR_EVENT_OFF__); return 0; } @@ -52,7 +56,7 @@ wait__(ethr_event *e, int spincount) { unsigned sc = spincount; int res; - long val; + ethr_sint32_t val; int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; if (spincount < 0) @@ -60,7 +64,7 @@ wait__(ethr_event *e, int spincount) while (1) { while (1) { - val = ethr_atomic_read(&e->futex); + val = ethr_atomic32_read(&e->futex); if (val == ETHR_EVENT_ON__) return 0; if (sc == 0) @@ -76,16 +80,18 @@ wait__(ethr_event *e, int spincount) } if (val != ETHR_EVENT_OFF_WAITER__) { - val = ethr_atomic_cmpxchg(&e->futex, - ETHR_EVENT_OFF_WAITER__, - ETHR_EVENT_OFF__); + val = ethr_atomic32_cmpxchg(&e->futex, + ETHR_EVENT_OFF_WAITER__, + ETHR_EVENT_OFF__); if (val == ETHR_EVENT_ON__) return 0; ETHR_ASSERT(val == ETHR_EVENT_OFF__); } - res = ETHR_FUTEX__(&e->futex, ETHR_FUTEX_WAIT__, ETHR_EVENT_OFF_WAITER__); + res = ETHR_FUTEX__(&e->futex, + ETHR_FUTEX_WAIT__, + ETHR_EVENT_OFF_WAITER__); if (res == EINTR) break; if (res != 0 && res != EWOULDBLOCK) @@ -102,7 +108,7 @@ int ethr_event_init(ethr_event *e) { int res; - ethr_atomic_init(&e->state, ETHR_EVENT_OFF__); + ethr_atomic32_init(&e->state, ETHR_EVENT_OFF__); res = pthread_mutex_init(&e->mtx, NULL); if (res != 0) return res; @@ -131,7 +137,7 @@ static ETHR_INLINE int wait__(ethr_event *e, int spincount) { int sc = spincount; - long val; + ethr_sint32_t val; int res, ulres; int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; @@ -139,7 +145,7 @@ wait__(ethr_event *e, int spincount) ETHR_FATAL_ERROR__(EINVAL); while (1) { - val = ethr_atomic_read(&e->state); + val = ethr_atomic32_read(&e->state); if (val == ETHR_EVENT_ON__) return 0; if (sc == 0) @@ -155,9 +161,9 @@ wait__(ethr_event *e, int spincount) } if (val != ETHR_EVENT_OFF_WAITER__) { - val = ethr_atomic_cmpxchg(&e->state, - ETHR_EVENT_OFF_WAITER__, - ETHR_EVENT_OFF__); + val = ethr_atomic32_cmpxchg(&e->state, + ETHR_EVENT_OFF_WAITER__, + ETHR_EVENT_OFF__); if (val == ETHR_EVENT_ON__) return 0; ETHR_ASSERT(val == ETHR_EVENT_OFF__); @@ -172,7 +178,7 @@ wait__(ethr_event *e, int spincount) while (1) { - val = ethr_atomic_read(&e->state); + val = ethr_atomic32_read(&e->state); if (val == ETHR_EVENT_ON__) break; diff --git a/erts/lib_src/pthread/ethread.c b/erts/lib_src/pthread/ethread.c index ea1d9d43f0..f047104103 100644 --- a/erts/lib_src/pthread/ethread.c +++ b/erts/lib_src/pthread/ethread.c @@ -72,7 +72,7 @@ static void thr_exit_cleanup(void) /* Argument passed to thr_wrapper() */ typedef struct { - ethr_atomic_t result; + ethr_atomic32_t result; ethr_ts_event *tse; void *(*thr_func)(void *); void *arg; @@ -81,14 +81,14 @@ typedef struct { static void *thr_wrapper(void *vtwd) { - long result; + ethr_sint32_t result; void *res; ethr_thr_wrap_data__ *twd = (ethr_thr_wrap_data__ *) vtwd; void *(*thr_func)(void *) = twd->thr_func; void *arg = twd->arg; ethr_ts_event *tsep = NULL; - result = (long) ethr_make_ts_event__(&tsep); + result = (ethr_sint32_t) ethr_make_ts_event__(&tsep); if (result == 0) { tsep->iflgs |= ETHR_TS_EV_ETHREAD; @@ -99,7 +99,7 @@ static void *thr_wrapper(void *vtwd) tsep = twd->tse; /* We aren't allowed to follow twd after result has been set! */ - ethr_atomic_set(&twd->result, result); + ethr_atomic32_set(&twd->result, result); ethr_event_set(&tsep->event); @@ -191,7 +191,7 @@ ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg, } #endif - ethr_atomic_init(&twd.result, -1); + ethr_atomic32_init(&twd.result, (ethr_sint32_t) -1); twd.tse = ethr_get_ts_event(); twd.thr_func = func; twd.arg = arg; @@ -252,10 +252,10 @@ ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg, /* Wait for child to initialize... */ while (1) { - long result; + ethr_sint32_t result; ethr_event_reset(&twd.tse->event); - result = ethr_atomic_read(&twd.result); + result = ethr_atomic32_read(&twd.result); if (result == 0) break; @@ -349,32 +349,6 @@ ethr_leave_ts_event(ethr_ts_event *tsep) } /* - * Current time - */ - -int -ethr_time_now(ethr_timeval *time) -{ - int res; - struct timeval tv; -#if ETHR_XCHK - if (ethr_not_inited__) { - ETHR_ASSERT(0); - return EACCES; - } - if (!time) { - ETHR_ASSERT(0); - return EINVAL; - } -#endif - - res = gettimeofday(&tv, NULL); - time->tv_sec = (long) tv.tv_sec; - time->tv_nsec = ((long) tv.tv_usec)*1000; - return res; -} - -/* * Thread specific data */ diff --git a/erts/lib_src/win/ethr_event.c b/erts/lib_src/win/ethr_event.c index ddb4780ff1..68f093f49c 100644 --- a/erts/lib_src/win/ethr_event.c +++ b/erts/lib_src/win/ethr_event.c @@ -28,6 +28,9 @@ /* --- Windows implementation of thread events ------------------------------ */ +#pragma intrinsic(_InterlockedExchangeAdd) +#pragma intrinsic(_InterlockedCompareExchange) + int ethr_event_init(ethr_event *e) { @@ -72,10 +75,10 @@ wait(ethr_event *e, int spincount) while (1) { long on; while (1) { -#if ETHR_IMMED_ATOMIC_SET_GET_SAFE__ +#if ETHR_READ_AND_SET_WITHOUT_INTERLOCKED_OP__ state = e->state; #else - state = InterlockedExchangeAdd(&e->state, (LONG) 0); + state = _InterlockedExchangeAdd(&e->state, (LONG) 0); #endif if (state == ETHR_EVENT_ON__) return 0; diff --git a/erts/lib_src/win/ethread.c b/erts/lib_src/win/ethread.c index 69523edf94..789a360b11 100644 --- a/erts/lib_src/win/ethread.c +++ b/erts/lib_src/win/ethread.c @@ -49,7 +49,7 @@ /* Argument passed to thr_wrapper() */ typedef struct { ethr_tid *tid; - ethr_atomic_t result; + ethr_atomic32_t result; ethr_ts_event *tse; void *(*thr_func)(void *); void *arg; @@ -93,20 +93,20 @@ static void thr_exit_cleanup(ethr_tid *tid, void *res) static unsigned __stdcall thr_wrapper(LPVOID vtwd) { ethr_tid my_tid; - long result; + ethr_sint32_t result; void *res; ethr_thr_wrap_data__ *twd = (ethr_thr_wrap_data__ *) vtwd; void *(*thr_func)(void *) = twd->thr_func; void *arg = twd->arg; ethr_ts_event *tsep = NULL; - result = (long) ethr_make_ts_event__(&tsep); + result = (ethr_sint32_t) ethr_make_ts_event__(&tsep); if (result == 0) { tsep->iflgs |= ETHR_TS_EV_ETHREAD; my_tid = *twd->tid; if (!TlsSetValue(own_tid_key, (LPVOID) &my_tid)) { - result = (long) ethr_win_get_errno__(); + result = (ethr_sint32_t) ethr_win_get_errno__(); ethr_free_ts_event__(tsep); } else { @@ -118,7 +118,7 @@ static unsigned __stdcall thr_wrapper(LPVOID vtwd) tsep = twd->tse; /* We aren't allowed to follow twd after result has been set! */ - ethr_atomic_set(&twd->result, result); + ethr_atomic32_set(&twd->result, result); ethr_event_set(&tsep->event); @@ -128,28 +128,6 @@ static unsigned __stdcall thr_wrapper(LPVOID vtwd) return 0; } -#ifdef __GNUC__ -#define LL_LITERAL(X) X##LL -#else -#define LL_LITERAL(X) X##i64 -#endif - -#define EPOCH_JULIAN_DIFF LL_LITERAL(11644473600) - -static ETHR_INLINE void -get_curr_time(long *sec, long *nsec) -{ - SYSTEMTIME t; - FILETIME ft; - LONGLONG lft; - - GetSystemTime(&t); - SystemTimeToFileTime(&t, &ft); - memcpy(&lft, &ft, sizeof(lft)); - *nsec = ((long) (lft % LL_LITERAL(10000000)))*100; - *sec = (long) ((lft / LL_LITERAL(10000000)) - EPOCH_JULIAN_DIFF); -} - /* internal exports */ int @@ -320,7 +298,7 @@ ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg, ETHR_PAGE_ALIGN(ETHR_KW2B(suggested_stack_size)); } - ethr_atomic_init(&twd.result, -1); + ethr_atomic32_init(&twd.result, -1); twd.tid = tid; twd.thr_func = func; @@ -352,11 +330,11 @@ ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg, /* Wait for child to initialize... */ while (1) { - long result; + ethr_sint32_t result; int err; ethr_event_reset(&twd.tse->event); - result = ethr_atomic_read(&twd.result); + result = ethr_atomic32_read(&twd.result); if (result == 0) break; @@ -517,23 +495,6 @@ ethr_equal_tids(ethr_tid tid1, ethr_tid tid2) return tid1.id == tid2.id && tid1.id != ETHR_INVALID_TID_ID; } -int -ethr_time_now(ethr_timeval *time) -{ -#if ETHR_XCHK - if (ethr_not_inited__) { - ETHR_ASSERT(0); - return EACCES; - } - if (!time) { - ETHR_ASSERT(0); - return EINVAL; - } -#endif - get_curr_time(&time->tv_sec, &time->tv_nsec); - return 0; -} - /* * Thread specific data */ diff --git a/erts/test/ethread_SUITE.erl b/erts/test/ethread_SUITE.erl index 93e27fa8d3..69e5af802f 100644 --- a/erts/test/ethread_SUITE.erl +++ b/erts/test/ethread_SUITE.erl @@ -37,7 +37,6 @@ equal_tids/1, mutex/1, try_lock_mutex/1, - time_now/1, cond_wait/1, broadcast/1, detached_thread/1, @@ -55,7 +54,6 @@ tests() -> equal_tids, mutex, try_lock_mutex, - time_now, cond_wait, broadcast, detached_thread, @@ -104,17 +102,6 @@ try_lock_mutex(suite) -> try_lock_mutex(Config) -> run_case(Config, "try_lock_mutex", ""). -time_now(doc) -> - ["Tests ethr_time_now by comparing time values with Erlang."]; -time_now(suite) -> - []; -time_now(Config) -> - run_case(Config, "time_now", "", fun (P) -> - spawn_link(fun () -> - watchdog(P) - end) - end). - wd_dispatch(P) -> receive bye -> diff --git a/erts/test/ethread_SUITE_data/ethread_tests.c b/erts/test/ethread_SUITE_data/ethread_tests.c index 7fc71d8047..0b59ff5aa6 100644 --- a/erts/test/ethread_SUITE_data/ethread_tests.c +++ b/erts/test/ethread_SUITE_data/ethread_tests.c @@ -514,69 +514,6 @@ try_lock_mutex_test(void) } /* - * The time now test. - * - * Tests ethr_time_now by comparing time values with Erlang. - */ -#define TNT_MAX_TIME_DIFF 200000 -#define TNT_MAX_TIME_VALUES 52 - -static void -time_now_test(void) -{ - int scanf_res, time_now_res, i, no_values, max_abs_diff; - static ethr_timeval tv[TNT_MAX_TIME_VALUES]; - static int ms[TNT_MAX_TIME_VALUES]; - - i = 0; - do { - ASSERT(i < TNT_MAX_TIME_VALUES); - scanf_res = scanf("%d", &ms[i]); - time_now_res = ethr_time_now(&tv[i]); - ASSERT(scanf_res == 1); - ASSERT(time_now_res == 0); -#if 0 - print_line("Got %d; %ld:%ld", ms[i], tv[i].tv_sec, tv[i].tv_nsec); -#endif - i++; - } while (ms[i-1] >= 0); - - no_values = i-1; - - ASSERT(ms[0] == 0); - - print_line("TNT_MAX_TIME_DIFF = %d (us)", TNT_MAX_TIME_DIFF); - - max_abs_diff = 0; - - for (i = 1; i < no_values; i++) { - long diff; - long tn_us; - long e_us; - - tn_us = (tv[i].tv_sec - tv[0].tv_sec) * 1000000; - tn_us += (tv[i].tv_nsec - tv[0].tv_nsec)/1000; - - e_us = ms[i]*1000; - - diff = e_us - tn_us; - - print_line("Erlang time = %ld us; ethr_time_now = %ld us; diff %ld us", - e_us, tn_us, diff); - - if (max_abs_diff < abs((int) diff)) { - max_abs_diff = abs((int) diff); - } - - ASSERT(e_us - TNT_MAX_TIME_DIFF <= tn_us); - ASSERT(tn_us <= e_us + TNT_MAX_TIME_DIFF); - } - - print_line("Max absolute diff = %d us", max_abs_diff); - succeed("Max absolute diff = %d us", max_abs_diff); -} - -/* * The cond wait test case. * * Tests ethr_cond_wait with ethr_cond_signal and ethr_cond_broadcast. @@ -1538,8 +1475,6 @@ main(int argc, char *argv[]) mutex_test(); else if (strcmp(testcase, "try_lock_mutex") == 0) try_lock_mutex_test(); - else if (strcmp(testcase, "time_now") == 0) - time_now_test(); else if (strcmp(testcase, "cond_wait") == 0) cond_wait_test(); else if (strcmp(testcase, "broadcast") == 0) diff --git a/erts/vsn.mk b/erts/vsn.mk index a5dd62feb2..8a1590e74c 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,8 +17,8 @@ # %CopyrightEnd% # -VSN = 5.8.2 -SYSTEM_VSN = R14B01 +VSN = 5.8.3 +SYSTEM_VSN = R14B02 # Port number 4365 in 4.2 # Port number 4366 in 4.3 diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml index 375e859d20..c93adeffe2 100644 --- a/lib/asn1/doc/src/notes.xml +++ b/lib/asn1/doc/src/notes.xml @@ -31,6 +31,31 @@ <p>This document describes the changes made to the asn1 application.</p> +<section><title>Asn1 1.6.15</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The encoding of ExtensionAdditionGroup (for PER and UPER) + is corrected.</p> + <p> + Own Id: OTP-8866 Aux Id: OTP-8797, SEQ-11557 </p> + </item> + <item> + <p> + A race condition when several processes in parallel start + to do encode/decode using the driver could cause an error + log regarding crashing port owner process. This race is + now eliminated.</p> + <p> + Own Id: OTP-8948 Aux Id: seq11733 </p> + </item> + </list> + </section> + +</section> + <section><title>Asn1 1.6.14.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl index 968468cb7f..947578f07d 100644 --- a/lib/asn1/src/asn1ct.erl +++ b/lib/asn1/src/asn1ct.erl @@ -39,7 +39,7 @@ add_tobe_refed_func/1,add_generated_refed_func/1, maybe_rename_function/3,latest_sindex/0,current_sindex/0, set_current_sindex/1,next_sindex/0,maybe_saved_sindex/2, - parse_and_save/2,report_verbose/3]). + parse_and_save/2,verbose/3,warning/3,error/3]). -include("asn1_records.hrl"). -include_lib("stdlib/include/erl_compile.hrl"). @@ -103,8 +103,8 @@ compile(File,Options) when is_list(Options) -> compile1(File,Options) when is_list(Options) -> - report_verbose("Erlang ASN.1 version ~p compiling ~p ~n",[?vsn,File],Options), - report_verbose("Compiler Options: ~p~n",[Options],Options), + verbose("Erlang ASN.1 version ~p compiling ~p ~n",[?vsn,File],Options), + verbose("Compiler Options: ~p~n",[Options],Options), Ext = filename:extension(File), Base = filename:basename(File,Ext), OutFile = outfile(Base,"",Options), @@ -149,17 +149,17 @@ compile1(File,Options) when is_list(Options) -> inline(true,Name,Module,Options) -> RTmodule = get_runtime_mod(Options), IgorOptions = igorify_options(remove_asn_flags(Options)), - IgorName = filename:rootname(filename:basename(Name)), + IgorName = list_to_atom(filename:rootname(filename:basename(Name))), % io:format("*****~nName: ~p~nModules: ~p~nIgorOptions: ~p~n*****~n", % [IgorName,Modules++RTmodule,IgorOptions]), - report_verbose("Inlining modules: ~p in ~p~n",[[Module]++RTmodule,IgorName],Options), + verbose("Inlining modules: ~p in ~p~n",[[Module]++RTmodule,IgorName],Options), case catch igor:merge(IgorName,[Module]++RTmodule,[{preprocess,true},{stubs,false},{backups,false}]++IgorOptions) of {'EXIT',{undef,Reason}} -> %% module igor first in R10B - io:format("Module igor in syntax_tools must be available:~n~p~n", - [Reason]), + error("Module igor in syntax_tools must be available:~n~p~n", + [Reason],Options), {error,'no_compilation'}; {'EXIT',Reason} -> - io:format("Merge by igor module failed due to ~p~n",[Reason]), + error("Merge by igor module failed due to ~p~n",[Reason],Options), {error,'no_compilation'}; _ -> %% io:format("compiling output module: ~p~n",[generated_file(Name,IgorOptions)]), @@ -173,8 +173,8 @@ inline(_,_,_,_) -> compile_set(SetBase,Files,Options) when is_list(hd(Files)),is_list(Options) -> %% case when there are several input files in a list - report_verbose("Erlang ASN.1 version ~p compiling ~p ~n",[?vsn,Files],Options), - report_verbose("Compiler Options: ~p~n",[Options],Options), + verbose("Erlang ASN.1 version ~p compiling ~p ~n",[?vsn,Files],Options), + verbose("Compiler Options: ~p~n",[Options],Options), OutFile = outfile(SetBase,"",Options), DbFile = outfile(SetBase,"asn1db",Options), Includes = [I || {i,I} <- Options], @@ -728,7 +728,7 @@ parse_set(ScanRes,Options) -> scan(File,Options) -> case asn1ct_tok:file(File) of {error,Reason} -> - io:format("~p~n",[Reason]), + error("~p~n",[Reason],Options), {false,{error,Reason}}; Tokens -> case lists:member(ss,Options) of @@ -753,16 +753,17 @@ parse({true,Tokens},File,Options) -> if is_integer(Line) -> BaseName = filename:basename(File), - io:format("syntax error at line ~p in module ~s:~n", - [Line,BaseName]); + error("syntax error at line ~p in module ~s:~n", + [Line,BaseName],Options); true -> - io:format("syntax error in module ~p:~n",[File]) + error("syntax error in module ~p:~n", + [File],Options) end, print_error_message(Message), {false,{error,Message}}; {error,{Line,_Mod,[Message,Token]}} -> - io:format("syntax error: ~p ~p at line ~p~n", - [Message,Token,Line]), + error("syntax error: ~p ~p at line ~p~n", + [Message,Token,Line],Options), {false,{error,{Line,[Message,Token]}}}; {ok,M} -> case lists:member(sp,Options) of @@ -772,7 +773,7 @@ parse({true,Tokens},File,Options) -> {true,M} end; OtherError -> - io:format("~p~n",[OtherError]) + error("~p~n",[OtherError],Options) end; parse({false,Tokens},_,_) -> {false,Tokens}. @@ -802,7 +803,7 @@ check({true,M},File,OutFile,Includes,EncodingRule,DbFile,Options,InputMods) -> NewM = Module#module{typeorval=NewTypeOrVal}, asn1_db:dbput(NewM#module.name,'MODULE',NewM), asn1_db:dbsave(DbFile,M#module.name), - report_verbose("--~p--~n",[{generated,DbFile}],Options), + verbose("--~p--~n",[{generated,DbFile}],Options), {true,{M,NewM,GenTypeOrVal}} end end; @@ -823,11 +824,11 @@ generate({true,{M,_Module,GenTOrV}},OutFile,EncodingRule,Options) -> % io:format("Options: ~p~n",[Options]), case catch specialized_decode_prepare(EncodingRule,M,GenTOrV,Options) of {error, enoent} -> ok; - {error, Reason} -> io:format("WARNING: Error in configuration" - "file: ~n~p~n",[Reason]); - {'EXIT',Reason} -> io:format("WARNING: Internal error when " - "analyzing configuration" - "file: ~n~p~n",[Reason]); + {error, Reason} -> warning("Error in configuration " + "file: ~n~p~n",[Reason],Options); + {'EXIT',Reason} -> warning("Internal error when " + "analyzing configuration " + "file: ~n~p~n",[Reason],Options); _ -> ok end, @@ -835,7 +836,7 @@ generate({true,{M,_Module,GenTOrV}},OutFile,EncodingRule,Options) -> case (catch asn1ct_gen:pgen(OutFile,EncodingRule, M#module.name,GenTOrV,Options)) of {'EXIT',Reason2} -> - io:format("ERROR: ~p~n",[Reason2]), + error("~p~n",[Reason2],Options), {error,Reason2}; _ -> ok @@ -878,7 +879,8 @@ parse_and_save(Module,S) -> _ -> ok end; Err -> - io:format("Warning: could not do a consistency check of the ~p file: no asn1 source file was found.~n",[lists:concat([Module,".asn1db"])]), + warning("could not do a consistency check of the ~p file: no asn1 source file was found.~n", + [lists:concat([Module,".asn1db"])],Options), {error,{asn1,input_file_error,Err}} end. parse_and_save1(S,File,Options,Includes) -> @@ -1183,6 +1185,7 @@ is_inline(Options) -> _ -> lists:keymember(inline,1,Options) end. + inline_output(Options,Default) -> case [X||{inline,X}<-Options] of [OutputName] -> @@ -1207,7 +1210,7 @@ compile_py(File,OutFile,Options) -> compile(File, _OutFile, Options) -> case catch compile(File, make_erl_options(Options)) of Exit = {'EXIT',_Reason} -> - io:format("~p~n~s~n",[Exit,"error"]), + error("~p~n~s~n",[Exit,"error"],Options), error; {error,_Reason} -> %% case occurs due to error in asn1ct_parser2,asn1ct_check @@ -1223,7 +1226,7 @@ compile(File, _OutFile, Options) -> io:format("~p~n",[ScanRes]), ok; Unknown -> - io:format("~p~n~s~n",[Unknown,"error"]), + error("~p~n~s~n",[Unknown,"error"],Options), error end. @@ -1237,7 +1240,7 @@ make_erl_options(Opts) -> Includes = Opts#options.includes, Defines = Opts#options.defines, Outdir = Opts#options.outdir, -%% Warning = Opts#options.warning, + Warning = Opts#options.warning, Verbose = Opts#options.verbose, Specific = Opts#options.specific, Optimize = Opts#options.optimize, @@ -1249,10 +1252,10 @@ make_erl_options(Opts) -> true -> [verbose]; false -> [] end ++ -%%% case Warning of -%%% 0 -> []; -%%% _ -> [report_warnings] -%%% end ++ + case Warning of + 0 -> []; + _ -> [warnings] + end ++ [] ++ case Optimize of 1 -> [optimize]; @@ -1276,7 +1279,7 @@ make_erl_options(Opts) -> uper_bin -> [uper_bin] end, - Options++[report_errors, {cwd, Cwd}, {outdir, Outdir}| + Options++[errors, {cwd, Cwd}, {outdir, Outdir}| lists:map(fun(Dir) -> {i, Dir} end, Includes)]++Specific. pretty2(Module,AbsFile) -> @@ -2518,13 +2521,48 @@ type_check(#'Externaltypereference'{}) -> make_suffix(_) -> "". -report_verbose(Format, Args, S) -> +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Report functions. +%% +%% Errors messages are controlled with the 'errors' compiler option +%% Warning messages are controlled with the 'warnings' compiler option +%% Verbose messages are controlled with the 'verbose' compiler option + +error(Format, Args, S) -> + case is_error(S) of + true -> + io:format("Error: " ++ Format, Args); + false -> + ok + end. + +warning(Format, Args, S) -> + case is_warning(S) of + true -> + io:format("Warning: " ++ Format, Args); + false -> + ok + end. + +verbose(Format, Args, S) -> case is_verbose(S) of - true -> - io:format(Format, Args); - false -> - ok + true -> + io:format(Format, Args); + false -> + ok end. -is_verbose(S) -> - lists:member(verbose, S). +is_error(S) when is_record(S, state) -> + is_error(S#state.options); +is_error(O) -> + lists:member(errors, O) orelse is_verbose(O). + +is_warning(S) when is_record(S, state) -> + is_warning(S#state.options); +is_warning(O) -> + lists:member(warnings, O) orelse is_verbose(O). + +is_verbose(S) when is_record(S, state) -> + is_verbose(S#state.options); +is_verbose(O) -> + lists:member(verbose, O). diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index c6f3b60786..8b1ee6e601 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -2029,8 +2029,9 @@ get_objectset_def2(_S,Set,CField) when is_list(Set) -> set=Set}}; get_objectset_def2(_S,T = #typedef{typespec=#'ObjectSet'{}},_CField) -> T; -get_objectset_def2(_S,T,_CField) -> - io:format("Warning get_objectset_def2: uncontrolled object set structure:~n~p~n",[T]). +get_objectset_def2(S,T,_CField) -> + asn1ct:warning("get_objectset_def2: uncontrolled object set structure:~n~p~n", + [T],S). type_name(S,#type{def=Def}) -> CurrMod = S#state.mname, @@ -2687,7 +2688,7 @@ normalize_value(S,Type,{'DEFAULT',Value},NameList) -> {'REAL',_,_} -> normalize_real(Value); {'ENUMERATED',CType,_} -> - normalize_enumerated(Value,CType); + normalize_enumerated(S,Value,CType); {'CHOICE',CType,NewNameList} -> normalize_choice(S,Value,CType,NewNameList); {'SEQUENCE',CType,NewNameList} -> @@ -2703,7 +2704,8 @@ normalize_value(S,Type,{'DEFAULT',Value},NameList) -> {'ASN1_OPEN_TYPE',{typefield,_TF},NL} -> %an open type normalize_objectclassfieldvalue(S,Value,NL); Err -> - io:format("WARNING: could not check default value ~p~nType:~n~p~nNameList:~n~p~n",[Value,Type,Err]), + asn1ct:warning("could not check default value ~p~nType:~n~p~nNameList:~n~p~n", + [Value,Type,Err],S), Value end; normalize_value(S,Type,Val,NameList) -> @@ -2788,23 +2790,23 @@ normalize_bitstring(S,Value,Type)-> end, case catch lists:map(F,RecList) of {error,Reason} -> - io:format("WARNING: default value not " + asn1ct:warning("default value not " "compatible with type definition ~p~n", - [Reason]), + [Reason],S), Value; NewList -> NewList end; _ -> - io:format("WARNING: default value not " + asn1ct:warning("default value not " "compatible with type definition ~p~n", - [RecList]), + [RecList],S), Value end; {Name,String} when is_atom(Name) -> normalize_bitstring(S,String,Type); Other -> - io:format("WARNING: illegal default value ~p~n",[Other]), + asn1ct:warning("illegal default value ~p~n",[Other],S), Value end. @@ -2843,12 +2845,13 @@ normalize_octetstring(S,Value,CType) -> %% check if list elements are valid octet values lists:map(fun([])-> ok; (H)when H > 255-> - io:format("WARNING: not legal octet value ~p in OCTET STRING, ~p~n",[H,List]); + asn1ct:warning("not legal octet value ~p in OCTET STRING, ~p~n", + [H,List],S); (_)-> ok end, List), List; Other -> - io:format("WARNING: unknown default value ~p~n",[Other]), + asn1ct:warning("unknown default value ~p~n",[Other],S), Value end. @@ -2895,23 +2898,23 @@ normalize_objectdescriptor(Value) -> normalize_real(Value) -> Value. -normalize_enumerated(#'Externalvaluereference'{value=V},CType) +normalize_enumerated(S,#'Externalvaluereference'{value=V},CType) when is_list(CType) -> - normalize_enumerated2(V,CType); -normalize_enumerated(Value,CType) when is_atom(Value),is_list(CType) -> - normalize_enumerated2(Value,CType); -normalize_enumerated({Name,EnumV},CType) when is_atom(Name) -> - normalize_enumerated(EnumV,CType); -normalize_enumerated(Value,{CType1,CType2}) when is_list(CType1), is_list(CType2)-> - normalize_enumerated(Value,CType1++CType2); -normalize_enumerated(V,CType) -> - io:format("WARNING: Enumerated unknown type ~p~n",[CType]), + normalize_enumerated2(S,V,CType); +normalize_enumerated(S,Value,CType) when is_atom(Value),is_list(CType) -> + normalize_enumerated2(S,Value,CType); +normalize_enumerated(S,{Name,EnumV},CType) when is_atom(Name) -> + normalize_enumerated(S,EnumV,CType); +normalize_enumerated(S,Value,{CType1,CType2}) when is_list(CType1), is_list(CType2)-> + normalize_enumerated(S,Value,CType1++CType2); +normalize_enumerated(S,V,CType) -> + asn1ct:warning("Enumerated unknown type ~p~n",[CType],S), V. -normalize_enumerated2(V,Enum) -> +normalize_enumerated2(S,V,Enum) -> case lists:keysearch(V,1,Enum) of {value,{Val,_}} -> Val; _ -> - io:format("WARNING: Enumerated value is not correct ~p~n",[V]), + asn1ct:warning("Enumerated value is not correct ~p~n",[V],S), V end. @@ -2922,8 +2925,7 @@ normalize_choice(S,{'CHOICE',{C,V}},CType,NameList) when is_atom(C) -> {C,normalize_value(S,CT,{'DEFAULT',V}, [Name|NameList])}; Other -> - io:format("WARNING: Wrong format of type/value ~p/~p~n", - [Other,V]), + asn1ct:warning("Wrong format of type/value ~p/~p~n",[Other,V],S), {C,V} end; normalize_choice(S,{'DEFAULT',ValueList},CType,NameList) when is_list(ValueList) -> @@ -3099,8 +3101,7 @@ normalize_s_of(SorS,S,Value,Type,NameList) when is_list(Value) -> List when is_list(List) -> List; _ -> - io:format("WARNING: ~p could not handle value ~p~n", - [SorS,Value]), + asn1ct:warning("~p could not handle value ~p~n",[SorS,Value],S), Value end; normalize_s_of(SorS,S,Value,Type,NameList) @@ -3152,15 +3153,13 @@ get_normalized_value(S,Val,Type,Func,AddArg) -> V2 = sort_val_if_set(AddArg,V,Type), call_Func(update_state(S,ExtM),V2,Type,Func,AddArg); {error,_} -> - io:format("WARNING: default value not " - "comparable ~p~n",[Val]), + asn1ct:warning("default value not comparable ~p~n",[Val],S), Val; {ExtM,NewVal} -> V2 = sort_val_if_set(AddArg,NewVal,Type), call_Func(update_state(S,ExtM),V2,Type,Func,AddArg); _ -> - io:format("WARNING: default value not " - "comparable ~p~n",[Val]), + asn1ct:warning("default value not comparable ~p~n",[Val],S), Val end. @@ -4109,7 +4108,7 @@ resolve_namednumber(S,#typedef{typespec=Type},Name) -> case Type#type.def of {'ENUMERATED',NameList} -> NamedNumberList=check_enumerated(S,NameList,Type#type.constraint), - N = normalize_enumerated(Name,NamedNumberList), + N = normalize_enumerated(S,Name,NamedNumberList), {value,{_,V}} = lists:keysearch(N,1,NamedNumberList), V; {'INTEGER',NameList} -> @@ -5710,9 +5709,9 @@ sort_components(der,S=#state{tname=TypeName},Components) -> end, case {untagged_choice(S,CompsList),Ext} of {false,noext} -> - {true,sort_components1(TypeName,CompsList,[],[],[],[])}; + {true,sort_components1(S,TypeName,CompsList,[],[],[],[])}; {false,_} -> - {true,{sort_components1(TypeName,CompsList,[],[],[],[]), []}}; + {true,{sort_components1(S,TypeName,CompsList,[],[],[],[]), []}}; {true,noext} -> %% sort in run-time {dynamic,R1}; @@ -5724,57 +5723,57 @@ sort_components(per,S=#state{tname=TypeName},Components) -> Root = tag_untagged_choice(S,R1++R2), case Ext of noext -> - {true,sort_components1(TypeName,Root,[],[],[],[])}; + {true,sort_components1(S,TypeName,Root,[],[],[],[])}; _ -> - {true,{sort_components1(TypeName,Root,[],[],[],[]), + {true,{sort_components1(S,TypeName,Root,[],[],[],[]), Ext}} end. -sort_components1(TypeName,[C=#'ComponentType'{tags=[{'UNIVERSAL',_}|_R]}|Cs], +sort_components1(S,TypeName,[C=#'ComponentType'{tags=[{'UNIVERSAL',_}|_R]}|Cs], UnivAcc,ApplAcc,ContAcc,PrivAcc) -> - sort_components1(TypeName,Cs,[C|UnivAcc],ApplAcc,ContAcc,PrivAcc); -sort_components1(TypeName,[C=#'ComponentType'{tags=[{'APPLICATION',_}|_R]}|Cs], + sort_components1(S,TypeName,Cs,[C|UnivAcc],ApplAcc,ContAcc,PrivAcc); +sort_components1(S,TypeName,[C=#'ComponentType'{tags=[{'APPLICATION',_}|_R]}|Cs], UnivAcc,ApplAcc,ContAcc,PrivAcc) -> - sort_components1(TypeName,Cs,UnivAcc,[C|ApplAcc],ContAcc,PrivAcc); -sort_components1(TypeName,[C=#'ComponentType'{tags=[{'CONTEXT',_}|_R]}|Cs], + sort_components1(S,TypeName,Cs,UnivAcc,[C|ApplAcc],ContAcc,PrivAcc); +sort_components1(S,TypeName,[C=#'ComponentType'{tags=[{'CONTEXT',_}|_R]}|Cs], UnivAcc,ApplAcc,ContAcc,PrivAcc) -> - sort_components1(TypeName,Cs,UnivAcc,ApplAcc,[C|ContAcc],PrivAcc); -sort_components1(TypeName,[C=#'ComponentType'{tags=[{'PRIVATE',_}|_R]}|Cs], + sort_components1(S,TypeName,Cs,UnivAcc,ApplAcc,[C|ContAcc],PrivAcc); +sort_components1(S,TypeName,[C=#'ComponentType'{tags=[{'PRIVATE',_}|_R]}|Cs], UnivAcc,ApplAcc,ContAcc,PrivAcc) -> - sort_components1(TypeName,Cs,UnivAcc,ApplAcc,ContAcc,[C|PrivAcc]); -sort_components1(TypeName,[],UnivAcc,ApplAcc,ContAcc,PrivAcc) -> + sort_components1(S,TypeName,Cs,UnivAcc,ApplAcc,ContAcc,[C|PrivAcc]); +sort_components1(S,TypeName,[],UnivAcc,ApplAcc,ContAcc,PrivAcc) -> I = #'ComponentType'.tags, - ascending_order_check(TypeName,sort_universal_type(UnivAcc)) ++ - ascending_order_check(TypeName,lists:keysort(I,ApplAcc)) ++ - ascending_order_check(TypeName,lists:keysort(I,ContAcc)) ++ - ascending_order_check(TypeName,lists:keysort(I,PrivAcc)). + ascending_order_check(S,TypeName,sort_universal_type(UnivAcc)) ++ + ascending_order_check(S,TypeName,lists:keysort(I,ApplAcc)) ++ + ascending_order_check(S,TypeName,lists:keysort(I,ContAcc)) ++ + ascending_order_check(S,TypeName,lists:keysort(I,PrivAcc)). -ascending_order_check(TypeName,Components) -> - ascending_order_check1(TypeName,Components), +ascending_order_check(S,TypeName,Components) -> + ascending_order_check1(S,TypeName,Components), Components. -ascending_order_check1(TypeName, +ascending_order_check1(S,TypeName, [C1 = #'ComponentType'{tags=[{_,T}|_]}, C2 = #'ComponentType'{tags=[{_,T}|_]}|Rest]) -> - io:format("WARNING: Indistinct tag ~p in SET ~p, components ~p and ~p~n", - [T,TypeName,C1#'ComponentType'.name,C2#'ComponentType'.name]), - ascending_order_check1(TypeName,[C2|Rest]); -ascending_order_check1(TypeName, + asn1ct:warning("Indistinct tag ~p in SET ~p, components ~p and ~p~n", + [T,TypeName,C1#'ComponentType'.name,C2#'ComponentType'.name],S), + ascending_order_check1(S,TypeName,[C2|Rest]); +ascending_order_check1(S,TypeName, [C1 = #'ComponentType'{tags=[{'UNIVERSAL',T1}|_]}, C2 = #'ComponentType'{tags=[{'UNIVERSAL',T2}|_]}|Rest]) -> case (decode_type(T1) == decode_type(T2)) of true -> - io:format("WARNING: Indistinct tags ~p and ~p in" + asn1ct:warning("Indistinct tags ~p and ~p in" " SET ~p, components ~p and ~p~n", [T1,T2,TypeName,C1#'ComponentType'.name, - C2#'ComponentType'.name]), - ascending_order_check1(TypeName,[C2|Rest]); + C2#'ComponentType'.name],S), + ascending_order_check1(S,TypeName,[C2|Rest]); _ -> - ascending_order_check1(TypeName,[C2|Rest]) + ascending_order_check1(S,TypeName,[C2|Rest]) end; -ascending_order_check1(N,[_|Rest]) -> - ascending_order_check1(N,Rest); -ascending_order_check1(_,[]) -> +ascending_order_check1(S,N,[_|Rest]) -> + ascending_order_check1(S,N,Rest); +ascending_order_check1(_,_,[]) -> ok. sort_universal_type(Components) -> diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index cce6eb9831..d6f23aca06 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -326,16 +326,14 @@ gen_decode_constructed(Erules,Typename,D) when is_record(D,type) -> textual_order([#'ComponentType'{textual_order=undefined}|_],TermList) -> TermList; textual_order(CompList,TermList) when is_list(CompList) -> - OrderList = [Ix||#'ComponentType'{textual_order=Ix} <- CompList], + OrderList = [Ix||#'ComponentType'{textual_order=Ix} <- CompList], [Term||{_,Term}<- lists:sort(lists:zip(OrderList, lists:sublist(TermList,length(OrderList))))]; %% sublist is just because Termlist can sometimes be longer than %% OrderList, which it really shouldn't textual_order({Root,Ext},TermList) -> - textual_order(Root ++ Ext,TermList); -textual_order({Root1,Ext,Root2},TermList) -> - textual_order(Root1 ++ Ext ++ Root2, TermList). + textual_order(Root ++ Ext,TermList). to_textual_order({Root,Ext}) -> {to_textual_order(Root),Ext}; diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index 0bb0b65e5d..0844c38353 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -87,7 +87,7 @@ pgen_module(OutFile,Erules,Module, % gen_vars(asn1_db:mod_to_vars(Module)), % gen_tag_table(AllTypes), file:close(Fid), - asn1ct:report_verbose("--~p--~n",[{generated,ErlFile}],Options). + asn1ct:verbose("--~p--~n",[{generated,ErlFile}],Options). pgen_typeorval(Erules,Module,N2nConvEnums,{Types,Values,_Ptypes,_Classes,Objects,ObjectSets}) -> @@ -1340,9 +1340,9 @@ pgen_hrl(Erules,Module,TypeOrVal,Options,_Indent) -> Y -> Fid = get(gen_file_out), file:close(Fid), - asn1ct:report_verbose("--~p--~n", - [{generated,lists:concat([get(outfile),".hrl"])}], - Options), + asn1ct:verbose("--~p--~n", + [{generated,lists:concat([get(outfile),".hrl"])}], + Options), Y end. diff --git a/lib/asn1/src/asn1rt_ber_bin.erl b/lib/asn1/src/asn1rt_ber_bin.erl index ab04d981b0..22f9f2ecfd 100644 --- a/lib/asn1/src/asn1rt_ber_bin.erl +++ b/lib/asn1/src/asn1rt_ber_bin.erl @@ -2192,12 +2192,12 @@ decode_tag_and_length(Buffer) -> %% Check if valid tag %% %% check_if_valid_tag(Tag, List_of_valid_tags, OptOrMand) -> name of the tag -%%=============================================================================== +%%============================================================================ check_if_valid_tag(<<0,0,_/binary>>,_,_) -> asn1_EOC; check_if_valid_tag(<<>>, _, OptOrMand) -> - check_if_valid_tag2(false,[],[],OptOrMand); + check_if_valid_tag2_error([], OptOrMand); check_if_valid_tag(Bytes, ListOfTags, OptOrMand) when is_binary(Bytes) -> {Tag, _, _} = decode_tag(Bytes), check_if_valid_tag(Tag, ListOfTags, OptOrMand); @@ -2217,7 +2217,6 @@ check_if_valid_tag(Tag, ListOfTags, OptOrMand) -> check_if_valid_tag2(_Class_TagNo, [], Tag, MandOrOpt) -> check_if_valid_tag2_error(Tag,MandOrOpt); - check_if_valid_tag2(Class_TagNo, [{TagName,TagList}|T], Tag, OptOrMand) -> case check_if_valid_tag_loop(Class_TagNo, TagList) of true -> @@ -2226,7 +2225,7 @@ check_if_valid_tag2(Class_TagNo, [{TagName,TagList}|T], Tag, OptOrMand) -> check_if_valid_tag2(Class_TagNo, T, Tag, OptOrMand) end. --spec(check_if_valid_tag2_error/2 :: (term(),atom()) -> no_return()). +-spec check_if_valid_tag2_error(term(), atom()) -> no_return(). check_if_valid_tag2_error(Tag,mandatory) -> exit({error,{asn1,{invalid_tag,Tag}}}); diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk index 0399ff2732..e900a52286 100644 --- a/lib/asn1/vsn.mk +++ b/lib/asn1/vsn.mk @@ -1,2 +1,2 @@ #next version number to use is 1.6.15 | 1.7 | 2.0 -ASN1_VSN = 1.6.14.1 +ASN1_VSN = 1.6.15 diff --git a/lib/common_test/doc/src/Makefile b/lib/common_test/doc/src/Makefile index 1a767a8197..a914dd0c19 100644 --- a/lib/common_test/doc/src/Makefile +++ b/lib/common_test/doc/src/Makefile @@ -52,7 +52,7 @@ CT_XML_FILES = $(CT_MODULES:=.xml) XML_APPLICATION_FILES = ref_man.xml XML_REF1_FILES = ct_run.xml -XML_REF3_FILES = $(CT_XML_FILES) +XML_REF3_FILES = $(CT_XML_FILES) ct_hooks.xml XML_REF6_FILES = common_test_app.xml XML_PART_FILES = part.xml @@ -71,6 +71,7 @@ XML_CHAPTER_FILES = \ cover_chapter.xml \ ct_master_chapter.xml \ event_handler_chapter.xml \ + ct_hooks_chapter.xml \ dependencies_chapter.xml \ notes.xml \ notes_history.xml diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml index e30eef2488..b4e4b45d62 100644 --- a/lib/common_test/doc/src/common_test_app.xml +++ b/lib/common_test/doc/src/common_test_app.xml @@ -131,7 +131,8 @@ <type> <v> Info = {timetrap,Time} | {require,Required} | {require,Name,Required} | {userdata,UserData} | - {silent_connections,Conns} | {stylesheet,CSSFile}</v> + {silent_connections,Conns} | {stylesheet,CSSFile} | + {ct_hooks, CTHs}</v> <v> Time = MilliSec | {seconds,integer()} | {minutes,integer()} | {hours,integer()}</v> <v> MilliSec = integer()</v> @@ -143,6 +144,9 @@ <v> UserData = term()</v> <v> Conns = [atom()]</v> <v> CSSFile = string()</v> + <v> CTHs = [CTHModule | {CTHModule, CTHInitArgs}]</v> + <v> CTHModule = atom()</v> + <v> CTHInitArgs = term()</v> </type> <desc> @@ -170,6 +174,10 @@ <p>With <c>userdata</c>, it is possible for the user to specify arbitrary test suite related information which can be read by calling <c>ct:userdata/2</c>.</p> + + <p>The <c>ct_hooks</c> tag specifies which + <seealso marker="ct_hooks_chapter">Common Test Hooks</seealso> + are to be run together with this suite.</p> <p>Other tuples than the ones defined will simply be ignored.</p> diff --git a/lib/common_test/doc/src/ct_hooks.xml b/lib/common_test/doc/src/ct_hooks.xml new file mode 100644 index 0000000000..0d59ce3b22 --- /dev/null +++ b/lib/common_test/doc/src/ct_hooks.xml @@ -0,0 +1,556 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>2010</year><year>2010</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + 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 online 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. + + </legalnotice> + + <title>Common Test Hooks</title> + <prepared>Lukas Larsson</prepared> + <responsible>Lukas Larsson</responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date>2010-12-02</date> + <rev>PA1</rev> + <file>ct_hooks.sgml</file> + </header> + <module>ct_hooks</module> + <modulesummary>A callback interface on top of Common Test</modulesummary> + + <description> + + <warning><p>This feature is in alpha release right now. This means that the + interface may change in the future and that there may be bugs. We + encourage you to use this feature, but be prepared + that there might be bugs and that the interface might change + inbetween releases.</p></warning> + + <p>The <em>Common Test Hook</em> (henceforth called CTH) framework allows + extensions of the default behaviour of Common Test by means of callbacks + before and after all test suite calls. It is meant for advanced users of + Common Test which want to abstract out behaviour which is common to + multiple test suites. </p> + + <p>In brief, Common Test Hooks allows you to:</p> + + <list> + <item>Manipulate the runtime config before each suite + configuration call</item> + <item>Manipulate the return of all suite configuration calls and in + extension the result of the test themselves.</item> + </list> + + <p>The following sections describe the mandatory and optional CTH + functions Common Test will call during test execution. For more details + see <seealso marker="ct_hooks_chapter">Common Test Hooks</seealso> in + the User's Guide.</p> + + <p>For information about how to add a CTH to your suite see + <seealso marker="ct_hooks_chapter#installing">Installing a CTH + </seealso> in the User's Guide.</p> + + <note><p>See the + <seealso marker="ct_hooks_chapter#example">Example CTH</seealso> + in the User's Guide for a minimal example of a CTH. </p></note> + + </description> + + <section> + <title>CALLBACK FUNCTIONS</title> + <p>The following functions define the callback interface + for a Common Test Hook.</p> + </section> + + <funcs> + <func> + <name>Module:init(Id, Opts) -> State</name> + <fsummary>Initiates the Common Test Hook</fsummary> + <type> + <v>Id = reference() | term()</v> + <v>Opts = term()</v> + <v>State = term()</v> + </type> + + <desc> + <p> MANDATORY </p> + + <p>Always called before any other callback function. + Use this to initiate any common state. + It should return a state for this CTH.</p> + + <p><c>Id</c> is the return value of + <seealso marker="#Module:id-1">id/1</seealso>, or a <c>reference</c> + (created using + <seealso marker="erts:erlang#make_ref-0">make_ref/0</seealso>) + if <seealso marker="#Module:id-1">id/1</seealso> is not implemented. + </p> + + <p>For details about when init is called see + <seealso marker="ct_hooks_chapter#scope">scope</seealso> + in the User's Guide.</p> + + </desc> + </func> + + <func> + <name>Module:pre_init_per_suite(SuiteName, Config, CTHState) -> + Result</name> + <fsummary>Called before init_per_suite</fsummary> + <type> + <v>SuiteName = atom()</v> + <v>Config = NewConfig = [{Key,Value}]</v> + <v>CTHState = NewCTHState = term()</v> + <v>Result = {Return, NewCTHState}</v> + <v>Return = NewConfig | SkipOrFail</v> + <v>SkipOrFail = {fail, Reason} | {skip, Reason}</v> + <v>Key = atom()</v> + <v>Value = term()</v> + <v>Reason = term()</v> + </type> + + <desc> + <p> OPTIONAL </p> + + <p>This function is called before + <seealso marker="common_test#Module:init_per_suite-1"> + init_per_suite</seealso> if it exists. + It typically contains initialization/logging which needs to be done + before init_per_suite is called. + If <c>{skip,Reason}</c> or <c>{fail,Reason}</c> is returned, + init_per_suite and all test cases of the suite will be skipped and + Reason printed in the overview log of the suite.</p> + + <p><c>SuiteName</c> is the name of the suite to be run.</p> + + <p><c>Config</c> is the original config list of the test suite.</p> + + <p><c>CTHState</c> is the current internal state of the CTH.</p> + + <p><c>Return</c> is the result of the init_per_suite function. + If it is <c>{skip,Reason}</c> or <c>{fail,Reason}</c> + <seealso marker="common_test#Module:init_per_suite-1">init_per_suite + </seealso> will never be called, instead the initiation is considered + to be skipped/failed respectively. If a <c>NewConfig</c> list + is returned, <seealso marker="common_test#Module:init_per_suite-1"> + init_per_suite</seealso> will be called with that <c>NewConfig</c> list. + See <seealso marker="ct_hooks_chapter#pre"> + Pre Hooks</seealso> in the User's Guide for more details.</p> + + + <p>Note that this function is only called if the CTH has been added + before init_per_suite is run, see + <seealso marker="ct_hooks_chapter#scope">CTH Scoping</seealso> + in the User's Guide for details.</p> + </desc> + </func> + + <func> + <name>Module:post_init_per_suite(SuiteName, Config, Return, CTHState) -> + Result</name> + <fsummary>Called after init_per_suite</fsummary> + <type> + <v>SuiteName = atom()</v> + <v>Config = [{Key,Value}]</v> + <v>Return = NewReturn = Config | SkipOrFail | term()</v> + <v>SkipOrFail = {fail, Reason} | {skip, Reason} | term()</v> + <v>CTHState = NewCTHState = term()</v> + <v>Result = {NewReturn, NewCTHState}</v> + <v>Key = atom()</v> + <v>Value = term()</v> + <v>Reason = term()</v> + </type> + + <desc> + <p> OPTIONAL </p> + + <p>This function is called after + <seealso marker="common_test#Module:init_per_suite-1"> + init_per_suite</seealso> if it exists. It typically contains extra + checks to make sure that all the correct dependencies have + been started correctly.</p> + + <p><c>Return</c> is what + <seealso marker="common_test#Module:init_per_suite-1">init_per_suite + </seealso> returned, i.e. {fail,Reason}, {skip,Reason}, a <c>Config</c> + list or a term describing how + <seealso marker="common_test#Module:init_per_suite-1">init_per_suite + </seealso> failed.</p> + + <p><c>NewReturn</c> is the possibly modified return value of + <seealso marker="common_test#Module:init_per_suite-1">init_per_suite + </seealso>. It is here possible to recover from a failure in + <seealso marker="common_test#Module:init_per_suite-1">init_per_suite + </seealso> by returning the <c>ConfigList</c> with the <c>tc_status</c> + element removed. See <seealso marker="ct_hooks_chapter#post"> + Post Hooks</seealso> in the User's Guide for more details.</p> + + <p><c>CTHState</c> is the current internal state of the CTH.</p> + + <p>Note that this function is only called if the CTH has been added + before or in init_per_suite, see + <seealso marker="ct_hooks_chapter#scope">CTH Scoping</seealso> + in the User's Guide for details.</p> + </desc> + </func> + + <func> + <name>Module:pre_init_per_group(GroupName, Config, CTHState) -> + Result</name> + <fsummary>Called before init_per_group</fsummary> + <type> + <v>GroupName = atom()</v> + <v>Config = NewConfig = [{Key,Value}]</v> + <v>CTHState = NewCTHState = term()</v> + <v>Result = {NewConfig | SkipOrFail, NewCTHState}</v> + <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v> + <v>Key = atom()</v> + <v>Value = term()</v> + <v>Reason = term()</v> + </type> + + <desc> + <p> OPTIONAL </p> + + <p>This function is called before + <seealso marker="common_test#Module:init_per_group-2"> + init_per_group</seealso> if it exists. It behaves the same way as + <seealso marker="ct_hooks#Module:pre_init_per_suite-3"> + pre_init_per_suite</seealso>, but for the + <seealso marker="common_test#Module:init_per_group-2"> + init_per_group</seealso> instead.</p> + </desc> + </func> + + <func> + <name>Module:post_init_per_group(GroupName, Config, Return, CTHState) -> + Result</name> + <fsummary>Called after init_per_group</fsummary> + <type> + <v>GroupName = atom()</v> + <v>Config = [{Key,Value}]</v> + <v>Return = NewReturn = Config | SkipOrFail | term()</v> + <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v> + <v>CTHState = NewCTHState = term()</v> + <v>Result = {NewReturn, NewCTHState}</v> + <v>Key = atom()</v> + <v>Value = term()</v> + <v>Reason = term()</v> + </type> + + <desc> + <p> OPTIONAL </p> + + <p>This function is called after + <seealso marker="common_test#Module:init_per_group-2"> + init_per_group</seealso> if it exists. It behaves the same way as + <seealso marker="ct_hooks#Module:post_init_per_suite-4"> + post_init_per_suite</seealso>, but for the + <seealso marker="common_test#Module:init_per_group-2"> + init_per_group</seealso> instead.</p> + </desc> + </func> + + <func> + <name>Module:pre_init_per_testcase(TestcaseName, Config, CTHState) -> + Result</name> + <fsummary>Called before init_per_testcase</fsummary> + <type> + <v>TestcaseName = atom()</v> + <v>Config = NewConfig = [{Key,Value}]</v> + <v>CTHState = NewCTHState = term()</v> + <v>Result = {NewConfig | SkipOrFail, NewCTHState}</v> + <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v> + <v>Key = atom()</v> + <v>Value = term()</v> + <v>Reason = term()</v> + </type> + + <desc> + <p> OPTIONAL </p> + + <p>This function is called before + <seealso marker="common_test#Module:init_per_testcase-2"> + init_per_testcase</seealso> if it exists. It behaves the same way as + <seealso marker="ct_hooks#Module:pre_init_per_suite-3"> + pre_init_per_suite</seealso>, but for the + <seealso marker="common_test#Module:init_per_testcase-2"> + init_per_testcase</seealso> function instead.</p> + + <p>Note that it is not possible to add CTH's here right now, + that feature might be added later, + but it would right now break backwards compatability.</p> + </desc> + </func> + + <func> + <name>Module:post_end_per_testcase(TestcaseName, Config, Return, CTHState) + -> Result</name> + <fsummary>Called after end_per_testcase</fsummary> + <type> + <v>TestcaseName = atom()</v> + <v>Config = [{Key,Value}]</v> + <v>Return = NewReturn = Config | SkipOrFail | term()</v> + <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v> + <v>CTHState = NewCTHState = term()</v> + <v>Result = {NewReturn, NewCTHState}</v> + <v>Key = atom()</v> + <v>Value = term()</v> + <v>Reason = term()</v> + </type> + + <desc> + <p> OPTIONAL </p> + + <p>This function is called after + <seealso marker="common_test#Module:end_per_testcase-2"> + end_per_testcase</seealso> if it exists. It behaves the same way as + <seealso marker="ct_hooks#Module:post_init_per_suite-4"> + post_init_per_suite</seealso>, but for the + <seealso marker="common_test#Module:end_per_testcase-2"> + end_per_testcase</seealso> function instead.</p> + </desc> + </func> + + <func> + <name>Module:pre_end_per_group(GroupName, Config, CTHState) -> + Result</name> + <fsummary>Called before end_per_group</fsummary> + <type> + <v>GroupName = atom()</v> + <v>Config = NewConfig = [{Key,Value}]</v> + <v>CTHState = NewCTHState = term()</v> + <v>Result = {NewConfig | SkipOrFail, NewCTHState}</v> + <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v> + <v>Key = atom()</v> + <v>Value = term()</v> + <v>Reason = term()</v> + </type> + + <desc> + <p> OPTIONAL </p> + + <p>This function is called before + <seealso marker="common_test#Module:end_per_group-2"> + end_per_group</seealso> if it exists. It behaves the same way as + <seealso marker="ct_hooks#Module:pre_init_per_suite-3"> + pre_init_per_suite</seealso>, but for the + <seealso marker="common_test#Module:end_per_group-2"> + end_per_group</seealso> function instead.</p> + </desc> + </func> + + <func> + <name>Module:post_end_per_group(GroupName, Config, Return, CTHState) -> + Result</name> + <fsummary>Called after end_per_group</fsummary> + <type> + <v>GroupName = atom()</v> + <v>Config = [{Key,Value}]</v> + <v>Return = NewReturn = Config | SkipOrFail | term()</v> + <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v> + <v>CTHState = NewCTHState = term()</v> + <v>Result = {NewReturn, NewCTHState}</v> + <v>Key = atom()</v> + <v>Value = term()</v> + <v>Reason = term()</v> + </type> + + <desc> + <p> OPTIONAL </p> + + <p>This function is called after + <seealso marker="common_test#Module:end_per_group-2"> + end_per_group</seealso> if it exists. It behaves the same way as + <seealso marker="ct_hooks#Module:post_init_per_suite-4"> + post_init_per_suite</seealso>, but for the + <seealso marker="common_test#Module:end_per_group-2"> + end_per_group</seealso> function instead.</p> + </desc> + </func> + + <func> + <name>Module:pre_end_per_suite(SuiteName, Config, CTHState) -> + Result</name> + <fsummary>Called before end_per_suite</fsummary> + <type> + <v>SuiteName = atom()</v> + <v>Config = NewConfig = [{Key,Value}]</v> + <v>CTHState = NewCTHState = term()</v> + <v>Result = {NewConfig | SkipOrFail, NewCTHState}</v> + <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v> + <v>Key = atom()</v> + <v>Value = term()</v> + <v>Reason = term()</v> + </type> + + <desc> + <p> OPTIONAL </p> + + <p>This function is called before + <seealso marker="common_test#Module:end_per_suite-1"> + end_per_suite</seealso> if it exists. It behaves the same way as + <seealso marker="ct_hooks#Module:pre_init_per_suite-3"> + pre_init_per_suite</seealso>, but for the + <seealso marker="common_test#Module:end_per_suite-2"> + end_per_suite</seealso> function instead.</p> + </desc> + </func> + + <func> + <name>Module:post_end_per_suite(SuiteName, Config, Return, CTHState) -> + Result</name> + <fsummary>Called after end_per_suite</fsummary> + <type> + <v>SuiteName = atom()</v> + <v>Config = [{Key,Value}]</v> + <v>Return = NewReturn = Config | SkipOrFail | term()</v> + <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v> + <v>CTHState = NewCTHState = term()</v> + <v>Result = {NewReturn, NewCTHState}</v> + <v>Key = atom()</v> + <v>Value = term()</v> + <v>Reason = term()</v> + </type> + + <desc> + <p> OPTIONAL </p> + + <p>This function is called after + <seealso marker="common_test#Module:end_per_suite-1"> + end_per_suite</seealso> if it exists. It behaves the same way as + <seealso marker="ct_hooks#Module:post_init_per_suite-4"> + post_init_per_suite</seealso>, but for the + <seealso marker="common_test#Module:end_per_suite-2"> + end_per_suite</seealso> function instead.</p> + </desc> + </func> + + <func> + <name>Module:on_tc_fail(TestcaseName, Reason, CTHState) -> + NewCTHState</name> + <fsummary>Called after the CTH scope ends</fsummary> + <type> + <v>TestcaseName = init_per_suite | end_per_suite | + init_per_group | end_per_group | atom()</v> + <v>Reason = term()</v> + <v>CTHState = NewCTHState = term()</v> + </type> + + <desc> + <p> OPTIONAL </p> + + <p>This function is called whenever a testcase fails. + It is called after the post function has been called for + the testcase which failed. i.e. + if init_per_suite fails this function is called after + <seealso marker="#Module:post_init_per_suite-4"> + post_init_per_suite</seealso>, and if a testcase fails it is called + after <seealso marker="#Module:post_end_per_testcase-4"> + post_end_per_testcase</seealso>.</p> + + <p>The data which comes with the Reason follows the same format as the + <seealso marker="event_handler_chapter#failreason">FailReason + </seealso> in the <seealso marker="event_handler_chapter#tc_done">tc_done</seealso> event. + See <seealso marker="event_handler_chapter#events">Event Handling + </seealso> in the User's Guide for details.</p> + </desc> + </func> + + <func> + <name>Module:on_tc_skip(TestcaseName, Reason, CTHState) -> + NewCTHState</name> + <fsummary>Called after the CTH scope ends</fsummary> + <type> + <v>TestcaseName = end_per_suite | init_per_group | + end_per_group | atom()</v> + <v>Reason = {tc_auto_skip | tc_user_skip, term()}</v> + <v>CTHState = NewCTHState = term()</v> + </type> + + <desc> + <p> OPTIONAL </p> + + <p>This function is called whenever a testcase is skipped. + It is called after the post function has been called for the + testcase which was skipped. + i.e. if init_per_group is skipped this function is called after + <seealso marker="#Module:post_init_per_suite-4">post_init_per_group + </seealso>, and if a testcase is skipped it is called after + <seealso marker="#Module:post_end_per_testcase-4">post_end_per_testcase + </seealso>.</p> + + <p>The data which comes with the Reason follows the same format as + <seealso marker="event_handler_chapter#tc_auto_skip">tc_auto_skip + </seealso> and <seealso marker="event_handler_chapter#tc_user_skip"> + tc_user_skip</seealso> events. + See <seealso marker="event_handler_chapter#events">Event Handling + </seealso> in the User's Guide for details.</p> + </desc> + </func> + + <func> + <name>Module:terminate(CTHState)</name> + <fsummary>Called after the CTH scope ends</fsummary> + <type> + <v>CTHState = term()</v> + </type> + + <desc> + <p> OPTIONAL </p> + + <p>This function is called at the end of a CTH's + <seealso marker="ct_hooks_chapter#scope">scope</seealso>. + </p> + </desc> + </func> + + <func> + <name>Module:id(Opts) -> Id</name> + <fsummary>Called before the init function of a CTH</fsummary> + <type> + <v>Opts = term()</v> + <v>Id = term()</v> + </type> + + <desc> + <p> OPTIONAL </p> + + <p>The <c>Id</c> is used to uniquely identify a CTH instance, + if two CTH's return the same <c>Id</c> the second CTH is ignored + and subsequent calls to the CTH will only be made to the first + instance. For more information see + <seealso marker="ct_hooks_chapter#installing">Installing a CTH + </seealso> in the User's Guide. + </p> + + <p>This function should NOT have any side effects as it might + be called multiple times by Common Test.</p> + + <p>If not implemented the CTH will act as if this function returned a + call to <c>make_ref/0</c>.</p> + </desc> + </func> + + </funcs> + +</erlref> + + diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml new file mode 100644 index 0000000000..fc5ab48e1b --- /dev/null +++ b/lib/common_test/doc/src/ct_hooks_chapter.xml @@ -0,0 +1,401 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2011</year><year>2011</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + 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 online 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. + + </legalnotice> + + <title>Common Test Hooks</title> + <prepared>Lukas Larsson</prepared> + <docno></docno> + <date></date> + <rev></rev> + <file>ct_hooks_chapter.xml</file> + </header> + + <marker id="general"></marker> + <section> + <title>General</title> + <warning><p>This feature is in alpha release right now. This means that the + interface may change in the future and that there may be bugs. We + encourage you to use this feature, but be prepared + that there might be bugs and that the interface might change + inbetween releases.</p></warning> + <p> + The <em>Common Test Hook</em> (henceforth called CTH) framework allows + extensions of the default behaviour of Common Test by means of hooks + before and after all test suite calls. CTHs allow advanced Common Test + users to abstract out behaviour which is common to multiple test suites + without littering all test suites with library calls. Some example + usages are: logging, starting and monitoring external systems, + building C files needed by the tests and much more!</p> + + <p>In brief, Common Test Hooks allows you to:</p> + + <list> + <item>Manipulate the runtime config before each suite + configuration call</item> + <item>Manipulate the return of all suite configuration calls and in + extension the result of the test themselves.</item> + </list> + + <p>The following sections describe how to use CTHs, when they are run + and how to manipulate your test results in a CTH</p> + + <warning><p>When executing within a CTH all timetraps are shutoff. So + if your CTH never returns, the entire test run will be stalled!</p> + </warning> + + </section> + + <marker id="installing"></marker> + <section> + <title>Installing a CTH</title> + <p>There are multiple ways to install a CTH in your test run. You can do it + for all tests in a run, for specific test suites and for specific groups + within a test suite. If you want a CTH to be present in all test suites + within your test run there are three different ways to accomplish that. + </p> + + <list> + <item>Add <c>-ct_hooks</c> as an argument to + <seealso marker="run_test_chapter#ct_run">ct_run</seealso>. + To add multiple CTHs using this method append them to each other + using the keyword <c>and</c>, i.e. + <c>ct_run -ct_hooks cth1 [{debug,true}] and cth2 ...</c>.</item> + <item>Add the <c>ct_hooks</c> tag to your + <seealso marker="run_test_chapter#test_specifications"> + Test Specification</seealso></item> + <item>Add the <c>ct_hooks</c> tag to your call to + <seealso marker="ct#run_test-1">ct:run_test/1</seealso></item> + </list> + + <p>You can also add CTHs within a test suite. This is done by returning + <c>{ct_hooks,[CTH]}</c> in the config list from + <seealso marker="common_test#Module:suite-0">suite/0</seealso>, + <seealso marker="common_test#Module:init_per_suite-1"> + init_per_suite/1</seealso> or + <seealso marker="common_test#Module:init_per_group-2"> + init_per_group/2</seealso>. <c>CTH</c> in this case can be either + only the module name of the CTH or a tuple with the module name and the + initial arguments to the CTH. Eg: + <c>{ct_hooks,[my_cth_module]}</c> or + <c>{ct_hooks,[{my_cth_module,[{debug,true}]}]}</c></p> + + <section> + <title>Overriding CTHs</title> + <p>By default each installation of a CTH will cause a new instance of it + to be activated. This can cause problems if you want to be able to + override CTHs in test specifications while still having them in the + suite info function. The + <seealso marker="ct_hooks#Module:id-1">id/1</seealso> + callback exists to address this problem. By returning the same + <c>id</c> in both places, Common Test knows that this CTH + has already been installed and will not try to install it again.</p> + </section> + + </section> + + <marker id="scope"/> + <section> + <title>CTH Scope</title> + <p>Once the CTH is installed into a certain test run it will be there until + its scope is expired. The scope of a CTH depends on when it is + installed. + The <seealso marker="ct_hooks#Module:init-2">init/2</seealso> is + called at the beginning of the scope and the + <seealso marker="ct_hooks#Module:terminate-1">terminate/1 + </seealso> function is called when the scope ends.</p> + <table> + <row> + <cell><em>CTH Installed in</em></cell> + <cell><em>CTH scope begins before</em></cell> + <cell><em>CTH scope ends after</em></cell> + </row> + <row> + <cell><seealso marker="run_test_chapter#ct_run">ct_run</seealso></cell> + <cell>the first test suite is to be run.</cell> + <cell>the last test suite has been run.</cell> + </row> + <row> + <cell><seealso marker="ct#run_test-1">ct:run_test</seealso></cell> + <cell>the first test suite is to be run.</cell> + <cell>the last test suite has been run.</cell> + </row> + <row> + <cell><seealso marker="run_test_chapter#test_specifications"> + Test Specification</seealso></cell> + <cell>the first test suite is to be run.</cell> + <cell>the last test suite has been run.</cell> + </row> + <row> + <cell><seealso marker="common_test#Module:suite-0">suite/0 + </seealso></cell> + <cell><seealso marker="ct_hooks#Module:pre_init_per_suite-3"> + pre_init_per_suite/3</seealso> is called.</cell> + <cell><seealso marker="ct_hooks#Module:post_end_per_suite-4"> + post_end_per_suite/4</seealso> has been called for that test suite.</cell> + </row> + <row> + <cell><seealso marker="common_test#Module:init_per_suite-1"> + init_per_suite/1</seealso></cell> + <cell><seealso marker="ct_hooks#Module:post_init_per_suite-4"> + post_init_per_suite/4</seealso> is called.</cell> + <cell><seealso marker="ct_hooks#Module:post_end_per_suite-4"> + post_end_per_suite/4</seealso> has been called for that test suite.</cell> + </row> + <row> + <cell><seealso marker="common_test#Module:init_per_group-2"> + init_per_group/2</seealso></cell> + <cell><seealso marker="ct_hooks#Module:post_init_per_group-4"> + post_init_per_group/4</seealso> is called.</cell> + <cell><seealso marker="ct_hooks#Module:post_end_per_suite-4"> + post_end_per_group/4</seealso> has been called for that group.</cell> + </row> + <tcaption>Scope of a CTH</tcaption> + </table> + + <section> + <title>CTH Processes and Tables</title> + <p>CTHs are run with the same process scoping as normal test suites + i.e. a different process will execute the init_per_suite hooks then the + init_per_group or per_testcase hooks. So if you want to spawn a + process in the CTH you cannot link with the CTH process as it will exit + after the post hook ends. Also if you for some reason need an ETS + table with your CTH, you will have to spawn a process which handles + it.</p> + </section> + + </section> + + <marker id="manipulating"/> + <section> + <title>Manipulating tests</title> + <p>It is through CTHs possible to manipulate the results of tests and + configuration functions. The main purpose of doing this with CTHs is to + allow common patterns to be abstracted out from test test suites and applied to + multiple test suites without duplicating any code. All of the callback + functions for a CTH follow a common interface, this interface is + described below.</p> + + <p>It is only possible to hook into test function which exists in the test + suite. So in order for a CTH to hook in before + <seealso marker="common_test#Module:init_per_suite-1">init_per_suite</seealso>, + the <seealso marker="common_test#Module:init_per_suite-1">init_per_suite</seealso> + function must exist in the test suite.</p> + + <marker id="pre"/> + <section> + <title>Pre Hooks</title> + <p> + It is possible in a CTH to hook in behaviour before + <seealso marker="common_test#Module:init_per_suite-1">init_per_suite</seealso>, + <seealso marker="common_test#Module:init_per_suite-1">init_per_group</seealso>, + <seealso marker="common_test#Module:init_per_suite-1">init_per_testcase</seealso>, + <seealso marker="common_test#Module:init_per_suite-1">end_per_group</seealso> and + <seealso marker="common_test#Module:init_per_suite-1">end_per_suite</seealso>. + This is done in the CTH functions called pre_<name of function>. + All of these functions take the same three arguments: <c>Name</c>, + <c>Config</c> and <c>CTHState</c>. The return value of the CTH function + is always a combination of an result for the suite/group/test and an + updated <c>CTHState</c>. If you want the test suite to continue on + executing you should return the config list which you want the test to + use as the result. If you for some reason want to skip/fail the test, + return a tuple with <c>skip</c> or <c>fail</c> and a reason as the + result. Example: + </p> + <code>pre_init_per_suite(SuiteName, Config, CTHState) -> + case db:connect() of + {error,_Reason} -> + {{fail, "Could not connect to DB"}, CTHState}; + {ok, Handle} -> + {[{db_handle, Handle} | Config], CTHState#state{ handle = Handle }} + end.</code> + + </section> + + <marker id="post"/> + <section> + <title>Post Hooks</title> + <p>It is also possible in a CTH to hook in behaviour after + <seealso marker="common_test#Module:init_per_suite-1">init_per_suite</seealso>, + <seealso marker="common_test#Module:init_per_suite-1">init_per_group</seealso>, + <seealso marker="common_test#Module:init_per_suite-1">end_per_testcase</seealso>, + <seealso marker="common_test#Module:init_per_suite-1">end_per_group</seealso> and + <seealso marker="common_test#Module:init_per_suite-1">end_per_suite</seealso>. + This is done in the CTH functions called post_<name of function>. + All of these function take the same four arguments: <c>Name</c>, + <c>Config</c>, <c>Return</c> and <c>CTHState</c>. <c>Config</c> in this + case is the same <c>Config</c> as the testcase is called with. + <c>Return</c> is the value returned by the testcase. If the testcase + failed by crashing, <c>Return</c> will be + <c>{'EXIT',{{Error,Reason},Stacktrace}}</c>.</p> + + <p>The return value of the CTH function is always a combination of an + result for the suite/group/test and an updated <c>CTHState</c>. If + you want the callback to not affect the outcome of the test you should + return the <c>Return</c> data as it is given to the CTH. You can also + modify the result of the test. By returning the <c>Config</c> list + with the <c>tc_status</c> element removed you can recover from a test + failure. As in all the pre hooks, it is also possible to fail/skip + the test case in the post hook. Example: </p> + + <code>post_end_per_testcase(_TC, Config, {'EXIT',{_,_}}, CTHState) -> + case db:check_consistency() of + true -> + %% DB is good, pass the test. + {proplists:delete(tc_status, Config), CTHState}; + false -> + %% DB is not good, mark as skipped instead of failing + {{skip, "DB is inconsisten!"}, CTHState} + end; +post_end_per_testcase(_TC, Config, Return, CTHState) -> + %% Do nothing if tc does not crash. + {Return, CTHState}.</code> + + <note>Recovering from a testcase failure using CTHs should only be done as + a last resort. If used wrongly it could become very difficult to + determine which tests pass or fail in a test run</note> + + </section> + + <marker id="skip_n_fail"/> + <section> + <title>Skip and Fail hooks</title> + <p> + After any post hook has been executed for all installed CTHs, + <seealso marker="ct_hooks#Module:on_tc_fail-3">on_tc_fail</seealso> + or <seealso marker="ct_hooks#Module:on_tc_fail-3">on_tc_skip</seealso> + might be called if the testcase failed or was skipped + respectively. You cannot affect the outcome of the tests any further at + this point. + </p> + </section> + + </section> + + <marker id="example"/> + <section> + <title>Example CTH</title> + <p>The CTH below will log information about a test run into a format + parseable by <seealso marker="kernel:file#consult-1">file:consult/1</seealso>. + </p> + <code>%%% @doc Common Test Example Common Test Hook module. +-module(example_cth). + +%% Callbacks +-export([id/1]). +-export([init/2]). + +-export([pre_init_per_suite/3]). +-export([post_init_per_suite/4]). +-export([pre_end_per_suite/3]). +-export([post_end_per_suite/4]). + +-export([pre_init_per_group/3]). +-export([post_init_per_group/4]). +-export([pre_end_per_group/3]). +-export([post_end_per_group/4]). + +-export([pre_init_per_testcase/3]). +-export([post_end_per_testcase/4]). + +-export([on_tc_fail/3]). +-export([on_tc_skip/3]). + +-export([terminate/1]). + +-record(state, { file_handle, total, suite_total, ts, tcs, data }). + +%% @doc Return a unique id for this CTH. +id(Opts) -> + proplists:get_value(filename, Opts, "/tmp/file.log"). + +%% @doc Always called before any other callback function. Use this to initiate +%% any common state. +init(Id, Opts) -> + {ok,D} = file:open(Id,[write]), + #state{ file_handle = D, total = 0, data = [] }. + +%% @doc Called before init_per_suite is called. +pre_init_per_suite(Suite,Config,State) -> + {Config, State#state{ suite_total = 0, tcs = [] }}. + +%% @doc Called after init_per_suite. +post_init_per_suite(Suite,Config,Return,State) -> + {Return, State}. + +%% @doc Called before end_per_suite. +pre_end_per_suite(Suite,Config,State) -> + {Config, State}. + +%% @doc Called after end_per_suite. +post_end_per_suite(Suite,Config,Return,State) -> + Data = {suites, Suite, State#state.suite_total, lists:reverse(State#state.tcs)}, + {Return, State#state{ data = [Data | State#state.data] , + total = State#state.total + State#state.suite_total } }. + +%% @doc Called before each init_per_group. +pre_init_per_group(Group,Config,State) -> + {Config, State}. + +%% @doc Called after each init_per_group. +post_init_per_group(Group,Config,Return,State) -> + {Return, State}. + +%% @doc Called after each end_per_group. +pre_end_per_group(Group,Config,State) -> + {Config, State}. + +%% @doc Called after each end_per_group. +post_end_per_group(Group,Config,Return,State) -> + {Return, State}. + +%% @doc Called before each test case. +pre_init_per_testcase(TC,Config,State) -> + {Config, State#state{ ts = now(), total = State#state.suite_total + 1 } }. + +%% @doc Called after each test case. +post_end_per_testcase(TC,Config,Return,State) -> + TCInfo = {testcase, TC, Return, timer:now_diff(now(), State#state.ts)}, + {Return, State#state{ ts = undefined, tcs = [TCInfo | State#state.tcs] } }. + +%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group, +%% post_end_per_group and post_end_per_testcase if the suite, group or test case failed. +on_tc_fail(TC, Reason, State) -> + State. + +%% @doc Called when a test case is skipped by either user action +%% or due to an init function failing. +on_tc_skip(TC, Reason, State) -> + State. + +%% @doc Called when the scope of the CTH is done +terminate(State) -> + io:format(State#state.file_handle, "~p.~n", + [{test_run, State#state.total, State#state.data}]), + file:close(State#state.file_handle), + ok.</code> + </section> + +</chapter> + + + + diff --git a/lib/common_test/doc/src/event_handler_chapter.xml b/lib/common_test/doc/src/event_handler_chapter.xml index 904876ac46..a01feb59d1 100644 --- a/lib/common_test/doc/src/event_handler_chapter.xml +++ b/lib/common_test/doc/src/event_handler_chapter.xml @@ -61,6 +61,7 @@ itself.</p> </section> <section> + <marker id="usage"></marker> <title>Usage</title> <p>Event handlers may be installed by means of an <c>event_handler</c> start flag (<c>ct_run</c>) or option (<c>ct:run_test/1</c>), where the @@ -120,6 +121,7 @@ node the event has originated from (only relevant for CT Master event handlers). <c>data</c> is specific for the particular event.</p> + <marker id="events"></marker> <p><em>General events:</em></p> <list> @@ -172,6 +174,7 @@ are also given. </p></item> + <marker id="tc_done"/> <item><c>#event{name = tc_done, data = {Suite,FuncOrGroup,Result}}</c> <p><c>Suite = atom()</c>, name of the suite.</p> <p><c>FuncOrGroup = Func | {Conf,GroupName,GroupProperties}</c></p> @@ -181,12 +184,14 @@ (unknown if init- or end function times out).</p> <p><c>GroupProperties = list()</c>, list of execution properties for the group.</p> <p><c>Result = ok | {skipped,SkipReason} | {failed,FailReason}</c>, the result.</p> + <marker id="skipreason"/> <p><c>SkipReason = {require_failed,RequireInfo} | {require_failed_in_suite0,RequireInfo} | {failed,{Suite,init_per_testcase,FailInfo}} | UserTerm</c>, the reason why the case has been skipped.</p> - <p><c>FailReason = {error,FailInfo} | + <marker id="failreason"/> + <p><c>FailReason = {error,FailInfo} | {error,{RunTimeError,StackTrace}} | {timetrap_timeout,integer()} | {failed,{Suite,end_per_testcase,FailInfo}}</c>, reason for failure.</p> @@ -209,6 +214,7 @@ <c>end_per_testcase</c> for the case failed. </p></item> + <marker id="tc_auto_skip"></marker> <item><c>#event{name = tc_auto_skip, data = {Suite,Func,Reason}}</c> <p><c>Suite = atom()</c>, the name of the suite.</p> <p><c>Func = atom()</c>, the name of the test case or configuration function.</p> @@ -234,7 +240,8 @@ skipped because of <c>init_per_testcase</c> failing, since that information is carried with the <c>tc_done</c> event. </p></item> - + + <marker id="tc_user_skip"></marker> <item><c>#event{name = tc_user_skip, data = {Suite,TestCase,Comment}}</c> <p><c>Suite = atom()</c>, name of the suite.</p> <p><c>TestCase = atom()</c>, name of the test case.</p> diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml index af9dbfa9ec..2fd5dcf4f1 100644 --- a/lib/common_test/doc/src/notes.xml +++ b/lib/common_test/doc/src/notes.xml @@ -32,6 +32,57 @@ <file>notes.xml</file> </header> +<section><title>Common_Test 1.5.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Updated ct:get_status documentation to describe + no_tests_running return value.</p> + <p> + Own Id: OTP-8895 Aux Id: seq11701 </p> + </item> + <item> + <p> + Fixed race condition test failures in the test suites + testing common test's parallel groups feature.</p> + <p> + Own Id: OTP-8921</p> + </item> + <item> + <p> + The include directive of testspecs now work when used on + a remote node.</p> + <p> + Own Id: OTP-8935 Aux Id: seq11731 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + ct:parse_table can now handle multiline sql rows</p> + <p> + Own Id: OTP-8907 Aux Id: seq11702 </p> + </item> + <item> + <p> + The run_test executable has been renamed to the less + generic ct_run to better work with other applications. + run_test will remain until R16B at which point it will be + removed.</p> + <p> + Own Id: OTP-8936</p> + </item> + </list> + </section> + +</section> + <section><title>Common_Test 1.5.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/common_test/doc/src/part.xml b/lib/common_test/doc/src/part.xml index 53a4cb1bbf..41371b60be 100644 --- a/lib/common_test/doc/src/part.xml +++ b/lib/common_test/doc/src/part.xml @@ -75,6 +75,7 @@ <xi:include href="ct_master_chapter.xml"/> <xi:include href="event_handler_chapter.xml"/> <xi:include href="dependencies_chapter.xml"/> + <xi:include href="ct_hooks_chapter.xml"/> <xi:include href="why_test_chapter.xml"/> </part> diff --git a/lib/common_test/doc/src/ref_man.xml b/lib/common_test/doc/src/ref_man.xml index d5985bb021..631e3871c2 100644 --- a/lib/common_test/doc/src/ref_man.xml +++ b/lib/common_test/doc/src/ref_man.xml @@ -76,6 +76,7 @@ <xi:include href="ct_telnet.xml"/> <xi:include href="unix_telnet.xml"/> <xi:include href="ct_slave.xml"/> + <xi:include href="ct_hooks.xml"/> </application> diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml index 94fcf6bf01..7b485bdc35 100644 --- a/lib/common_test/doc/src/run_test_chapter.xml +++ b/lib/common_test/doc/src/run_test_chapter.xml @@ -105,6 +105,7 @@ RPC from a remote node.</p> </section> + <marker id="ct_run"></marker> <section> <title>Running tests from the OS command line</title> @@ -147,6 +148,8 @@ <seealso marker="event_handler_chapter#event_handling">event handlers</seealso>.</item> <item><c><![CDATA[-event_handler_init <event_handlers>]]></c>, to install <seealso marker="event_handler_chapter#event_handling">event handlers</seealso> including start arguments.</item> + <item><c><![CDATA[-ct_hooks <ct_hooks>]]></c>, to install + <seealso marker="ct_hooks_chapter#installing">Common Test Hooks</seealso> including start arguments.</item> <item><c><![CDATA[-include]]></c>, specifies include directories (see above).</item> <item><c><![CDATA[-no_auto_compile]]></c>, disables the automatic test suite compilation feature (see above).</item> <item><c><![CDATA[-multiply_timetraps <n>]]></c>, extends <seealso marker="write_test_chapter#timetraps">timetrap @@ -333,8 +336,8 @@ with <c>dir</c>.</p> </section> + <marker id="test_specifications"></marker> <section> - <marker id="test_specifications"></marker> <title>Using test specifications</title> <p>The most flexible way to specify what to test, is to use a so @@ -440,6 +443,9 @@ {event_handler, NodeRefs, EventHandlers}. {event_handler, EventHandlers, InitArgs}. {event_handler, NodeRefs, EventHandlers, InitArgs}. + + {ct_hooks, CTHModules}. + {ct_hooks, NodeRefs, CTHModules}. </pre> <p>Test terms:</p> <pre> @@ -478,6 +484,9 @@ LogDir = string() EventHandlers = atom() | [atom()] InitArgs = [term()] + CTHModules = [CTHModule | {CTHModule, CTHInitArgs}] + CTHModule = atom() + CTHInitArgs = term() DirRef = DirAlias | Dir Suites = atom() | [atom()] | all Suite = atom() diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml index 5afec6de6a..76493d3616 100644 --- a/lib/common_test/doc/src/write_test_chapter.xml +++ b/lib/common_test/doc/src/write_test_chapter.xml @@ -115,6 +115,7 @@ </p> </section> + <marker id="per_testcase"/> <section> <title>Init and end per test case</title> diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile index 027667e6b0..378a7ba08c 100644 --- a/lib/common_test/src/Makefile +++ b/lib/common_test/src/Makefile @@ -67,7 +67,9 @@ MODULES= \ ct_config \ ct_config_plain \ ct_config_xml \ - ct_slave + ct_slave \ + ct_hooks\ + ct_hooks_lock TARGET_MODULES= $(MODULES:%=$(EBIN)/%) diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl index 405dc40c8b..b0a92dcc15 100644 --- a/lib/common_test/src/ct.erl +++ b/lib/common_test/src/ct.erl @@ -148,7 +148,8 @@ run(TestDirs) -> %%% {auto_compile,Bool} | {multiply_timetraps,M} | {scale_timetraps,Bool} | %%% {repeat,N} | {duration,DurTime} | {until,StopTime} | %%% {force_stop,Bool} | {decrypt,DecryptKeyOrFile} | -%%% {refresh_logs,LogDir} | {basic_html,Bool} +%%% {refresh_logs,LogDir} | {basic_html,Bool} | +%%% {ct_hooks, CTHs} %%% TestDirs = [string()] | string() %%% Suites = [string()] | string() %%% Cases = [atom()] | atom() @@ -176,6 +177,9 @@ run(TestDirs) -> %%% DecryptKeyOrFile = {key,DecryptKey} | {file,DecryptFile} %%% DecryptKey = string() %%% DecryptFile = string() +%%% CTHs = [CTHModule | {CTHModule, CTHInitArgs}] +%%% CTHModule = atom() +%%% CTHInitArgs = term() %%% Result = [TestResult] | {error,Reason} %%% @doc Run tests as specified by the combination of options in <code>Opts</code>. %%% The options are the same as those used with the diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index 0d82a106d5..b61eda7152 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -24,7 +24,7 @@ -module(ct_framework). --export([init_tc/3, end_tc/3, get_suite/2, report/2, warn/1]). +-export([init_tc/3, end_tc/4, get_suite/2, report/2, warn/1]). -export([error_notification/4]). -export([overview_html_header/1]). @@ -207,7 +207,7 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) -> {skip,{require_failed_in_suite0,Reason}}; {error,Reason} -> {auto_skip,{require_failed,Reason}}; - FinalConfig -> + {ok, FinalConfig} -> case MergeResult of {error,Reason} -> %% suite0 configure finished now, report that @@ -216,13 +216,25 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) -> _ -> case get('$test_server_framework_test') of undefined -> - FinalConfig; + ct_suite_init(Mod, FuncSpec, FinalConfig); Fun -> - Fun(init_tc, FinalConfig) + case Fun(init_tc, FinalConfig) of + NewConfig when is_list(NewConfig) -> + {ok,NewConfig}; + Else -> + Else + end end end end. - + +ct_suite_init(Mod, Func, [Config]) when is_list(Config) -> + case ct_hooks:init_tc( Mod, Func, Config) of + NewConfig when is_list(NewConfig) -> + {ok, [NewConfig]}; + Else -> + Else + end. add_defaults(Mod,Func,FuncInfo,DoInit) -> case (catch Mod:suite()) of @@ -239,7 +251,9 @@ add_defaults(Mod,Func,FuncInfo,DoInit) -> (_) -> false end, SuiteInfo) of true -> - SuiteInfo1 = merge_with_suite_defaults(Mod,SuiteInfo), + SuiteInfoNoCTH = + lists:keydelete(ct_hooks,1,SuiteInfo), + SuiteInfo1 = merge_with_suite_defaults(Mod,SuiteInfoNoCTH), case add_defaults1(Mod,Func,FuncInfo,SuiteInfo1,DoInit) of Error = {error,_} -> {SuiteInfo1,Error}; MergedInfo -> {SuiteInfo1,MergedInfo} @@ -362,6 +376,8 @@ configure([{timetrap,off}|Rest],Info,SuiteInfo,Scope,Config) -> configure([{timetrap,Time}|Rest],Info,SuiteInfo,Scope,Config) -> Dog = test_server:timetrap(Time), configure(Rest,Info,SuiteInfo,Scope,[{watchdog,Dog}|Config]); +configure([{ct_hooks, Hook} | Rest], Info, SuiteInfo, Scope, Config) -> + configure(Rest, Info, SuiteInfo, Scope, [{ct_hooks, Hook} | Config]); configure([_|Rest],Info,SuiteInfo,Scope,Config) -> configure(Rest,Info,SuiteInfo,Scope,Config); configure([],_,_,_,Config) -> @@ -418,14 +434,14 @@ try_set_default(Name,Key,Info,Where) -> %%% %%% @doc Test server framework callback, called by the test_server %%% when a test case is finished. -end_tc(?MODULE,error_in_suite,_) -> % bad start! +end_tc(?MODULE,error_in_suite,_, _) -> % bad start! ok; -end_tc(Mod,Func,{TCPid,Result,[Args]}) when is_pid(TCPid) -> - end_tc(Mod,Func,TCPid,Result,Args); -end_tc(Mod,Func,{Result,[Args]}) -> - end_tc(Mod,Func,self(),Result,Args). +end_tc(Mod,Func,{TCPid,Result,[Args]}, Return) when is_pid(TCPid) -> + end_tc(Mod,Func,TCPid,Result,Args,Return); +end_tc(Mod,Func,{Result,[Args]}, Return) -> + end_tc(Mod,Func,self(),Result,Args,Return). -end_tc(Mod,Func,TCPid,Result,Args) -> +end_tc(Mod,Func,TCPid,Result,Args,Return) -> case lists:keysearch(watchdog,1,Args) of {value,{watchdog,Dog}} -> test_server:timetrap_cancel(Dog); false -> ok @@ -448,8 +464,10 @@ end_tc(Mod,Func,TCPid,Result,Args) -> {_,GroupName,_Props} = Group -> case lists:keysearch(save_config,1,Args) of {value,{save_config,SaveConfig}} -> - ct_util:save_suite_data(last_saved_config, - {Mod,{group,GroupName}},SaveConfig), + ct_util:save_suite_data( + last_saved_config, + {Mod,{group,GroupName}}, + SaveConfig), Group; false -> Group @@ -466,12 +484,33 @@ end_tc(Mod,Func,TCPid,Result,Args) -> end, ct_util:reset_silent_connections(), - %% send sync notification so that event handlers may print - %% in the log file before it gets closed - ct_event:sync_notify(#event{name=tc_done, - node=node(), - data={Mod,FuncSpec,tag(Result)}}), - case Result of + case get('$test_server_framework_test') of + undefined -> + {FinalResult,FinalNotify} = + case ct_hooks:end_tc( + Mod, FuncSpec, Args, Result, Return) of + '$ct_no_change' -> + {FinalResult = ok,Result}; + FinalResult -> + {FinalResult,FinalResult} + end, + % send sync notification so that event handlers may print + % in the log file before it gets closed + ct_event:sync_notify(#event{name=tc_done, + node=node(), + data={Mod,FuncSpec, + tag_cth(FinalNotify)}}); + Fun -> + % send sync notification so that event handlers may print + % in the log file before it gets closed + ct_event:sync_notify(#event{name=tc_done, + node=node(), + data={Mod,FuncSpec,tag(Result)}}), + FinalResult = Fun(end_tc, Return) + end, + + + case FinalResult of {skip,{sequence_failed,_,_}} -> %% ct_logs:init_tc is never called for a skipped test case %% in a failing sequence, so neither should end_tc @@ -490,12 +529,7 @@ end_tc(Mod,Func,TCPid,Result,Args) -> _ -> ok end, - case get('$test_server_framework_test') of - undefined -> - ok; - Fun -> - Fun(end_tc, ok) - end. + FinalResult. %% {error,Reason} | {skip,Reason} | {timetrap_timeout,TVal} | %% {testcase_aborted,Reason} | testcase_aborted_or_killed | @@ -511,6 +545,21 @@ tag(E = testcase_aborted_or_killed) -> tag(Other) -> Other. +tag_cth({STag,Reason}) when STag == skip; STag == skipped -> + {skipped,Reason}; +tag_cth({fail, Reason}) -> + {failed, {error,Reason}}; +tag_cth(E = {ETag,_}) when ETag == error; ETag == 'EXIT'; + ETag == timetrap_timeout; + ETag == testcase_aborted -> + {failed,E}; +tag_cth(E = testcase_aborted_or_killed) -> + {failed,E}; +tag_cth(List) when is_list(List) -> + ok; +tag_cth(Other) -> + Other. + %%%----------------------------------------------------------------- %%% @spec error_notification(Mod,Func,Args,Error) -> ok %%% Mod = atom() @@ -1174,6 +1223,18 @@ report(What,Data) -> ok; tc_done -> {_Suite,Case,Result} = Data, + case Result of + {failed, _} -> + ct_hooks:on_tc_fail(What, Data); + {skipped,{failed,{_,init_per_testcase,_}}} -> + ct_hooks:on_tc_skip(tc_auto_skip, Data); + {skipped,{require_failed,_}} -> + ct_hooks:on_tc_skip(tc_auto_skip, Data); + {skipped,_} -> + ct_hooks:on_tc_skip(tc_user_skip, Data); + _Else -> + ok + end, case {Case,Result} of {init_per_suite,_} -> ok; @@ -1191,8 +1252,8 @@ report(What,Data) -> add_to_stats(auto_skipped); {_,{skipped,_}} -> add_to_stats(user_skipped); - {_,{FailOrSkip,_Reason}} -> - add_to_stats(FailOrSkip) + {_,{SkipOrFail,_Reason}} -> + add_to_stats(SkipOrFail) end; tc_user_skip -> %% test case specified as skipped in testspec @@ -1200,6 +1261,7 @@ report(What,Data) -> ct_event:sync_notify(#event{name=tc_user_skip, node=node(), data=Data}), + ct_hooks:on_tc_skip(What, Data), add_to_stats(user_skipped); tc_auto_skip -> %% test case skipped because of error in init_per_suite @@ -1212,6 +1274,7 @@ report(What,Data) -> ct_event:sync_notify(#event{name=tc_auto_skip, node=node(), data=Data}), + ct_hooks:on_tc_skip(What, Data), if Case /= end_per_suite, Case /= end_per_group -> add_to_stats(auto_skipped); true -> diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl new file mode 100644 index 0000000000..77b7566d9e --- /dev/null +++ b/lib/common_test/src/ct_hooks.erl @@ -0,0 +1,305 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2010. 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 +%% 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 online 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. +%% +%% %CopyrightEnd% +%% + +%%% @doc Common Test Framework test execution control module. +%%% +%%% <p>This module is a proxy for calling and handling common test hooks.</p> + +-module(ct_hooks). + +%% API Exports +-export([init/1]). +-export([init_tc/3]). +-export([end_tc/5]). +-export([terminate/1]). +-export([on_tc_skip/2]). +-export([on_tc_fail/2]). + +-type proplist() :: [{atom(),term()}]. + +%% If you change this, remember to update ct_util:look -> stop clause as well. +-define(config_name, ct_hooks). + +%% ------------------------------------------------------------------------- +%% API Functions +%% ------------------------------------------------------------------------- + +%% @doc Called before any suites are started +-spec init(State :: term()) -> ok | + {error, Reason :: term()}. +init(Opts) -> + call([{Hook, call_id, undefined} || Hook <- get_new_hooks(Opts)], + ok, init, []). + + +%% @doc Called after all suites are done. +-spec terminate(Hooks :: term()) -> + ok. +terminate(Hooks) -> + call([{HookId, fun call_terminate/3} || {HookId,_,_} <- Hooks], + ct_hooks_terminate_dummy, terminate, Hooks), + ok. + +%% @doc Called as each test case is started. This includes all configuration +%% tests. +-spec init_tc(Mod :: atom(), Func :: atom(), Args :: list()) -> + NewConfig :: proplist() | + {skip, Reason :: term()} | + {auto_skip, Reason :: term()} | + {fail, Reason :: term()}. +init_tc(ct_framework, _Func, Args) -> + Args; +init_tc(Mod, init_per_suite, Config) -> + Info = case catch proplists:get_value(ct_hooks, Mod:suite()) of + List when is_list(List) -> + [{ct_hooks,List}]; + _Else -> + [] + end, + call(fun call_generic/3, Config ++ Info, [pre_init_per_suite, Mod]); +init_tc(Mod, end_per_suite, Config) -> + call(fun call_generic/3, Config, [pre_end_per_suite, Mod]); +init_tc(Mod, {init_per_group, GroupName, Opts}, Config) -> + maybe_start_locker(Mod, GroupName, Opts), + call(fun call_generic/3, Config, [pre_init_per_group, GroupName]); +init_tc(_Mod, {end_per_group, GroupName, _}, Config) -> + call(fun call_generic/3, Config, [pre_end_per_group, GroupName]); +init_tc(_Mod, TC, Config) -> + call(fun call_generic/3, Config, [pre_init_per_testcase, TC]). + +%% @doc Called as each test case is completed. This includes all configuration +%% tests. +-spec end_tc(Mod :: atom(), + Func :: atom(), + Args :: list(), + Result :: term(), + Resturn :: term()) -> + NewConfig :: proplist() | + {skip, Reason :: term()} | + {auto_skip, Reason :: term()} | + {fail, Reason :: term()} | + ok | '$ct_no_change'. +end_tc(ct_framework, _Func, _Args, Result, _Return) -> + Result; + +end_tc(Mod, init_per_suite, Config, _Result, Return) -> + call(fun call_generic/3, Return, [post_init_per_suite, Mod, Config], + '$ct_no_change'); + +end_tc(Mod, end_per_suite, Config, Result, _Return) -> + call(fun call_generic/3, Result, [post_end_per_suite, Mod, Config], + '$ct_no_change'); + +end_tc(_Mod, {init_per_group, GroupName, _}, Config, _Result, Return) -> + call(fun call_generic/3, Return, [post_init_per_group, GroupName, Config], + '$ct_no_change'); + +end_tc(Mod, {end_per_group, GroupName, Opts}, Config, Result, _Return) -> + Res = call(fun call_generic/3, Result, + [post_end_per_group, GroupName, Config], '$ct_no_change'), + maybe_stop_locker(Mod, GroupName,Opts), + Res; + +end_tc(_Mod, TC, Config, Result, _Return) -> + call(fun call_generic/3, Result, [post_end_per_testcase, TC, Config], + '$ct_no_change'). + +on_tc_skip(How, {_Suite, Case, Reason}) -> + call(fun call_cleanup/3, {How, Reason}, [on_tc_skip, Case]). + +on_tc_fail(_How, {_Suite, Case, Reason}) -> + call(fun call_cleanup/3, Reason, [on_tc_fail, Case]). + +%% ------------------------------------------------------------------------- +%% Internal Functions +%% ------------------------------------------------------------------------- +call_id(Mod, Config, Meta) when is_atom(Mod) -> + call_id({Mod, []}, Config, Meta); +call_id({Mod, Opts}, Config, Scope) -> + Id = catch_apply(Mod,id,[Opts], make_ref()), + {Config, {Id, scope(Scope), {Mod, {Id,Opts}}}}. + +call_init({Mod,{Id,Opts}},Config,_Meta) -> + NewState = Mod:init(Id, Opts), + {Config, {Mod, NewState}}. + +call_terminate({Mod, State}, _, _) -> + catch_apply(Mod,terminate,[State], ok), + {[],{Mod,State}}. + +call_cleanup({Mod, State}, Reason, [Function | Args]) -> + NewState = catch_apply(Mod,Function, Args ++ [Reason, State], + State), + {Reason, {Mod, NewState}}. + +call_generic({Mod, State}, Value, [Function | Args]) -> + {NewValue, NewState} = catch_apply(Mod, Function, Args ++ [Value, State], + {Value,State}), + {NewValue, {Mod, NewState}}. + +%% Generic call function +call(Fun, Config, Meta) -> + maybe_lock(), + Hooks = get_hooks(), + Res = call([{HookId,Fun} || {HookId,_, _} <- Hooks] ++ + get_new_hooks(Config, Fun), + remove(?config_name,Config), Meta, Hooks), + maybe_unlock(), + Res. + +call(Fun, Config, Meta, NoChangeRet) when is_function(Fun) -> + case call(Fun,Config,Meta) of + Config -> NoChangeRet; + NewReturn -> NewReturn + end; + +call([{Hook, call_id, NextFun} | Rest], Config, Meta, Hooks) -> + try + {Config, {NewId, _, _} = NewHook} = call_id(Hook, Config, Meta), + {NewHooks, NewRest} = + case lists:keyfind(NewId, 1, Hooks) of + false when NextFun =:= undefined -> + {Hooks ++ [NewHook], + [{NewId, fun call_init/3} | Rest]}; + ExistingHook when is_tuple(ExistingHook) -> + {Hooks, Rest}; + _ -> + {Hooks ++ [NewHook], + [{NewId, fun call_init/3},{NewId,NextFun} | Rest]} + end, + call(NewRest, Config, Meta, NewHooks) + catch Error:Reason -> + Trace = erlang:get_stacktrace(), + ct_logs:log("Suite Hook","Failed to start a CTH: ~p:~p", + [Error,{Reason,Trace}]), + call([], {fail,"Failed to start CTH" + ", see the CT Log for details"}, Meta, Hooks) + end; +call([{HookId, Fun} | Rest], Config, Meta, Hooks) -> + try + {_,Scope,ModState} = lists:keyfind(HookId, 1, Hooks), + {NewConf, NewHookInfo} = Fun(ModState, Config, Meta), + NewCalls = get_new_hooks(NewConf, Fun), + NewHooks = lists:keyreplace(HookId, 1, Hooks, {HookId, Scope, NewHookInfo}), + call(NewCalls ++ Rest, remove(?config_name, NewConf), Meta, + terminate_if_scope_ends(HookId, Meta, NewHooks)) + catch throw:{error_in_cth_call,Reason} -> + call(Rest, {fail, Reason}, Meta, + terminate_if_scope_ends(HookId, Meta, Hooks)) + end; +call([], Config, _Meta, Hooks) -> + save_suite_data_async(Hooks), + Config. + +remove(Key,List) when is_list(List) -> + [Conf || Conf <- List, is_tuple(Conf) =:= false + orelse element(1, Conf) =/= Key]; +remove(_, Else) -> + Else. + +%% Translate scopes, i.e. init_per_group,group1 -> end_per_group,group1 etc +scope([pre_init_per_testcase, TC|_]) -> + [post_end_per_testcase, TC]; +scope([pre_init_per_group, GroupName|_]) -> + [post_end_per_group, GroupName]; +scope([post_init_per_group, GroupName|_]) -> + [post_end_per_group, GroupName]; +scope([pre_init_per_suite, SuiteName|_]) -> + [post_end_per_suite, SuiteName]; +scope([post_init_per_suite, SuiteName|_]) -> + [post_end_per_suite, SuiteName]; +scope(init) -> + none. + +terminate_if_scope_ends(HookId, [Function,Tag|T], Hooks) when T =/= [] -> + terminate_if_scope_ends(HookId,[Function,Tag],Hooks); +terminate_if_scope_ends(HookId, Function, Hooks) -> + case lists:keyfind(HookId, 1, Hooks) of + {HookId, Function, _ModState} = Hook -> + terminate([Hook]), + lists:keydelete(HookId, 1, Hooks); + _ -> + Hooks + end. + +%% Fetch hook functions +get_new_hooks(Config, Fun) -> + lists:foldl(fun(NewHook, Acc) -> + [{NewHook, call_id, Fun} | Acc] + end, [], get_new_hooks(Config)). + +get_new_hooks(Config) when is_list(Config) -> + lists:flatmap(fun({?config_name, HookConfigs}) -> + HookConfigs; + (_) -> + [] + end, Config); +get_new_hooks(_Config) -> + []. + +save_suite_data_async(Hooks) -> + ct_util:save_suite_data_async(?config_name, Hooks). + +get_hooks() -> + ct_util:read_suite_data(?config_name). + +catch_apply(M,F,A, Default) -> + try + apply(M,F,A) + catch error:Reason -> + case erlang:get_stacktrace() of + %% Return the default if it was the CTH module which did not have the function. + [{M,F,A}|_] when Reason == undef -> + Default; + Trace -> + ct_logs:log("Suite Hook","Call to CTH failed: ~p:~p", + [error,{Reason,Trace}]), + throw({error_in_cth_call, + lists:flatten( + io_lib:format("~p:~p/~p CTH call failed", + [M,F,length(A)]))}) + end + end. + + +%% We need to lock around the state for parallel groups only. This is because +%% we will get several processes reading and writing the state for a single +%% cth at the same time. +maybe_start_locker(Mod,GroupName,Opts) -> + case lists:member(parallel,Opts) of + true -> + {ok, _Pid} = ct_hooks_lock:start({Mod,GroupName}); + false -> + ok + end. + +maybe_stop_locker(Mod,GroupName,Opts) -> + case lists:member(parallel,Opts) of + true -> + stopped = ct_hooks_lock:stop({Mod,GroupName}); + false -> + ok + end. + + +maybe_lock() -> + locked = ct_hooks_lock:request(). + +maybe_unlock() -> + unlocked = ct_hooks_lock:release(). diff --git a/lib/common_test/src/ct_hooks_lock.erl b/lib/common_test/src/ct_hooks_lock.erl new file mode 100644 index 0000000000..98794201bb --- /dev/null +++ b/lib/common_test/src/ct_hooks_lock.erl @@ -0,0 +1,132 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2010. 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 +%% 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 online 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. +%% +%% %CopyrightEnd% +%% + +%%% @doc Common Test Framework test execution control module. +%%% +%%% <p>This module is a proxy for calling and handling locks in +%%% common test hooks.</p> + +-module(ct_hooks_lock). + +-behaviour(gen_server). + +%% API +-export([start/1, stop/1, request/0, release/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, { id, locked = false, requests = [] }). + +%%%=================================================================== +%%% API +%%%=================================================================== + +%% @doc Starts the server +start(Id) -> + case gen_server:start({local, ?SERVER}, ?MODULE, Id, []) of + {error,{already_started, Pid}} -> + {ok,Pid}; + Else -> + Else + end. + +stop(Id) -> + try + gen_server:call(?SERVER, {stop,Id}) + catch exit:{noproc,_} -> + stopped + end. + +request() -> + try + gen_server:call(?SERVER,{request,self()},infinity) + catch exit:{noproc,_} -> + locked + end. + +release() -> + try + gen_server:call(?SERVER,{release,self()}) + catch exit:{noproc,_} -> + unlocked + end. + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +%% @doc Initiates the server +init(Id) -> + {ok, #state{ id = Id }}. + +%% @doc Handling call messages +handle_call({stop,Id}, _From, #state{ id = Id, requests = Reqs } = State) -> + [gen_server:reply(Req, locker_stopped) || {Req,_ReqId} <- Reqs], + {stop, normal, stopped, State}; +handle_call({stop,_Id}, _From, State) -> + {reply, stopped, State}; +handle_call({request, Pid}, _From, #state{ locked = false, + requests = [] } = State) -> + Ref = monitor(process, Pid), + {reply, locked, State#state{ locked = {true, Pid, Ref}} }; +handle_call({request, Pid}, From, #state{ requests = Reqs } = State) -> + {noreply, State#state{ requests = Reqs ++ [{From,Pid}] }}; +handle_call({release, Pid}, _From, #state{ locked = {true, Pid, Ref}, + requests = []} = State) -> + demonitor(Ref,[flush]), + {reply, unlocked, State#state{ locked = false }}; +handle_call({release, Pid}, _From, + #state{ locked = {true, Pid, Ref}, + requests = [{NextFrom,NextPid}|Rest]} = State) -> + demonitor(Ref,[flush]), + gen_server:reply(NextFrom,locked), + NextRef = monitor(process, NextPid), + {reply,unlocked,State#state{ locked = {true, NextPid, NextRef}, + requests = Rest } }; +handle_call({release, _Pid}, _From, State) -> + {reply, not_locked, State}. + +%% @doc Handling cast messages +handle_cast(_Msg, State) -> + {noreply, State}. + +%% @doc Handling all non call/cast messages +handle_info({'DOWN',Ref,process,Pid,_}, + #state{ locked = {true, Pid, Ref}, + requests = [{NextFrom,NextPid}|Rest] } = State) -> + gen_server:reply(NextFrom, locked), + NextRef = monitor(process, NextPid), + {noreply,State#state{ locked = {true, NextPid, NextRef}, + requests = Rest } }. + +%% @doc This function is called by a gen_server when it is about to terminate. +terminate(_Reason, _State) -> + ok. + +%% @doc Convert process state when code is changed +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%% ------------------------------------------------------------------------- +%% Internal Functions +%% ------------------------------------------------------------------------- diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl index d0e6ba5fa6..f50a46a241 100644 --- a/lib/common_test/src/ct_run.erl +++ b/lib/common_test/src/ct_run.erl @@ -54,6 +54,7 @@ logdir, config = [], event_handlers = [], + ct_hooks = [], include = [], silent_connections, stylesheet, @@ -171,6 +172,7 @@ script_start1(Parent, Args) -> ([]) -> true end, false, Args), EvHandlers = event_handler_args2opts(Args), + CTHooks = ct_hooks_args2opts(Args), %% check flags and set corresponding application env variables @@ -234,6 +236,7 @@ script_start1(Parent, Args) -> StartOpts = #opts{label = Label, vts = Vts, shell = Shell, cover = Cover, logdir = LogDir, event_handlers = EvHandlers, + ct_hooks = CTHooks, include = IncludeDirs, silent_connections = SilentConns, stylesheet = Stylesheet, @@ -305,6 +308,10 @@ script_start2(StartOpts = #opts{vts = undefined, SpecStartOpts#opts.scale_timetraps), AllEvHs = merge_vals([StartOpts#opts.event_handlers, SpecStartOpts#opts.event_handlers]), + AllCTHooks = merge_vals( + [StartOpts#opts.ct_hooks, + SpecStartOpts#opts.ct_hooks]), + AllInclude = merge_vals([StartOpts#opts.include, SpecStartOpts#opts.include]), application:set_env(common_test, include, AllInclude), @@ -315,6 +322,7 @@ script_start2(StartOpts = #opts{vts = undefined, logdir = LogDir, config = SpecStartOpts#opts.config, event_handlers = AllEvHs, + ct_hooks = AllCTHooks, include = AllInclude, multiply_timetraps = MultTT, scale_timetraps = ScaleTT}} @@ -332,7 +340,8 @@ script_start2(StartOpts = #opts{vts = undefined, {error,no_testspec_specified}; {undefined,_} -> % no testspec used case check_and_install_configfiles(InitConfig, TheLogDir, - Opts#opts.event_handlers) of + Opts#opts.event_handlers, + Opts#opts.ct_hooks) of ok -> % go on read tests from start flags script_start3(Opts#opts{config=InitConfig, logdir=TheLogDir}, Args); @@ -343,7 +352,8 @@ script_start2(StartOpts = #opts{vts = undefined, %% merge config from start flags with config from testspec AllConfig = merge_vals([InitConfig, Opts#opts.config]), case check_and_install_configfiles(AllConfig, TheLogDir, - Opts#opts.event_handlers) of + Opts#opts.event_handlers, + Opts#opts.ct_hooks) of ok -> % read tests from spec {Run,Skip} = ct_testspec:prepare_tests(Terms, node()), do_run(Run, Skip, Opts#opts{config=AllConfig, @@ -358,7 +368,8 @@ script_start2(StartOpts, Args) -> InitConfig = ct_config:prepare_config_list(Args), LogDir = which(logdir, StartOpts#opts.logdir), case check_and_install_configfiles(InitConfig, LogDir, - StartOpts#opts.event_handlers) of + StartOpts#opts.event_handlers, + StartOpts#opts.ct_hooks) of ok -> % go on read tests from start flags script_start3(StartOpts#opts{config=InitConfig, logdir=LogDir}, Args); @@ -366,11 +377,12 @@ script_start2(StartOpts, Args) -> Error end. -check_and_install_configfiles(Configs, LogDir, EvHandlers) -> +check_and_install_configfiles(Configs, LogDir, EvHandlers, CTHooks) -> case ct_config:check_config_files(Configs) of false -> install([{config,Configs}, - {event_handler,EvHandlers}], LogDir); + {event_handler,EvHandlers}, + {ct_hooks,CTHooks}], LogDir); {value,{error,{nofile,File}}} -> {error,{cant_read_config_file,File}}; {value,{error,{wrong_config,Message}}}-> @@ -438,11 +450,13 @@ script_start4(#opts{vts = true, config = Config, event_handlers = EvHandlers, script_start4(#opts{label = Label, shell = true, config = Config, event_handlers = EvHandlers, + ct_hooks = CTHooks, logdir = LogDir, testspecs = Specs}, _Args) -> %% label - used by ct_logs application:set_env(common_test, test_label, Label), - InstallOpts = [{config,Config},{event_handler,EvHandlers}], + InstallOpts = [{config,Config},{event_handler,EvHandlers}, + {ct_hooks, CTHooks}], if Config == [] -> ok; true -> @@ -508,6 +522,7 @@ script_usage() -> "\n\t[-stylesheet CSSFile]" "\n\t[-cover CoverCfgFile]" "\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]" + "\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]" "\n\t[-include InclDir1 InclDir2 .. InclDirN]" "\n\t[-no_auto_compile]" "\n\t[-multiply_timetraps N]" @@ -526,6 +541,7 @@ script_usage() -> "\n\t[-stylesheet CSSFile]" "\n\t[-cover CoverCfgFile]" "\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]" + "\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]" "\n\t[-include InclDir1 InclDir2 .. InclDirN]" "\n\t[-no_auto_compile]" "\n\t[-multiply_timetraps N]" @@ -664,6 +680,9 @@ run_test1(StartOpts) -> end, Hs)) end, + %% CT Hooks + CTHooks = get_start_opt(ct_hooks, value, [], StartOpts), + %% silent connections SilentConns = get_start_opt(silent_connections, fun(all) -> []; @@ -733,7 +752,9 @@ run_test1(StartOpts) -> Opts = #opts{label = Label, cover = Cover, step = Step, logdir = LogDir, config = CfgFiles, - event_handlers = EvHandlers, include = Include, + event_handlers = EvHandlers, + ct_hooks = CTHooks, + include = Include, silent_connections = SilentConns, stylesheet = Stylesheet, multiply_timetraps = MultiplyTT, @@ -784,11 +805,16 @@ run_spec_file(Relaxed, SpecOpts#opts.event_handlers]), AllInclude = merge_vals([Opts#opts.include, SpecOpts#opts.include]), + + AllCTHooks = merge_vals([Opts#opts.ct_hooks, + SpecOpts#opts.ct_hooks]), + application:set_env(common_test, include, AllInclude), case check_and_install_configfiles(AllConfig, which(logdir,LogDir), - AllEvHs) of + AllEvHs, + AllCTHooks) of ok -> Opts1 = Opts#opts{label = Label, cover = Cover, @@ -798,7 +824,8 @@ run_spec_file(Relaxed, include = AllInclude, testspecs = AbsSpecs, multiply_timetraps = MultTT, - scale_timetraps = ScaleTT}, + scale_timetraps = ScaleTT, + ct_hooks = AllCTHooks}, {Run,Skip} = ct_testspec:prepare_tests(TS, node()), reformat_result(catch do_run(Run, Skip, Opts1, StartOpts)); {error,GCFReason} -> @@ -808,10 +835,12 @@ run_spec_file(Relaxed, run_prepared(Run, Skip, Opts = #opts{logdir = LogDir, config = CfgFiles, - event_handlers = EvHandlers}, + event_handlers = EvHandlers, + ct_hooks = CTHooks}, StartOpts) -> LogDir1 = which(logdir, LogDir), - case check_and_install_configfiles(CfgFiles, LogDir1, EvHandlers) of + case check_and_install_configfiles(CfgFiles, LogDir1, + EvHandlers, CTHooks) of ok -> reformat_result(catch do_run(Run, Skip, Opts#opts{logdir = LogDir1}, StartOpts)); @@ -842,7 +871,8 @@ check_config_file(Callback, File)-> run_dir(Opts = #opts{logdir = LogDir, config = CfgFiles, - event_handlers = EvHandlers}, StartOpts) -> + event_handlers = EvHandlers, + ct_hooks = CTHook }, StartOpts) -> LogDir1 = which(logdir, LogDir), Opts1 = Opts#opts{logdir = LogDir1}, AbsCfgFiles = @@ -863,7 +893,9 @@ run_dir(Opts = #opts{logdir = LogDir, check_config_file(Callback, File) end, FileList)} end, CfgFiles), - case install([{config,AbsCfgFiles},{event_handler,EvHandlers}], LogDir1) of + case install([{config,AbsCfgFiles}, + {event_handler,EvHandlers}, + {ct_hooks, CTHook}], LogDir1) of ok -> ok; {error,IReason} -> exit(IReason) end, @@ -968,7 +1000,8 @@ run_testspec1(TestSpec) -> application:set_env(common_test, include, AllInclude), LogDir1 = which(logdir,Opts#opts.logdir), case check_and_install_configfiles(Opts#opts.config, LogDir1, - Opts#opts.event_handlers) of + Opts#opts.event_handlers, + Opts#opts.ct_hooks) of ok -> Opts1 = Opts#opts{testspecs = [], logdir = LogDir1, @@ -986,6 +1019,7 @@ get_data_for_node(#testspec{label = Labels, config = Cfgs, userconfig = UsrCfgs, event_handler = EvHs, + ct_hooks = CTHooks, include = Incl, multiply_timetraps = MTs, scale_timetraps = STs}, Node) -> @@ -1000,12 +1034,14 @@ get_data_for_node(#testspec{label = Labels, ConfigFiles = [{?ct_config_txt,F} || {N,F} <- Cfgs, N==Node] ++ [CBF || {N,CBF} <- UsrCfgs, N==Node], EvHandlers = [{H,A} || {N,H,A} <- EvHs, N==Node], + FiltCTHooks = [Hook || {N,Hook} <- CTHooks, N==Node], Include = [I || {N,I} <- Incl, N==Node], #opts{label = Label, logdir = LogDir, cover = Cover, config = ConfigFiles, event_handlers = EvHandlers, + ct_hooks = FiltCTHooks, include = Include, multiply_timetraps = MT, scale_timetraps = ST}. @@ -1036,15 +1072,7 @@ refresh_logs(LogDir) -> which(logdir, undefined) -> "."; which(logdir, Dir) -> - Dir; -which(multiply_timetraps, undefined) -> - 1; -which(multiply_timetraps, MT) -> - MT; -which(scale_timetraps, undefined) -> - false; -which(scale_timetraps, ST) -> - ST. + Dir. choose_val(undefined, V1) -> V1; @@ -2032,12 +2060,37 @@ get_start_opt(Key, IfExists, IfNotExists, Args) -> Val; {value,{Key,_Val}} -> IfExists; - _ when is_function(IfNotExists) -> - IfNotExists(); _ -> IfNotExists end. +ct_hooks_args2opts(Args) -> + ct_hooks_args2opts( + proplists:get_value(ct_hooks, Args, []),[]). + +ct_hooks_args2opts([CTH,Arg,"and"| Rest],Acc) -> + ct_hooks_args2opts(Rest,[{list_to_atom(CTH), + parse_cth_args(Arg)}|Acc]); +ct_hooks_args2opts([CTH], Acc) -> + ct_hooks_args2opts([CTH,"and"],Acc); +ct_hooks_args2opts([CTH, "and" | Rest], Acc) -> + ct_hooks_args2opts(Rest,[list_to_atom(CTH)|Acc]); +ct_hooks_args2opts([CTH, Args], Acc) -> + ct_hooks_args2opts([CTH, Args, "and"],Acc); +ct_hooks_args2opts([],Acc) -> + lists:reverse(Acc). + +parse_cth_args(String) -> + try + true = io_lib:printable_list(String), + {ok,Toks,_} = erl_scan:string(String++"."), + {ok, Args} = erl_parse:parse_term(Toks), + Args + catch _:_ -> + String + end. + + event_handler_args2opts(Args) -> case proplists:get_value(event_handler, Args) of undefined -> @@ -2165,6 +2218,22 @@ opts2args(EnvStartOpts) -> end, EHs), [_LastAnd|StrsR] = lists:reverse(lists:flatten(Strs)), [{event_handler_init,lists:reverse(StrsR)}]; + ({ct_hooks,[]}) -> + []; + ({ct_hooks,CTHs}) when is_list(CTHs) -> + io:format(user,"ct_hooks: ~p",[CTHs]), + Strs = lists:flatmap( + fun({CTH,Arg}) -> + [atom_to_list(CTH), + lists:flatten( + io_lib:format("~p",[Arg])), + "and"]; + (CTH) when is_atom(CTH) -> + [atom_to_list(CTH),"and"] + end,CTHs), + [_LastAnd|StrsR] = lists:reverse(Strs), + io:format(user,"return: ~p",[lists:reverse(StrsR)]), + [{ct_hooks,lists:reverse(StrsR)}]; ({Opt,As=[A|_]}) when is_atom(A) -> [{Opt,[atom_to_list(Atom) || Atom <- As]}]; ({Opt,Strs=[S|_]}) when is_list(S) -> @@ -2263,12 +2332,19 @@ do_trace(Terms) -> dbg:tracer(), dbg:p(self(), [sos,call]), lists:foreach(fun({m,M}) -> - case dbg:tpl(M,[{'_',[],[{return_trace}]}]) of + case dbg:tpl(M,x) of + {error,What} -> exit({error,{tracing_failed,What}}); + _ -> ok + end; + ({me,M}) -> + case dbg:tp(M,[{'_',[],[{exception_trace}, + {message,{caller}}]}]) of {error,What} -> exit({error,{tracing_failed,What}}); _ -> ok end; ({f,M,F}) -> - case dbg:tpl(M,F,[{'_',[],[{return_trace}]}]) of + case dbg:tpl(M,F,[{'_',[],[{exception_trace}, + {message,{caller}}]}]) of {error,What} -> exit({error,{tracing_failed,What}}); _ -> ok end; diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl index f5069427a2..2b6abefb72 100644 --- a/lib/common_test/src/ct_testspec.erl +++ b/lib/common_test/src/ct_testspec.erl @@ -394,8 +394,6 @@ filter_init_terms([Term|Ts], NewTerms, Spec)-> filter_init_terms([], NewTerms, Spec)-> {lists:reverse(NewTerms), Spec}. -add_option([], _, List, _)-> - List; add_option({Key, Value}, Node, List, WarnIfExists) when is_list(Value)-> OldOptions = case lists:keyfind(Node, 1, List) of {Node, Options}-> @@ -625,6 +623,20 @@ add_tests([{event_handler,Node,H,Args}|Ts],Spec) when is_atom(H) -> Node1 = ref2node(Node,Spec#testspec.nodes), add_tests(Ts,Spec#testspec{event_handler=[{Node1,H,Args}|EvHs]}); +%% --- ct_hooks -- +add_tests([{ct_hooks, all_nodes, Hooks} | Ts], Spec) -> + Tests = [{ct_hooks,N,Hooks} || N <- list_nodes(Spec)], + add_tests(Tests ++ Ts, Spec); +add_tests([{ct_hooks, Node, [Hook|Hooks]}|Ts], Spec) -> + SuiteCbs = Spec#testspec.ct_hooks, + Node1 = ref2node(Node,Spec#testspec.nodes), + add_tests([{ct_hooks, Node, Hooks} | Ts], + Spec#testspec{ct_hooks = [{Node1,Hook} | SuiteCbs]}); +add_tests([{ct_hooks, _Node, []}|Ts], Spec) -> + add_tests(Ts, Spec); +add_tests([{ct_hooks, Hooks}|Ts], Spec) -> + add_tests([{ct_hooks, all_nodes, Hooks}|Ts], Spec); + %% --- include --- add_tests([{include,all_nodes,InclDirs}|Ts],Spec) -> Tests = lists:map(fun(N) -> {include,N,InclDirs} end, list_nodes(Spec)), @@ -1051,6 +1063,8 @@ valid_terms() -> {event_handler,2}, {event_handler,3}, {event_handler,4}, + {ct_hooks,2}, + {ct_hooks,3}, {multiply_timetraps,2}, {multiply_timetraps,3}, {scale_timetraps,2}, diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl index 1b60820565..0476e1599f 100644 --- a/lib/common_test/src/ct_util.erl +++ b/lib/common_test/src/ct_util.erl @@ -32,7 +32,9 @@ -export([close_connections/0]). --export([save_suite_data/3, save_suite_data/2, read_suite_data/1, +-export([save_suite_data/3, save_suite_data/2, + save_suite_data_async/3, save_suite_data_async/2, + read_suite_data/1, delete_suite_data/0, delete_suite_data/1, match_delete_suite_data/1, delete_testdata/0, delete_testdata/1, set_testdata/1, get_testdata/1, update_testdata/2]). @@ -159,6 +161,17 @@ do_start(Parent,Mode,LogDir) -> ok end, {StartTime,TestLogDir} = ct_logs:init(Mode), + + %% Initiate ct_hooks + case catch ct_hooks:init(Opts) of + ok -> + ok; + {_,CTHReason} -> + ct_logs:tc_print('Suite Callback',CTHReason,[]), + Parent ! {self(), CTHReason}, + self() ! {{stop,normal},{self(),make_ref()}} + end, + ct_event:notify(#event{name=test_start, node=node(), data={StartTime, @@ -182,12 +195,19 @@ read_opts() -> {error,{bad_installation,Error}} end. + save_suite_data(Key, Value) -> call({save_suite_data, {Key, undefined, Value}}). save_suite_data(Key, Name, Value) -> call({save_suite_data, {Key, Name, Value}}). +save_suite_data_async(Key, Value) -> + save_suite_data_async(Key, undefined, Value). + +save_suite_data_async(Key, Name, Value) -> + cast({save_suite_data, {Key, Name, Value}}). + read_suite_data(Key) -> call({read_suite_data, Key}). @@ -302,6 +322,10 @@ loop(Mode,TestData,StartDir) -> ct_event:sync_notify(#event{name=test_done, node=node(), data=Time}), + Callbacks = ets:lookup_element(?suite_table, + ct_hooks, + #suite_data.value), + ct_hooks:terminate(Callbacks), close_connections(ets:tab2list(?conn_table)), ets:delete(?conn_table), ets:delete(?board_table), @@ -311,6 +335,9 @@ loop(Mode,TestData,StartDir) -> ct_config:stop(), file:set_cwd(StartDir), return(From,ok); + {Ref, _Msg} when is_reference(Ref) -> + %% This clause is used when doing cast operations. + loop(Mode,TestData,StartDir); {get_mode,From} -> return(From,Mode), loop(Mode,TestData,StartDir); @@ -716,6 +743,9 @@ call(Msg) -> return({To,Ref},Result) -> To ! {Ref, Result}. +cast(Msg) -> + ct_util_server ! {Msg, {ct_util_server, make_ref()}}. + seconds(T) -> test_server:seconds(T). diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl index ee973f6220..4ed29bdcd1 100644 --- a/lib/common_test/src/ct_util.hrl +++ b/lib/common_test/src/ct_util.hrl @@ -36,6 +36,7 @@ config=[], userconfig=[], event_handler=[], + ct_hooks=[], include=[], multiply_timetraps=[], scale_timetraps=[], diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile index f2fe3390cf..be4b4c32b8 100644 --- a/lib/common_test/test/Makefile +++ b/lib/common_test/test/Makefile @@ -40,7 +40,8 @@ MODULES= \ ct_test_server_if_1_SUITE \ ct_config_SUITE \ ct_master_SUITE \ - ct_misc_1_SUITE + ct_misc_1_SUITE \ + ct_hooks_SUITE ERL_FILES= $(MODULES:%=%.erl) diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl new file mode 100644 index 0000000000..1e187aa205 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE.erl @@ -0,0 +1,1021 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2010. 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 +%% 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 online 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. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_error_SUITE +%%% +%%% Description: +%%% Test various errors in Common Test suites. +%%% +%%% The suites used for the test are located in the data directory. +%%%------------------------------------------------------------------- +-module(ct_hooks_SUITE). + +-compile(export_all). + +-include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-define(eh, ct_test_support_eh). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Description: Since Common Test starts another Test Server +%% instance, the tests need to be performed on a separate node (or +%% there will be clashes with logging processes etc). +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + DataDir = ?config(data_dir, Config), + TestDir = filename:join(DataDir,"cth/tests/"), + CTHs = filelib:wildcard(filename:join(TestDir,"*_cth.erl")), + io:format("CTHs: ~p",[CTHs]), + [io:format("Compiling ~p: ~p", + [FileName,compile:file(FileName,[{outdir,TestDir},debug_info])]) || + FileName <- CTHs], + ct_test_support:init_per_suite([{path_dirs,[TestDir]} | Config]). + +end_per_suite(Config) -> + ct_test_support:end_per_suite(Config). + +init_per_testcase(TestCase, Config) -> + ct_test_support:init_per_testcase(TestCase, Config). + +end_per_testcase(TestCase, Config) -> + ct_test_support:end_per_testcase(TestCase, Config). + + +suite() -> + [{timetrap,{seconds,20}}]. + +all() -> + all(suite). + +all(suite) -> + lists:reverse( + [ + one_cth, two_cth, faulty_cth_no_init, faulty_cth_id_no_init, + faulty_cth_exit_in_init, faulty_cth_exit_in_id, + faulty_cth_exit_in_init_scope_suite, minimal_cth, + minimal_and_maximal_cth, faulty_cth_undef, + scope_per_suite_cth, scope_per_group_cth, scope_suite_cth, + scope_per_suite_state_cth, scope_per_group_state_cth, + scope_suite_state_cth, + fail_pre_suite_cth, fail_post_suite_cth, skip_pre_suite_cth, + skip_post_suite_cth, recover_post_suite_cth, update_config_cth, + state_update_cth, options_cth, same_id_cth, + fail_n_skip_with_minimal_cth + ] + ) + . + + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +one_cth(Config) when is_list(Config) -> + do_test(one_empty_cth, "ct_cth_empty_SUITE.erl",[empty_cth], Config). + +two_cth(Config) when is_list(Config) -> + do_test(two_empty_cth, "ct_cth_empty_SUITE.erl",[empty_cth,empty_cth], + Config). + +faulty_cth_no_init(Config) when is_list(Config) -> + do_test(faulty_cth_no_init, "ct_cth_empty_SUITE.erl",[askjhdkljashdkaj], + Config,{error,"Failed to start CTH, see the " + "CT Log for details"}). + +faulty_cth_id_no_init(Config) when is_list(Config) -> + do_test(faulty_cth_id_no_init, "ct_cth_empty_SUITE.erl",[id_no_init_cth], + Config,{error,"Failed to start CTH, see the " + "CT Log for details"}). + +minimal_cth(Config) when is_list(Config) -> + do_test(minimal_cth, "ct_cth_empty_SUITE.erl",[minimal_cth],Config). + +minimal_and_maximal_cth(Config) when is_list(Config) -> + do_test(minimal_and_maximal_cth, "ct_cth_empty_SUITE.erl", + [minimal_cth, empty_cth],Config). + +faulty_cth_undef(Config) when is_list(Config) -> + do_test(faulty_cth_undef, "ct_cth_empty_SUITE.erl", + [undef_cth],Config). + +faulty_cth_exit_in_init_scope_suite(Config) when is_list(Config) -> + do_test(faulty_cth_exit_in_init_scope_suite, + "ct_exit_in_init_scope_suite_cth_SUITE.erl", + [],Config). + +faulty_cth_exit_in_init(Config) when is_list(Config) -> + do_test(faulty_cth_exit_in_init, "ct_cth_empty_SUITE.erl", + [crash_init_cth], Config, + {error,"Failed to start CTH, see the " + "CT Log for details"}). + +faulty_cth_exit_in_id(Config) when is_list(Config) -> + do_test(faulty_cth_exit_in_id, "ct_cth_empty_SUITE.erl", + [crash_id_cth], Config, + {error,"Failed to start CTH, see the " + "CT Log for details"}). + +scope_per_suite_cth(Config) when is_list(Config) -> + do_test(scope_per_suite_cth, "ct_scope_per_suite_cth_SUITE.erl", + [],Config). + +scope_suite_cth(Config) when is_list(Config) -> + do_test(scope_suite_cth, "ct_scope_suite_cth_SUITE.erl", + [],Config). + +scope_per_group_cth(Config) when is_list(Config) -> + do_test(scope_per_group_cth, "ct_scope_per_group_cth_SUITE.erl", + [],Config). + +scope_per_suite_state_cth(Config) when is_list(Config) -> + do_test(scope_per_suite_state_cth, "ct_scope_per_suite_state_cth_SUITE.erl", + [],Config). + +scope_suite_state_cth(Config) when is_list(Config) -> + do_test(scope_suite_state_cth, "ct_scope_suite_state_cth_SUITE.erl", + [],Config). + +scope_per_group_state_cth(Config) when is_list(Config) -> + do_test(scope_per_group_state_cth, "ct_scope_per_group_state_cth_SUITE.erl", + [],Config). + +fail_pre_suite_cth(Config) when is_list(Config) -> + do_test(fail_pre_suite_cth, "ct_cth_empty_SUITE.erl", + [fail_pre_suite_cth],Config). + +fail_post_suite_cth(Config) when is_list(Config) -> + do_test(fail_post_suite_cth, "ct_cth_empty_SUITE.erl", + [fail_post_suite_cth],Config). + +skip_pre_suite_cth(Config) when is_list(Config) -> + do_test(skip_pre_suite_cth, "ct_cth_empty_SUITE.erl", + [skip_pre_suite_cth],Config). + +skip_post_suite_cth(Config) when is_list(Config) -> + do_test(skip_post_suite_cth, "ct_cth_empty_SUITE.erl", + [skip_post_suite_cth],Config). + +recover_post_suite_cth(Config) when is_list(Config) -> + do_test(recover_post_suite_cth, "ct_cth_fail_per_suite_SUITE.erl", + [recover_post_suite_cth],Config). + +update_config_cth(Config) when is_list(Config) -> + do_test(update_config_cth, "ct_update_config_SUITE.erl", + [update_config_cth],Config). + +state_update_cth(Config) when is_list(Config) -> + do_test(state_update_cth, "ct_cth_fail_one_skip_one_SUITE.erl", + [state_update_cth,state_update_cth],Config). + +options_cth(Config) when is_list(Config) -> + do_test(options_cth, "ct_cth_empty_SUITE.erl", + [{empty_cth,[test]}],Config). + +same_id_cth(Config) when is_list(Config) -> + do_test(same_id_cth, "ct_cth_empty_SUITE.erl", + [same_id_cth,same_id_cth],Config). + +fail_n_skip_with_minimal_cth(Config) when is_list(Config) -> + do_test(fail_n_skip_with_minimal_cth, "ct_cth_fail_one_skip_one_SUITE.erl", + [minimal_terminate_cth],Config). + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- + +do_test(Tag, SWC, CTHs, Config) -> + do_test(Tag, SWC, CTHs, Config, ok). +do_test(Tag, SWC, CTHs, Config, {error,_} = Res) -> + do_test(Tag, SWC, CTHs, Config, Res, 1); +do_test(Tag, SWC, CTHs, Config, Res) -> + do_test(Tag, SWC, CTHs, Config, Res, 2). + +do_test(Tag, SuiteWildCard, CTHs, Config, Res, EC) -> + + DataDir = ?config(data_dir, Config), + Suites = filelib:wildcard( + filename:join([DataDir,"cth/tests",SuiteWildCard])), + {Opts,ERPid} = setup([{suite,Suites}, + {ct_hooks,CTHs},{label,Tag}], Config), + Res = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(Tag, + reformat(Events, ?eh), + ?config(priv_dir, Config)), + + TestEvents = events_to_check(Tag, EC), + ok = ct_test_support:verify_events(TestEvents, Events, Config). + +setup(Test, Config) -> + Opts0 = ct_test_support:get_opts(Config), + Level = ?config(trace_level, Config), + EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], + Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test], + ERPid = ct_test_support:start_event_receiver(Config), + {Opts,ERPid}. + +reformat(Events, EH) -> + ct_test_support:reformat(Events, EH). +%reformat(Events, _EH) -> +% Events. + +%%%----------------------------------------------------------------- +%%% TEST EVENTS +%%%----------------------------------------------------------------- +events_to_check(Test) -> + %% 2 tests (ct:run_test + script_start) is default + events_to_check(Test, 2). + +events_to_check(_, 0) -> + []; +events_to_check(Test, N) -> + test_events(Test) ++ events_to_check(Test, N-1). + +test_events(one_empty_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,cth,{empty_cth,id,[[]]}}, + {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}}, + {?eh,cth,{empty_cth,pre_init_per_suite, + [ct_cth_empty_SUITE,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_init_per_suite, + [ct_cth_empty_SUITE,'$proplist','$proplist',[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}}, + + {?eh,tc_start,{ct_cth_empty_SUITE,test_case}}, + {?eh,cth,{empty_cth,pre_init_per_testcase,[test_case,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_end_per_testcase,[test_case,'$proplist','_',[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}}, + + {?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}}, + {?eh,cth,{empty_cth,pre_end_per_suite, + [ct_cth_empty_SUITE,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_end_per_suite,[ct_cth_empty_SUITE,'$proplist','_',[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth,{empty_cth,terminate,[[]]}}, + {?eh,stop_logging,[]} + ]; + +test_events(two_empty_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,cth,{'_',id,[[]]}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,cth,{'_',id,[[]]}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}}, + {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}}, + {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}}, + {?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist','$proplist',[]]}}, + {?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist','$proplist',[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}}, + + {?eh,tc_start,{ct_cth_empty_SUITE,test_case}}, + {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}}, + {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}}, + {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}}, + {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}}, + + {?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}}, + {?eh,cth,{'_',pre_end_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}}, + {?eh,cth,{'_',pre_end_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}}, + {?eh,cth,{'_',post_end_per_suite,[ct_cth_empty_SUITE,'$proplist','_',[]]}}, + {?eh,cth,{'_',post_end_per_suite,[ct_cth_empty_SUITE,'$proplist','_',[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth,{'_',terminate,[[]]}}, + {?eh,cth,{'_',terminate,[[]]}}, + {?eh,stop_logging,[]} + ]; + +test_events(faulty_cth_no_init) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]} + ]; + +test_events(faulty_cth_id_no_init) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,cth,{'_',id,[[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {negative,{?eh,tc_start,'_'}, + {?eh,test_done,{'DEF','STOP_TIME'}}}, + {?eh,stop_logging,[]} + ]; + +test_events(minimal_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {negative,{?eh,cth,{'_',id,['_',[]]}}, + {?eh,cth,{'_',init,['_',[]]}}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}}, + {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}}, + + {?eh,tc_start,{ct_cth_empty_SUITE,test_case}}, + {?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}}, + + {?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}}, + {?eh,tc_done,{ct_cth_empty_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]} + ]; + +test_events(minimal_and_maximal_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {negative,{?eh,cth,{'_',id,['_',[]]}}, + {?eh,cth,{'_',init,['_',[]]}}}, + {?eh,cth,{'_',id,[[]]}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}}, + {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}}, + {?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist','$proplist',[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}}, + + {?eh,tc_start,{ct_cth_empty_SUITE,test_case}}, + {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}}, + {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}}, + + {?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}}, + {?eh,cth,{'_',pre_end_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}}, + {?eh,cth,{'_',post_end_per_suite,[ct_cth_empty_SUITE,'$proplist','_',[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth,{'_',terminate,[[]]}}, + {?eh,stop_logging,[]} + ]; + +test_events(faulty_cth_undef) -> + FailReasonStr = "undef_cth:pre_init_per_suite/3 CTH call failed", + FailReason = {ct_cth_empty_SUITE,init_per_suite, + {failed,FailReasonStr}}, + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}}, + {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite, + {failed, {error,FailReasonStr}}}}, + {?eh,cth,{'_',on_tc_fail,'_'}}, + + {?eh,tc_auto_skip,{ct_cth_empty_SUITE,test_case, + {failed, FailReason}}}, + {?eh,cth,{'_',on_tc_skip,'_'}}, + + {?eh,tc_auto_skip,{ct_cth_empty_SUITE,end_per_suite, + {failed, FailReason}}}, + {?eh,cth,{'_',on_tc_skip,'_'}}, + + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]} + ]; + +test_events(faulty_cth_exit_in_init_scope_suite) -> + [{?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{'_',init_per_suite}}, + {?eh,cth,{empty_cth,init,['_',[]]}}, + {?eh,tc_done, + {ct_exit_in_init_scope_suite_cth_SUITE,init_per_suite, + {failed, + {error, + "Failed to start CTH, see the CT Log for details"}}}}, + {?eh,tc_auto_skip, + {ct_exit_in_init_scope_suite_cth_SUITE,test_case, + {failed, + {ct_exit_in_init_scope_suite_cth_SUITE,init_per_suite, + {failed, + "Failed to start CTH, see the CT Log for details"}}}}}, + {?eh,tc_auto_skip, + {ct_exit_in_init_scope_suite_cth_SUITE,end_per_suite, + {failed, + {ct_exit_in_init_scope_suite_cth_SUITE,init_per_suite, + {failed, + "Failed to start CTH, see the CT Log for details"}}}}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]}]; + +test_events(faulty_cth_exit_in_init) -> + [{?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,cth,{empty_cth,init,['_',[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]}]; + +test_events(faulty_cth_exit_in_id) -> + [{?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,cth,{empty_cth,id,[[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {negative, {?eh,tc_start,'_'}, + {?eh,test_done,{'DEF','STOP_TIME'}}}, + {?eh,stop_logging,[]}]; + +test_events(scope_per_suite_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{ct_scope_per_suite_cth_SUITE,init_per_suite}}, + {?eh,cth,{'_',id,[[]]}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,cth,{'_',post_init_per_suite,[ct_scope_per_suite_cth_SUITE,'$proplist','$proplist',[]]}}, + {?eh,tc_done,{ct_scope_per_suite_cth_SUITE,init_per_suite,ok}}, + + {?eh,tc_start,{ct_scope_per_suite_cth_SUITE,test_case}}, + {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}}, + {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_scope_per_suite_cth_SUITE,test_case,ok}}, + + {?eh,tc_start,{ct_scope_per_suite_cth_SUITE,end_per_suite}}, + {?eh,cth,{'_',pre_end_per_suite, + [ct_scope_per_suite_cth_SUITE,'$proplist',[]]}}, + {?eh,cth,{'_',post_end_per_suite,[ct_scope_per_suite_cth_SUITE,'$proplist','_',[]]}}, + {?eh,cth,{'_',terminate,[[]]}}, + {?eh,tc_done,{ct_scope_per_suite_cth_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]} + ]; + +test_events(scope_suite_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{ct_scope_suite_cth_SUITE,init_per_suite}}, + {?eh,cth,{'_',id,[[]]}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,cth,{'_',pre_init_per_suite,[ct_scope_suite_cth_SUITE,'$proplist',[]]}}, + {?eh,cth,{'_',post_init_per_suite,[ct_scope_suite_cth_SUITE,'$proplist','$proplist',[]]}}, + {?eh,tc_done,{ct_scope_suite_cth_SUITE,init_per_suite,ok}}, + + {?eh,tc_start,{ct_scope_suite_cth_SUITE,test_case}}, + {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}}, + {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_scope_suite_cth_SUITE,test_case,ok}}, + + {?eh,tc_start,{ct_scope_suite_cth_SUITE,end_per_suite}}, + {?eh,cth,{'_',pre_end_per_suite,[ct_scope_suite_cth_SUITE,'$proplist',[]]}}, + {?eh,cth,{'_',post_end_per_suite,[ct_scope_suite_cth_SUITE,'$proplist','_',[]]}}, + {?eh,cth,{'_',terminate,[[]]}}, + {?eh,tc_done,{ct_scope_suite_cth_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]} + ]; + +test_events(scope_per_group_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{ct_scope_per_group_cth_SUITE,init_per_suite}}, + {?eh,tc_done,{ct_scope_per_group_cth_SUITE,init_per_suite,ok}}, + + [{?eh,tc_start,{ct_scope_per_group_cth_SUITE,{init_per_group,group1,[]}}}, + {?eh,cth,{'_',id,[[]]}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,cth,{'_',post_init_per_group,[group1,'$proplist','$proplist',[]]}}, + {?eh,tc_done,{ct_scope_per_group_cth_SUITE,{init_per_group,group1,[]},ok}}, + + {?eh,tc_start,{ct_scope_per_group_cth_SUITE,test_case}}, + {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}}, + {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_scope_per_group_cth_SUITE,test_case,ok}}, + + {?eh,tc_start,{ct_scope_per_group_cth_SUITE,{end_per_group,group1,[]}}}, + {?eh,cth,{'_',pre_end_per_group,[group1,'$proplist',[]]}}, + {?eh,cth,{'_',post_end_per_group,[group1,'$proplist','_',[]]}}, + {?eh,cth,{'_',terminate,[[]]}}, + {?eh,tc_done,{ct_scope_per_group_cth_SUITE,{end_per_group,group1,[]},ok}}], + + {?eh,tc_start,{ct_scope_per_group_cth_SUITE,end_per_suite}}, + {?eh,tc_done,{ct_scope_per_group_cth_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]} + ]; + +test_events(scope_per_suite_state_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{ct_scope_per_suite_state_cth_SUITE,init_per_suite}}, + {?eh,cth,{'_',id,[[test]]}}, + {?eh,cth,{'_',init,['_',[test]]}}, + {?eh,cth,{'_',post_init_per_suite,[ct_scope_per_suite_state_cth_SUITE,'$proplist','$proplist',[test]]}}, + {?eh,tc_done,{ct_scope_per_suite_state_cth_SUITE,init_per_suite,ok}}, + + {?eh,tc_start,{ct_scope_per_suite_state_cth_SUITE,test_case}}, + {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[test]]}}, + {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[test]]}}, + {?eh,tc_done,{ct_scope_per_suite_state_cth_SUITE,test_case,ok}}, + + {?eh,tc_start,{ct_scope_per_suite_state_cth_SUITE,end_per_suite}}, + {?eh,cth,{'_',pre_end_per_suite, + [ct_scope_per_suite_state_cth_SUITE,'$proplist',[test]]}}, + {?eh,cth,{'_',post_end_per_suite,[ct_scope_per_suite_state_cth_SUITE,'$proplist','_',[test]]}}, + {?eh,cth,{'_',terminate,[[test]]}}, + {?eh,tc_done,{ct_scope_per_suite_state_cth_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]} + ]; + +test_events(scope_suite_state_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{ct_scope_suite_state_cth_SUITE,init_per_suite}}, + {?eh,cth,{'_',id,[[test]]}}, + {?eh,cth,{'_',init,['_',[test]]}}, + {?eh,cth,{'_',pre_init_per_suite,[ct_scope_suite_state_cth_SUITE,'$proplist',[test]]}}, + {?eh,cth,{'_',post_init_per_suite,[ct_scope_suite_state_cth_SUITE,'$proplist','$proplist',[test]]}}, + {?eh,tc_done,{ct_scope_suite_state_cth_SUITE,init_per_suite,ok}}, + + {?eh,tc_start,{ct_scope_suite_state_cth_SUITE,test_case}}, + {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[test]]}}, + {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[test]]}}, + {?eh,tc_done,{ct_scope_suite_state_cth_SUITE,test_case,ok}}, + + {?eh,tc_start,{ct_scope_suite_state_cth_SUITE,end_per_suite}}, + {?eh,cth,{'_',pre_end_per_suite,[ct_scope_suite_state_cth_SUITE,'$proplist',[test]]}}, + {?eh,cth,{'_',post_end_per_suite,[ct_scope_suite_state_cth_SUITE,'$proplist','_',[test]]}}, + {?eh,cth,{'_',terminate,[[test]]}}, + {?eh,tc_done,{ct_scope_suite_state_cth_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]} + ]; + +test_events(scope_per_group_state_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{ct_scope_per_group_state_cth_SUITE,init_per_suite}}, + {?eh,tc_done,{ct_scope_per_group_state_cth_SUITE,init_per_suite,ok}}, + + [{?eh,tc_start,{ct_scope_per_group_state_cth_SUITE,{init_per_group,group1,[]}}}, + {?eh,cth,{'_',id,[[test]]}}, + {?eh,cth,{'_',init,['_',[test]]}}, + {?eh,cth,{'_',post_init_per_group,[group1,'$proplist','$proplist',[test]]}}, + {?eh,tc_done,{ct_scope_per_group_state_cth_SUITE,{init_per_group,group1,[]},ok}}, + + {?eh,tc_start,{ct_scope_per_group_state_cth_SUITE,test_case}}, + {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[test]]}}, + {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[test]]}}, + {?eh,tc_done,{ct_scope_per_group_state_cth_SUITE,test_case,ok}}, + + {?eh,tc_start,{ct_scope_per_group_state_cth_SUITE,{end_per_group,group1,[]}}}, + {?eh,cth,{'_',pre_end_per_group,[group1,'$proplist',[test]]}}, + {?eh,cth,{'_',post_end_per_group,[group1,'$proplist','_',[test]]}}, + {?eh,cth,{'_',terminate,[[test]]}}, + {?eh,tc_done,{ct_scope_per_group_state_cth_SUITE,{end_per_group,group1,[]},ok}}], + + {?eh,tc_start,{ct_scope_per_group_state_cth_SUITE,end_per_suite}}, + {?eh,tc_done,{ct_scope_per_group_state_cth_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]} + ]; + +test_events(fail_pre_suite_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + + + {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}}, + {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}}, + {?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist', + {fail,"Test failure"},[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite, + {failed, {error,"Test failure"}}}}, + {?eh,cth,{'_',on_tc_fail, + [init_per_suite,{failed,"Test failure"},[]]}}, + + + {?eh,tc_auto_skip,{ct_cth_empty_SUITE,test_case, + {failed,{ct_cth_empty_SUITE,init_per_suite, + {failed,"Test failure"}}}}}, + {?eh,cth,{'_',on_tc_skip, + [test_case, {tc_auto_skip, + {failed, {ct_cth_empty_SUITE, init_per_suite, + {failed, "Test failure"}}}},[]]}}, + + + {?eh,tc_auto_skip, {ct_cth_empty_SUITE, end_per_suite, + {failed, {ct_cth_empty_SUITE, init_per_suite, + {failed, "Test failure"}}}}}, + {?eh,cth,{'_',on_tc_skip, + [end_per_suite, {tc_auto_skip, + {failed, {ct_cth_empty_SUITE, init_per_suite, + {failed, "Test failure"}}}},[]]}}, + + + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth, {'_',terminate,[[]]}}, + {?eh,stop_logging,[]} + ]; + +test_events(fail_post_suite_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}}, + {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}}, + {?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist','$proplist',[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite, + {failed,{error,"Test failure"}}}}, + {?eh,cth,{'_',on_tc_fail,[init_per_suite, {failed,"Test failure"}, []]}}, + + {?eh,tc_auto_skip,{ct_cth_empty_SUITE,test_case, + {failed,{ct_cth_empty_SUITE,init_per_suite, + {failed,"Test failure"}}}}}, + {?eh,cth,{'_',on_tc_skip,[test_case,{tc_auto_skip,'_'},[]]}}, + + {?eh,tc_auto_skip, {ct_cth_empty_SUITE, end_per_suite, + {failed, {ct_cth_empty_SUITE, init_per_suite, + {failed, "Test failure"}}}}}, + {?eh,cth,{'_',on_tc_skip,[end_per_suite,{tc_auto_skip,'_'},[]]}}, + + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth, {'_',terminate,[[]]}}, + {?eh,stop_logging,[]} + ]; + +test_events(skip_pre_suite_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}}, + {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}}, + {?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist',{skip,"Test skip"},[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,{skipped,"Test skip"}}}, + {?eh,cth,{'_',on_tc_skip, + [init_per_suite,{tc_user_skip,{skipped,"Test skip"}},[]]}}, + + {?eh,tc_auto_skip,{ct_cth_empty_SUITE,test_case,"Test skip"}}, + {?eh,cth,{'_',on_tc_skip,[test_case,{tc_auto_skip,"Test skip"},[]]}}, + + {?eh,tc_auto_skip, {ct_cth_empty_SUITE, end_per_suite,"Test skip"}}, + {?eh,cth,{'_',on_tc_skip,[end_per_suite,{tc_auto_skip,"Test skip"},[]]}}, + + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth, {'_',terminate,[[]]}}, + {?eh,stop_logging,[]} + ]; + +test_events(skip_post_suite_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + + {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}}, + {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}}, + {?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist','$proplist',[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,{skipped,"Test skip"}}}, + {?eh,cth,{'_',on_tc_skip, + [init_per_suite,{tc_user_skip,{skipped,"Test skip"}},[]]}}, + + {?eh,tc_auto_skip,{ct_cth_empty_SUITE,test_case,"Test skip"}}, + {?eh,cth,{'_',on_tc_skip,[test_case,{tc_auto_skip,"Test skip"},[]]}}, + + {?eh,tc_auto_skip, {ct_cth_empty_SUITE, end_per_suite,"Test skip"}}, + {?eh,cth,{'_',on_tc_skip,[end_per_suite,{tc_auto_skip,"Test skip"},[]]}}, + + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth,{'_',terminate,[[]]}}, + {?eh,stop_logging,[]} + ]; + +test_events(recover_post_suite_cth) -> + Suite = ct_cth_fail_per_suite_SUITE, + [ + {?eh,start_logging,'_'}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{Suite,init_per_suite}}, + {?eh,cth,{'_',pre_init_per_suite,[Suite,'$proplist','$proplist']}}, + {?eh,cth,{'_',post_init_per_suite,[Suite,contains([tc_status]), + {'EXIT',{'_','_'}},[]]}}, + {?eh,tc_done,{Suite,init_per_suite,ok}}, + + {?eh,tc_start,{Suite,test_case}}, + {?eh,cth,{'_',pre_init_per_testcase, + [test_case, not_contains([tc_status]),[]]}}, + {?eh,cth,{'_',post_end_per_testcase, + [test_case, contains([tc_status]),'_',[]]}}, + {?eh,tc_done,{Suite,test_case,ok}}, + + {?eh,tc_start,{Suite,end_per_suite}}, + {?eh,cth,{'_',pre_end_per_suite, + [Suite,not_contains([tc_status]),[]]}}, + {?eh,cth,{'_',post_end_per_suite, + [Suite,not_contains([tc_status]),'_',[]]}}, + {?eh,tc_done,{Suite,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth,{'_',terminate,[[]]}}, + {?eh,stop_logging,[]} + ]; + +test_events(update_config_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + + {?eh,tc_start,{ct_update_config_SUITE,init_per_suite}}, + {?eh,cth,{'_',pre_init_per_suite, + [ct_update_config_SUITE,contains([]),[]]}}, + {?eh,cth,{'_',post_init_per_suite, + [ct_update_config_SUITE, + '$proplist', + contains( + [init_per_suite, + pre_init_per_suite]), + []]}}, + {?eh,tc_done,{ct_update_config_SUITE,init_per_suite,ok}}, + + {?eh,tc_start,{ct_update_config_SUITE, {init_per_group,group1,[]}}}, + {?eh,cth,{'_',pre_init_per_group, + [group1,contains( + [post_init_per_suite, + init_per_suite, + pre_init_per_suite]), + []]}}, + {?eh,cth,{'_',post_init_per_group, + [group1, + contains( + [post_init_per_suite, + init_per_suite, + pre_init_per_suite]), + contains( + [init_per_group, + pre_init_per_group, + post_init_per_suite, + init_per_suite, + pre_init_per_suite]), + []]}}, + {?eh,tc_done,{ct_update_config_SUITE,{init_per_group,group1,[]},ok}}, + + {?eh,tc_start,{ct_update_config_SUITE,test_case}}, + {?eh,cth,{'_',pre_init_per_testcase, + [test_case,contains( + [post_init_per_group, + init_per_group, + pre_init_per_group, + post_init_per_suite, + init_per_suite, + pre_init_per_suite]), + []]}}, + {?eh,cth,{'_',post_end_per_testcase, + [test_case,contains( + [init_per_testcase, + pre_init_per_testcase, + post_init_per_group, + init_per_group, + pre_init_per_group, + post_init_per_suite, + init_per_suite, + pre_init_per_suite]), + ok,[]]}}, + {?eh,tc_done,{ct_update_config_SUITE,test_case,ok}}, + + {?eh,tc_start,{ct_update_config_SUITE, {end_per_group,group1,[]}}}, + {?eh,cth,{'_',pre_end_per_group, + [group1,contains( + [post_init_per_group, + init_per_group, + pre_init_per_group, + post_init_per_suite, + init_per_suite, + pre_init_per_suite]), + []]}}, + {?eh,cth,{'_',post_end_per_group, + [group1, + contains( + [pre_end_per_group, + post_init_per_group, + init_per_group, + pre_init_per_group, + post_init_per_suite, + init_per_suite, + pre_init_per_suite]), + ok,[]]}}, + {?eh,tc_done,{ct_update_config_SUITE,{end_per_group,group1,[]},ok}}, + + {?eh,tc_start,{ct_update_config_SUITE,end_per_suite}}, + {?eh,cth,{'_',pre_end_per_suite, + [ct_update_config_SUITE,contains( + [post_init_per_suite, + init_per_suite, + pre_init_per_suite]), + []]}}, + {?eh,cth,{'_',post_end_per_suite, + [ct_update_config_SUITE,contains( + [pre_end_per_suite, + post_init_per_suite, + init_per_suite, + pre_init_per_suite]), + '_',[]]}}, + {?eh,tc_done,{ct_update_config_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth,{'_',terminate,[contains( + [post_end_per_suite, + pre_end_per_suite, + post_init_per_suite, + init_per_suite, + pre_init_per_suite])]}}, + {?eh,stop_logging,[]} + ]; + +test_events(state_update_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{'_',init_per_suite}}, + + {?eh,tc_done,{'_',end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth,{'_',terminate,[contains( + [post_end_per_suite,pre_end_per_suite, + post_end_per_group,pre_end_per_group, + {not_in_order, + [post_end_per_testcase,pre_init_per_testcase, + on_tc_skip,post_end_per_testcase, + pre_init_per_testcase,on_tc_fail, + post_end_per_testcase,pre_init_per_testcase] + }, + post_init_per_group,pre_init_per_group, + post_init_per_suite,pre_init_per_suite, + init])]}}, + {?eh,cth,{'_',terminate,[contains( + [post_end_per_suite,pre_end_per_suite, + post_end_per_group,pre_end_per_group, + {not_in_order, + [post_end_per_testcase,pre_init_per_testcase, + on_tc_skip,post_end_per_testcase, + pre_init_per_testcase,on_tc_fail, + post_end_per_testcase,pre_init_per_testcase] + }, + post_init_per_group,pre_init_per_group, + post_init_per_suite,pre_init_per_suite, + init] + )]}}, + {?eh,stop_logging,[]} + ]; + +test_events(options_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,cth,{empty_cth,init,['_',[test]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}}, + {?eh,cth,{empty_cth,pre_init_per_suite, + [ct_cth_empty_SUITE,'$proplist',[test]]}}, + {?eh,cth,{empty_cth,post_init_per_suite, + [ct_cth_empty_SUITE,'$proplist','$proplist',[test]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}}, + + {?eh,tc_start,{ct_cth_empty_SUITE,test_case}}, + {?eh,cth,{empty_cth,pre_init_per_testcase,[test_case,'$proplist',[test]]}}, + {?eh,cth,{empty_cth,post_end_per_testcase,[test_case,'$proplist','_',[test]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}}, + + {?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}}, + {?eh,cth,{empty_cth,pre_end_per_suite, + [ct_cth_empty_SUITE,'$proplist',[test]]}}, + {?eh,cth,{empty_cth,post_end_per_suite,[ct_cth_empty_SUITE,'$proplist','_',[test]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth,{empty_cth,terminate,[[test]]}}, + {?eh,stop_logging,[]} + ]; + +test_events(same_id_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,cth,{'_',id,[[]]}}, + {?eh,cth,{'_',init,[same_id_cth,[]]}}, + {?eh,cth,{'_',id,[[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}}, + {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}}, + {negative, + {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}}, + {?eh,cth,{'_',post_init_per_suite, + [ct_cth_empty_SUITE,'$proplist','$proplist',[]]}}}, + {negative, + {?eh,cth,{'_',post_init_per_suite, + [ct_cth_empty_SUITE,'$proplist','$proplist',[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}}}, + + {?eh,tc_start,{ct_cth_empty_SUITE,test_case}}, + {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}}, + {negative, + {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}}, + {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}}}, + {negative, + {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}}}, + + {?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}}, + {?eh,cth,{'_',pre_end_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}}, + {negative, + {?eh,cth,{'_',pre_end_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}}, + {?eh,cth,{'_',post_end_per_suite,[ct_cth_empty_SUITE,'$proplist','_',[]]}}}, + {negative, + {?eh,cth,{'_',post_end_per_suite, + [ct_cth_empty_SUITE,'$proplist','_',[]]}}, + {?eh,tc_done,{ct_cth_empty_SUITE,end_per_suite,ok}}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth,{'_',terminate,[[]]}}, + {?eh,stop_logging,[]} + ]; + +test_events(fail_n_skip_with_minimal_cth) -> + [{?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,cth,{'_',init,['_',[]]}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,tc_start,{'_',init_per_suite}}, + + {?eh,tc_done,{'_',end_per_suite,ok}}, + {?eh,cth,{'_',terminate,[[]]}}, + {?eh,stop_logging,[]} + ]; + +test_events(ok) -> + ok. + + +%% test events help functions +contains(List) -> + fun(Proplist) when is_list(Proplist) -> + contains(List,Proplist) + end. + +contains([{not_in_order,List}|T],Rest) -> + contains_parallel(List,Rest), + contains(T,Rest); +contains([{Ele,Pos}|T] = L,[H|T2]) -> + case element(Pos,H) of + Ele -> + contains(T,T2); + _ -> + contains(L,T2) + end; +contains([Ele|T],[{Ele,_}|T2])-> + contains(T,T2); +contains([Ele|T],[Ele|T2])-> + contains(T,T2); +contains(List,[_|T]) -> + contains(List,T); +contains([],_) -> + match. + +contains_parallel([Key | T], Elems) -> + contains([Key],Elems), + contains_parallel(T,Elems); +contains_parallel([],_Elems) -> + match. + +not_contains(List) -> + fun(Proplist) when is_list(Proplist) -> + [] = [Ele || {Ele,_} <- Proplist, + Test <- List, + Test =:= Ele] + end. diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/crash_id_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/crash_id_cth.erl new file mode 100644 index 0000000000..02c36e378c --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/crash_id_cth.erl @@ -0,0 +1,34 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(crash_id_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-export([id/1]).
+
+id(Opts) ->
+ empty_cth:id(Opts),
+ exit(diediedie).
+
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/crash_init_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/crash_init_cth.erl new file mode 100644 index 0000000000..6ed23565f6 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/crash_init_cth.erl @@ -0,0 +1,34 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(crash_init_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-export([init/2]).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts),
+ exit(diediedie).
+
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_empty_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_empty_SUITE.erl new file mode 100644 index 0000000000..499069b382 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_empty_SUITE.erl @@ -0,0 +1,47 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_cth_empty_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_fail_one_skip_one_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_fail_one_skip_one_SUITE.erl new file mode 100644 index 0000000000..017812c719 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_fail_one_skip_one_SUITE.erl @@ -0,0 +1,64 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_cth_fail_one_skip_one_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_Group,Config) ->
+ Config.
+
+end_per_group(_Group,_Config) ->
+ ok.
+
+init_per_testcase(test_case2, Config) ->
+ {skip,"skip it"};
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+groups() ->
+ [{group1,[parallel],[{group2,[parallel],[test_case1,test_case2,test_case3]}]}].
+
+all() ->
+ [{group,group1}].
+
+%% Test cases starts here.
+test_case1(Config) ->
+ ok = nok.
+
+test_case2(Config) ->
+ ok.
+
+test_case3(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_fail_per_suite_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_fail_per_suite_SUITE.erl new file mode 100644 index 0000000000..136a15ec96 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_fail_per_suite_SUITE.erl @@ -0,0 +1,47 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_cth_fail_per_suite_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ ok = nok.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_exit_in_init_scope_suite_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_exit_in_init_scope_suite_cth_SUITE.erl new file mode 100644 index 0000000000..42be0a659e --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_exit_in_init_scope_suite_cth_SUITE.erl @@ -0,0 +1,50 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_exit_in_init_scope_suite_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%% Test server callback functions
+suite() ->
+ [{ct_hooks,[crash_init_cth]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_group_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_group_cth_SUITE.erl new file mode 100644 index 0000000000..628bca774c --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_group_cth_SUITE.erl @@ -0,0 +1,56 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_scope_per_group_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+init_per_group(GroupName, Config) ->
+ [{ct_hooks,[empty_cth]}|Config].
+
+end_per_group(GroupName, Config) ->
+ ok.
+
+all() ->
+ [{group,group1}].
+
+groups() ->
+ [{group1,[],[test_case]}].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_group_state_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_group_state_cth_SUITE.erl new file mode 100644 index 0000000000..14ea52bf8c --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_group_state_cth_SUITE.erl @@ -0,0 +1,56 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_scope_per_group_state_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ [{ct_hooks,[{empty_cth,[test]}]}|Config].
+
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+all() ->
+ [{group,group1}].
+
+groups() ->
+ [{group1,[],[test_case]}].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_suite_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_suite_cth_SUITE.erl new file mode 100644 index 0000000000..5c1658be44 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_suite_cth_SUITE.erl @@ -0,0 +1,47 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_scope_per_suite_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ [{ct_hooks,[empty_cth]}|Config].
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_suite_state_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_suite_state_cth_SUITE.erl new file mode 100644 index 0000000000..96d00e3b28 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_suite_state_cth_SUITE.erl @@ -0,0 +1,47 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_scope_per_suite_state_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ [{ct_hooks,[{empty_cth,[test]}]}|Config].
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_tc_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_tc_cth_SUITE.erl new file mode 100644 index 0000000000..fa632444c5 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_tc_cth_SUITE.erl @@ -0,0 +1,110 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_scope_per_tc_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+%%--------------------------------------------------------------------
+%% @doc
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Initiation before the whole suite
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%
+%% @spec init_per_suite(Config) -> Config
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Cleanup after the whole suite
+%%
+%% @spec end_per_suite(Config) -> _
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Initiation before each test case
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%% Initiation before each test case
+%%
+%% @spec init_per_testcase(TestCase, Config) -> Config
+%% @end
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ [{ct_hooks,[empty_cth]}|Config].
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Cleanup after each test case
+%%
+%% @spec end_per_testcase(TestCase, Config) -> _
+%% @end
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @doc
+%% TestCases - [Case]
+%% Case - atom()
+%% Name of a test case.
+%%
+%% Returns a list of all test cases in this test suite
+%%
+%% @spec all() -> TestCases
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+%%--------------------------------------------------------------------
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_cth_SUITE.erl new file mode 100644 index 0000000000..988a0969ca --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_cth_SUITE.erl @@ -0,0 +1,50 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_scope_suite_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+suite() ->
+ [{ct_hooks,[empty_cth]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_state_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_state_cth_SUITE.erl new file mode 100644 index 0000000000..18b68fbcdc --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_state_cth_SUITE.erl @@ -0,0 +1,50 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_scope_suite_state_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+suite() ->
+ [{ct_hooks,[{empty_cth,[test]}]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl new file mode 100644 index 0000000000..57fea347f6 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl @@ -0,0 +1,56 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_update_config_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ [{init_per_suite,now()}|Config].
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ [{init_per_testcase,now()}|Config].
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+init_per_group(GroupName, Config) ->
+ [{init_per_group,now()}|Config].
+
+end_per_group(GroupName, Config) ->
+ ok.
+
+all() ->
+ [{group,group1}].
+
+groups() ->
+ [{group1,[],[test_case]}].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl new file mode 100644 index 0000000000..5d07cd3dea --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl @@ -0,0 +1,278 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%% @doc Common Test Example Suite Callback module.
+%%%
+%%% <p>This module gives an example of a common test CTH (Common Test Hook).
+%%% There are many ways to add a CTH to a test run, you can do it either in
+%%% the command line using -ct_hook, in a test spec using
+%%% {ct_hook,M} or in the suite it self by returning ct_hook
+%%% from either suite/0, init_per_suite/1, init_per_group/2 and
+%%% init_per_testcase/2. The scope of the CTH is determined by where is it
+%%% started. If it is started in the command line or test spec then it will
+%%% be stopped at the end of all tests. If it is started in init_per_suite,
+%%% it will be stopped after end_per_suite and so on. See terminate
+%%% documentation for a table describing the scoping machanics.
+%%%
+%%% All of callbacks except init/1 in a CTH are optional.</p>
+
+-module(empty_cth).
+
+%% CT Hooks
+-export([id/1]).
+-export([init/2]).
+
+-export([pre_init_per_suite/3]).
+-export([post_init_per_suite/4]).
+-export([pre_end_per_suite/3]).
+-export([post_end_per_suite/4]).
+
+-export([pre_init_per_group/3]).
+-export([post_init_per_group/4]).
+-export([pre_end_per_group/3]).
+-export([post_end_per_group/4]).
+
+-export([pre_init_per_testcase/3]).
+-export([post_end_per_testcase/4]).
+
+-export([on_tc_fail/3]).
+-export([on_tc_skip/3]).
+
+-export([terminate/1]).
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-type proplist() :: list({atom(),term()}).
+-type config() :: proplist().
+-type reason() :: term().
+-type skip_or_fail() :: {skip, reason()} |
+ {auto_skip, reason()} |
+ {fail, reason()} |
+ {'EXIT',reason()}.
+
+-record(state, { id = ?MODULE :: term()}).
+
+%% @doc Always called before any other callback function. Use this to initiate
+%% any common state. It should return an state for this CTH.
+-spec init(Id :: term(), Opts :: proplist()) ->
+ State :: #state{}.
+init(Id, Opts) ->
+ gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, init, [Id, Opts]}}),
+ Opts.
+
+%% @doc The ID is used to uniquly identify an CTH instance, if two CTH's
+%% return the same ID the seconds CTH is ignored. This function should NOT
+%% have any side effects as it might be called multiple times by common test.
+-spec id(Opts :: proplist()) ->
+ Id :: term().
+id(Opts) ->
+ gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, id, [Opts]}}),
+ now().
+
+%% @doc Called before init_per_suite is called. Note that this callback is
+%% only called if the CTH is added before init_per_suite is run (eg. in a test
+%% specification, suite/0 function etc).
+%% You can change the config in the this function.
+-spec pre_init_per_suite(Suite :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+pre_init_per_suite(Suite,Config,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, pre_init_per_suite,
+ [Suite,Config,State]}}),
+ {Config, State}.
+
+%% @doc Called after init_per_suite.
+%% you can change the return value in this function.
+-spec post_init_per_suite(Suite :: atom(),
+ Config :: config(),
+ Return :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+post_init_per_suite(Suite,Config,Return,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_init_per_suite,
+ [Suite,Config,Return,State]}}),
+ {Return, State}.
+
+%% @doc Called before end_per_suite. The config/state can be changed here,
+%% though it will only affect the *end_per_suite function.
+-spec pre_end_per_suite(Suite :: atom(),
+ Config :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+pre_end_per_suite(Suite,Config,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, pre_end_per_suite,
+ [Suite,Config,State]}}),
+ {Config, State}.
+
+%% @doc Called after end_per_suite. Note that the config cannot be
+%% changed here, only the status of the suite.
+-spec post_end_per_suite(Suite :: atom(),
+ Config :: config(),
+ Return :: term(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+post_end_per_suite(Suite,Config,Return,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_end_per_suite,
+ [Suite,Config,Return,State]}}),
+ {Return, State}.
+
+%% @doc Called before each init_per_group.
+%% You can change the config in this function.
+-spec pre_init_per_group(Group :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+pre_init_per_group(Group,Config,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, pre_init_per_group,
+ [Group,Config,State]}}),
+ {Config, State}.
+
+%% @doc Called after each init_per_group.
+%% You can change the return value in this function.
+-spec post_init_per_group(Group :: atom(),
+ Config :: config(),
+ Return :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+post_init_per_group(Group,Config,Return,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_init_per_group,
+ [Group,Config,Return,State]}}),
+ {Return, State}.
+
+%% @doc Called after each end_per_group. The config/state can be changed here,
+%% though it will only affect the *end_per_group functions.
+-spec pre_end_per_group(Group :: atom(),
+ Config :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+pre_end_per_group(Group,Config,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, pre_end_per_group,
+ [Group,Config,State]}}),
+ {Config, State}.
+
+%% @doc Called after each end_per_group. Note that the config cannot be
+%% changed here, only the status of the group.
+-spec post_end_per_group(Group :: atom(),
+ Config :: config(),
+ Return :: term(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+post_end_per_group(Group,Config,Return,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_end_per_group,
+ [Group,Config,Return,State]}}),
+ {Return, State}.
+
+%% @doc Called before each test case.
+%% You can change the config in this function.
+-spec pre_init_per_testcase(TC :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+pre_init_per_testcase(TC,Config,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, pre_init_per_testcase,
+ [TC,Config,State]}}),
+ {Config, State}.
+
+%% @doc Called after each test case. Note that the config cannot be
+%% changed here, only the status of the test case.
+-spec post_end_per_testcase(TC :: atom(),
+ Config :: config(),
+ Return :: term(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+post_end_per_testcase(TC,Config,Return,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_end_per_testcase,
+ [TC,Config,Return,State]}}),
+ {Return, State}.
+
+%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group,
+%% post_end_per_group and post_end_per_tc if the suite, group or test case failed.
+%% This function should be used for extra cleanup which might be needed.
+%% It is not possible to modify the config or the status of the test run.
+-spec on_tc_fail(TC :: init_per_suite | end_per_suite |
+ init_per_group | end_per_group | atom(),
+ Reason :: term(), State :: #state{}) ->
+ NewState :: #state{}.
+on_tc_fail(TC, Reason, State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, on_tc_fail,
+ [TC,Reason,State]}}),
+ State.
+
+%% @doc Called when a test case is skipped by either user action
+%% or due to an init function failing. Test case can be
+%% end_per_suite, init_per_group, end_per_group and the actual test cases.
+-spec on_tc_skip(TC :: end_per_suite |
+ init_per_group | end_per_group | atom(),
+ {tc_auto_skip, {failed, {Mod :: atom(), Function :: atom(), Reason :: term()}}} |
+ {tc_user_skip, {skipped, Reason :: term()}},
+ State :: #state{}) ->
+ NewState :: #state{}.
+on_tc_skip(TC, Reason, State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, on_tc_skip,
+ [TC,Reason,State]}}),
+ State.
+
+%% @doc Called when the scope of the CTH is done, this depends on
+%% when the CTH was specified. This translation table describes when this
+%% function is called.
+%%
+%% | Started in | terminate called |
+%% |---------------------|-------------------------|
+%% | command_line | after all tests are run |
+%% | test spec | after all tests are run |
+%% | suite/0 | after SUITE is done |
+%% | init_per_suite/1 | after SUITE is done |
+%% | init_per_group/2 | after group is done |
+%% |-----------------------------------------------|
+%%
+-spec terminate(State :: #state{}) ->
+ term().
+terminate(State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, terminate, [State]}}),
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_suite_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_suite_cth.erl new file mode 100644 index 0000000000..b4c26259a6 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_suite_cth.erl @@ -0,0 +1,72 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(fail_post_suite_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State),
+ {{fail, "Test failure"}, State}.
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_pre_suite_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_pre_suite_cth.erl new file mode 100644 index 0000000000..acf80a1b2e --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_pre_suite_cth.erl @@ -0,0 +1,72 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(fail_pre_suite_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State),
+ {{fail, "Test failure"}, State}.
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/id_no_init_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/id_no_init_cth.erl new file mode 100644 index 0000000000..58ed400e1c --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/id_no_init_cth.erl @@ -0,0 +1,32 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(id_no_init_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-export([id/1]).
+
+id(Opts) ->
+ empty_cth:id(Opts).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_cth.erl new file mode 100644 index 0000000000..a18f4bf2f3 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_cth.erl @@ -0,0 +1,33 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(minimal_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-export([init/2]).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_terminate_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_terminate_cth.erl new file mode 100644 index 0000000000..79cd55f68e --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_terminate_cth.erl @@ -0,0 +1,38 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(minimal_terminate_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-export([init/2]).
+-export([terminate/1]).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+terminate(State) ->
+ empty_cth:terminate(State).
+
+
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/recover_post_suite_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/recover_post_suite_cth.erl new file mode 100644 index 0000000000..01a932bd59 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/recover_post_suite_cth.erl @@ -0,0 +1,74 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(recover_post_suite_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State).
+
+post_init_per_suite(Suite,Config,{'EXIT',Reason} = Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State),
+ {lists:keydelete(tc_status,1,Config),State};
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/same_id_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/same_id_cth.erl new file mode 100644 index 0000000000..acfb93fe26 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/same_id_cth.erl @@ -0,0 +1,75 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(same_id_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+id(Opts) ->
+ empty_cth:id(Opts),
+ ?MODULE.
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_post_suite_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_post_suite_cth.erl new file mode 100644 index 0000000000..6d4605b33b --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_post_suite_cth.erl @@ -0,0 +1,72 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(skip_post_suite_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State),
+ {{skip, "Test skip"}, State}.
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_suite_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_suite_cth.erl new file mode 100644 index 0000000000..49efd0d0cd --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_suite_cth.erl @@ -0,0 +1,73 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(skip_pre_suite_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State),
+ {{skip, "Test skip"}, State}.
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/state_update_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/state_update_cth.erl new file mode 100644 index 0000000000..53d75e6ce3 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/state_update_cth.erl @@ -0,0 +1,83 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(state_update_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ State = empty_cth:init(Id, Opts),
+ [init|State].
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State),
+ {Config, [pre_init_per_suite|State]}.
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State),
+ {Config, [post_init_per_suite|State]}.
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State),
+ {Config, [pre_end_per_suite|State]}.
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State),
+ {Return, [post_end_per_suite|State]}.
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State),
+ {Config, [pre_init_per_group|State]}.
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State),
+ {Return, [post_init_per_group|State]}.
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State),
+ {Config, [pre_end_per_group|State]}.
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State),
+ {Return, [post_end_per_group|State]}.
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State),
+ {Config, [pre_init_per_testcase|State]}.
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State),
+ {Return, [post_end_per_testcase|State]}.
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State),
+ [on_tc_fail|State].
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State),
+ [on_tc_skip|State].
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/undef_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/undef_cth.erl new file mode 100644 index 0000000000..4c44ef025b --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/undef_cth.erl @@ -0,0 +1,71 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(undef_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(_Suite, _Config, _State) ->
+ lists:flaten([1,2,[3,4]]).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl new file mode 100644 index 0000000000..788ef2cec2 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl @@ -0,0 +1,82 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(update_config_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State),
+ {[{pre_init_per_suite,now()}|Config],State}.
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State),
+ {[{post_init_per_suite,now()}|Return],State}.
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State),
+ {[{pre_end_per_suite,now()}|Config],State}.
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State),
+ NewConfig = [{post_end_per_suite,now()}|Config],
+ {NewConfig,NewConfig}.
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State),
+ {[{pre_init_per_group,now()}|Config],State}.
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State),
+ {[{post_init_per_group,now()}|Return],State}.
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State),
+ {[{pre_end_per_group,now()}|Config],State}.
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State),
+ {[{post_end_per_group,now()}|Config],State}.
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State),
+ {[{pre_init_per_testcase,now()}|Config],State}.
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State),
+ {[{post_end_per_testcase,now()}|Config],State}.
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl index 5e9792f02c..b4f1a0e71f 100644 --- a/lib/common_test/test/ct_test_support.erl +++ b/lib/common_test/test/ct_test_support.erl @@ -58,6 +58,10 @@ init_per_suite(Config, Level) -> _ -> ok end, + + start_slave(Config, Level). + +start_slave(Config,Level) -> [_,Host] = string:tokens(atom_to_list(node()), "@"), test_server:format(0, "Trying to start ~s~n", ["ct@"++Host]), @@ -136,9 +140,16 @@ init_per_testcase(_TestCase, Config) -> end_per_testcase(_TestCase, Config) -> CTNode = ?config(ct_node, Config), - wait_for_ct_stop(CTNode), - ok. - + case wait_for_ct_stop(CTNode) of + %% Common test was not stopped to we restart node. + false -> + cover:stop(CTNode), + slave:stop(CTNode), + start_slave(Config,proplists:get_value(trace_level,Config)), + {fail, "Could not stop common_test"}; + true -> + ok + end. %%%----------------------------------------------------------------- %%% @@ -229,11 +240,11 @@ wait_for_ct_stop(CTNode) -> wait_for_ct_stop(0, CTNode) -> test_server:format(0, "Giving up! Stopping ~p.", [CTNode]), - ok; + false; wait_for_ct_stop(Retries, CTNode) -> case rpc:call(CTNode, erlang, whereis, [ct_util_server]) of undefined -> - ok; + true; Pid -> test_server:format(0, "Waiting for CT (~p) to finish (~p)...", [Pid,Retries]), @@ -876,22 +887,49 @@ locate({TEH,tc_done,{undefined,undefined,{testcase_aborted, nomatch end; -%% matches any event of type Name -locate({TEH,Name,Data}, Node, [Ev|Evs], Config) when Data == '_' -> - case Ev of - {TEH,#event{name=Name, node=Node}} -> - {Config,Evs}; +%% Negative matching: Given two events, the first should not be present before +%% the other is matched. +locate({negative,NotMatch, Match} = Neg, Node, Evs, Config) -> + case locate(NotMatch, Node, Evs, Config) of + nomatch -> + locate(Match, Node, Evs, Config); _ -> - nomatch + exit({found_negative_event,Neg}) end; -locate({TEH,Name,Data}, Node, [Ev|Evs], Config) -> - case Ev of - {TEH,#event{name=Name, node=Node, data=Data}} -> - {Config,Evs}; - _ -> +%% matches any event of type Name +locate({TEH,Name,Data}, Node, [{TEH,#event{name=Name, + data = EvData, + node = Node}}|Evs], + Config) -> + try match_data(Data, EvData) of + match -> + {Config,Evs} + catch _:_ -> nomatch - end. + end; + +locate({_TEH,_Name,_Data}, _Node, [_|_Evs], _Config) -> + nomatch. + +match_data(D,D) -> + match; +match_data('_',_) -> + match; +match_data(Fun,Data) when is_function(Fun) -> + Fun(Data); +match_data('$proplist',Proplist) -> + match_data( + fun(List) -> + lists:foreach(fun({_,_}) -> ok end,List) + end,Proplist); +match_data([H1|MatchT],[H2|ValT]) -> + match_data(H1,H2), + match_data(MatchT,ValT); +match_data(Tuple1,Tuple2) when is_tuple(Tuple1),is_tuple(Tuple2) -> + match_data(tuple_to_list(Tuple1),tuple_to_list(Tuple2)); +match_data([],[]) -> + match. log_events(TC, Events, PrivDir) -> LogFile = filename:join(PrivDir, atom_to_list(TC)++".events"), diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk index 413ef21df3..1a820848b5 100644 --- a/lib/common_test/vsn.mk +++ b/lib/common_test/vsn.mk @@ -1,3 +1,3 @@ -COMMON_TEST_VSN = 1.5.1 +COMMON_TEST_VSN = 1.5.2 diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml index 00ea0da55c..9d89b17afb 100644 --- a/lib/compiler/doc/src/notes.xml +++ b/lib/compiler/doc/src/notes.xml @@ -31,6 +31,56 @@ <p>This document describes the changes made to the Compiler application.</p> +<section><title>Compiler 4.7.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Two compiler bugs (that would cause the compiler to + terminate) reported by Christopher Williams have been + fixed.</p> + <p> + Own Id: OTP-8949</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>The compiler would translate binary comprehensions + containing tail segments in a way that would would + confuse Dialyzer. For instance:</p> + <p><c>[42 || <<_:8/integer, _/bits>> <= + Bits]</c></p> + <p> + would produce a Dialyzer warning.</p> + <p> + Own Id: OTP-8864</p> + </item> + <item> + <p> + Code such as <c>foo(A) -> <<A:0>></c> + would crash the compiler.</p> + <p> + Own Id: OTP-8865</p> + </item> + <item> + <p> + The compiler could fail with an internal error when + variables were exported from a receive block but the + return value of the receive block were not used. (Thanks + to Jim Engquist for reporting this error.)</p> + <p> + Own Id: OTP-8888</p> + </item> + </list> + </section> + +</section> + <section><title>Compiler 4.7.1</title> <section><title>Improvements and New Features</title> diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile index 0f6d2f6193..9da9253f5b 100644 --- a/lib/compiler/src/Makefile +++ b/lib/compiler/src/Makefile @@ -118,7 +118,9 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE) ifeq ($(NATIVE_LIBS_ENABLED),yes) ERL_COMPILE_FLAGS += +native endif -ERL_COMPILE_FLAGS += +inline +warn_unused_import -I../../stdlib/include -I$(EGEN) -W +ERL_COMPILE_FLAGS += +inline +warn_unused_import \ + +warnings_as_errors \ + -I../../stdlib/include -I$(EGEN) -W # ---------------------------------------------------- # Targets diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index 77da6c8d00..f24a46c5a9 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -235,7 +235,7 @@ match_cg(M, Rs, Le, Vdb, Bef, St0) -> I = Le#l.i, {Sis,Int0} = adjust_stack(Bef, I, I+1, Vdb), {B,St1} = new_label(St0), - {Mis,Int1,St2} = match_cg(M, St0#cg.ultimate_failure, + {Mis,Int1,St2} = match_cg(M, St1#cg.ultimate_failure, Int0, St1#cg{break=B}), %% Put return values in registers. Reg = load_vars(Rs, Int1#sr.reg), diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index fbe4d8617e..3b33a08cf7 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -147,6 +147,7 @@ attributes([]) -> []. include_attribute(type) -> false; include_attribute(spec) -> false; include_attribute(opaque) -> false; +include_attribute(export_type) -> false; include_attribute(_) -> true. function({#c_var{name={F,Arity}=FA},Body}, St0) -> diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl index a300dd283f..9bd13f7032 100644 --- a/lib/compiler/src/v3_kernel_pp.erl +++ b/lib/compiler/src/v3_kernel_pp.erl @@ -50,28 +50,34 @@ format(Node) -> format(Node, #ctxt{}). format(Node, Ctxt) -> case canno(Node) of -%% [] -> -%% format_1(Node, Ctxt); -%% [L,{file,_}] when is_integer(L) -> -%% format_1(Node, Ctxt); -%% #k{a=Anno}=K when Anno =/= [] -> -%% format(setelement(2, Node, K#k{a=[]}), Ctxt); -%% List -> -%% format_anno(List, Ctxt, fun (Ctxt1) -> -%% format_1(Node, Ctxt1) -%% end); - _ -> - format_1(Node, Ctxt) + [] -> + format_1(Node, Ctxt); + [L,{file,_}] when is_integer(L) -> + format_1(Node, Ctxt); + #k{a=Anno}=K when Anno =/= [] -> + format(setelement(2, Node, K#k{a=[]}), Ctxt); + List -> + format_anno(List, Ctxt, fun (Ctxt1) -> + format_1(Node, Ctxt1) + end) end. -%% format_anno(Anno, Ctxt0, ObjFun) -> -%% Ctxt1 = ctxt_bump_indent(Ctxt0, 1), -%% ["( ", -%% ObjFun(Ctxt0), -%% nl_indent(Ctxt1), -%% "-| ",io_lib:write(Anno), -%% " )"]. - +format_anno(Anno, Ctxt0, ObjFun) -> + case annotations_enabled() of + true -> + Ctxt1 = ctxt_bump_indent(Ctxt0, 1), + ["( ", + ObjFun(Ctxt0), + nl_indent(Ctxt1), + "-| ",io_lib:write(Anno), + " )"]; + false -> + ObjFun(Ctxt0) + end. + +%% By default, don't show annotations since they clutter up the output. +annotations_enabled() -> + false. %% format_1(Kexpr, Context) -> string(). @@ -107,6 +113,8 @@ format_1(#k_bin_int{size=Sz,unit=U,flags=Fs,val=Val,next=Next}, Ctxt) -> [format_bin_seg_1(S, Ctxt), format_bin_seg(Next, ctxt_bump_indent(Ctxt, 2))]; format_1(#k_bin_end{}, _Ctxt) -> "#<>#"; +format_1(#k_literal{val=Term}, _Ctxt) -> + io_lib:format("~p", [Term]); format_1(#k_local{name=N,arity=A}, Ctxt) -> "local " ++ format_fa_pair({N,A}, Ctxt); format_1(#k_remote{mod=M,name=N,arity=A}, _Ctxt) -> diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk index 4658eccd19..d180ecd4e2 100644 --- a/lib/compiler/vsn.mk +++ b/lib/compiler/vsn.mk @@ -1 +1 @@ -COMPILER_VSN = 4.7.1 +COMPILER_VSN = 4.7.2 diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml index 3c571eb2a3..54dd0cb01f 100644 --- a/lib/crypto/doc/src/notes.xml +++ b/lib/crypto/doc/src/notes.xml @@ -30,6 +30,21 @@ </header> <p>This document describes the changes made to the Crypto application.</p> +<section><title>Crypto 2.0.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + AES CTR encryption support in <c>crypto</c>.</p> + <p> + Own Id: OTP-8752 Aux Id: seq11642 </p> + </item> + </list> + </section> + +</section> + <section><title>Crypto 2.0.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk index e3549f0c50..4b35c7c0b4 100644 --- a/lib/crypto/vsn.mk +++ b/lib/crypto/vsn.mk @@ -1 +1 @@ -CRYPTO_VSN = 2.0.1 +CRYPTO_VSN = 2.0.2 diff --git a/lib/debugger/doc/src/notes.xml b/lib/debugger/doc/src/notes.xml index c72a5271ba..2f8bdc36a1 100644 --- a/lib/debugger/doc/src/notes.xml +++ b/lib/debugger/doc/src/notes.xml @@ -32,6 +32,20 @@ <p>This document describes the changes made to the Debugger application.</p> +<section><title>Debugger 3.2.5</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>Miscellaneous updates</p> + <p> + Own Id: OTP-8976</p> + </item> + </list> + </section> + +</section> + <section><title>Debugger 3.2.4</title> <section><title>Improvements and New Features</title> diff --git a/lib/debugger/vsn.mk b/lib/debugger/vsn.mk index 654dc11e20..b9786b4a75 100644 --- a/lib/debugger/vsn.mk +++ b/lib/debugger/vsn.mk @@ -1 +1 @@ -DEBUGGER_VSN = 3.2.4 +DEBUGGER_VSN = 3.2.5 diff --git a/lib/dialyzer/RELEASE_NOTES b/lib/dialyzer/RELEASE_NOTES index 08f274a996..3fd5e9cc7d 100644 --- a/lib/dialyzer/RELEASE_NOTES +++ b/lib/dialyzer/RELEASE_NOTES @@ -3,8 +3,13 @@ (in reversed chronological order) ============================================================================== -Version 2.x.x (in Erlang/OTP R14B01) +Version 2.4.0 (in Erlang/OTP R14B01) ------------------------------------ + - Added ability to supply multiple PLTs for the analysis (option --plts). + Currently these PLTs must be independent (i.e., no module appears in more + than one PLT) and there must not include files with module name clashes. + - Strengthened and streamlined hard-coded type information for some BIFs + and key library functions. - Fixed pretty rare infinite loop when refining the types of an SCC whose functions all returned none() (thanks to Stavros Aronis). - Fixed pretty rare crash when taking the infimum of two tuple_sets. diff --git a/lib/dialyzer/doc/manual.txt b/lib/dialyzer/doc/manual.txt index d9cb52f722..cc6f9130c7 100644 --- a/lib/dialyzer/doc/manual.txt +++ b/lib/dialyzer/doc/manual.txt @@ -123,9 +123,9 @@ The exit status of the command line version is: Usage: dialyzer [--help] [--version] [--shell] [--quiet] [--verbose] - [-pa dir]* [--plt plt] [--plts plts] [-Ddefine]* + [-pa dir]* [--plt plt] [--plts plt*] [-Ddefine]* [-I include_dir]* [--output_plt file] [-Wwarn]* - [--src] [--gui | --wx] [files_or_dirs] [-r dirs] + [--src] [--gui | --wx] [files_or_dirs] [-r dirs] [--apps applications] [-o outfile] [--build_plt] [--add_to_plt] [--remove_from_plt] [--check_plt] [--no_check_plt] [--plt_info] [--get_warnings] @@ -135,63 +135,75 @@ Options: files_or_dirs (for backwards compatibility also as: -c files_or_dirs) Use Dialyzer from the command line to detect defects in the specified files or directories containing .erl or .beam files, - depending on the type of the analysis + depending on the type of the analysis. -r dirs Same as the previous but the specified directories are searched recursively for subdirectories containing .erl or .beam files in - them, depending on the type of analysis + them, depending on the type of analysis. --apps applications - Option typically used when building or modifying PLT as in: + Option typically used when building or modifying a plt as in: dialyzer --build_plt --apps erts kernel stdlib mnesia ... to conveniently refer to library applications corresponding to the Erlang/OTP installation. However, the option is general and can also be used during analysis in order to refer to Erlang/OTP applications. In addition, file or directory names can also be included, as in: dialyzer --apps inets ssl ./ebin ../other_lib/ebin/my_module.beam + -o outfile (or --output outfile) + When using Dialyzer from the command line, send the analysis + results to the specified outfile rather than to stdout. --raw When using Dialyzer from the command line, output the raw analysis results (Erlang terms) instead of the formatted result. The raw format is easier to post-process (for instance, to filter - warnings or to output HTML pages) + warnings or to output HTML pages). --src - Override the default, which is to analyze BEAM bytecode, and - analyze starting from Erlang source code instead + Override the default, which is to analyze BEAM files, and + analyze starting from Erlang source code instead. -Dname (or -Dname=value) - When analyzing from source, pass the define to Dialyzer (**) + When analyzing from source, pass the define to Dialyzer. (**) -I include_dir - When analyzing from source, pass the include_dir to Dialyzer (**) + When analyzing from source, pass the include_dir to Dialyzer. (**) -pa dir Include dir in the path for Erlang (useful when analyzing files - that have '-include_lib()' directives) + that have '-include_lib()' directives). --output_plt file - Store the plt at the specified file after building it + Store the plt at the specified file after building it. --plt plt Use the specified plt as the initial plt (if the plt was built - during setup the files will be checked for consistency) - --plts plts - Merges the specified plts to create the initial plt -- requires - that the plts are disjoint (i.e., do not have any module - appearing in more than one plt) + during setup the files will be checked for consistency). + --plts plt* + Merge the specified plts to create the initial plt -- requires + that the plts are disjoint (i.e., do not have any module + appearing in more than one plt). + The plts are created in the usual way: + dialyzer --build_plt --output_plt plt_1 files_to_include + ... + dialyzer --build_plt --output_plt plt_n files_to_include + and then can be used in either of the following ways: + dialyzer files_to_analyze --plts plt_1 ... plt_n + or: + dialyzer --plts plt_1 ... plt_n -- files_to_analyze + (Note the -- delimiter in the second case) -Wwarn A family of options which selectively turn on/off warnings - (for help on the names of warnings use dialyzer -Whelp) + (for help on the names of warnings use dialyzer -Whelp). --shell - Do not disable the Erlang shell while running the GUI + Do not disable the Erlang shell while running the GUI. --version (or -v) - Prints the Dialyzer version and some more information and exits + Print the Dialyzer version and some more information and exit. --help (or -h) - Prints this message and exits + Print this message and exit. --quiet (or -q) - Makes Dialyzer a bit more quiet + Make Dialyzer a bit more quiet. --verbose - Makes Dialyzer a bit more verbose + Make Dialyzer a bit more verbose. --build_plt The analysis starts from an empty plt and creates a new one from the files specified with -c and -r. Only works for beam files. Use --plt or --output_plt to override the default plt location. --add_to_plt The plt is extended to also include the files specified with -c and -r. - Use --plt to specify wich plt to start from, and --output_plt to + Use --plt to specify which plt to start from, and --output_plt to specify where to put the plt. Note that the analysis might include files from the plt if they depend on the new files. This option only works with beam files. @@ -200,23 +212,23 @@ Options: from the plt. Note that this may cause a re-analysis of the remaining dependent files. --check_plt - Checks the plt for consistency and rebuilds it if it is not up-to-date. + Check the plt for consistency and rebuild it if it is not up-to-date. --no_check_plt Skip the plt check when running Dialyzer. Useful when working with installed plts that never change. --plt_info - Makes Dialyzer print information about the plt and then quit. The plt - can be specified with --plt. + Make Dialyzer print information about the plt and then quit. The plt + can be specified with --plt(s). --get_warnings - Makes Dialyzer emit warnings even when manipulating the plt. Only - emits warnings for files that are actually analyzed. + Make Dialyzer emit warnings even when manipulating the plt. Warnings + are only emitted for files that are actually analyzed. --dump_callgraph file Dump the call graph into the specified file whose format is determined by the file name extension. Supported extensions are: raw, dot, and ps. If something else is used as file name extension, default format '.raw' will be used. --no_native (or -nn) - Bypass the native code compilation of some key files that dialyzer + Bypass the native code compilation of some key files that Dialyzer heuristically performs when dialyzing many files; this avoids the compilation time but it may result in (much) longer analysis time. --gui @@ -236,12 +248,17 @@ Warning options: Suppress warnings for unused functions. -Wno_improper_lists Suppress warnings for construction of improper lists. + -Wno_tuple_as_fun + Suppress warnings for using tuples instead of funs. -Wno_fun_app Suppress warnings for fun applications that will fail. -Wno_match Suppress warnings for patterns that are unused or cannot match. + -Wno_opaque + Suppress warnings for violations of opaqueness of data types. -Wunmatched_returns *** - Include warnings for function calls which ignore the return value(s). + Include warnings for function calls which ignore a structured return + value or do not match against one of many possible return value(s). -Werror_handling *** Include warnings for functions that only return by means of an exception. -Wrace_conditions *** @@ -262,7 +279,7 @@ The following options are also available but their use is not recommended: Warn when the -spec is different than the success typing. Note: - *** These are options that turn on warnings rather than turning them off. + *** Identifies options that turn on warnings rather than turning them off. ----------------------------------------------- diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml index 29308885fd..01a7e478bc 100644 --- a/lib/dialyzer/doc/src/dialyzer.xml +++ b/lib/dialyzer/doc/src/dialyzer.xml @@ -64,81 +64,144 @@ ]]></code> <p>Usage:</p> <code type="none"><![CDATA[ - dialyzer [--help] [--version] [--shell] [--quiet] [--verbose] - [-pa dir]* [--plt plt] [-Ddefine]* [-I include_dir]* - [--output_plt file] [-Wwarn]* [--src] - [-c applications] [-r applications] [-o outfile] + dialyzer [--help] [--version] [--shell] [--quiet] [--verbose] + [-pa dir]* [--plt plt] [--plts plt*] [-Ddefine]* + [-I include_dir]* [--output_plt file] [-Wwarn]* + [--src] [--gui | --wx] [files_or_dirs] [-r dirs] + [--apps applications] [-o outfile] [--build_plt] [--add_to_plt] [--remove_from_plt] [--check_plt] [--no_check_plt] [--plt_info] [--get_warnings] + [--no_native] ]]></code> <p>Options:</p> <taglist> - <tag><c><![CDATA[-c applications]]></c>(or <c><![CDATA[--command-line applications]]></c>)</tag> - <item>use Dialyzer from the command line (no GUI) to detect defects in the - specified applications (directories or <c><![CDATA[.erl]]></c> or <c><![CDATA[.beam]]></c> files)</item> - <tag><c><![CDATA[-r applications]]></c></tag> - <item>same as <c><![CDATA[-c]]></c> only that directories are searched recursively for - subdirectories containing <c><![CDATA[.erl]]></c> or <c><![CDATA[.beam]]></c> files (depending on the - type of analysis)</item> - <tag><c><![CDATA[-o outfile]]></c>(or <c><![CDATA[--output outfile]]></c>)</tag> - <item>when using Dialyzer from the command line, send the analysis - results in the specified <c><![CDATA[outfile]]></c> rather than in stdout</item> - <tag><c><![CDATA[--src]]></c></tag> - <item>override the default, which is to analyze debug compiled BEAM - bytecode, and analyze starting from Erlang source code instead</item> + <tag><c><![CDATA[files_or_dirs]]></c> (for backwards compatibility also + as: <c><![CDATA[-c files_or_dirs]]></c></tag> + <item>Use Dialyzer from the command line to detect defects in the + specified files or directories containing <c><![CDATA[.erl]]></c> or + <c><![CDATA[.beam]]></c> files, depending on the type of the + analysis.</item> + <tag><c><![CDATA[-r dirs]]></c></tag> + <item>Same as the previous but the specified directories are searched + recursively for subdirectories containing <c><![CDATA[.erl]]></c> or + <c><![CDATA[.beam]]></c> files in them, depending on the type of + analysis.</item> + <tag><c><![CDATA[--apps applications]]></c></tag> + <item>Option typically used when building or modifying a plt as in: + <code type="none"><![CDATA[ + dialyzer --build_plt --apps erts kernel stdlib mnesia ... + ]]></code> + to conveniently refer to library applications corresponding to the + Erlang/OTP installation. However, the option is general and can also + be used during analysis in order to refer to Erlang/OTP applications. + In addition, file or directory names can also be included, as in: + <code type="none"><![CDATA[ + dialyzer --apps inets ssl ./ebin ../other_lib/ebin/my_module.beam + ]]></code></item> + <tag><c><![CDATA[-o outfile]]></c> (or + <c><![CDATA[--output outfile]]></c>)</tag> + <item>When using Dialyzer from the command line, send the analysis + results to the specified outfile rather than to stdout.</item> <tag><c><![CDATA[--raw]]></c></tag> <item>When using Dialyzer from the command line, output the raw analysis - results (Erlang terms) instead of the formatted result. - The raw format is easier to post-process (for instance, to filter - warnings or to output HTML pages).</item> - <tag><c><![CDATA[-Dname]]></c>(or <c><![CDATA[-Dname=value]]></c>)</tag> - <item>when analyzing from source, pass the define to Dialyzer (**)</item> + results (Erlang terms) instead of the formatted result. The raw format + is easier to post-process (for instance, to filter warnings or to + output HTML pages).</item> + <tag><c><![CDATA[--src]]></c></tag> + <item>Override the default, which is to analyze BEAM files, and + analyze starting from Erlang source code instead.</item> + <tag><c><![CDATA[-Dname]]></c> (or <c><![CDATA[-Dname=value]]></c>)</tag> + <item>When analyzing from source, pass the define to Dialyzer. (**)</item> <tag><c><![CDATA[-I include_dir]]></c></tag> - <item>when analyzing from source, pass the <c><![CDATA[include_dir]]></c> to Dialyzer (**)</item> + <item>When analyzing from source, pass the <c><![CDATA[include_dir]]></c> + to Dialyzer. (**)</item> <tag><c><![CDATA[-pa dir]]></c></tag> - <item>Include <c><![CDATA[dir]]></c> in the path for Erlang. Useful when analyzing files - that have <c><![CDATA[-include_lib()]]></c> directives.</item> + <item>Include <c><![CDATA[dir]]></c> in the path for Erlang (useful when + analyzing files that have <c><![CDATA['-include_lib()']]></c> + directives).</item> <tag><c><![CDATA[--output_plt file]]></c></tag> - <item>Store the PLT at the specified location after building it.</item> + <item>Store the plt at the specified file after building it.</item> <tag><c><![CDATA[--plt plt]]></c></tag> - <item>Use the specified PLT as the initial persistent lookup table.</item> + <item>Use the specified plt as the initial plt (if the plt was built + during setup the files will be checked for consistency).</item> + <tag><c><![CDATA[--plts plt*]]></c></tag> + <item>Merge the specified plts to create the initial plt -- requires + that the plts are disjoint (i.e., do not have any module + appearing in more than one plt). + The plts are created in the usual way: + <code type="none"><![CDATA[ + dialyzer --build_plt --output_plt plt_1 files_to_include + ... + dialyzer --build_plt --output_plt plt_n files_to_include + ]]></code> + and then can be used in either of the following ways: + <code type="none"><![CDATA[ + dialyzer files_to_analyze --plts plt_1 ... plt_n + ]]></code> + or: + <code type="none"><![CDATA[ + dialyzer --plts plt_1 ... plt_n -- files_to_analyze + ]]></code> + (Note the -- delimiter in the second case)</item> <tag><c><![CDATA[-Wwarn]]></c></tag> - <item>a family of options which selectively turn on/off warnings. - (for help on the names of warnings use <c><![CDATA[dialyzer -Whelp]]></c>)</item> + <item>A family of options which selectively turn on/off warnings + (for help on the names of warnings use + <c><![CDATA[dialyzer -Whelp]]></c>).</item> <tag><c><![CDATA[--shell]]></c></tag> - <item>do not disable the Erlang shell while running the GUI</item> - <tag><c><![CDATA[--version (or -v)]]></c></tag> - <item>prints the Dialyzer version and some more information and exits</item> - <tag><c><![CDATA[--help (or -h)]]></c></tag> - <item>prints this message and exits</item> - <tag><c><![CDATA[--quiet (or -q)]]></c></tag> - <item>makes Dialyzer a bit more quiet</item> + <item>Do not disable the Erlang shell while running the GUI.</item> + <tag><c><![CDATA[--version]]></c> (or <c><![CDATA[-v]]></c>)</tag> + <item>Print the Dialyzer version and some more information and + exit.</item> + <tag><c><![CDATA[--help]]></c> (or <c><![CDATA[-h]]></c>)</tag> + <item>Print this message and exit.</item> + <tag><c><![CDATA[--quiet]]></c> (or <c><![CDATA[-q]]></c>)</tag> + <item>Make Dialyzer a bit more quiet.</item> <tag><c><![CDATA[--verbose]]></c></tag> - <item>makes Dialyzer a bit more verbose</item> - <tag><c><![CDATA[--check_plt]]></c></tag> - <item>Only checks if the initial PLT is up to date and rebuilds it if this is not the case</item> - <tag><c><![CDATA[--no_check_plt (or -n)]]></c></tag> - <item>Skip the PLT integrity check when running Dialyzer. - Useful when working with installed PLTs that never change.</item> + <item>Make Dialyzer a bit more verbose.</item> <tag><c><![CDATA[--build_plt]]></c></tag> - <item>The analysis starts from an empty PLT and creates a new one from - the files specified with -c and -r. Only works for beam files. - Use --plt or --output_plt to override the default PLT location.</item> - <tag><c><![CDATA[--add_to_plt]]></c></tag> - <item> The PLT is extended to also include the files specified with - -c and -r. Use --plt to specify which PLT to start from, and --output_plt - to specify where to put the PLT. Note that the analysis might include - files from the PLT if they depend on the new files. - This option only works with beam files.</item> + <item>The analysis starts from an empty plt and creates a new one from + the files specified with <c><![CDATA[-c]]></c> and + <c><![CDATA[-r]]></c>. Only works for beam files. Use + <c><![CDATA[--plt]]></c> or <c><![CDATA[--output_plt]]></c> to + override the default plt location.</item> + <tag><c><![CDATA[--add_to_plt]]></c></tag> + <item>The plt is extended to also include the files specified with + <c><![CDATA[-c]]></c> and <c><![CDATA[-r]]></c>. Use + <c><![CDATA[--plt]]></c> to specify which plt to start from, + and <c><![CDATA[--output_plt]]></c> to specify where to put the plt. + Note that the analysis might include files from the plt if they depend + on the new files. This option only works with beam files.</item> <tag><c><![CDATA[--remove_from_plt]]></c></tag> - <item>The information from the files specified with -c and -r is removed - from the PLT. Note that this may cause a re-analysis of the remaining - dependent files.</item> + <item>The information from the files specified with + <c><![CDATA[-c]]></c> and <c><![CDATA[-r]]></c> is removed + from the plt. Note that this may cause a re-analysis of the remaining + dependent files.</item> + <tag><c><![CDATA[--check_plt]]></c></tag> + <item>Check the plt for consistency and rebuild it if it is not + up-to-date.</item> + <tag><c><![CDATA[--no_check_plt]]></c></tag> + <item>Skip the plt check when running Dialyzer. Useful when working with + installed plts that never change.</item> + <tag><c><![CDATA[--plt_info]]></c></tag> + <item>Make Dialyzer print information about the plt and then quit. The + plt can be specified with <c><![CDATA[--plt(s)]]></c>.</item> <tag><c><![CDATA[--get_warnings]]></c></tag> - <item>Makes Dialyzer emit warnings even when manipulating the PLT. Only - emits warnings for files that are actually analyzed. The default is to - not emit any warnings when manipulating the PLT. This option has no - effect when performing a normal analysis.</item> + <item>Make Dialyzer emit warnings even when manipulating the plt. + Warnings are only emitted for files that are actually analyzed.</item> + <tag><c><![CDATA[--dump_callgraph file]]></c></tag> + <item>Dump the call graph into the specified file whose format is + determined by the file name extension. Supported extensions are: raw, + dot, and ps. If something else is used as file name extension, default + format '.raw' will be used.</item> + <tag><c><![CDATA[--no_native]]></c> (or <c><![CDATA[-nn]]></c>)</tag> + <item>Bypass the native code compilation of some key files that Dialyzer + heuristically performs when dialyzing many files; this avoids the + compilation time but it may result in (much) longer analysis + time.</item> + <tag><c><![CDATA[--gui]]></c></tag> + <item>Use the gs-based GUI.</item> + <tag><c><![CDATA[--wx]]></c></tag> + <item>Use the wx-based GUI..</item> </taglist> <note> <p>* denotes that multiple occurrences of these options are possible.</p> @@ -148,11 +211,14 @@ <p>Warning options:</p> <taglist> <tag><c><![CDATA[-Wno_return]]></c></tag> - <item>Suppress warnings for functions of no return.</item> + <item>Suppress warnings for functions that will never return a + value.</item> <tag><c><![CDATA[-Wno_unused]]></c></tag> <item>Suppress warnings for unused functions.</item> <tag><c><![CDATA[-Wno_improper_lists]]></c></tag> <item>Suppress warnings for construction of improper lists.</item> + <tag><c><![CDATA[-Wno_tuple_as_fun]]></c></tag> + <item>Suppress warnings for using tuples instead of funs.</item> <tag><c><![CDATA[-Wno_fun_app]]></c></tag> <item>Suppress warnings for fun applications that will fail.</item> <tag><c><![CDATA[-Wno_match]]></c></tag> @@ -160,6 +226,10 @@ match.</item> <tag><c><![CDATA[-Wno_opaque]]></c></tag> <item>Suppress warnings for violations of opaqueness of data types.</item> + <tag><c><![CDATA[-Wunmatched_returns]]></c>***</tag> + <item>Include warnings for function calls which ignore a structured return + value or do not match against one of many possible return + value(s).</item> <tag><c><![CDATA[-Werror_handling]]></c>***</tag> <item>Include warnings for functions that only return by means of an exception.</item> @@ -168,20 +238,22 @@ <tag><c><![CDATA[-Wbehaviours]]></c>***</tag> <item>Include warnings about behaviour callbacks which drift from the published recommended interfaces.</item> - <tag><c><![CDATA[-Wunmatched_returns]]></c>***</tag> - <item>Include warnings for function calls which ignore a structured return - value or do not match against one of many possible return value(s).</item> <tag><c><![CDATA[-Wunderspecs]]></c>***</tag> <item>Warn about underspecified functions - (the -spec is strictly more allowing than the success typing)</item> + (the -spec is strictly more allowing than the success typing).</item> + </taglist> + <p>The following options are also available but their use is not + recommended: (they are mostly for Dialyzer developers and internal + debugging)</p> + <taglist> <tag><c><![CDATA[-Woverspecs]]></c>***</tag> <item>Warn about overspecified functions - (the -spec is strictly less allowing than the success typing)</item> + (the -spec is strictly less allowing than the success typing).</item> <tag><c><![CDATA[-Wspecdiffs]]></c>***</tag> - <item>Warn when the -spec is different than the success typing</item> + <item>Warn when the -spec is different than the success typing.</item> </taglist> <note> - <p>*** These are options that turn on warnings rather than + <p>*** Identifies options that turn on warnings rather than turning them off.</p> </note> </section> @@ -210,6 +282,7 @@ Option : {files, [Filename : string()]} | {defines, [{Macro: atom(), Value : term()}]} | {from, src_code | byte_code} %% Defaults to byte_code | {init_plt, FileName : string()} %% If changed from default + | {plts, [FileName :: string()]} %% If changed from default | {include_dirs, [DirName : string()]} | {output_file, FileName : string()} | {output_plt, FileName :: string()} diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml index ac3857b9ef..3678291be7 100755 --- a/lib/dialyzer/doc/src/notes.xml +++ b/lib/dialyzer/doc/src/notes.xml @@ -31,6 +31,40 @@ <p>This document describes the changes made to the Dialyzer application.</p> +<section><title>Dialyzer 2.4.0</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> - Fixed pretty rare infinite loop when refining the + types of an SCC whose functions all returned none() + (thanks to Stavros Aronis). </p><p> - Fixed pretty rare + crash when taking the infimum of two tuple_sets. </p> + <p> + Own Id: OTP-8979</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> - Added ability to supply multiple PLTs for the + analysis (option --plts). Currently these PLTs must be + independent (i.e., no module appears in more than one + PLT) and there must not include files with module name + clashes.</p><p> - Strengthened and streamlined hard-coded + type information for some BIFs and key library + functions.</p> + <p> + Own Id: OTP-8962</p> + </item> + </list> + </section> + +</section> + <section><title>Dialyzer 2.3.1</title> <section><title>Improvements and New Features</title> diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl index 2f9e577544..5ca7599b35 100644 --- a/lib/dialyzer/src/dialyzer_cl_parse.erl +++ b/lib/dialyzer/src/dialyzer_cl_parse.erl @@ -329,7 +329,7 @@ help_warnings() -> help_message() -> S = "Usage: dialyzer [--help] [--version] [--shell] [--quiet] [--verbose] - [-pa dir]* [--plt plt] [--plts plts] [-Ddefine]* + [-pa dir]* [--plt plt] [--plts plt*] [-Ddefine]* [-I include_dir]* [--output_plt file] [-Wwarn]* [--src] [--gui | --wx] [files_or_dirs] [-r dirs] [--apps applications] [-o outfile] @@ -340,13 +340,13 @@ Options: files_or_dirs (for backwards compatibility also as: -c files_or_dirs) Use Dialyzer from the command line to detect defects in the specified files or directories containing .erl or .beam files, - depending on the type of the analysis + depending on the type of the analysis. -r dirs Same as the previous but the specified directories are searched recursively for subdirectories containing .erl or .beam files in - them, depending on the type of analysis + them, depending on the type of analysis. --apps applications - Option typically used when building or modifying a PLT as in: + Option typically used when building or modifying a plt as in: dialyzer --build_plt --apps erts kernel stdlib mnesia ... to conveniently refer to library applications corresponding to the Erlang/OTP installation. However, the option is general and can also @@ -355,51 +355,60 @@ Options: dialyzer --apps inets ssl ./ebin ../other_lib/ebin/my_module.beam -o outfile (or --output outfile) When using Dialyzer from the command line, send the analysis - results to the specified \"outfile\" rather than to stdout + results to the specified outfile rather than to stdout. --raw When using Dialyzer from the command line, output the raw analysis results (Erlang terms) instead of the formatted result. The raw format is easier to post-process (for instance, to filter - warnings or to output HTML pages) + warnings or to output HTML pages). --src Override the default, which is to analyze BEAM files, and - analyze starting from Erlang source code instead + analyze starting from Erlang source code instead. -Dname (or -Dname=value) - When analyzing from source, pass the define to Dialyzer (**) + When analyzing from source, pass the define to Dialyzer. (**) -I include_dir - When analyzing from source, pass the include_dir to Dialyzer (**) + When analyzing from source, pass the include_dir to Dialyzer. (**) -pa dir Include dir in the path for Erlang (useful when analyzing files - that have '-include_lib()' directives) + that have '-include_lib()' directives). --output_plt file - Store the plt at the specified file after building it + Store the plt at the specified file after building it. --plt plt Use the specified plt as the initial plt (if the plt was built - during setup the files will be checked for consistency) - --plts plts - Merges the specified plts to create the initial plt -- requires + during setup the files will be checked for consistency). + --plts plt* + Merge the specified plts to create the initial plt -- requires that the plts are disjoint (i.e., do not have any module - appearing in more than one plt) + appearing in more than one plt). + The plts are created in the usual way: + dialyzer --build_plt --output_plt plt_1 files_to_include + ... + dialyzer --build_plt --output_plt plt_n files_to_include + and then can be used in either of the following ways: + dialyzer files_to_analyze --plts plt_1 ... plt_n + or: + dialyzer --plts plt_1 ... plt_n -- files_to_analyze + (Note the -- delimiter in the second case) -Wwarn A family of options which selectively turn on/off warnings - (for help on the names of warnings use dialyzer -Whelp) + (for help on the names of warnings use dialyzer -Whelp). --shell - Do not disable the Erlang shell while running the GUI + Do not disable the Erlang shell while running the GUI. --version (or -v) - Prints the Dialyzer version and some more information and exits + Print the Dialyzer version and some more information and exit. --help (or -h) - Prints this message and exits + Print this message and exit. --quiet (or -q) - Makes Dialyzer a bit more quiet + Make Dialyzer a bit more quiet. --verbose - Makes Dialyzer a bit more verbose + Make Dialyzer a bit more verbose. --build_plt The analysis starts from an empty plt and creates a new one from the files specified with -c and -r. Only works for beam files. Use --plt(s) or --output_plt to override the default plt location. --add_to_plt The plt is extended to also include the files specified with -c and -r. - Use --plt(s) to specify wich plt to start from, and --output_plt to + Use --plt(s) to specify which plt to start from, and --output_plt to specify where to put the plt. Note that the analysis might include files from the plt if they depend on the new files. This option only works with beam files. @@ -408,24 +417,24 @@ Options: from the plt. Note that this may cause a re-analysis of the remaining dependent files. --check_plt - Checks the plt for consistency and rebuilds it if it is not up-to-date. + Check the plt for consistency and rebuild it if it is not up-to-date. Actually, this option is of rare use as it is on by default. --no_check_plt (or -n) Skip the plt check when running Dialyzer. Useful when working with installed plts that never change. --plt_info - Makes Dialyzer print information about the plt and then quit. The plt + Make Dialyzer print information about the plt and then quit. The plt can be specified with --plt(s). --get_warnings - Makes Dialyzer emit warnings even when manipulating the plt. Only - emits warnings for files that are actually analyzed. + Make Dialyzer emit warnings even when manipulating the plt. Warnings + are only emitted for files that are actually analyzed. --dump_callgraph file Dump the call graph into the specified file whose format is determined by the file name extension. Supported extensions are: raw, dot, and ps. If something else is used as file name extension, default format '.raw' will be used. --no_native (or -nn) - Bypass the native code compilation of some key files that dialyzer + Bypass the native code compilation of some key files that Dialyzer heuristically performs when dialyzing many files; this avoids the compilation time but it may result in (much) longer analysis time. --gui diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl index f68472d2fc..c45615d670 100644 --- a/lib/dialyzer/src/dialyzer_typesig.erl +++ b/lib/dialyzer/src/dialyzer_typesig.erl @@ -1406,9 +1406,13 @@ get_bif_constr({erlang, 'or', 2}, Dst, [Arg1, Arg2] = Args, _State) -> ArgV1 = mk_fun_var(ArgFun(Arg2), [Arg2, Dst]), ArgV2 = mk_fun_var(ArgFun(Arg1), [Arg1, Dst]), DstV = mk_fun_var(DstFun, Args), - Disj = mk_disj_constraint_list([mk_constraint(Arg1, sub, True), - mk_constraint(Arg2, sub, True), - mk_constraint(Dst, sub, False)]), + F = fun(A) -> + try [mk_constraint(A, sub, True)] + catch throw:error -> [] + end + end, + Constrs = F(Arg1) ++ F(Arg2), + Disj = mk_disj_constraint_list([mk_constraint(Dst, sub, False)|Constrs]), mk_conj_constraint_list([mk_constraint(Dst, sub, DstV), mk_constraint(Arg1, sub, ArgV1), mk_constraint(Arg2, sub, ArgV2), diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk index d3574e0a71..b2902e95ed 100644 --- a/lib/dialyzer/vsn.mk +++ b/lib/dialyzer/vsn.mk @@ -1 +1 @@ -DIALYZER_VSN = 2.3.1 +DIALYZER_VSN = 2.4.0 diff --git a/lib/docbuilder/doc/src/notes.xml b/lib/docbuilder/doc/src/notes.xml index 019cf1b083..4b8c04f323 100644 --- a/lib/docbuilder/doc/src/notes.xml +++ b/lib/docbuilder/doc/src/notes.xml @@ -31,6 +31,21 @@ <p>This document describes the changes made to the DocBuilder application.</p> +<section><title>Docbuilder 0.9.8.9</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> Fix compatibility issues with docbuilder for R11 + documentation patches. </p> + <p> + Own Id: OTP-8946</p> + </item> + </list> + </section> + +</section> + <section><title>Docbuilder 0.9.8.8</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/docbuilder/vsn.mk b/lib/docbuilder/vsn.mk index b23ee521c7..1209b80d94 100644 --- a/lib/docbuilder/vsn.mk +++ b/lib/docbuilder/vsn.mk @@ -1 +1 @@ -DOCB_VSN = 0.9.8.8 +DOCB_VSN = 0.9.8.9 diff --git a/lib/edoc/doc/src/notes.xml b/lib/edoc/doc/src/notes.xml index 83ad27ed31..afcccf22b5 100644 --- a/lib/edoc/doc/src/notes.xml +++ b/lib/edoc/doc/src/notes.xml @@ -31,6 +31,21 @@ <p>This document describes the changes made to the EDoc application.</p> +<section><title>Edoc 0.7.6.8</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Compiler warnings were eliminated.</p> + <p> + Own Id: OTP-8855</p> + </item> + </list> + </section> + +</section> + <section><title>Edoc 0.7.6.7</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk index 75e9a5c971..e030174862 100644 --- a/lib/edoc/vsn.mk +++ b/lib/edoc/vsn.mk @@ -1 +1 @@ -EDOC_VSN = 0.7.6.7 +EDOC_VSN = 0.7.6.8 diff --git a/lib/erl_docgen/doc/src/notes.xml b/lib/erl_docgen/doc/src/notes.xml index 5b5398fec6..c7a7926c40 100644 --- a/lib/erl_docgen/doc/src/notes.xml +++ b/lib/erl_docgen/doc/src/notes.xml @@ -29,7 +29,36 @@ <file>notes.xml</file> </header> <p>This document describes the changes made to the erl_docgen application.</p> - <section><title>Erl_Docgen 0.2.2</title> + <section><title>Erl_Docgen 0.2.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix format_man_pages so it handles all man sections + and remove warnings/errors in various man pages. </p> + <p> + Own Id: OTP-8600</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> Support for using Dialyzer specifications and types + has been added. This is an experimental release; changes + are expected before the new functionality is used when + building the OTP documentation. </p> + <p> + Own Id: OTP-8720</p> + </item> + </list> + </section> + +</section> + +<section><title>Erl_Docgen 0.2.2</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk index 0bc01f7d49..fb0f5ca0cd 100644 --- a/lib/erl_docgen/vsn.mk +++ b/lib/erl_docgen/vsn.mk @@ -1 +1 @@ -ERL_DOCGEN_VSN = 0.2.2 +ERL_DOCGEN_VSN = 0.2.3 diff --git a/lib/erl_interface/doc/src/ei.xml b/lib/erl_interface/doc/src/ei.xml index d7af7a1b67..de4e4b4301 100644 --- a/lib/erl_interface/doc/src/ei.xml +++ b/lib/erl_interface/doc/src/ei.xml @@ -641,12 +641,14 @@ ei_x_encode_empty_list(&x); <p></p> <pre> ~a - an atom, char* +~c - a character, char ~s - a string, char* ~i - an integer, int ~l - a long integer, long int ~u - a unsigned long integer, unsigned long int ~f - a float, float ~d - a double float, double float +~p - an Erlang PID, erlang_pid* </pre> <p>For instance, to encode a tuple with some stuff:</p> <pre> diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml index ff89802599..de5ba61938 100644 --- a/lib/erl_interface/doc/src/notes.xml +++ b/lib/erl_interface/doc/src/notes.xml @@ -30,6 +30,56 @@ </header> <p>This document describes the changes made to the Erl_interface application.</p> +<section><title>Erl_Interface 3.7.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + erl_call: remove get_hostent</p> + <p> + get_hostent does not properly handle IPv4 addresses on + little endian platforms and fails with hostnames + beginning with a number. Remove get_hostent and use + ei_gethostbyname directly since gethostbyname supports + IPv4 addresses.</p> + <p> + (Thanks to Michael Santos)</p> + <p> + Own Id: OTP-8890</p> + </item> + <item> + <p> teach ei_x_format to handle unary - and + (Thanks to + Steve Vinoski)</p> + <p> + Own Id: OTP-8891</p> + </item> + <item> + <p>Fix zero byte allocation in registry. (Thanks to + Michael Santos)</p> + <p> + Own Id: OTP-8893</p> + </item> + <item> + <p> Check the length of the node name to prevent an + overflow. Memory error control of ei_alloc_big. (Thanks + to Michael Santos) </p> + <p> + Own Id: OTP-8943</p> + </item> + <item> + <p> + erl_term_len() in erl_interface could returned too large + values for integers (since R14B) and too small values for + refs (since R9B).</p> + <p> + Own Id: OTP-8945</p> + </item> + </list> + </section> + +</section> + <section><title>Erl_Interface 3.7.1.1</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h index 466d84bb99..ae815b414a 100644 --- a/lib/erl_interface/include/ei.h +++ b/lib/erl_interface/include/ei.h @@ -80,21 +80,24 @@ #define ERL_NO_TIMEOUT -1 /* these are the control message types */ -#define ERL_LINK 1 -#define ERL_SEND 2 -#define ERL_EXIT 3 -#define ERL_UNLINK 4 -#define ERL_NODE_LINK 5 -#define ERL_REG_SEND 6 -#define ERL_GROUP_LEADER 7 -#define ERL_EXIT2 8 -#define ERL_PASS_THROUGH 'p' +#define ERL_LINK 1 +#define ERL_SEND 2 +#define ERL_EXIT 3 +#define ERL_UNLINK 4 +#define ERL_NODE_LINK 5 +#define ERL_REG_SEND 6 +#define ERL_GROUP_LEADER 7 +#define ERL_EXIT2 8 +#define ERL_PASS_THROUGH 'p' /* new ones for tracing, from Kenneth */ -#define ERL_SEND_TT 12 -#define ERL_EXIT_TT 13 -#define ERL_REG_SEND_TT 16 -#define ERL_EXIT2_TT 18 +#define ERL_SEND_TT 12 +#define ERL_EXIT_TT 13 +#define ERL_REG_SEND_TT 16 +#define ERL_EXIT2_TT 18 +#define ERL_MONITOR_P 19 +#define ERL_DEMONITOR_P 20 +#define ERL_MONITOR_P_EXIT 21 /* -------------------------------------------------------------------- */ diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c index 99ccba0686..6dc6ebb348 100644 --- a/lib/erl_interface/src/connect/ei_connect.c +++ b/lib/erl_interface/src/connect/ei_connect.c @@ -938,7 +938,7 @@ int ei_do_receive_msg(int fd, int staticbuffer_p, return ERL_ERROR; } x->index = x->buffsz; - switch (msg->msgtype) { /* FIXME are these all? */ + switch (msg->msgtype) { /* FIXME does not handle trace tokens and monitors */ case ERL_SEND: case ERL_REG_SEND: case ERL_LINK: @@ -946,7 +946,6 @@ int ei_do_receive_msg(int fd, int staticbuffer_p, case ERL_GROUP_LEADER: case ERL_EXIT: case ERL_EXIT2: - case ERL_NODE_LINK: return ERL_MSG; default: @@ -1329,6 +1328,7 @@ static int send_name_or_challenge(int fd, char *nodename, put8(s, 'n'); put16be(s, version); put32be(s, (DFLAG_EXTENDED_REFERENCES + | DFLAG_DIST_MONITOR | DFLAG_EXTENDED_PIDS_PORTS | DFLAG_FUN_TAGS | DFLAG_NEW_FUN_TAGS diff --git a/lib/erl_interface/src/connect/eirecv.c b/lib/erl_interface/src/connect/eirecv.c index 7d72ddeeae..86852f947d 100644 --- a/lib/erl_interface/src/connect/eirecv.c +++ b/lib/erl_interface/src/connect/eirecv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * Copyright Ericsson AB 1998-2010. 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 @@ -196,10 +196,6 @@ ei_recv_internal (int fd, ei_trace(1,&msg->token); /* turn on tracing */ break; - case ERL_NODE_LINK: /* { NODE_LINK } */ - if (ei_tracelevel >= 4) show_this_msg = 1; - break; - default: /* unknown type, just put any remaining bytes into buffer */ break; diff --git a/lib/erl_interface/src/legacy/erl_marshal.c b/lib/erl_interface/src/legacy/erl_marshal.c index a6c2f64dd0..70949a7adf 100644 --- a/lib/erl_interface/src/legacy/erl_marshal.c +++ b/lib/erl_interface/src/legacy/erl_marshal.c @@ -1646,11 +1646,14 @@ static int cmp_exe2(unsigned char **e1, unsigned char **e2) min = (i < j) ? i : j; k = 0; while (1) { - if (k++ == min) - return compare_top_ext(e1 , e2); - if ((ret = compare_top_ext(e1 , e2)) == 0) - continue; - return ret; + if (k++ == min){ + if (i == j) return 0; + if (i < j) return -1; + return 1; + } + if ((ret = compare_top_ext(e1 , e2)) == 0) + continue; + return ret; } case ERL_STRING_EXT: i = (**e1 << 8) | ((*e1)[1]); diff --git a/lib/erl_interface/src/legacy/global_register.c b/lib/erl_interface/src/legacy/global_register.c index 3a4de8b08e..f12eb6b448 100644 --- a/lib/erl_interface/src/legacy/global_register.c +++ b/lib/erl_interface/src/legacy/global_register.c @@ -31,7 +31,7 @@ int erl_global_register(int fd, const char *name, ETERM *pid) int index = 0; erlang_pid self; erlang_msg msg; - int needlink, needatom; + int needlink, needatom, needmonitor; int arity; int version; int msglen; @@ -65,7 +65,7 @@ int erl_global_register(int fd, const char *name, ETERM *pid) if (ei_send_reg_encoded(fd,&self,"rex",buf,index)) return -1; /* get the reply: expect link and an atom, or just an atom */ - needlink = needatom = 1; + needlink = needatom = needmonitor = 1; while (1) { /* get message */ while (1) { @@ -78,9 +78,15 @@ int erl_global_register(int fd, const char *name, ETERM *pid) case ERL_LINK: /* got link */ if (!needlink) return -1; - needlink = 0; + needlink = 0; break; + case ERL_MONITOR_P-10: + /* got monitor */ + if (!needmonitor) { return -1;} + needmonitor = 0; + break; + case ERL_SEND: /* got message - does it contain our atom? */ if (!needatom) return -1; diff --git a/lib/erl_interface/src/legacy/global_unregister.c b/lib/erl_interface/src/legacy/global_unregister.c index 514dbc3c68..97a1c2d03c 100644 --- a/lib/erl_interface/src/legacy/global_unregister.c +++ b/lib/erl_interface/src/legacy/global_unregister.c @@ -37,7 +37,7 @@ int erl_global_unregister(int fd, const char *name) erlang_msg msg; int i; int version,arity,msglen; - int needunlink, needatom; + int needunlink, needatom, needdemonitor; /* make a self pid */ self->num = fd; @@ -57,7 +57,7 @@ int erl_global_unregister(int fd, const char *name) if (ei_send_reg_encoded(fd,self,"rex",buf,index)) return -1; /* get the reply: expect unlink and an atom, or just an atom */ - needunlink = needatom = 1; + needunlink = needatom = needdemonitor = 1; while (1) { /* get message */ while (1) { @@ -68,11 +68,17 @@ int erl_global_unregister(int fd, const char *name) switch (i) { case ERL_UNLINK: - /* got link */ + /* got unlink */ if (!needunlink) return -1; needunlink = 0; break; + case ERL_DEMONITOR_P-10: + /* got demonitor */ + if (!needdemonitor) return -1; + needdemonitor = 0; + break; + case ERL_SEND: /* got message - does it contain our atom? */ if (!needatom) return -1; diff --git a/lib/erl_interface/src/misc/ei_format.c b/lib/erl_interface/src/misc/ei_format.c index b35421d4b2..dbd7a4479a 100644 --- a/lib/erl_interface/src/misc/ei_format.c +++ b/lib/erl_interface/src/misc/ei_format.c @@ -47,10 +47,12 @@ * array of unions. */ union arg { + char c; char* s; long l; unsigned long u; double d; + erlang_pid* pid; }; static int eiformat(const char** s, union arg** args, ei_x_buff* x); @@ -224,12 +226,14 @@ static int pquotedatom(const char** fmt, ei_x_buff* x) /* * The format letters are: * a - An atom + * c - A character * s - A string * i - An integer * l - A long integer * u - An unsigned long integer * f - A float * d - A double float + * p - An Erlang PID */ static int pformat(const char** fmt, union arg** args, ei_x_buff* x) { @@ -240,6 +244,10 @@ static int pformat(const char** fmt, union arg** args, ei_x_buff* x) res = ei_x_encode_atom(x, (*args)->s); (*args)++; break; + case 'c': + res = ei_x_encode_char(x, (*args)->c); + (*args)++; + break; case 's': res = ei_x_encode_string(x, (*args)->s); (*args)++; @@ -261,6 +269,10 @@ static int pformat(const char** fmt, union arg** args, ei_x_buff* x) res = ei_x_encode_double(x, (*args)->d); (*args)++; break; + case 'p': + res = ei_x_encode_pid(x, (*args)->pid); + (*args)++; + break; default: res = -1; break; @@ -396,6 +408,9 @@ static int read_args(const char* fmt, va_list ap, union arg **argp) return -1; /* Error, string not complete */ } switch (*p++) { + case 'c': + args[i++].c = (char) va_arg(ap, int); + break; case 'a': case 's': args[i++].s = va_arg(ap, char*); @@ -415,6 +430,9 @@ static int read_args(const char* fmt, va_list ap, union arg **argp) case 'd': args[i++].d = va_arg(ap, double); break; + case 'p': + args[i++].pid = va_arg(ap, erlang_pid*); + break; default: ei_free(args); /* Invalid specifier */ return -1; diff --git a/lib/erl_interface/src/misc/show_msg.c b/lib/erl_interface/src/misc/show_msg.c index 14bea5e01f..194296798b 100644 --- a/lib/erl_interface/src/misc/show_msg.c +++ b/lib/erl_interface/src/misc/show_msg.c @@ -181,11 +181,6 @@ int ei_show_sendmsg(FILE *stream, const char *header, const char *msgbuf) mbuf = header; break; - case ERL_NODE_LINK: - /* nothing to do */ - mbuf = header; - break; - default: break; } @@ -241,10 +236,6 @@ static void show_msg(FILE *stream, int direction, const erlang_msg *msg, show_pid(stream,&msg->to); break; - case ERL_NODE_LINK: - fprintf(stream,"NODE_LINK"); - break; - case ERL_REG_SEND: fprintf(stream,"REG_SEND From: "); show_pid(stream,&msg->from); diff --git a/lib/erl_interface/test/Makefile b/lib/erl_interface/test/Makefile index b7a1a4e4d8..07404fda4d 100644 --- a/lib/erl_interface/test/Makefile +++ b/lib/erl_interface/test/Makefile @@ -33,6 +33,7 @@ MODULES= \ ei_print_SUITE \ ei_tmo_SUITE \ erl_connect_SUITE \ + erl_global_SUITE \ erl_eterm_SUITE \ erl_ext_SUITE \ erl_format_SUITE \ diff --git a/lib/erl_interface/test/ei_connect_SUITE.erl b/lib/erl_interface/test/ei_connect_SUITE.erl index fe82a73ef9..3c72188e16 100644 --- a/lib/erl_interface/test/ei_connect_SUITE.erl +++ b/lib/erl_interface/test/ei_connect_SUITE.erl @@ -30,6 +30,7 @@ ei_send/1, ei_reg_send/1, + ei_format_pid/1, ei_rpc/1, rpc_test/1, ei_send_funs/1, @@ -41,6 +42,7 @@ all(suite) -> [ ei_send, ei_reg_send, + ei_format_pid, ei_rpc, ei_send_funs, ei_threaded_send, @@ -67,6 +69,19 @@ ei_send(Config) when is_list(Config) -> ?line runner:recv_eot(P), ok. +ei_format_pid(Config) when is_list(Config) -> + ?line S = self(), + ?line P = runner:start(?interpret), + ?line 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), + ?line {ok,Fd} = ei_connect(P, node()), + + ?line ok = ei_format_pid(P, Fd, S), + ?line receive S -> ok end, + + ?line runner:send_eot(P), + ?line runner:recv_eot(P), + ok. + ei_send_funs(Config) when is_list(Config) -> ?line P = runner:start(?interpret), ?line 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), @@ -189,6 +204,10 @@ ei_send(P, Fd, To, Msg) -> send_command(P, ei_send, [Fd,To,Msg]), get_send_result(P). +ei_format_pid(P, Fd, To) -> + send_command(P, ei_format_pid, [Fd, To]), + get_send_result(P). + ei_send_funs(P, Fd, To, Msg) -> send_command(P, ei_send_funs, [Fd,To,Msg]), get_send_result(P). diff --git a/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c b/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c index debd3e789b..8183ac9dd8 100644 --- a/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c +++ b/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c @@ -35,6 +35,7 @@ static void cmd_ei_connect_init(char* buf, int len); static void cmd_ei_connect(char* buf, int len); static void cmd_ei_send(char* buf, int len); +static void cmd_ei_format_pid(char* buf, int len); static void cmd_ei_send_funs(char* buf, int len); static void cmd_ei_reg_send(char* buf, int len); static void cmd_ei_rpc(char* buf, int len); @@ -57,6 +58,7 @@ static struct { "ei_reg_send", 3, cmd_ei_reg_send, "ei_rpc", 4, cmd_ei_rpc, "ei_set_get_tracelevel", 1, cmd_ei_set_get_tracelevel, + "ei_format_pid", 2, cmd_ei_format_pid, }; @@ -111,7 +113,7 @@ static void cmd_ei_connect_init(char* buf, int len) ei_x_buff res; if (ei_decode_long(buf, &index, &l) < 0) fail("expected int"); - sprintf(b, "c%d", l); + sprintf(b, "c%ld", l); /* FIXME don't use internal and maybe use skip?! */ ei_get_type_internal(buf, &index, &type, &size); if (ei_decode_atom(buf, &index, cookie) < 0) @@ -183,6 +185,25 @@ static void cmd_ei_send(char* buf, int len) ei_x_free(&x); } +static void cmd_ei_format_pid(char* buf, int len) +{ + int index = 0; + long fd; + erlang_pid pid; + ei_x_buff x; + + if (ei_decode_long(buf, &index, &fd) < 0) + fail("expected long"); + if (ei_decode_pid(buf, &index, &pid) < 0) + fail("expected pid (node)"); + if (ei_x_new_with_version(&x) < 0) + fail("ei_x_new_with_version"); + if (ei_x_format_wo_ver(&x, "~p", &pid) < 0) + fail("ei_x_format_wo_ver"); + send_errno_result(ei_send(fd, &pid, x.buff, x.index)); + ei_x_free(&x); +} + static void cmd_ei_send_funs(char* buf, int len) { int index = 0, n; diff --git a/lib/erl_interface/test/ei_format_SUITE.erl b/lib/erl_interface/test/ei_format_SUITE.erl index cbe9fa52d7..6d44e0adf3 100644 --- a/lib/erl_interface/test/ei_format_SUITE.erl +++ b/lib/erl_interface/test/ei_format_SUITE.erl @@ -155,7 +155,7 @@ format_wo_ver(suite) -> []; format_wo_ver(Config) when is_list(Config) -> ?line P = runner:start(?format_wo_ver), - ?line {term, [-1, 2, {a, "b"}, {c, 10}]} = get_term(P), + ?line {term, [-1, 2, $c, {a, "b"}, {c, 10}]} = get_term(P), ?line runner:recv_eot(P), ok. diff --git a/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c b/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c index ecdce402f5..a6eeb25abc 100644 --- a/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c +++ b/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c @@ -176,7 +176,7 @@ TESTCASE(format_wo_ver) { ei_x_buff x; ei_x_new (&x); - ei_x_format(&x, "[-1, +2, {~a,~s},{~a,~i}]", "a", "b", "c", 10); + ei_x_format(&x, "[-1, +2, ~c, {~a,~s},{~a,~i}]", 'c', "a", "b", "c", 10); send_bin_term(&x); free(x.buff); diff --git a/lib/erl_interface/test/erl_ext_SUITE_data/ext_test.c b/lib/erl_interface/test/erl_ext_SUITE_data/ext_test.c index ba1a6c66da..59e0e0cce7 100644 --- a/lib/erl_interface/test/erl_ext_SUITE_data/ext_test.c +++ b/lib/erl_interface/test/erl_ext_SUITE_data/ext_test.c @@ -82,6 +82,11 @@ TESTCASE(compare_list) { // erlang:term_to_binary([34,{a,n},a,erlang]) unsigned char term2[] = {131,108,0,0,0,4,97,34,104,2,100,0,1,97,100,0,1,110,100,0,1,97,100,0,6,101,114,108,97,110,103,106}; + // erlang:term_to_binary([0]) + unsigned char term3[] = {131,107,0,1,0}; + // erlang:term_to_binary([0, 1000]) + unsigned char term4[] = {131,108,0,0,0,2,97,0,98,0,0,3,232,106}; + erl_init(NULL, 0); start_a = term1; start_b = term2; @@ -90,6 +95,13 @@ TESTCASE(compare_list) { test_compare_ext("lists", start_a, end_a, start_b, end_b, 1); + start_a = term3; + start_b = term4; + end_a = term3 + sizeof(term3); + end_b = term4 + sizeof(term4); + + test_compare_ext("lists1", start_a, end_a, start_b, end_b, -1); + report(1); } diff --git a/lib/erl_interface/test/erl_global_SUITE.erl b/lib/erl_interface/test/erl_global_SUITE.erl new file mode 100644 index 0000000000..4f332037c6 --- /dev/null +++ b/lib/erl_interface/test/erl_global_SUITE.erl @@ -0,0 +1,133 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2000-2009. 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 +%% 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 online 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. +%% +%% %CopyrightEnd% +%% + +%% +-module(erl_global_SUITE). + +-include("test_server.hrl"). +-include("erl_global_SUITE_data/erl_global_test_cases.hrl"). + +-export([all/1,init_per_testcase/2,fin_per_testcase/2, + erl_global_registration/1, erl_global_whereis/1, erl_global_names/1]). + +-import(runner, [get_term/1,send_term/2]). + +-define(GLOBAL_NAME, global_register_node_test). + +all(suite) -> + [erl_global_registration, erl_global_whereis, erl_global_names]. + +init_per_testcase(_Case, Config) -> + Dog = ?t:timetrap(?t:minutes(0.25)), + [{watchdog, Dog}|Config]. + +fin_per_testcase(_Case, Config) -> + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +erl_global_registration(Config) when is_list(Config) -> + ?line P = runner:start(?interpret), + ?line {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0), + + ?line ok = erl_global_register(P, Fd, ?GLOBAL_NAME), + ?line ok = erl_global_unregister(P, Fd, ?GLOBAL_NAME), + + ?line 0 = erl_close_connection(P,Fd), + ?line runner:send_eot(P), + ?line runner:recv_eot(P), + ok. + +erl_global_whereis(Config) when is_list(Config) -> + ?line P = runner:start(?interpret), + ?line {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0), + + ?line Self = self(), + ?line yes = global:register_name(?GLOBAL_NAME, Self), + ?line Self = erl_global_whereis(P, Fd, ?GLOBAL_NAME), + ?line global:unregister_name(?GLOBAL_NAME), + ?line 0 = erl_close_connection(P, Fd), + ?line runner:send_eot(P), + ?line runner:recv_eot(P), + ok. + +erl_global_names(Config) when is_list(Config) -> + ?line P = runner:start(?interpret), + ?line {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0), + + ?line Self = self(), + ?line global:register_name(?GLOBAL_NAME, Self), + ?line {Names1, _N1} = erl_global_names(P, Fd), + ?line true = lists:member(atom_to_list(?GLOBAL_NAME), Names1), + ?line global:unregister_name(?GLOBAL_NAME), + ?line {Names2, _N2} = erl_global_names(P, Fd), + ?line false = lists:member(atom_to_list(?GLOBAL_NAME), Names2), + ?line 0 = erl_close_connection(P, Fd), + ?line runner:send_eot(P), + ?line runner:recv_eot(P), + ok. + +%%% Interface functions for erl_interface functions. + +erl_connect(P, Node, Num, Cookie, Creation) -> + send_command(P, erl_connect, [Num, Node, Cookie, Creation]), + case get_term(P) of + {term,{Fd,_}} when Fd >= 0 -> {ok,Fd}; + {term,{-1,Errno}} -> {error,Errno} + end. + +erl_close_connection(P, FD) -> + send_command(P, erl_close_connection, [FD]), + case get_term(P) of + {term,Int} when is_integer(Int) -> Int + end. + +erl_global_register(P, Fd, Name) -> + send_command(P, erl_global_register, [Fd,Name]), + get_send_result(P). + +erl_global_whereis(P, Fd, Name) -> + send_command(P, erl_global_whereis, [Fd,Name]), + case get_term(P) of + {term, What} -> + What + end. + +erl_global_names(P, Fd) -> + send_command(P, erl_global_names, [Fd]), + case get_term(P) of + {term, What} -> + What + end. + +erl_global_unregister(P, Fd, Name) -> + send_command(P, erl_global_unregister, [Fd,Name]), + get_send_result(P). + +get_send_result(P) -> + case get_term(P) of + {term,{1,_}} -> ok; + {term,{0, 0}} -> ok; + {term,{-1, Errno}} -> {error,Errno}; + {term,{_,_}}-> + ?t:fail(bad_return_value) + end. + +send_command(P, Name, Args) -> + runner:send_term(P, {Name,list_to_tuple(Args)}). diff --git a/lib/erl_interface/test/erl_global_SUITE_data/Makefile.first b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.first new file mode 100644 index 0000000000..8e3fcb924e --- /dev/null +++ b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.first @@ -0,0 +1,21 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2001-2010. 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 +# 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 online 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. +# +# %CopyrightEnd% +# + +erl_global_test_decl.c: erl_global_test.c + erl -noinput -pa ../all_SUITE_data -s init_tc run erl_global_test -s erlang halt diff --git a/lib/erl_interface/test/erl_global_SUITE_data/Makefile.src b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.src new file mode 100644 index 0000000000..ef846bc440 --- /dev/null +++ b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.src @@ -0,0 +1,41 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2000-2010. 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 +# 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 online 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. +# +# %CopyrightEnd% +# + +include @erl_interface_mk_include@@[email protected] + +CC0 = @CC@ +CC = ..@DS@all_SUITE_data@DS@gccifier@exe@ -CC"$(CC0)" +LD = @LD@ +LIBPATH = @erl_interface_libpath@ +LIBERL = $(LIBPATH)/@erl_interface_lib@ +LIBEI = $(LIBPATH)/@erl_interface_eilib@ +LIBFLAGS = ../all_SUITE_data/runner@obj@ \ + $(LIBERL) $(LIBEI) @LIBS@ @erl_interface_sock_libs@ \ + @erl_interface_threadlib@ +CFLAGS = @EI_CFLAGS@ $(THR_DEFS) -I@erl_interface_include@ -I../all_SUITE_data +OBJS = erl_global_test@obj@ erl_global_test_decl@obj@ + +all: erl_global_test@exe@ + +erl_global_test@exe@: $(OBJS) $(LIBERL) $(LIBEI) + $(LD) @CROSSLDFLAGS@ -o $@ $(OBJS) $(LIBFLAGS) + +clean: + $(RM) $(OBJS) + $(RM) erl_global_test@exe@ diff --git a/lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c b/lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c new file mode 100644 index 0000000000..dc0d8a0091 --- /dev/null +++ b/lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c @@ -0,0 +1,263 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2000-2010. 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 + * 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 online 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. + * + * %CopyrightEnd% + */ + +/* + * Purpose: Tests the functions in erl_global.c. + * + * See the erl_global_SUITE.erl file for a "table of contents". + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "runner.h" + +static void cmd_erl_connect(ETERM* args); +static void cmd_erl_global_register(ETERM *args); +static void cmd_erl_global_whereis(ETERM *args); +static void cmd_erl_global_names(ETERM *args); +static void cmd_erl_global_unregister(ETERM *args); +static void cmd_erl_close_connection(ETERM *args); + +static void send_errno_result(int value); + +static struct { + char* name; + int num_args; /* Number of arguments. */ + void (*func)(ETERM* args); +} commands[] = { + "erl_connect", 4, cmd_erl_connect, + "erl_close_connection", 1, cmd_erl_close_connection, + "erl_global_register", 2, cmd_erl_global_register, + "erl_global_whereis", 2, cmd_erl_global_whereis, + "erl_global_names", 1, cmd_erl_global_names, + "erl_global_unregister", 2, cmd_erl_global_unregister, +}; + + +/* + * Sends a list contaning all data types to the Erlang side. + */ + +TESTCASE(interpret) +{ + ETERM* term; + + erl_init(NULL, 0); + + outer_loop: + + term = get_term(); + + if (term == NULL) { + report(1); + return; + } else { + ETERM* Func; + ETERM* Args; + int i; + + if (!ERL_IS_TUPLE(term) || ERL_TUPLE_SIZE(term) != 2) { + fail("term should be a tuple of size 2"); + } + + Func = erl_element(1, term); + if (!ERL_IS_ATOM(Func)) { + fail("function name should be an atom"); + } + Args = erl_element(2, term); + if (!ERL_IS_TUPLE(Args)) { + fail("function arguments should be a tuple"); + } + erl_free_term(term); + for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) { + int n = strlen(commands[i].name); + if (ERL_ATOM_SIZE(Func) != n) { + continue; + } + if (memcmp(ERL_ATOM_PTR(Func), commands[i].name, n) == 0) { + erl_free_term(Func); + if (ERL_TUPLE_SIZE(Args) != commands[i].num_args) { + fail("wrong number of arguments"); + } + commands[i].func(Args); + erl_free_term(Args); + goto outer_loop; + } + } + fail("bad command"); + } +} + +#define VERIFY_TYPE(Test, Term) \ +if (!Test(Term)) { \ + fail("wrong type for " #Term); \ +} else { \ +} + +static void +cmd_erl_connect(ETERM* args) +{ + ETERM* number; + ETERM* node; + ETERM* cookie; + + int res; + char buffer[256]; + + number = ERL_TUPLE_ELEMENT(args, 0); + VERIFY_TYPE(ERL_IS_INTEGER, number); + node = ERL_TUPLE_ELEMENT(args, 1); + VERIFY_TYPE(ERL_IS_ATOM, node); + cookie = ERL_TUPLE_ELEMENT(args, 2); + VERIFY_TYPE(ERL_IS_ATOM, cookie); + + if (ERL_ATOM_SIZE(cookie) == 0) { + res = erl_connect_init(ERL_INT_VALUE(number), 0, 0); + } else { + memcpy(buffer, ERL_ATOM_PTR(cookie), ERL_ATOM_SIZE(cookie)); + buffer[ERL_ATOM_SIZE(cookie)] = '\0'; + res = erl_connect_init(ERL_INT_VALUE(number), buffer, 0); + } + + if(!res) { + send_errno_result(res); + return; + } + + memcpy(buffer, ERL_ATOM_PTR(node), ERL_ATOM_SIZE(node)); + buffer[ERL_ATOM_SIZE(node)] = '\0'; + send_errno_result(erl_connect(buffer)); +} + +static void +cmd_erl_close_connection(ETERM* args) +{ + ETERM* number; + ETERM* res; + + number = ERL_TUPLE_ELEMENT(args, 0); + VERIFY_TYPE(ERL_IS_INTEGER, number); + res = erl_mk_int(erl_close_connection(ERL_INT_VALUE(number))); + send_term(res); + erl_free_term(res); +} + +static void +cmd_erl_global_register(ETERM* args) +{ + ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0); + ETERM* name = ERL_TUPLE_ELEMENT(args, 1); + ETERM* pid = erl_mk_pid(erl_thisnodename(), 14, 0, 0); + + char buffer[256]; + + VERIFY_TYPE(ERL_IS_INTEGER, fd_term); + VERIFY_TYPE(ERL_IS_ATOM, name); + + memcpy(buffer, ERL_ATOM_PTR(name), ERL_ATOM_SIZE(name)); + buffer[ERL_ATOM_SIZE(name)] = '\0'; + + send_errno_result(erl_global_register(ERL_INT_VALUE(fd_term), buffer, pid)); + erl_free_term(pid); +} + +static void +cmd_erl_global_whereis(ETERM* args) +{ + ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0); + ETERM* name = ERL_TUPLE_ELEMENT(args, 1); + ETERM* pid = NULL; + + char buffer[256]; + + VERIFY_TYPE(ERL_IS_INTEGER, fd_term); + VERIFY_TYPE(ERL_IS_ATOM, name); + + memcpy(buffer, ERL_ATOM_PTR(name), ERL_ATOM_SIZE(name)); + buffer[ERL_ATOM_SIZE(name)] = '\0'; + + pid = erl_global_whereis(ERL_INT_VALUE(fd_term), buffer, NULL); + send_term(pid); + erl_free_term(pid); +} + +static void +cmd_erl_global_names(ETERM* args) +{ + ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0); + + ETERM* res_array[2], *res_tuple, *name; + char** names = NULL; + int count = 0, i; + + VERIFY_TYPE(ERL_IS_INTEGER, fd_term); + + names = erl_global_names(ERL_INT_VALUE(fd_term), &count); + + res_array[0] = erl_mk_empty_list(); + for(i=0; i<count; i++) { + name = erl_mk_string(names[i]); + res_array[0] = erl_cons(name, res_array[0]); + } + + free(names); + + res_array[1] = erl_mk_int(count); + res_tuple = erl_mk_tuple(res_array, 2); + + send_term(res_tuple); + + erl_free_compound(res_array[0]); + erl_free_term(res_array[1]); + erl_free_term(res_tuple); +} + +static void +cmd_erl_global_unregister(ETERM* args) +{ + ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0); + ETERM* name = ERL_TUPLE_ELEMENT(args, 1); + + char buffer[256]; + + VERIFY_TYPE(ERL_IS_INTEGER, fd_term); + VERIFY_TYPE(ERL_IS_ATOM, name); + + memcpy(buffer, ERL_ATOM_PTR(name), ERL_ATOM_SIZE(name)); + buffer[ERL_ATOM_SIZE(name)] = '\0'; + + send_errno_result(erl_global_unregister(ERL_INT_VALUE(fd_term), buffer)); +} + +static void +send_errno_result(int value) +{ + ETERM* res_array[2]; + ETERM* res_tuple; + + res_array[0] = erl_mk_int(value); + res_array[1] = erl_mk_int(erl_errno); + res_tuple = erl_mk_tuple(res_array, 2); + send_term(res_tuple); + erl_free_term(res_array[0]); + erl_free_term(res_array[1]); + erl_free_term(res_tuple); +} diff --git a/lib/erl_interface/test/port_call_SUITE.erl b/lib/erl_interface/test/port_call_SUITE.erl index 895e29ad2e..2c550e4c0c 100644 --- a/lib/erl_interface/test/port_call_SUITE.erl +++ b/lib/erl_interface/test/port_call_SUITE.erl @@ -42,6 +42,8 @@ all(suite) -> basic(suite) -> []; basic(Config) when is_list(Config) -> case os:type() of + {unix, linux} -> + do_basic(Config); {unix, sunos} -> do_basic(Config); {win32,_} -> diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk index 6c664959a3..ffda886553 100644 --- a/lib/erl_interface/vsn.mk +++ b/lib/erl_interface/vsn.mk @@ -1 +1 @@ -EI_VSN = 3.7.1.1 +EI_VSN = 3.7.2 diff --git a/lib/et/doc/src/notes.xml b/lib/et/doc/src/notes.xml index 4ce7548414..cd4787c5e7 100644 --- a/lib/et/doc/src/notes.xml +++ b/lib/et/doc/src/notes.xml @@ -36,6 +36,21 @@ one section in this document. The title of each section is the version number of <c>Event Tracer (ET)</c>.</p> +<section><title>ET 1.4.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix error when module et was used in et_selector + trace patterns. </p> + <p> + Own Id: OTP-8904</p> + </item> + </list> + </section> + +</section> + <section><title>ET 1.4.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/et/vsn.mk b/lib/et/vsn.mk index b5b7fa52f4..d7cfd7bc84 100644 --- a/lib/et/vsn.mk +++ b/lib/et/vsn.mk @@ -1 +1 @@ -ET_VSN = 1.4.1 +ET_VSN = 1.4.2 diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml index 974ba1db4e..1717dd7988 100644 --- a/lib/eunit/doc/src/notes.xml +++ b/lib/eunit/doc/src/notes.xml @@ -32,6 +32,21 @@ </header> <p>This document describes the changes made to the EUnit application.</p> +<section><title>Eunit 2.1.6</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix format_man_pages so it handles all man sections + and remove warnings/errors in various man pages. </p> + <p> + Own Id: OTP-8600</p> + </item> + </list> + </section> + +</section> + <section><title>Eunit 2.1.5</title> <section><title>Improvements and New Features</title> diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk index 3bfa9c8000..e1965630e3 100644 --- a/lib/eunit/vsn.mk +++ b/lib/eunit/vsn.mk @@ -1 +1 @@ -EUNIT_VSN = 2.1.5 +EUNIT_VSN = 2.1.6 diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index ed5bf03804..309c118107 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -48,6 +48,7 @@ t_boolean/0, t_byte/0, t_char/0, + t_charlist/0, t_cons/0, t_cons/2, t_cons_hd/1, @@ -124,7 +125,8 @@ t_tuple/1, t_tuple_args/1, t_tuple_size/1, - t_tuple_subtypes/1 + t_tuple_subtypes/1, + t_unicode_string/0 ]). -ifdef(DO_ERL_BIF_TYPES_TEST). @@ -1865,6 +1867,8 @@ type(erts_debug, flat_size, 1, Xs) -> strict(arg_types(erts_debug, flat_size, 1), Xs, fun (_) -> t_integer() end); type(erts_debug, get_internal_state, 1, _) -> t_any(); +type(erts_debug, instructions, 0, _) -> + t_list(t_list(t_byte())); type(erts_debug, lock_counters, 1, Xs) -> strict(arg_types(erts_debug, lock_counters, 1), Xs, fun ([Arg]) -> @@ -3799,7 +3803,7 @@ arg_types(erlang, now, 0) -> arg_types(erlang, open_port, 2) -> [t_sup(t_atom(), t_sup([t_tuple([t_atom('spawn'), t_string()]), t_tuple([t_atom('spawn_driver'), t_string()]), - t_tuple([t_atom('spawn_executable'), t_string()]), + t_tuple([t_atom('spawn_executable'), t_sup(t_unicode_string(),t_binary())]), t_tuple([t_atom('fd'), t_integer(), t_integer()])])), t_list(t_sup(t_sup([t_atom('stream'), t_atom('exit_status'), @@ -3815,8 +3819,8 @@ arg_types(erlang, open_port, 2) -> t_tuple([t_atom('line'), t_integer()]), t_tuple([t_atom('cd'), t_string()]), t_tuple([t_atom('env'), t_list(t_tuple(2))]), % XXX: More - t_tuple([t_atom('args'), t_list(t_string())]), - t_tuple([t_atom('arg0'), t_string()])])))]; + t_tuple([t_atom('args'), t_list(t_sup(t_unicode_string(),t_binary()))]), + t_tuple([t_atom('arg0'),t_sup(t_unicode_string(),t_binary())])])))]; arg_types(erlang, phash, 2) -> [t_any(), t_pos_integer()]; arg_types(erlang, phash2, 1) -> @@ -4091,6 +4095,8 @@ arg_types(erts_debug, flat_size, 1) -> [t_any()]; arg_types(erts_debug, get_internal_state, 1) -> [t_any()]; +arg_types(erts_debug, instructions, 0) -> + []; arg_types(erts_debug, lock_counters, 1) -> [t_sup([t_atom(enabled), t_atom(info), @@ -4517,11 +4523,11 @@ arg_types(os, timestamp, 0) -> arg_types(re, compile, 1) -> [t_iodata()]; arg_types(re, compile, 2) -> - [t_iodata(), t_list(t_re_compile_option())]; + [t_sup(t_iodata(), t_charlist()), t_list(t_re_compile_option())]; arg_types(re, run, 2) -> - [t_iodata(), t_re_RE()]; + [t_sup(t_iodata(), t_charlist()), t_re_RE()]; arg_types(re, run, 3) -> - [t_iodata(), t_re_RE(), t_list(t_re_run_option())]; + [t_sup(t_iodata(), t_charlist()), t_re_RE(), t_list(t_re_run_option())]; %%------- string -------------------------------------------------------------- arg_types(string, chars, 2) -> [t_char(), t_non_neg_integer()]; @@ -4940,10 +4946,11 @@ t_matchres() -> %% From the 'ets' documentation %%----------------------------- %% Option = Type | Access | named_table | {keypos,Pos} -%% | {heir,pid(),HeirData} | {heir,none} -%% | {write_concurrency,boolean()} +%% | {heir,pid(),HeirData} | {heir,none} | Tweaks %% Type = set | ordered_set | bag | duplicate_bag %% Access = public | protected | private +%% Tweaks = {write_concurrency,boolean()} +%% | {read_concurrency,boolean()} | compressed %% Pos = integer() %% HeirData = term() t_ets_new_options() -> @@ -4955,10 +4962,12 @@ t_ets_new_options() -> t_atom('protected'), t_atom('private'), t_atom('named_table'), + t_tuple([t_atom('keypos'), t_integer()]), t_tuple([t_atom('heir'), t_pid(), t_any()]), t_tuple([t_atom('heir'), t_atom('none')]), - t_tuple([t_atom('keypos'), t_integer()]), - t_tuple([t_atom('write_concurrency'), t_boolean()])])). + t_tuple([t_atom('write_concurrency'), t_boolean()]), + t_tuple([t_atom('read_concurrency'), t_boolean()]), + t_atom('compressed')])). t_ets_info_items() -> t_sup([t_atom('fixed'), @@ -4978,8 +4987,7 @@ t_ets_info_items() -> %% ===================================================================== t_prim_file_name() -> - t_sup([t_string(), - t_binary()]). + t_sup(t_unicode_string(), t_binary()). %% ===================================================================== %% These are used for the built-in functions of 'gen_tcp' @@ -5136,13 +5144,14 @@ t_re_MP() -> %% it's supposed to be an opaque data type t_tuple([t_atom('re_pattern'), t_integer(), t_integer(), t_binary()]). t_re_RE() -> - t_sup(t_re_MP(), t_iodata()). + t_sup([t_re_MP(), t_iodata(), t_charlist()]). t_re_compile_option() -> - t_sup([t_atoms(['anchored', 'caseless', 'dollar_endonly', 'dotall', - 'extended', 'firstline', 'multiline', 'no_auto_capture', - 'dupnames', 'ungreedy']), - t_tuple([t_atom('newline'), t_re_NLSpec()])]). + t_sup([t_atoms(['unicode', 'anchored', 'caseless', 'dollar_endonly', + 'dotall', 'extended', 'firstline', 'multiline', + 'no_auto_capture', 'dupnames', 'ungreedy']), + t_tuple([t_atom('newline'), t_re_NLSpec()]), + t_atoms(['bsr_anycrlf', 'bsr_unicode'])]). t_re_run_option() -> t_sup([t_atoms(['anchored', 'global', 'notbol', 'noteol', 'notempty']), @@ -5159,7 +5168,7 @@ t_re_Type() -> t_atoms(['index', 'list', 'binary']). t_re_NLSpec() -> - t_atoms(['cr', 'crlf', 'lf', 'anycrlf']). + t_atoms(['cr', 'crlf', 'lf', 'anycrlf', 'any']). t_re_ValueSpec() -> t_sup(t_atoms(['all', 'all_but_first', 'first', 'none']), t_re_ValueList()). diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index 1ed85af172..080d6936b2 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -62,6 +62,7 @@ t_boolean/0, t_byte/0, t_char/0, + t_charlist/0, t_collect_vars/1, t_cons/0, t_cons/2, @@ -195,6 +196,7 @@ t_tuple_size/1, t_tuple_sizes/1, t_tuple_subtypes/1, + t_unicode_string/0, t_unify/2, t_unify/3, t_unit/0, @@ -1455,6 +1457,26 @@ t_is_tuple(_) -> false. %% Non-primitive types, including some handy syntactic sugar types %% +-spec t_unicode_string() -> erl_type(). + +t_unicode_string() -> + t_list(t_unicode_char()). + +-spec t_charlist() -> erl_type(). + +t_charlist() -> + t_charlist(1). + +-spec t_charlist(non_neg_integer()) -> erl_type(). + +t_charlist(N) when N > 0 -> + t_maybe_improper_list(t_sup([t_unicode_char(), + t_unicode_binary(), + t_charlist(N-1)]), + t_sup(t_unicode_binary(), t_nil())); +t_charlist(0) -> + t_maybe_improper_list(t_any(), t_sup(t_unicode_binary(), t_nil())). + -spec t_constant() -> erl_type(). t_constant() -> @@ -1549,6 +1571,16 @@ t_parameterized_module() -> t_timeout() -> t_sup(t_non_neg_integer(), t_atom('infinity')). +-spec t_unicode_binary() -> erl_type(). + +t_unicode_binary() -> + t_binary(). % with characters encoded in UTF-8 coding standard + +-spec t_unicode_char() -> erl_type(). + +t_unicode_char() -> + t_integer(). % representing a valid unicode codepoint + %%----------------------------------------------------------------------------- %% Some built-in opaque types %% @@ -2825,7 +2857,7 @@ t_subtract(?list(Contents1, Termination1, Size1) = T, true -> case {Size1, Size2} of {?nonempty_qual, ?unknown_qual} -> ?none; - {?unknown_qual, ?nonempty_qual} -> Termination1; + {?unknown_qual, ?nonempty_qual} -> ?nil; {S, S} -> ?none end; false -> diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml index cf30db0482..8c9dbc0c18 100644 --- a/lib/hipe/doc/src/notes.xml +++ b/lib/hipe/doc/src/notes.xml @@ -30,6 +30,39 @@ </header> <p>This document describes the changes made to HiPE.</p> +<section><title>Hipe 3.7.8.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Several type specifications for standard libraries were + wrong in the R14B01 release. This is now corrected. The + corrections concern types in re,io,filename and the + module erlang itself.</p> + <p> + Own Id: OTP-9008</p> + </item> + </list> + </section> + +</section> + +<section><title>Hipe 3.7.8</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Compiler warnings were eliminated.</p> + <p> + Own Id: OTP-8855</p> + </item> + </list> + </section> + +</section> + <section><title>Hipe 3.7.7</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl index 920c94d85c..d7eb035551 100644 --- a/lib/hipe/icode/hipe_beam_to_icode.erl +++ b/lib/hipe/icode/hipe_beam_to_icode.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-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 @@ -918,7 +918,7 @@ trans_fun([{bs_add, {f,Lbl}, [Old,New,Unit], Res}|Instructions], Env) -> Succ = mk_label(new), [hipe_icode:mk_primop([Temp], '*', [NewVar, hipe_icode:mk_const(Unit)], - hipe_icode:label_name(Succ), Lbl), + hipe_icode:label_name(Succ), map_label(Lbl)), Succ] end end, @@ -930,7 +930,7 @@ trans_fun([{bs_add, {f,Lbl}, [Old,New,Unit], Res}|Instructions], Env) -> [FailLbl, hipe_icode:mk_fail([hipe_icode:mk_const(badarg)], error)]}; true -> - {Lbl, []} + {map_label(Lbl), []} end, IsPos = [hipe_icode:mk_if('>=', [Temp, hipe_icode:mk_const(0)], diff --git a/lib/hipe/icode/hipe_icode_primops.erl b/lib/hipe/icode/hipe_icode_primops.erl index b0fe7eb708..a413531c07 100644 --- a/lib/hipe/icode/hipe_icode_primops.erl +++ b/lib/hipe/icode/hipe_icode_primops.erl @@ -2,19 +2,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. -%% +%% Copyright Ericsson AB 2001-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 %% 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 online 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. -%% +%% %% %CopyrightEnd% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -26,9 +26,6 @@ %% Notes : %% History : * 2001-06-13 Erik Johansson ([email protected]): %% Created. -%% -%% $Id$ -%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -module(hipe_icode_primops). @@ -197,7 +194,7 @@ fails(#element{}) -> true; %% fails(#gc_test{}) -> ??? fails({hipe_bs_primop, {bs_start_match, _}}) -> true; fails({hipe_bs_primop, {{bs_start_match, bitstr}, _}}) -> true; -fails({hipe_bs_primop, {{bs_start_match, ok_matchstate}, _}}) -> false; +fails({hipe_bs_primop, {{bs_start_match, ok_matchstate}, _}}) -> true; fails({hipe_bs_primop, {bs_get_binary, _, _}}) -> true; fails({hipe_bs_primop, {bs_get_binary_all, _, _}}) -> true; fails({hipe_bs_primop, {bs_get_binary_all_2, _, _}}) -> true; diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk index 8e421ce9b2..513b1f4943 100644 --- a/lib/hipe/vsn.mk +++ b/lib/hipe/vsn.mk @@ -1 +1 @@ -HIPE_VSN = 3.7.7 +HIPE_VSN = 3.7.8.1 diff --git a/lib/ic/doc/src/notes.xml b/lib/ic/doc/src/notes.xml index 6684547572..5f6c31069c 100644 --- a/lib/ic/doc/src/notes.xml +++ b/lib/ic/doc/src/notes.xml @@ -31,6 +31,26 @@ </header> <section> + <title>IC 4.2.26</title> + + <section> + <title>Improvements and New Features</title> + <list type="bulleted"> + <item> + <p> + Partial support for recursive structs and unions. Only available + for the erl_corba backend and requires that Light IFR is used. + I.e. the IC option {light_ifr, true} and that Orber is configured + in such a way that Light IFR is activated. Recursive TypeCode is + currently not supported.</p> + <p> + Own Id: OTP-8868 Aux Id: seq11633</p> + </item> + </list> + </section> + </section> + + <section> <title>IC 4.2.25</title> <section> diff --git a/lib/ic/src/ic_forms.erl b/lib/ic/src/ic_forms.erl index 7409ddeb7b..fc46a2ed40 100644 --- a/lib/ic/src/ic_forms.erl +++ b/lib/ic/src/ic_forms.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2010. 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 @@ -65,6 +65,7 @@ get_line(X) when is_record(X, scoped_id) -> X#scoped_id.line; get_line(X) when is_record(X, module) -> get_line(X#module.id); get_line(X) when is_record(X, interface) -> get_line(X#interface.id); get_line(X) when is_record(X, forward) -> get_line(X#forward.id); +get_line(X) when is_record(X, constr_forward) -> get_line(X#constr_forward.id); get_line(X) when is_record(X, const) -> get_line(X#const.id); get_line(X) when is_record(X, typedef) -> get_line(X#typedef.id); get_line(X) when is_record(X, struct) -> get_line(X#struct.id); @@ -114,6 +115,7 @@ get_line(_) -> -1. get_id2(X) when is_record(X, module) -> get_id(X#module.id); get_id2(X) when is_record(X, interface) -> get_id(X#interface.id); get_id2(X) when is_record(X, forward) -> get_id(X#forward.id); +get_id2(X) when is_record(X, constr_forward) -> get_id(X#constr_forward.id); get_id2(X) when is_record(X, const) -> get_id(X#const.id); get_id2(X) when is_record(X, typedef) -> get_id(hd(X#typedef.id)); get_id2(X) when is_record(X, struct) -> get_id(X#struct.id); @@ -156,6 +158,7 @@ get_type(X) when is_record(X, param) -> X#param.type. %% Temporary place get_tk(X) when is_record(X, interface) -> X#interface.tk; get_tk(X) when is_record(X, forward) -> X#forward.tk; +get_tk(X) when is_record(X, constr_forward) -> X#constr_forward.tk; get_tk(X) when is_record(X, const) -> X#const.tk; get_tk(X) when is_record(X, type_dcl) -> X#type_dcl.tk; get_tk(X) when is_record(X, typedef) -> X#typedef.tk; @@ -228,6 +231,7 @@ clean_up_scope([N|Ns],Found) -> get_type_code2(_, _, X) when is_record(X, interface) -> X#interface.tk; get_type_code2(_, _, X) when is_record(X, forward) -> X#forward.tk; +get_type_code2(_, _, X) when is_record(X, constr_forward) -> X#constr_forward.tk; get_type_code2(_, _, X) when is_record(X, const) -> X#const.tk; get_type_code2(_, _, X) when is_record(X, type_dcl) -> X#type_dcl.tk; get_type_code2(_, _, X) when is_record(X, typedef) -> diff --git a/lib/ic/src/ic_pragma.erl b/lib/ic/src/ic_pragma.erl index 9165e3b03b..45cb64c9c8 100644 --- a/lib/ic/src/ic_pragma.erl +++ b/lib/ic/src/ic_pragma.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2010. 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 @@ -60,7 +60,7 @@ pragma_reg(G,X) -> init_pragma_status(S), registerOptions(G,S), pragma_reg_all(G, S, [], X), - denote_specific_code_opts(G), %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + denote_specific_code_opts(G), case get_pragma_compilation_status(S) of true -> %% Remove ugly pragmas from form @@ -132,6 +132,7 @@ applyCodeOpt(G) -> %% This removes all pragma records from the form. %% When debugged, it can be enbodied in pragma_reg_all. +cleanup(undefined,C) -> C; cleanup([],C) -> C; cleanup([X|Xs],CSF) -> cleanup(Xs, CSF++cleanup(X)). @@ -279,7 +280,12 @@ pragma_reg(G, S, N, X) when is_record(X, union) -> pragma_reg(G, S, N, X) when is_record(X, struct) -> mk_ref(G,[get_id2(X) | N],struct_ref), mk_file_data(G,X,N,struct), - pragma_reg_all(G, S, N, X#struct.body); + case X#struct.body of + undefined -> + ok; + _ -> + pragma_reg_all(G, S, N, X#struct.body) + end; pragma_reg(G, _S, N, X) when is_record(X, attr) -> XX = #id_of{type=X}, diff --git a/lib/ic/src/ic_symtab.erl b/lib/ic/src/ic_symtab.erl index 889c75e3a2..d710154a5d 100644 --- a/lib/ic/src/ic_symtab.erl +++ b/lib/ic/src/ic_symtab.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2010. 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 @@ -69,6 +69,8 @@ store(G, N, X) -> ets:insert(G#genobj.symtab, {Name, X}); {ok, Y} when is_record(Y, forward) -> ets:insert(G#genobj.symtab, {Name, X}); + {ok, Y} when is_record(Y, constr_forward) -> + ets:insert(G#genobj.symtab, {Name, X}); {ok, _Y} -> ic_error:error(G, {multiply_defined, X}) end. diff --git a/lib/ic/src/icforms.hrl b/lib/ic/src/icforms.hrl index d1869e6330..1b394a11b4 100644 --- a/lib/ic/src/icforms.hrl +++ b/lib/ic/src/icforms.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2010. 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 @@ -34,6 +34,7 @@ -record(module, {id, body}). -record(interface, {id, inherit, body, inherit_body, tk}). -record(forward, {id, tk}). +-record(constr_forward, {id, tk}). -record(const, {type, id, val, tk}). -record(type_dcl, {type, tk}). -record(typedef, {type, id, tk}). diff --git a/lib/ic/src/icparse.yrl b/lib/ic/src/icparse.yrl index 25b0f452e7..d0dd6cde4c 100644 --- a/lib/ic/src/icparse.yrl +++ b/lib/ic/src/icparse.yrl @@ -1,21 +1,20 @@ -%%<copyright> -%% <year>1997-2007</year> -%% <holder>Ericsson AB, All Rights Reserved</holder> -%%</copyright> -%%<legalnotice> +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2010. 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 %% 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 online 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 AB. -%%</legalnotice> +%% +%% %CopyrightEnd% %% %%------------------------------------------------------------ %% Yecc spec for IDL @@ -150,6 +149,7 @@ Nonterminals 'ZorM_<integer_literal>' '<fixed_pt_type>' '<fixed_pt_const_type>' + '<constr_forward_decl>' . @@ -473,6 +473,7 @@ OE_preproc -> '#' '<integer_literal>' '<string_literal>' '<type_dcl>' -> '<struct_type>' : '$1' . '<type_dcl>' -> '<union_type>' : '$1' . '<type_dcl>' -> '<enum_type>' : '$1' . +'<type_dcl>' -> '<constr_forward_decl>' : '$1' . %% (28) NIY multiple declarators (FIXED) '<type_declarator>' -> '<type_spec>' '<declarators>' @@ -832,6 +833,9 @@ OE_preproc -> '#' '<integer_literal>' '<string_literal>' '<fixed_pt_type>' -> 'fixed' '<' '<positive_int_const>' ',' '<positive_int_const>' '>' : #fixed{digits='$3',scale='$5'} . +%% (99) +'<constr_forward_decl>' -> 'struct' '<identifier>' : #constr_forward{id='$2', tk=tk_struct} . +'<constr_forward_decl>' -> 'union' '<identifier>' : #constr_forward{id='$2', tk=tk_union} . %% Added clause 'ZorM_<string_literal>' -> '$empty' : [] . diff --git a/lib/ic/src/ictype.erl b/lib/ic/src/ictype.erl index 4704191bee..9e20801464 100644 --- a/lib/ic/src/ictype.erl +++ b/lib/ic/src/ictype.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2010. 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 @@ -407,6 +407,18 @@ check(G, S, N, X) when is_record(X, forward) -> tktab_add(G, S, N, X, {tk_objref, ictk:get_IR_ID(G, N, X), ic_forms:get_id2(X)}), X; +check(G, S, N, #constr_forward{tk = tk_struct} = X) -> + ?STDDBG, + ID = ic_forms:get_id2(X), + Module = list_to_atom(string:join(lists:reverse([ID|N]), "_")), + tktab_add(G, S, N, X, {tk_struct, ictk:get_IR_ID(G, N, X), ID, Module}), + X; +check(G, S, N, #constr_forward{tk = tk_union} = X) -> + ?STDDBG, + ID = ic_forms:get_id2(X), + Module = list_to_atom(string:join(lists:reverse([ID|N]), "_")), + tktab_add(G, S, N, X, {tk_union, ictk:get_IR_ID(G, N, X), ID, [], [], Module}), + X; check(G, S, N, X) when is_record(X, const) -> ?STDDBG, @@ -427,21 +439,6 @@ check(G, S, N, X) when is_record(X, const) -> end end; -check(G, S, N, X) when is_record(X, const) -> - ?STDDBG, - case tk_base(G, S, N, ic_forms:get_type(X)) of - Err when element(1, Err) == error -> X; - TK -> - check_const_tk(G, S, N, X, TK), - case iceval:eval_const(G, S, N, TK, X#const.val) of - Err when element(1, Err) == error -> X; - Val -> - V = iceval:get_val(Val), - tktab_add(G, S, N, X, TK, V), - X#const{val=V, tk=TK} - end - end; - check(G, S, N, X) when is_record(X, except) -> ?STDDBG, TK = tk(G, S, N, X), @@ -795,9 +792,15 @@ tktab_add_id(G, S, N, X, Id, TK, Aux) -> Name = [Id | N], UName = mk_uppercase(Name), case ets:lookup(S, Name) of - [{_, forward, _, _}] when is_record(X, interface) -> ok; - [XX] when is_record(X, forward) andalso element(2, XX)==interface -> ok; - [_] -> ic_error:error(G, {multiply_defined, X}); + [{_, forward, _, _}] when is_record(X, interface) -> + ok; + [{_, constr_forward, _, _}] when is_record(X, union) orelse + is_record(X, struct) -> + ok; + [XX] when is_record(X, forward) andalso element(2, XX)==interface -> + ok; + [_] -> + ic_error:error(G, {multiply_defined, X}); [] -> case ets:lookup(S, UName) of [] -> ok; diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk index 074d0b3d39..6d6c7fa625 100644 --- a/lib/ic/vsn.mk +++ b/lib/ic/vsn.mk @@ -1 +1 @@ -IC_VSN = 4.2.25 +IC_VSN = 4.2.26 diff --git a/lib/inets/doc/src/http_server.xml b/lib/inets/doc/src/http_server.xml index 68dfd1add0..47ed9cd229 100644 --- a/lib/inets/doc/src/http_server.xml +++ b/lib/inets/doc/src/http_server.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> @@ -766,7 +766,7 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[ <code> -module(mnesia_test). -export([start/0,load_data/0]). --include("mod_auth.hrl"). +-include_lib("mod_auth.hrl"). first_start() -> mnesia:create_schema([node()]), diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 7b16189860..11b0af4310 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -32,7 +32,50 @@ <file>notes.xml</file> </header> - <section><title>Inets 5.5</title> + <section><title>Inets 5.5.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix format_man_pages so it handles all man sections + and remove warnings/errors in various man pages. </p> + <p> + Own Id: OTP-8600</p> + </item> + <item> + <p> + [httpc] Pipelined and queued requests not processed when + connection closed remotelly.</p> + <p> + Own Id: OTP-8906</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Miscellaneous inet6 related problems.</p> + <p> + Own Id: OTP-8927</p> + </item> + <item> + <p> + Updated http-server to make sure URLs in error-messages + are URL-encoded. Added support in http-client to use + URL-encoding. Also added the missing include directory + for the inets application.</p> + <p> + Own Id: OTP-8940 Aux Id: seq11735 </p> + </item> + </list> + </section> + +</section> + +<section><title>Inets 5.5</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/inets/include/mod_auth.hrl b/lib/inets/include/mod_auth.hrl new file mode 100644 index 0000000000..cf931e681a --- /dev/null +++ b/lib/inets/include/mod_auth.hrl @@ -0,0 +1,33 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2010. 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 +%% 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 online 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. +%% +%% %CopyrightEnd% +%% +%% + +-ifndef(mod_auth_hrl). +-define(mod_auth_hrl, true). + +-record(httpd_user, + {username, + password, + user_data}). + +-record(httpd_group, + {name, + userlist}). + +-endif. % -ifdef(mod_auth_hrl). diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile index bdd8c5ee3c..55cc68dede 100644 --- a/lib/inets/src/http_server/Makefile +++ b/lib/inets/src/http_server/Makefile @@ -82,9 +82,7 @@ MODULES = \ mod_security \ mod_security_server -INCLUDE = ../../include - -HRL_FILES = $(INCLUDE)/httpd.hrl httpd_internal.hrl mod_auth.hrl +HRL_FILES = httpd.hrl httpd_internal.hrl mod_auth.hrl ERL_FILES = $(MODULES:%=%.erl) @@ -100,7 +98,6 @@ include ../inets_app/inets.mk ERL_COMPILE_FLAGS += \ $(INETS_FLAGS) \ $(INETS_ERL_COMPILE_FLAGS) \ - -I$(INCLUDE) \ -I../inets_app \ -I../http_lib \ diff --git a/lib/inets/src/http_server/httpd.hrl b/lib/inets/src/http_server/httpd.hrl new file mode 100644 index 0000000000..4eba833e2c --- /dev/null +++ b/lib/inets/src/http_server/httpd.hrl @@ -0,0 +1,27 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2010. 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 +%% 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 online 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. +%% +%% %CopyrightEnd% +%% +%% This is a simple wrapper for code that has not been updated to +%% handle the move of this file to the include dir. + +-ifndef(src_httpd_hrl). +-define(src_httpd_hrl, true). + +-include_lib("inets/include/httpd.hrl"). + +-endif. % -ifdef(src_httpd_hrl). diff --git a/lib/inets/src/http_server/mod_auth.hrl b/lib/inets/src/http_server/mod_auth.hrl index 9b316cecc4..674e6d1652 100644 --- a/lib/inets/src/http_server/mod_auth.hrl +++ b/lib/inets/src/http_server/mod_auth.hrl @@ -1,29 +1,27 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-2010. 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 %% 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 online 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. -%% -%% %CopyrightEnd% %% +%% %CopyrightEnd% %% +%% This is a simple wrapper for code that has not been updated to +%% handle the move of this file to the include dir. + +-ifndef(src_mod_auth_hrl). +-define(src_mod_auth_hrl, true). --record(httpd_user, - {username, - password, - user_data}). +-include_lib("inets/include/mod_auth.hrl"). --record(httpd_group, - {name, - userlist}). - +-endif. % -ifdef(src_mod_auth_hrl). diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile index 4632ff3b68..20e22917e2 100644 --- a/lib/inets/src/inets_app/Makefile +++ b/lib/inets/src/inets_app/Makefile @@ -47,7 +47,9 @@ MODULES = \ inets_sup \ inets_regexp -HRL_FILES = inets_internal.hrl +INTERNAL_HRL_FILES = inets_internal.hrl +EXTERNAL_HRL_FILES = ../../include/httpd.hrl \ + ../../include/mod_auth.hrl ERL_FILES = $(MODULES:%=%.erl) @@ -74,8 +76,7 @@ include inets.mk ERL_COMPILE_FLAGS += \ $(INETS_FLAGS) \ - $(INETS_ERL_COMPILE_FLAGS) \ - -I../../include + $(INETS_ERL_COMPILE_FLAGS) # ---------------------------------------------------- @@ -110,7 +111,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt $(INSTALL_DIR) $(RELSYSDIR)/src $(INSTALL_DIR) $(RELSYSDIR)/src/inets_app - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/inets_app + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/inets_app + $(INSTALL_DIR) $(RELSYSDIR)/include + $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(RELSYSDIR)/include $(INSTALL_DIR) $(RELSYSDIR)/ebin $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index f462290a99..67737ee552 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.5.1 +INETS_VSN = 5.5.2 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml index a571de6916..879634561b 100644 --- a/lib/jinterface/doc/src/notes.xml +++ b/lib/jinterface/doc/src/notes.xml @@ -30,6 +30,22 @@ </header> <p>This document describes the changes made to the Jinterface application.</p> +<section><title>Jinterface 1.5.3.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The OtpMbox class did not have a hash() method, which it + should have because it overrides equals().</p> + <p> + Own Id: OTP-8854</p> + </item> + </list> + </section> + +</section> + <section><title>Jinterface 1.5.3.1</title> <section><title>Improvements and New Features</title> diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java index ab0b299bf9..9ba6a4a0ab 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2009. All Rights Reserved. + * Copyright Ericsson AB 2000-2010. 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 @@ -68,7 +68,6 @@ public abstract class AbstractConnection extends Thread { protected static final int sendTag = 2; protected static final int exitTag = 3; protected static final int unlinkTag = 4; - protected static final int nodeLinkTag = 5; protected static final int regSendTag = 6; protected static final int groupLeaderTag = 7; protected static final int exit2Tag = 8; @@ -697,7 +696,6 @@ public abstract class AbstractConnection extends Thread { // absolutely no idea what to do with these, so we ignore // them... case groupLeaderTag: // { GROUPLEADER, FromPid, ToPid} - case nodeLinkTag: // { NODELINK } // (just show trace) if (traceLevel >= ctrlThreshold) { System.out.println("<- " + headerType(head) + " " @@ -880,9 +878,6 @@ public abstract class AbstractConnection extends Thread { case unlinkTag: return "UNLINK"; - case nodeLinkTag: - return "NODELINK"; - case regSendTag: return "REG_SEND"; diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java index 80d8a5ccae..6f507bf4bb 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2009. All Rights Reserved. + * Copyright Ericsson AB 2000-2010. 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 @@ -54,7 +54,6 @@ public class OtpMsg { public static final int sendTag = 2; public static final int exitTag = 3; public static final int unlinkTag = 4; - /* public static final int nodeLinkTag = 5; */ public static final int regSendTag = 6; /* public static final int groupLeaderTag = 7; */ public static final int exit2Tag = 8; diff --git a/lib/jinterface/vsn.mk b/lib/jinterface/vsn.mk index ed085b5d4d..24ffe7c5e6 100644 --- a/lib/jinterface/vsn.mk +++ b/lib/jinterface/vsn.mk @@ -1 +1 @@ -JINTERFACE_VSN = 1.5.3.1 +JINTERFACE_VSN = 1.5.3.2 diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index d3441d3623..36fce464c5 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -660,10 +660,10 @@ f.txt: {person, "kalle", 25}. </func> <func> <name>native_name_encoding() -> latin1 | utf8</name> - <fsummary>Retunr the VMs configure filename encoding.</fsummary> + <fsummary>Return the VM's configured filename encoding.</fsummary> <desc> <p>This function returns the configured default file name encoding to use for raw file names. Generally an application supplying file names raw (as binaries), should obey the character encoding returned by this function.</p> - <p>By default, the VM uses ISO-latin-1 file name encoding on filesystems and/or OSes that use completely transparent file naming. This includes all Unix versions except for MacOSX, where the vfs layer enforces UTF-8 file naming. By giving the experimental option <c>+fnu</c> when starting Erlang, UTF-8 translation of file names can be turned on even for those systems. If Unicode file name translation is in effect, the system behaves as usual as long as file names conform to the encoding, but will return file names that are not properly encoded in UTF-8 as raw file names (i.e. binaries).</p> + <p>By default, the VM uses ISO-latin-1 file name encoding on filesystems and/or OSes that use completely transparent file naming. This includes all Unix versions except MacOSX, where the vfs layer enforces UTF-8 file naming. By giving the experimental option <c>+fnu</c> when starting Erlang, UTF-8 translation of file names can be turned on even for those systems. If Unicode file name translation is in effect, the system behaves as usual as long as file names conform to the encoding, but will return file names that are not properly encoded in UTF-8 as raw file names (i.e. binaries).</p> <p>On Windows, this function also returns <c>utf8</c> by default. The OS uses a pure Unicode naming scheme and file names are always possible to interpret as valid Unicode. The fact that the underlying Windows OS actually encodes file names using little endian UTF-16 can be ignored by the Erlang programmer. Windows and MacOSX are the only operating systems where the VM operates in Unicode file name mode by default.</p> </desc> </func> diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index edd6ea52b0..29580a4cd1 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -30,6 +30,45 @@ </header> <p>This document describes the changes made to the Kernel application.</p> +<section><title>Kernel 2.14.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The Erlang VM now supports Unicode filenames. The feature + is turned on by default on systems where Unicode + filenames are mandatory (Windows and MacOSX), but can be + enabled on other systems with the '+fnu' emulator option. + Enabling the Unicode filename feature on systems where it + is not default is however considered experimental and not + to be used for production. Together with the Unicode file + name support, the concept of "raw filenames" is + introduced, which means filenames provided without + implicit unicode encoding translation. Raw filenames are + provided as binaries, not lists. For further information, + see stdlib users guide and the chapter about using + Unicode in Erlang. Also see the file module manual page.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-8887</p> + </item> + <item> + <p> + There is now a new function inet:getifaddrs/0 modeled + after C library function getifaddrs() on BSD and LInux + that reports existing interfaces and their addresses on + the host. This replaces the undocumented and unsupported + inet:getiflist/0 and inet:ifget/2.</p> + <p> + Own Id: OTP-8926</p> + </item> + </list> + </section> + +</section> + <section><title>Kernel 2.14.1.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index 21a96f804a..a215ec3608 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -845,13 +845,16 @@ monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) -> ?line {ok, Node} = start_node(Name, "", this), ?line receive {nodeup, Node} -> ok end, - ?line spawn(Node, + ?line RemotePid = spawn(Node, fun () -> - receive after 1000 -> ok end, - lists:foreach(fun (No) -> - Me ! {NodeMsg, No} - end, - Seq), + receive after 1500 -> ok end, + % infinit loop of msgs + % we want an endless stream of messages and the kill + % the node mercilessly. + % We then want to ensure that the nodedown message arrives + % last ... without garbage after it. + Pid = spawn(fun() -> node_loop_send(Me, NodeMsg, 1) end), + receive {Me, kill_it} -> ok end, halt() end), @@ -860,9 +863,11 @@ monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) -> %% Verify that '{nodeup, Node}' comes before '{NodeMsg, 1}' (the message %% bringing up the connection). - %%?line no_msgs(500), % Why wait? It fails test sometimes /sverker + ?line no_msgs(500), ?line {nodeup, Node} = receive Msg1 -> Msg1 end, - ?line {NodeMsg, 1} = receive Msg2 -> Msg2 end, + ?line {NodeMsg, 1} = receive Msg2 -> Msg2 end, + % msg stream has begun, kill the node + ?line RemotePid ! {self(), kill_it}, %% Verify that '{nodedown, Node}' comes after the last '{NodeMsg, N}' %% message. @@ -883,6 +888,10 @@ flush_node_msgs(NodeMsg, No) -> OtherMsg -> OtherMsg end. +node_loop_send(Pid, Msg, No) -> + Pid ! {Msg, No}, + node_loop_send(Pid, Msg, No + 1). + monitor_nodes_errors(doc) -> []; monitor_nodes_errors(suite) -> diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl index fea4df8539..fbafbcd9b7 100644 --- a/lib/kernel/test/file_name_SUITE.erl +++ b/lib/kernel/test/file_name_SUITE.erl @@ -507,8 +507,16 @@ check_very_icky(Mod) -> end, ?line {NumOK,NumNOK} = filelib:fold_files(".",".*",true,fun(_F,{N,M}) when is_list(_F) -> io:format("~ts~n",[_F]),{N+1,M}; (_F,{N,M}) -> io:format("~p~n",[_F]),{N,M+1} end,{0,0}), ?line ok = filelib:fold_files(".",[1076,1089,1072,124,46,42],true,fun(_F,_) -> ok end,false), - ?line SF3 = unicode:characters_to_binary("���subfil3",file:native_name_encoding()), - ?line Sorted = lists:sort([SF3,<<"���subfil2">>]), + ?line SF3 = unicode:characters_to_binary("���subfil3", + file:native_name_encoding()), + ?line SF2 = case treat_icky(<<"���subfil2">>) of + LF2 when is_list(LF2) -> + unicode:characters_to_binary(LF2, + file:native_name_encoding()); + BF2 -> + BF2 + end, + ?line Sorted = lists:sort([SF3,SF2]), ?line Sorted = lists:sort(filelib:wildcard("*",<<"���subdir2">>)), ok catch diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 03fe63e385..e33b90a274 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 2.14.2 +KERNEL_VSN = 2.14.3 diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml index 2352f11b93..5a6de05c8b 100644 --- a/lib/mnesia/doc/src/notes.xml +++ b/lib/mnesia/doc/src/notes.xml @@ -38,7 +38,36 @@ thus constitutes one section in this document. The title of each section is the version number of Mnesia.</p> - <section><title>Mnesia 4.4.15</title> + <section><title>Mnesia 4.4.16</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Sometimes a 'log_header' record was added to tables when + invoking mnesia:restore/2 with the option + 'recreate_tables'. Thanks Vance Shipley.</p> + <p> + Own Id: OTP-8960</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Compiler warnings were eliminated.</p> + <p> + Own Id: OTP-8855</p> + </item> + </list> + </section> + +</section> + +<section><title>Mnesia 4.4.15</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml index 3d7c4fa269..76c13fb3ff 100644 --- a/lib/observer/doc/src/notes.xml +++ b/lib/observer/doc/src/notes.xml @@ -31,6 +31,23 @@ <p>This document describes the changes made to the Observer application.</p> +<section><title>Observer 0.9.8.4</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The multitrace.erl installation example file is now + installed in the examples directory. (Thanks to Peter + Lemenkov.)</p> + <p> + Own Id: OTP-8857</p> + </item> + </list> + </section> + +</section> + <section><title>Observer 0.9.8.3</title> <section><title>Improvements and New Features</title> diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk index 499cce6b97..1b72d30eab 100644 --- a/lib/observer/vsn.mk +++ b/lib/observer/vsn.mk @@ -1 +1 @@ -OBSERVER_VSN = 0.9.8.3 +OBSERVER_VSN = 0.9.8.4 diff --git a/lib/odbc/doc/src/notes.xml b/lib/odbc/doc/src/notes.xml index 09d78c3248..7dece7c584 100644 --- a/lib/odbc/doc/src/notes.xml +++ b/lib/odbc/doc/src/notes.xml @@ -31,7 +31,31 @@ <p>This document describes the changes made to the odbc application. </p> - <section><title>ODBC 2.10.8</title> + <section><title>ODBC 2.10.9</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Ipv6 is now supported on Windows as well as on UNIX for + internal socket communication. (ODBC uses sockets instead + of the "Erlang port pipes" as some ODBC-drivers are known + to mess with stdin/stdout.) </p> + <p> + Loopback address constants are used when connecting the + c-side to the erlang-side over local socket API avoiding + getaddrinfo problems, and the {ip, loopback} option is + added as a listen option on the erlang-side. Also cleaned + up the TIME_STAMP contribution.</p> + <p> + Own Id: OTP-8917</p> + </item> + </list> + </section> + +</section> + +<section><title>ODBC 2.10.8</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml b/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml index a97ad65f0e..964ae3e92d 100644 --- a/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml +++ b/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -445,7 +445,19 @@ void op(in myEnum a);</cell> <section> <title>Struct Data Type</title> <p>A <c>struct</c> may have Basic, Template, Scoped Names and Constructed - types as members.</p> + types as members. By using forward declaration we can define a recursive struct:</p> + <code type="none"><![CDATA[ +struct myStruct; // Forward declaration +typedef sequence<myStruct> myStructSeq; +struct myStruct { + myStructSeq chain; +}; + +// Deprecated definition (anonymous) not supported by IC +struct myStruct { + sequence<myStruct> chain; +}; + ]]></code> </section> <section> @@ -510,6 +522,25 @@ union LongUnion2 switch(long) { default: boolean DefaultValue; }; </code> + <p>In the same way as structs, unions can be recursive if forward + declaration is used (anonymous types is deprecated and not supported):</p> + <code type="none"><![CDATA[ +// Forward declaration +union myUnion; +typedef sequence<myUnion>myUnionSeq; +union myUnion switch (long) { + case 1 : myUnionSeq chain; + default: boolean DefaultValue; +}; + ]]></code> + + <note> + <p>Recursive types (union and struct) require Light IFR. I.e. the + IC option {light_ifr, true} is used and that Orber is configured in such a way that + Light IFR is activated. Recursive TypeCode is currently not supported, which is + why these cannot be encapsulated in an any data type.</p> + </note> + </section> <warning> <p>Every field in, for example, a struct must be initiated. Otherwise @@ -890,7 +921,7 @@ attribute long RWAttribute; object internal state with its object reference. The object internal state is an Erlang term which has a format defined by the user.</p> <note> - <p>It is is not always the case that the internal state will be the first parameter, as stubs can use their own object reference as the first parameter (see the IC documentation).</p> + <p>It is not always the case that the internal state will be the first parameter, as stubs can use their own object reference as the first parameter (see the IC documentation).</p> </note> <p>A function call will invoke an operation. The first parameter of the function should be the object reference and then diff --git a/lib/orber/doc/src/notes.xml b/lib/orber/doc/src/notes.xml index 6eda16a517..ba16682f0b 100644 --- a/lib/orber/doc/src/notes.xml +++ b/lib/orber/doc/src/notes.xml @@ -33,6 +33,36 @@ </header> <section> + <title>Orber 3.6.19</title> + + <section> + <title>Improvements and New Features</title> + <list type="bulleted"> + <item> + <p> + Partial support for recursive structs and unions. + Only available for the erl_corba backend and requires + that Light IFR is used. I.e. the IC option {light_ifr, true} + and that Orber is configured in such a way that Light IFR + is activated. Recursive TypeCode is currently not supported.</p> + <p> + Own Id: OTP-8868 Aux Id: seq11633</p> + </item> + </list> + </section> + <section> + <title>Fixed Bugs and Malfunctions</title> + <list type="bulleted"> + <item> + <p>The SSL option {ssl_imp, old} was not used if ssl_generation was + set to 2. Only R14B was affected by this.</p> + <p>Own Id: OTP-8994 Aux Id: seq11747</p> + </item> + </list> + </section> + </section> + + <section> <title>Orber 3.6.18</title> <section> <title>Fixed Bugs and Malfunctions</title> diff --git a/lib/orber/src/cdr_decode.erl b/lib/orber/src/cdr_decode.erl index 9d30098940..36ef6ce02f 100644 --- a/lib/orber/src/cdr_decode.erl +++ b/lib/orber/src/cdr_decode.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2010. 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 @@ -898,9 +898,13 @@ dec_sequence_struct(Version, Message, N, TypeCodeList, Len, ByteOrder, Buff, C, {Seq, Rest2, Len2, NewC2} = dec_sequence_struct(Version, Rest1, N - 1, TypeCodeList, Len1, ByteOrder, Buff, NewC, Name), {[list_to_tuple([Name |Struct]) | Seq], Rest2, Len2, NewC2}. -dec_sequence_union(_, Message, 0, _DiscrTC, _Default, _ElementList, Len, _ByteOrder, _Buff, C, _Name) -> + + +dec_sequence_union(_, Message, 0, _DiscrTC, _Default, _ElementList, + Len, _ByteOrder, _Buff, C, _Name) -> {[], Message, Len, C}; -dec_sequence_union(Version, Message, N, DiscrTC, Default, ElementList, Len, ByteOrder, Buff, C, Name) -> +dec_sequence_union(Version, Message, N, DiscrTC, Default, ElementList, + Len, ByteOrder, Buff, C, Name) when is_list(ElementList) -> {Label, Rest1, Len1, NewC} = dec_type(DiscrTC, Version, Message, Len, ByteOrder, Buff, C), Result = dec_union(Version, stringify_enum(DiscrTC, Label), ElementList, Default, @@ -916,7 +920,20 @@ dec_sequence_union(Version, Message, N, DiscrTC, Default, ElementList, Len, Byte DiscrTC, Default, ElementList, Len2, ByteOrder, Buff, NewC3, Name), - {[{Name, Label, Value} | Seq], Rest3, Len3, NewC4}. + {[{Name, Label, Value} | Seq], Rest3, Len3, NewC4}; +dec_sequence_union(Version, Message, N, _DiscrTC, _Default, Module, + Len, ByteOrder, Buff, C, Name) when is_atom(Module) -> + case catch Module:tc() of + {tk_union, _, _, DiscrTC, Default, ElementList} -> + dec_sequence_union(Version, Message, N, DiscrTC, Default, ElementList, + Len, ByteOrder, Buff, C, Name); + What -> + orber:dbg("[~p] ~p:dec_sequence_union(~p). Union module doesn't exist or incorrect.", + [?LINE, ?MODULE, What], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}) + end. + + %% A special case; when something is encapsulated (i.e. sent as octet-sequence) %% we sometimes don not want the result to be converted to a list. @@ -993,14 +1010,16 @@ dec_wstring(Version, Message, Len, ByteOrder, Buff, C) -> %% Func: dec_union/9 %%----------------------------------------------------------------- %% ## NEW IIOP 1.2 ## -dec_union(Version, ?SYSTEM_TYPE, Name, DiscrTC, Default, ElementList, Bytes, Len, ByteOrder, Buff, C) -> +dec_union(Version, ?SYSTEM_TYPE, Name, DiscrTC, Default, ElementList, Bytes, + Len, ByteOrder, Buff, C) -> {Label, Rest1, Len1, NewC} = dec_type(DiscrTC, Version, Bytes, Len, ByteOrder, Buff, C), {Value, Rest2, Len2, NewC3} = dec_union(Version, Label, ElementList, Default, Rest1, Len1, ByteOrder, Buff, NewC), {{Name, Label, Value}, Rest2, Len2, NewC3}; -dec_union(Version, IFRId, _, DiscrTC, Default, ElementList, Bytes, Len, ByteOrder, Buff, C) -> +dec_union(Version, IFRId, _, DiscrTC, Default, ElementList, Bytes, Len, + ByteOrder, Buff, C) when is_list(ElementList) -> {Label, Rest1, Len1, NewC} = dec_type(DiscrTC, Version, Bytes, Len, ByteOrder, Buff, C), Result = dec_union(Version, stringify_enum(DiscrTC, Label), ElementList, Default, Rest1, Len1, ByteOrder, Buff, NewC), @@ -1012,7 +1031,20 @@ dec_union(Version, IFRId, _, DiscrTC, Default, ElementList, Bytes, Len, ByteOrde X end, Name = ifrid_to_name(IFRId, ?IFR_UnionDef), - {{Name, Label, Value}, Rest2, Len2, NewC3}. + {{Name, Label, Value}, Rest2, Len2, NewC3}; +dec_union(Version, IFRId, _, _DiscrTC, _Default, Module, Bytes, Len, + ByteOrder, Buff, C) when is_atom(Module) -> + case catch Module:tc() of + {tk_union, _, Name, DiscrTC, Default, ElementList} -> + dec_union(Version, IFRId, Name, DiscrTC, Default, ElementList, Bytes, Len, + ByteOrder, Buff, C); + What -> + orber:dbg("[~p] ~p:dec_union(~p). Union module doesn't exist or incorrect.", + [?LINE, ?MODULE, What], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}) + end. + + dec_union(_, _, [], Default, Message, Len, _, _Buff, C) when Default < 0 -> {undefined, Message, Len, C}; @@ -1047,7 +1079,16 @@ dec_struct1(_, [], Message, Len, _ByteOrder, _, C) -> dec_struct1(Version, [{_ElemName, ElemType} | TypeCodeList], Message, Len, ByteOrder, Buff, C) -> {Element, Rest, Len1, NewC} = dec_type(ElemType, Version, Message, Len, ByteOrder, Buff, C), {Struct, Rest1, Len2, NewC2} = dec_struct1(Version, TypeCodeList, Rest, Len1, ByteOrder, Buff, NewC), - {[Element |Struct], Rest1, Len2, NewC2}. + {[Element |Struct], Rest1, Len2, NewC2}; +dec_struct1(Version, Module, Message, Len, ByteOrder, Buff, C) -> + case catch Module:tc() of + {tk_struct, _, _, TypeCodeList} -> + dec_struct1(Version, TypeCodeList, Message, Len, ByteOrder, Buff, C); + What -> + orber:dbg("[~p] ~p:dec_struct1(~p). Struct module doesn't exist or incorrect.", + [?LINE, ?MODULE, What], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}) + end. ifrid_to_name([], Type) -> orber:dbg("[~p] ~p:ifrid_to_name([], ~p). No Id supplied.", @@ -1232,7 +1273,9 @@ get_user_exception_type(TypeId) -> %%----------------------------------------------------------------- dec_type_code(Version, Message, Len, ByteOrder, Buff, C) -> {TypeNo, Message1, Len1, NewC} = dec_type('tk_ulong', Version, Message, Len, ByteOrder, Buff, C), - dec_type_code(TypeNo, Version, Message1, Len1, ByteOrder, Buff, NewC). + TC = dec_type_code(TypeNo, Version, Message1, Len1, ByteOrder, Buff, NewC), + erase(orber_indirection), + TC. %%----------------------------------------------------------------- %% Func: dec_type_code/5 @@ -1441,13 +1484,22 @@ dec_type_code(33, Version, Message, Len, ByteOrder, Buff, C) -> {"name", {'tk_string', 0}}]}, Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex), {{'tk_local_interface', RepId, Name}, Message1, Len1, NewC}; -dec_type_code(16#ffffffff, Version, Message, Len, ByteOrder, Buff, C) -> %% placeholder +dec_type_code(16#ffffffff, Version, Message, Len, ByteOrder, Buff, C) -> {Indirection, Message1, Len1, NewC} = dec_type('tk_long', Version, Message, Len, ByteOrder, Buff, C), Position = C+Indirection, - <<_:Position/binary, SubBuff/binary>> = Buff, - {TC, _, _, _} = dec_type_code(Version, SubBuff, Position, ByteOrder, Buff, Position), - {TC, Message1, Len1, NewC}; + case put(orber_indirection, Position) of + Position -> +%% {{'none', Indirection}, Message1, Len1, NewC}; + %% Recursive TypeCode. Break the loop. + orber:dbg("[~p] cdr_decode:dec_type_code(~p); Recursive TC not supported.", + [?LINE,Position], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO}); + _ -> + <<_:Position/binary, SubBuff/binary>> = Buff, + {TC, _, _, _} = dec_type_code(Version, SubBuff, Position, ByteOrder, Buff, Position), + {TC, Message1, Len1, NewC} + end; dec_type_code(Type, _, _, _, _, _, _) -> orber:dbg("[~p] cdr_decode:dec_type_code(~p); No match.", [?LINE, Type], ?DEBUG_LEVEL), diff --git a/lib/orber/src/cdr_encode.erl b/lib/orber/src/cdr_encode.erl index 3ecb8833f5..eaf3c5b7dc 100644 --- a/lib/orber/src/cdr_encode.erl +++ b/lib/orber/src/cdr_encode.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2010. 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 @@ -815,11 +815,21 @@ enc_wstring(Env, String, MaxLength, Bytes, Len) -> %%----------------------------------------------------------------- %% Func: enc_union/5 %%----------------------------------------------------------------- -enc_union(Env, {_, Label, Value}, DiscrTC, Default, TypeCodeList, Bytes, Len) -> +enc_union(Env, {_, Label, Value}, DiscrTC, Default, TypeCodeList, + Bytes, Len) when is_list(TypeCodeList) -> {ByteSequence, Len1} = enc_type(DiscrTC, Env, Label, Bytes, Len), Label2 = stringify_enum(DiscrTC,Label), enc_union2(Env, {Label2, Value},TypeCodeList, Default, - ByteSequence, Len1, undefined). + ByteSequence, Len1, undefined); +enc_union(Env, Value, _DiscrTC, _Default, Module, Bytes, Len) when is_atom(Module) -> + case catch Module:tc() of + {tk_union, _, _, DiscrTC, Default, ElementList} -> + enc_union(Env, Value, DiscrTC, Default, ElementList, Bytes, Len); + What -> + orber:dbg("[~p] ~p:enc_union(~p). Union module doesn't exist or incorrect.", + [?LINE, ?MODULE, What], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}) + end. enc_union2(_Env, _What, [], Default, Bytes, Len, _) when Default < 0 -> {Bytes, Len}; @@ -840,9 +850,19 @@ stringify_enum(_, Label) -> %%----------------------------------------------------------------- %% Func: enc_struct/4 %%----------------------------------------------------------------- -enc_struct(Env, Struct, TypeCodeList, Bytes, Len) -> +enc_struct(Env, Struct, TypeCodeList, Bytes, Len) when is_list(TypeCodeList) -> [_Name | StructList] = tuple_to_list(Struct), - enc_struct1(Env, StructList, TypeCodeList, Bytes, Len). + enc_struct1(Env, StructList, TypeCodeList, Bytes, Len); +enc_struct(Env, Struct, Module, Bytes, Len) -> + [Module | StructList] = tuple_to_list(Struct), + case catch Module:tc() of + {tk_struct, _, _, TypeCodeList} -> + enc_struct1(Env, StructList, TypeCodeList, Bytes, Len); + What -> + orber:dbg("[~p] ~p:enc_struct([], ~p). Struct module doesn't exist or incorrect.", + [?LINE, ?MODULE, What], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}) + end. enc_struct1(_Env, [], [], Bytes, Len) -> {Bytes, Len}; diff --git a/lib/orber/src/orber_socket.erl b/lib/orber/src/orber_socket.erl index af6df01b7d..84ed193ebb 100644 --- a/lib/orber/src/orber_socket.erl +++ b/lib/orber/src/orber_socket.erl @@ -496,27 +496,17 @@ check_port(Port, _, _) -> %%----------------------------------------------------------------- %% Check Options. -%% We need this as a work-around since the SSL-app doesn't allow us -%% to pass 'inet' as an option. Also needed for R9B :-( check_options(normal, Options, _Generation) -> - case orber:ip_version() of - inet -> - Options; - inet6 -> - %% Necessary for R9B. Should be [orber:ip_version()|Options]; - [inet6|Options] - end; + [orber:ip_version()|Options]; check_options(ssl, Options, Generation) -> case orber:ip_version() of inet when Generation > 2 -> [{ssl_imp, new}|Options]; inet -> - Options; + [{ssl_imp, old}|Options]; inet6 when Generation > 2 -> [{ssl_imp, new}, inet6|Options]; inet6 -> - %% Will fail until SSL supports this option. - %% Note, we want this happen! - [inet6|Options] + [{ssl_imp, old}, inet6|Options] end. diff --git a/lib/orber/test/Makefile b/lib/orber/test/Makefile index 4601e84d2c..5495735318 100644 --- a/lib/orber/test/Makefile +++ b/lib/orber/test/Makefile @@ -120,7 +120,11 @@ GEN_MOD_TEST_SERVER = \ orber_test_server_uni \ orber_test_server_uni_d \ orber_test_timeout_server \ - orber_parent_inherrit + orber_parent_inherrit \ + orber_test_server_rec_struct \ + orber_test_server_rec_struct_seq \ + orber_test_server_rec_union \ + orber_test_server_rec_union_seq GEN_HRL_TEST_SERVER = \ oe_orber_test_server.hrl \ diff --git a/lib/orber/test/orber_test_lib.erl b/lib/orber/test/orber_test_lib.erl index a694dc58c4..132f02bb78 100644 --- a/lib/orber/test/orber_test_lib.erl +++ b/lib/orber/test/orber_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-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 @@ -126,13 +126,22 @@ version_ok() -> _ -> case gen_tcp:listen(0, [{reuseaddr, true}, inet6]) of {ok, LSock} -> - gen_tcp:close(LSock), - true; + {ok, Port} = inet:port(LSock), + case gen_tcp:connect(Hostname, Port, [inet6]) of + {error, _} -> + gen_tcp:close(LSock), + {skipped, "Inet cannot handle IPv6"}; + {ok, Socket} -> + gen_tcp:close(Socket), + gen_tcp:close(LSock), + true + end; {error, _} -> {skipped, "Inet cannot handle IPv6"} end end end. + %%------------------------------------------------------------ %% function : get_host %% Arguments: Family - inet | inet6 @@ -1280,6 +1289,22 @@ test_coding(Obj, Local) -> ?match({'EXCEPTION',{'MARSHAL',_,_,_}}, orber_test_server: testing_iiop_server_marshal(Obj, "string")), + + RecS = #orber_test_server_rec_struct{chain = [#orber_test_server_rec_struct{chain = []}]}, + ?match(RecS, orber_test_server:testing_iiop_rec_struct(Obj, RecS)), + + RecU = #orber_test_server_rec_union{label = 'RecursiveType', + value = [#orber_test_server_rec_union{label = 'RecursiveType', + value = []}]}, + ?match(RecU, orber_test_server:testing_iiop_rec_union(Obj, RecU)), + +%% RecA1 = #any{typecode = unsupported, value = RecS}, +%% RecA2 = #any{typecode = unsupported, value = RecU}, +%% ?match(RecA1, +%% orber_test_server:testing_iiop_rec_any(Obj, RecA1)), +%% ?match(RecA2, +%% orber_test_server:testing_iiop_rec_any(Obj, RecA2)), + ok. %%--------------- Testing Post- & Pre-cond ------------------- diff --git a/lib/orber/test/orber_test_server.idl b/lib/orber/test/orber_test_server.idl index a88211c941..438c10e19b 100644 --- a/lib/orber/test/orber_test_server.idl +++ b/lib/orber/test/orber_test_server.idl @@ -28,7 +28,7 @@ module orber_parent { }; module orber_test { - + // interface server interface server : orber_parent::inherrit { typedef string array[2]; @@ -89,6 +89,23 @@ module orber_test { const fixed52 fixed52negconst2 = -123.00d; const fixed52 fixed52negconst3 = -023.00d; + struct rec_struct; // Forward declaration + typedef sequence<rec_struct> rec_struct_seq; + struct rec_struct { + rec_struct_seq chain; + }; + + + union rec_union; // Forward declaration + typedef sequence<rec_union>rec_union_seq; + + enum MyEnum {RecursiveType, NameType}; + + union rec_union switch (MyEnum) { + case RecursiveType : rec_union_seq chain; + case NameType : string aName; + }; + void stop_normal(); void stop_brutal(); @@ -123,6 +140,12 @@ module orber_test { void testing_iiop_context(); void testing_iiop_server_marshal(inout StrLength6 Str); + // Recursive types + any testing_iiop_rec_any(in any RecType); + rec_struct testing_iiop_rec_struct(in rec_struct RecS); + rec_union testing_iiop_rec_union(in rec_union RecU); + + oneway void testing_iiop_oneway_delay(in long Time); void testing_iiop_twoway_delay(in long Time); diff --git a/lib/orber/test/orber_test_server_impl.erl b/lib/orber/test/orber_test_server_impl.erl index 35296cb619..10a9caf242 100644 --- a/lib/orber/test/orber_test_server_impl.erl +++ b/lib/orber/test/orber_test_server_impl.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2010. 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 @@ -55,6 +55,9 @@ testing_iiop_void/2, testing_iiop_context/2, testing_iiop_server_marshal/3, + testing_iiop_rec_any/3, + testing_iiop_rec_struct/3, + testing_iiop_rec_union/3, relay_call/3, relay_cast/3, %% Testing pseudo calls. @@ -197,6 +200,16 @@ testing_iiop_context(_Self, State) -> testing_iiop_server_marshal(_Self, State, _String) -> {reply, {ok, false}, State}. +testing_iiop_rec_any(_Self, State, RAny) -> + {reply, RAny, State}. + +testing_iiop_rec_struct(_Self, State, RecS) -> + {reply, RecS, State}. + +testing_iiop_rec_union(_Self, State, RecU) -> + {reply, RecU, State}. + + testing_iiop_oneway_delay(_Self, State, Time) -> timer:sleep(Time), {noreply, State}. diff --git a/lib/orber/vsn.mk b/lib/orber/vsn.mk index 584a52ab84..b0c5a253a2 100644 --- a/lib/orber/vsn.mk +++ b/lib/orber/vsn.mk @@ -1 +1 @@ -ORBER_VSN = 3.6.18 +ORBER_VSN = 3.6.19 diff --git a/lib/parsetools/doc/src/notes.xml b/lib/parsetools/doc/src/notes.xml index 544850308e..77b3a1a657 100644 --- a/lib/parsetools/doc/src/notes.xml +++ b/lib/parsetools/doc/src/notes.xml @@ -30,6 +30,21 @@ </header> <p>This document describes the changes made to the Parsetools application.</p> +<section><title>Parsetools 2.0.5</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> The formating of Yecc's error messages has been + improved. (Thanks to Joe Armstrong.) </p> + <p> + Own Id: OTP-8919</p> + </item> + </list> + </section> + +</section> + <section><title>Parsetools 2.0.4</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/parsetools/vsn.mk b/lib/parsetools/vsn.mk index 46915baed6..812bf21f03 100644 --- a/lib/parsetools/vsn.mk +++ b/lib/parsetools/vsn.mk @@ -1 +1 @@ -PARSETOOLS_VSN = 2.0.4 +PARSETOOLS_VSN = 2.0.5 diff --git a/lib/percept/src/percept.erl b/lib/percept/src/percept.erl index f5e0f7e469..3a2d9f7601 100644 --- a/lib/percept/src/percept.erl +++ b/lib/percept/src/percept.erl @@ -185,10 +185,27 @@ stop_webserver() -> undefined -> {error, not_started}; Pid -> - Pid ! {self(), get_port}, - receive Port -> ok end, - Pid ! quit, - stop_webserver(Port) + do_stop([], Pid) + end. + +do_stop([], Pid)-> + Pid ! {self(), get_port}, + Port = receive P -> P end, + do_stop(Port, Pid); +do_stop(Port, [])-> + case whereis(percept_httpd) of + undefined -> + {error, not_started}; + Pid -> + do_stop(Port, Pid) + end; +do_stop(Port, Pid)-> + case find_service_pid_from_port(inets:services_info(), Port) of + undefined -> + {error, not_started}; + Pid2 -> + Pid ! quit, + inets:stop(httpd, Pid2) end. %% @spec stop_webserver(integer()) -> ok | {error, not_started} @@ -196,12 +213,7 @@ stop_webserver() -> %% @hidden stop_webserver(Port) -> - case find_service_pid_from_port(inets:services_info(), Port) of - undefined -> - {error, not_started}; - Pid -> - inets:stop(httpd, Pid) - end. + do_stop(Port,[]). %%========================================================================== %% diff --git a/lib/percept/src/percept_db.erl b/lib/percept/src/percept_db.erl index edb0d79a29..52e9afb78f 100644 --- a/lib/percept/src/percept_db.erl +++ b/lib/percept/src/percept_db.erl @@ -33,7 +33,7 @@ ]). -include("percept.hrl"). - +-define(STOP_TIMEOUT, 1000). %%========================================================================== %% %% Type definitions @@ -77,17 +77,32 @@ start() -> case erlang:whereis(percept_db) of undefined -> - Pid = spawn( fun() -> init_percept_db() end), - erlang:register(percept_db, Pid), - {started, Pid}; + {started, do_start()}; PerceptDB -> - erlang:unregister(percept_db), - PerceptDB ! {action, stop}, - Pid = spawn( fun() -> init_percept_db() end), - erlang:register(percept_db, Pid), - {restarted, Pid} + {restarted, restart(PerceptDB)} end. +%% @spec restart(pid()) -> pid() +%% @private +%% @doc restarts the percept database. + +-spec restart(pid())-> pid(). + +restart(PerceptDB)-> + stop_sync(PerceptDB), + do_start(). + +%% @spec do_start(pid()) -> pid() +%% @private +%% @doc starts the percept database. + +-spec do_start()-> pid(). + +do_start()-> + Pid = spawn( fun() -> init_percept_db() end), + erlang:register(percept_db, Pid), + Pid. + %% @spec stop() -> not_started | {stopped, Pid} %% Pid = pid() %% @doc Stops the percept database. @@ -103,6 +118,22 @@ stop() -> {stopped, Pid} end. +%% @spec stop_sync(pid()) -> true +%% @private +%% @doc Stops the percept database, with a synchronous call. + +-spec stop_sync(pid())-> true. + +stop_sync(Pid)-> + MonitorRef = erlang:monitor(process, Pid), + stop(), + receive + {'DOWN', MonitorRef, _Type, Pid, _Info}-> + true + after ?STOP_TIMEOUT-> + exit(Pid, kill) + end. + %% @spec insert(tuple()) -> ok %% @doc Inserts a trace or profile message to the database. diff --git a/lib/percept/test/percept_SUITE.erl b/lib/percept/test/percept_SUITE.erl index ff7cccdaa8..964ac68481 100644 --- a/lib/percept/test/percept_SUITE.erl +++ b/lib/percept/test/percept_SUITE.erl @@ -70,6 +70,10 @@ webserver(Config) when is_list(Config) -> % Explicit start inets? ?line {started, _, Port} = percept:start_webserver(), ?line ok = percept:stop_webserver(Port), + ?line {started, _, _} = percept:start_webserver(), + ?line ok = percept:stop_webserver(), + ?line {started, _, NewPort} = percept:start_webserver(), + ?line ok = percept:stop_webserver(NewPort), ?line application:stop(inets), ok. diff --git a/lib/percept/test/percept_db_SUITE.erl b/lib/percept/test/percept_db_SUITE.erl new file mode 100644 index 0000000000..79be9714ba --- /dev/null +++ b/lib/percept/test/percept_db_SUITE.erl @@ -0,0 +1,76 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-2010. 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 +%% 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 online 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. +%% +%% %CopyrightEnd% +%% + +-module(percept_db_SUITE). +-include("test_server.hrl"). + +%% Test server specific exports +-export([all/1]). +-export([init_per_suite/1, end_per_suite/1]). +-export([init_per_testcase/2, end_per_testcase/2]). + +%% Test cases +-export([ + start/1 + ]). + +%% Default timetrap timeout (set in init_per_testcase) +-define(default_timeout, ?t:minutes(2)). +-define(restarts, 10). +-define(alive_timeout, 500). + +init_per_suite(Config) when is_list(Config) -> + Config. + +end_per_suite(Config) when is_list(Config) -> + Config. + +init_per_testcase(_Case, Config) -> + Dog = ?t:timetrap(?default_timeout), + [{max_size, 300}, {watchdog,Dog} | Config]. + +end_per_testcase(_Case, Config) -> + Dog = ?config(watchdog, Config), + ?t:timetrap_cancel(Dog), + ok. + +all(suite) -> + % Test cases + [start]. + +%%---------------------------------------------------------------------- +%% Tests +%%---------------------------------------------------------------------- + +start(suite) -> + []; +start(doc) -> + ["Percept_db start and restart test."]; +start(Config) when is_list(Config) -> + ok = restart(?restarts), + {stopped, _DB} = percept_db:stop(), + ok. + +restart(0)-> + ok; +restart(N)-> + {_, DB} = percept_db:start(), + timer:sleep(?alive_timeout), + true = erlang:is_process_alive(DB), + restart(N-1). diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml index 6e7381eb18..befbd3e586 100644 --- a/lib/public_key/doc/src/notes.xml +++ b/lib/public_key/doc/src/notes.xml @@ -34,6 +34,21 @@ <file>notes.xml</file> </header> +<section><title>Public_Key 0.10</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Improved dialyzer specs.</p> + <p> + Own Id: OTP-8964</p> + </item> + </list> + </section> + +</section> + <section><title>Public_Key 0.9</title> <section><title>Improvements and New Features</title> diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml index c72719fac4..91e058f74e 100644 --- a/lib/public_key/doc/src/public_key.xml +++ b/lib/public_key/doc/src/public_key.xml @@ -64,8 +64,8 @@ <p><c>decrypt_der() = binary() </c></p> - <p><c>pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| - 'DSAPrivateKey' | 'DHParameter'</c></p> + <p><c>pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| 'RSAPublicKey' + 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo'</c></p> <p><c>pem_entry () = {pki_asn1_type(), der_encoded() | decrypt_der(), not_encrypted | {"DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)}}.</c></p> @@ -207,17 +207,24 @@ <v> Password = string() </v> </type> <desc> - <p>Decodes a pem entry. pem_decode/1 returns a list of - pem entries.</p> + <p>Decodes a pem entry. pem_decode/1 returns a list of pem + entries. Note that if the pem entry is of type + 'SubjectPublickeyInfo' it will be further decoded to an + rsa_public_key() or dsa_public_key().</p> </desc> </func> <func> <name>pem_entry_encode(Asn1Type, Entity [,{CipherInfo, Password}]) -> pem_entry()</name> - <fsummary> Creates a pem entry that can be feed to pem_encode/1.</fsummary> + <fsummary> Creates a pem entry that can be fed to pem_encode/1.</fsummary> <type> - <v>Asn1Type = atom()</v> - <v>Entity = term()</v> + <v>Asn1Type = pki_asn1_type()</v> + <v>Entity = term() - The Erlang representation of + <c>Asn1Type</c>. If <c>Asn1Type</c> is 'SubjectPublicKeyInfo' + then <c>Entity</c> must be either an rsa_public_key() or a + dsa_public_key() and this function will create the appropriate + 'SubjectPublicKeyInfo' entry. + </v> <v>CipherInfo = {"DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)}</v> <v>Password = string()</v> </type> diff --git a/lib/public_key/include/public_key.hrl b/lib/public_key/include/public_key.hrl index 4950597fb5..f29ab859ed 100644 --- a/lib/public_key/include/public_key.hrl +++ b/lib/public_key/include/public_key.hrl @@ -73,8 +73,9 @@ -type der_encoded() :: binary(). -type decrypt_der() :: binary(). --type pki_asn1_type() :: 'Certificate' | 'RSAPrivateKey' - | 'DSAPrivateKey' | 'DHParameter'. +-type pki_asn1_type() :: 'Certificate' | 'RSAPrivateKey' | 'RSAPublicKey' + | 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' + | 'SubjectPublicKeyInfo'. -type pem_entry() :: {pki_asn1_type(), der_encoded() | decrypt_der(), not_encrypted | {Cipher :: string(), Salt :: binary()}}. -type asn1_type() :: atom(). %% see "OTP-PUB-KEY.hrl diff --git a/lib/public_key/src/pubkey_cert_records.erl b/lib/public_key/src/pubkey_cert_records.erl index 20b322b4a4..7a387e487c 100644 --- a/lib/public_key/src/pubkey_cert_records.erl +++ b/lib/public_key/src/pubkey_cert_records.erl @@ -23,7 +23,7 @@ -include("public_key.hrl"). --export([decode_cert/1, transform/2]). +-export([decode_cert/1, transform/2, supportedPublicKeyAlgorithms/1]). %%==================================================================== %% Internal application API @@ -80,16 +80,24 @@ transform(Other,_) -> Other. %%-------------------------------------------------------------------- -%%% Internal functions +-spec supportedPublicKeyAlgorithms(Oid::tuple()) -> asn1_type(). +%% +%% Description: Returns the public key type for an algorithm +%% identifier tuple as found in SubjectPublicKeyInfo. +%% %%-------------------------------------------------------------------- - -%%% SubjectPublicKey supportedPublicKeyAlgorithms(?'rsaEncryption') -> 'RSAPublicKey'; supportedPublicKeyAlgorithms(?'id-dsa') -> 'DSAPublicKey'; supportedPublicKeyAlgorithms(?'dhpublicnumber') -> 'DHPublicKey'; supportedPublicKeyAlgorithms(?'id-keyExchangeAlgorithm') -> 'KEA-PublicKey'; supportedPublicKeyAlgorithms(?'id-ecPublicKey') -> 'ECPoint'. +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- + +%%% SubjectPublicKey + decode_supportedPublicKey(#'OTPSubjectPublicKeyInfo'{algorithm= PA = #'PublicKeyAlgorithm'{algorithm=Algo}, subjectPublicKey = {0,SPK0}}) -> diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl index 31d881973a..78870e5cd7 100644 --- a/lib/public_key/src/pubkey_pem.erl +++ b/lib/public_key/src/pubkey_pem.erl @@ -93,11 +93,11 @@ encode_pem_entries(Entries) -> encode_pem_entry({Asn1Type, Der, not_encrypted}) -> StartStr = pem_start(Asn1Type), - [StartStr, "\n", b64encode_and_split(Der), pem_end(StartStr) ,"\n\n"]; + [StartStr, "\n", b64encode_and_split(Der), "\n", pem_end(StartStr) ,"\n\n"]; encode_pem_entry({Asn1Type, Der, {Cipher, Salt}}) -> StartStr = pem_start(Asn1Type), [StartStr,"\n", pem_decrypt(),"\n", pem_decrypt_info(Cipher, Salt),"\n", - b64encode_and_split(Der), pem_end(StartStr) ,"\n\n"]. + b64encode_and_split(Der), "\n", pem_end(StartStr) ,"\n\n"]. decode_pem_entries([], Entries) -> lists:reverse(Entries); @@ -145,16 +145,22 @@ split_bin(N, Bin) -> b64encode_and_split(Bin) -> split_lines(base64:encode(Bin)). +split_lines(<<Text:?ENCODED_LINE_LENGTH/binary>>) -> + [Text]; split_lines(<<Text:?ENCODED_LINE_LENGTH/binary, Rest/binary>>) -> [Text, $\n | split_lines(Rest)]; split_lines(Bin) -> - [Bin, $\n]. + [Bin]. %% Ignore white space at end of line join_entry([<<"-----END CERTIFICATE-----", _/binary>>| Lines], Entry) -> {lists:reverse(Entry), Lines}; join_entry([<<"-----END RSA PRIVATE KEY-----", _/binary>>| Lines], Entry) -> {lists:reverse(Entry), Lines}; +join_entry([<<"-----END PUBLIC KEY-----", _/binary>>| Lines], Entry) -> + {lists:reverse(Entry), Lines}; +join_entry([<<"-----END RSA PUBLIC KEY-----", _/binary>>| Lines], Entry) -> + {lists:reverse(Entry), Lines}; join_entry([<<"-----END DSA PRIVATE KEY-----", _/binary>>| Lines], Entry) -> {lists:reverse(Entry), Lines}; join_entry([<<"-----END DH PARAMETERS-----", _/binary>>| Lines], Entry) -> @@ -210,15 +216,22 @@ pem_start('Certificate') -> <<"-----BEGIN CERTIFICATE-----">>; pem_start('RSAPrivateKey') -> <<"-----BEGIN RSA PRIVATE KEY-----">>; +pem_start('RSAPublicKey') -> + <<"-----BEGIN RSA PUBLIC KEY-----">>; +pem_start('SubjectPublicKeyInfo') -> + <<"-----BEGIN PUBLIC KEY-----">>; pem_start('DSAPrivateKey') -> <<"-----BEGIN DSA PRIVATE KEY-----">>; pem_start('DHParameter') -> <<"-----BEGIN DH PARAMETERS-----">>. - pem_end(<<"-----BEGIN CERTIFICATE-----">>) -> <<"-----END CERTIFICATE-----">>; pem_end(<<"-----BEGIN RSA PRIVATE KEY-----">>) -> <<"-----END RSA PRIVATE KEY-----">>; +pem_end(<<"-----BEGIN RSA PUBLIC KEY-----">>) -> + <<"-----END RSA PUBLIC KEY-----">>; +pem_end(<<"-----BEGIN PUBLIC KEY-----">>) -> + <<"-----END PUBLIC KEY-----">>; pem_end(<<"-----BEGIN DSA PRIVATE KEY-----">>) -> <<"-----END DSA PRIVATE KEY-----">>; pem_end(<<"-----BEGIN DH PARAMETERS-----">>) -> @@ -230,6 +243,10 @@ asn1_type(<<"-----BEGIN CERTIFICATE-----">>) -> 'Certificate'; asn1_type(<<"-----BEGIN RSA PRIVATE KEY-----">>) -> 'RSAPrivateKey'; +asn1_type(<<"-----BEGIN RSA PUBLIC KEY-----">>) -> + 'RSAPublicKey'; +asn1_type(<<"-----BEGIN PUBLIC KEY-----">>) -> + 'SubjectPublicKeyInfo'; asn1_type(<<"-----BEGIN DSA PRIVATE KEY-----">>) -> 'DSAPrivateKey'; asn1_type(<<"-----BEGIN DH PARAMETERS-----">>) -> diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 30398df9cc..fad73e8e92 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -62,6 +62,7 @@ -type dss_digest_type() :: 'none' | 'sha'. -define(UINT32(X), X:32/unsigned-big-integer). +-define(DER_NULL, <<5, 0>>). %%==================================================================== %% API @@ -90,6 +91,17 @@ pem_encode(PemEntries) when is_list(PemEntries) -> %% Description: Decodes a pem entry. pem_decode/1 returns a list of %% pem entries. %%-------------------------------------------------------------------- +pem_entry_decode({'SubjectPublicKeyInfo', Der, _}) -> + {_, {'AlgorithmIdentifier', AlgId, Params}, {0, Key0}} + = der_decode('SubjectPublicKeyInfo', Der), + KeyType = pubkey_cert_records:supportedPublicKeyAlgorithms(AlgId), + case KeyType of + 'RSAPublicKey' -> + der_decode(KeyType, Key0); + 'DSAPublicKey' -> + {params, DssParams} = der_decode('DSAParams', Params), + {der_decode(KeyType, Key0), DssParams} + end; pem_entry_decode({Asn1Type, Der, not_encrypted}) when is_atom(Asn1Type), is_binary(Der) -> der_decode(Asn1Type, Der). @@ -114,6 +126,18 @@ pem_entry_decode({Asn1Type, CryptDer, {Cipher, Salt}} = PemEntry, % %% Description: Creates a pem entry that can be feed to pem_encode/1. %%-------------------------------------------------------------------- +pem_entry_encode('SubjectPublicKeyInfo', Entity=#'RSAPublicKey'{}) -> + Der = der_encode('RSAPublicKey', Entity), + Spki = {'SubjectPublicKeyInfo', + {'AlgorithmIdentifier', ?'rsaEncryption', ?DER_NULL}, {0, Der}}, + pem_entry_encode('SubjectPublicKeyInfo', Spki); +pem_entry_encode('SubjectPublicKeyInfo', + {DsaInt, Params=#'Dss-Parms'{}}) when is_integer(DsaInt) -> + KeyDer = der_encode('DSAPublicKey', DsaInt), + ParamDer = der_encode('DSAParams', {params, Params}), + Spki = {'SubjectPublicKeyInfo', + {'AlgorithmIdentifier', ?'id-dsa', ParamDer}, {0, KeyDer}}, + pem_entry_encode('SubjectPublicKeyInfo', Spki); pem_entry_encode(Asn1Type, Entity) when is_atom(Asn1Type) -> Der = der_encode(Asn1Type, Entity), {Asn1Type, Der, not_encrypted}. diff --git a/lib/public_key/test/pkits_SUITE.erl b/lib/public_key/test/pkits_SUITE.erl index 1d75e1aed2..a20f4e72c6 100644 --- a/lib/public_key/test/pkits_SUITE.erl +++ b/lib/public_key/test/pkits_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -129,7 +129,6 @@ private_certificate_extensions(Config) when is_list(Config) -> run(private_certificate_extensions()). run() -> - catch crypto:start(), Tests = [signature_verification(), validity_periods(), @@ -587,11 +586,15 @@ fin_per_testcase(_Func, Config) -> Config. init_per_suite(Config) -> - crypto:start(), - Config. + case application:start(crypto) of + ok -> + Config; + _ -> + {skip, "Crypto did not start"} + end. end_per_suite(_Config) -> - crypto:stop(). + application:stop(crypto). error(Format, Args, File0, Line) -> File = filename:basename(File0), diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index 88cfbcf2b6..185618e58e 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -41,9 +41,12 @@ %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> - crypto:start(), - Config. - + case application:start(crypto) of + ok -> + Config; + _ -> + {skip, "Crypto did not start"} + end. %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ %% Config - [tuple()] @@ -51,7 +54,7 @@ init_per_suite(Config) -> %% Description: Cleanup after the whole suite %%-------------------------------------------------------------------- end_per_suite(_Config) -> - crypto:stop(). + application:stop(crypto). %%-------------------------------------------------------------------- %% Function: init_per_testcase(TestCase, Config) -> Config @@ -136,6 +139,15 @@ pk_decode_encode(Config) when is_list(Config) -> DSAKey = public_key:der_decode('DSAPrivateKey', DerDSAKey), DSAKey = public_key:pem_entry_decode(Entry0), + + {ok, DSAPubPem} = file:read_file(filename:join(Datadir, "dsa_pub.pem")), + [{'SubjectPublicKeyInfo', _, _} = PubEntry0] = + public_key:pem_decode(DSAPubPem), + DSAPubKey = public_key:pem_entry_decode(PubEntry0), + true = check_entry_type(DSAPubKey, 'DSAPublicKey'), + PubEntry0 = public_key:pem_entry_encode('SubjectPublicKeyInfo', DSAPubKey), + DSAPubPemNoEndNewLines = strip_ending_newlines(DSAPubPem), + DSAPubPemEndNoNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry0])), [{'RSAPrivateKey', DerRSAKey, not_encrypted} = Entry1 ] = erl_make_certs:pem_to_der(filename:join(Datadir, "client_key.pem")), @@ -150,6 +162,22 @@ pk_decode_encode(Config) when is_list(Config) -> true = check_entry_type(public_key:pem_entry_decode(Entry2, "abcd1234"), 'RSAPrivateKey'), + {ok, RSAPubPem} = file:read_file(filename:join(Datadir, "rsa_pub.pem")), + [{'SubjectPublicKeyInfo', _, _} = PubEntry1] = + public_key:pem_decode(RSAPubPem), + RSAPubKey = public_key:pem_entry_decode(PubEntry1), + true = check_entry_type(RSAPubKey, 'RSAPublicKey'), + PubEntry1 = public_key:pem_entry_encode('SubjectPublicKeyInfo', RSAPubKey), + RSAPubPemNoEndNewLines = strip_ending_newlines(RSAPubPem), + RSAPubPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry1])), + + {ok, RSARawPem} = file:read_file(filename:join(Datadir, "rsa_pub_key.pem")), + [{'RSAPublicKey', _, _} = PubEntry2] = + public_key:pem_decode(RSARawPem), + RSAPubKey = public_key:pem_entry_decode(PubEntry2), + RSARawPemNoEndNewLines = strip_ending_newlines(RSARawPem), + RSARawPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry2])), + Salt0 = crypto:rand_bytes(8), Entry3 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey0, {{"DES-EDE3-CBC", Salt0}, "1234abcd"}), @@ -429,9 +457,16 @@ check_entry_type(#'DSAPrivateKey'{}, 'DSAPrivateKey') -> true; check_entry_type(#'RSAPrivateKey'{}, 'RSAPrivateKey') -> true; +check_entry_type(#'RSAPublicKey'{}, 'RSAPublicKey') -> + true; +check_entry_type({_Int, #'Dss-Parms'{}}, 'DSAPublicKey') when is_integer(_Int) -> + true; check_entry_type(#'DHParameter'{}, 'DHParameter') -> true; check_entry_type(#'Certificate'{}, 'Certificate') -> true; check_entry_type(_,_) -> false. + +strip_ending_newlines(Bin) -> + string:strip(binary_to_list(Bin), right, 10). diff --git a/lib/public_key/test/public_key_SUITE_data/dsa_pub.pem b/lib/public_key/test/public_key_SUITE_data/dsa_pub.pem new file mode 100644 index 0000000000..d3635e5b20 --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/dsa_pub.pem @@ -0,0 +1,12 @@ +-----BEGIN PUBLIC KEY----- +MIIBtzCCASwGByqGSM44BAEwggEfAoGBALez5tklY5CdFeTMos899pA6i4u4uCts +zgBzrdBk6cl5FVqzdzWMGTQiynnTpGsrOESinzP06Ip+pG15We2OORwgvCxD/W95 +aCiN0/+MdiXqlsmboBARMzsa+SmBENN3gF/+tuuEAFzOXU1q2cmEywRLyfbM2KIB +VE/TChWYw2eRAhUA1R64VvcQ90XA8SOKVDmMA0dBzukCgYEAlLMYP0pbgBlgHQVO +3/avAHlWNrIq52Lxk7SdPJWgMvPjTK9Z6sv88kxsCcydtjvO439j1yqcwk50GQc+ +86ktBWWz93/HkIdnFyqafef4mmWvm2Uq6ClQKS+A0Asfaj8Mys+HUMiI+qsfdjRb +yIpwb7MX1nsVdsKzALnZNMW27A0DgYQAAoGAfEIAb3mLjtFfiF/tsZb4/DGHdWSb +6Ir0hFkoBUZ9ymBO70wlfZVSQGs240kZtOMpAOpJL1Dy8oH6PUQ+JyacwZIo8fdq +19/Kwm6CPrpaEhzErmMvwT2CZJYZ+HOk55ljLkVCiyG7MzEj2+odLKym9yoQsbsJ +olHzIRpkLk45y4c= +-----END PUBLIC KEY----- diff --git a/lib/public_key/test/public_key_SUITE_data/rsa_pub.pem b/lib/public_key/test/public_key_SUITE_data/rsa_pub.pem new file mode 100644 index 0000000000..cbe81343f7 --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/rsa_pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANRiyZg0uci74Nc6mnqZ8AoDl88aT7x6 +JA0MfgHIHzteEj7Qg+lE5QxMGAafurVE5vqoHkDfwk4uzzsCAJuz91MCAwEAAQ== +-----END PUBLIC KEY----- diff --git a/lib/public_key/test/public_key_SUITE_data/rsa_pub_key.pem b/lib/public_key/test/public_key_SUITE_data/rsa_pub_key.pem new file mode 100644 index 0000000000..3b9d7568ff --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/rsa_pub_key.pem @@ -0,0 +1,4 @@ +-----BEGIN RSA PUBLIC KEY----- +MEgCQQDUYsmYNLnIu+DXOpp6mfAKA5fPGk+8eiQNDH4ByB87XhI+0IPpROUMTBgG +n7q1ROb6qB5A38JOLs87AgCbs/dTAgMBAAE= +-----END RSA PUBLIC KEY----- diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index 9bedd446f4..af667b1a71 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -29,6 +29,45 @@ <file>notes.xml</file> </header> +<section><title>Ssh 2.0.4</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>In some cases SSH returned {error, normal} when a channel was terminated + unexpectedly. This has now been changed to {error, channel_closed}.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-8987 Aux Id: seq11748</p> + </item> + <item> + <p> + SSH did not handle the error reason enetunreach + when trying to open a IPv6 connection.</p> + <p> + Own Id: OTP-9031</p> + </item> + </list> + </section> + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + It is now possible to use SSH to sign and verify binary data.</p> + <p> + Own Id: OTP-8986</p> + </item> + <item> + <p> + SSH now ensures that the .ssh directory exists before trying + to access files located in that directory.</p> + <p> + Own Id: OTP-9010</p> + </item> + </list> + </section> +</section> + <section><title>Ssh 2.0.3</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index 71e6b2cd3d..2c5096a25f 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -283,6 +283,22 @@ </func> <func> + <name>sign_data(Data, Algorithm) -> Signature | {error, Reason}</name> + <fsummary> </fsummary> + <type> + <v> Data = binary()</v> + <v> Algorithm = "ssh-rsa"</v> + <v> Signature = binary()</v> + <v> Reason = term()</v> + </type> + <desc> + <p>Signs the supplied binary using the SSH key. + </p> + </desc> + </func> + + + <func> <name>start() -> </name> <name>start(Type) -> ok | {error, Reason}</name> <fsummary>Starts the Ssh application. </fsummary> @@ -339,6 +355,22 @@ by the listener up and running.</p> </desc> </func> + + <func> + <name>verify_data(Data, Signature, Algorithm) -> ok | {error, Reason}</name> + <fsummary> </fsummary> + <type> + <v> Data = binary()</v> + <v> Algorithm = "ssh-rsa"</v> + <v> Signature = binary()</v> + <v> Reason = term()</v> + </type> + <desc> + <p>Verifies the supplied binary against the binary signature. + </p> + </desc> + </func> + </funcs> </erlref> diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src index 9c806bcd03..501da8ceb9 100644 --- a/lib/ssh/src/ssh.appup.src +++ b/lib/ssh/src/ssh.appup.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-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 @@ -19,11 +19,33 @@ {"%VSN%", [ - {"2.0.2", [{load_module, ssh_file, soft_purge, soft_purge, []}]}, + {"2.0.3", [{load_module, ssh_file, soft_purge, soft_purge, []}, + {load_module, ssh, soft_purge, soft_purge, []}, + {load_module, ssh_rsa, soft_purge, soft_purge, []}, + {load_module, ssh_acceptor, soft_purge, soft_purge, []}, + {load_module, ssh_transport, soft_purge, soft_purge, []}, + {load_module, ssh_connection_manager, soft_purge, soft_purge, []}]}, + {"2.0.2", [{load_module, ssh_file, soft_purge, soft_purge, []}, + {load_module, ssh, soft_purge, soft_purge, []}, + {load_module, ssh_rsa, soft_purge, soft_purge, []}, + {load_module, ssh_acceptor, soft_purge, soft_purge, []}, + {load_module, ssh_transport, soft_purge, soft_purge, []}, + {load_module, ssh_connection_manager, soft_purge, soft_purge, []}]}, {"2.0.1", [{restart_application, ssh}]} ], [ - {"2.0.2", [{load_module, ssh_file, soft_purge, soft_purge, []}]}, + {"2.0.3", [{load_module, ssh_file, soft_purge, soft_purge, []}, + {load_module, ssh, soft_purge, soft_purge, []}, + {load_module, ssh_rsa, soft_purge, soft_purge, []}, + {load_module, ssh_acceptor, soft_purge, soft_purge, []}, + {load_module, ssh_transport, soft_purge, soft_purge, []}, + {load_module, ssh_connection_manager, soft_purge, soft_purge, []}]}, + {"2.0.2", [{load_module, ssh_file, soft_purge, soft_purge, []}, + {load_module, ssh, soft_purge, soft_purge, []}, + {load_module, ssh_rsa, soft_purge, soft_purge, []}, + {load_module, ssh_acceptor, soft_purge, soft_purge, []}, + {load_module, ssh_transport, soft_purge, soft_purge, []}, + {load_module, ssh_connection_manager, soft_purge, soft_purge, []}]}, {"2.0.1", [{restart_application, ssh}]} ] }. diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 994c77436a..cada109df0 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -30,6 +30,8 @@ stop_listener/1, stop_listener/2, stop_daemon/1, stop_daemon/2, shell/1, shell/2, shell/3]). +-export([sign_data/2, verify_data/3]). + %%-------------------------------------------------------------------- %% Function: start([, Type]) -> ok %% @@ -94,11 +96,17 @@ connect(Host, Port, Options, Timeout) -> do_demonitor(MRef, Manager), {error, Other}; {'DOWN', MRef, _, Manager, Reason} when is_pid(Manager) -> + error_logger:warning_report([{ssh, connect}, + {diagnose, + "Connection was closed before properly set up."}, + {host, Host}, + {port, Port}, + {reason, Reason}]), receive %% Clear EXIT message from queue {'EXIT', Manager, _What} -> - {error, Reason} + {error, channel_closed} after 0 -> - {error, Reason} + {error, channel_closed} end after Timeout -> do_demonitor(MRef, Manager), @@ -239,6 +247,43 @@ shell(Host, Port, Options) -> Error end. + +%%-------------------------------------------------------------------- +%% Function: sign_data(Data, Algorithm) -> binary() | +%% {error, Reason} +%% +%% Data = binary() +%% Algorithm = "ssh-rsa" +%% +%% Description: Use SSH key to sign data. +%%-------------------------------------------------------------------- +sign_data(Data, Algorithm) when is_binary(Data) -> + case ssh_file:private_identity_key(Algorithm,[]) of + {ok, Key} when Algorithm == "ssh-rsa" -> + ssh_rsa:sign(Key, Data); + Error -> + Error + end. + +%%-------------------------------------------------------------------- +%% Function: verify_data(Data, Signature, Algorithm) -> ok | +%% {error, Reason} +%% +%% Data = binary() +%% Signature = binary() +%% Algorithm = "ssh-rsa" +%% +%% Description: Use SSH signature to verify data. +%%-------------------------------------------------------------------- +verify_data(Data, Signature, Algorithm) when is_binary(Data), is_binary(Signature) -> + case ssh_file:public_identity_key(Algorithm, []) of + {ok, Key} when Algorithm == "ssh-rsa" -> + ssh_rsa:verify(Key, Data, Signature); + Error -> + Error + end. + + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl index 9060626ab3..59fbd24cf5 100644 --- a/lib/ssh/src/ssh_acceptor.erl +++ b/lib/ssh/src/ssh_acceptor.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -55,6 +55,10 @@ acceptor_init(Parent, Port, Address, SockOpts, Opts, AcceptTimeout) -> do_socket_listen(Callback, Port, Opts) -> case Callback:listen(Port, Opts) of + {error, nxdomain} -> + Callback:listen(Port, lists:delete(inet6, Opts)); + {error, enetunreach} -> + Callback:listen(Port, lists:delete(inet6, Opts)); {error, eafnosupport} -> Callback:listen(Port, lists:delete(inet6, Opts)); Other -> diff --git a/lib/ssh/src/ssh_connection_manager.erl b/lib/ssh/src/ssh_connection_manager.erl index 6bf89224cf..9bfd5270da 100644 --- a/lib/ssh/src/ssh_connection_manager.erl +++ b/lib/ssh/src/ssh_connection_manager.erl @@ -147,7 +147,7 @@ close(ConnectionManager, ChannelId) -> try call(ConnectionManager, {close, ChannelId}) of ok -> ok; - {error,normal} -> + {error, channel_closed} -> ok catch exit:{noproc, _} -> @@ -158,7 +158,7 @@ stop(ConnectionManager) -> try call(ConnectionManager, stop) of ok -> ok; - {error,normal} -> + {error, channel_closed} -> ok catch exit:{noproc, _} -> @@ -604,7 +604,7 @@ call(Pid, Msg, Timeout) -> exit:{timeout, _} -> {error, timeout}; exit:{normal, _} -> - {error, normal} + {error, channel_closed} end. cast(Pid, Msg) -> diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl index 13722656db..ff23f714cd 100755 --- a/lib/ssh/src/ssh_file.erl +++ b/lib/ssh/src/ssh_file.erl @@ -27,14 +27,16 @@ -include("PKCS-1.hrl"). -include("DSS.hrl"). +-include_lib("kernel/include/file.hrl"). + -export([public_host_dsa_key/2,private_host_dsa_key/2, public_host_rsa_key/2,private_host_rsa_key/2, public_host_key/2,private_host_key/2, lookup_host_key/3, add_host_key/3, % del_host_key/2, lookup_user_key/3, ssh_dir/2, file_name/3]). --export([private_identity_key/2]). -%% , public_identity_key/2, +-export([private_identity_key/2, + public_identity_key/2]). %% identity_keys/2]). -export([encode_public_key/1, decode_public_key_v2/2]). @@ -43,6 +45,9 @@ -define(DBG_PATHS, true). +-define(PERM_700, 8#700). +-define(PERM_644, 8#644). + %% API public_host_dsa_key(Type, Opts) -> File = file_name(Type, "ssh_host_dsa_key.pub", Opts), @@ -113,8 +118,10 @@ do_lookup_host_key(Host, Alg, Opts) -> add_host_key(Host, Key, Opts) -> Host1 = add_ip(replace_localhost(Host)), - case file:open(file_name(user, "known_hosts", Opts),[write,append]) of + KnownHosts = file_name(user, "known_hosts", Opts), + case file:open(KnownHosts, [write,append]) of {ok, Fd} -> + ok = file:change_mode(KnownHosts, ?PERM_644), Res = add_key_fd(Fd, Host1, Key), file:close(Fd), Res; @@ -140,6 +147,11 @@ private_identity_key(Alg, Opts) -> Path = file_name(user, identity_key_filename(Alg), Opts), read_private_key_v2(Path, Alg). +public_identity_key(Alg, Opts) -> + Path = file_name(user, identity_key_filename(Alg) ++ ".pub", Opts), + read_public_key_v2(Path, Alg). + + read_public_key_v2(File, Type) -> case file:read_file(File) of {ok,Bin} -> @@ -532,4 +544,7 @@ file_name(Type, Name, Opts) -> default_user_dir()-> {ok,[[Home|_]]} = init:get_argument(home), - filename:join(Home, ".ssh"). + UserDir = filename:join(Home, ".ssh"), + ok = filelib:ensure_dir(filename:join(UserDir, "dummy")), + ok = file:change_mode(UserDir, ?PERM_700), + UserDir. diff --git a/lib/ssh/src/ssh_rsa.erl b/lib/ssh/src/ssh_rsa.erl index e27cdcf7bd..91b8285b2e 100755 --- a/lib/ssh/src/ssh_rsa.erl +++ b/lib/ssh/src/ssh_rsa.erl @@ -202,8 +202,7 @@ rsassa_pkcs1_v1_5_verify(Public=#ssh_key { public={N,_E}}, Mb, Sb) -> case emsa_pkcs1_v1_5_encode(Mb, K) of EM -> ok; _S -> - io:format("S: ~p~n", [_S]), - {error, invalid_signature} % exit(invalid_signature) + {error, invalid_signature} end. diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index e79ccdda0c..de3e29e2f1 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-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 @@ -169,6 +169,8 @@ do_connect(Callback, Address, Port, SocketOpts, Timeout) -> Callback:connect(Address, Port, lists:delete(inet6, Opts), Timeout); {error, eafnosupport} -> Callback:connect(Address, Port, lists:delete(inet6, Opts), Timeout); + {error, enetunreach} -> + Callback:connect(Address, Port, lists:delete(inet6, Opts), Timeout); Other -> Other end. diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index db03168ad9..51f9f47446 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,5 +1,5 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 2.0.3 +SSH_VSN = 2.0.4 APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 756c0d1b1f..8f81ccb567 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -31,7 +31,94 @@ <p>This document describes the changes made to the SSL application. </p> - <section><title>SSL 4.1</title> + <section><title>SSL 4.1.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed error in cache-handling fix from ssl-4.1.2</p> + <p> + Own Id: OTP-9018 Aux Id: seq11739 </p> + </item> + <item> + <p> + Verification of a critical extended_key_usage-extension + corrected</p> + <p> + Own Id: OTP-9029 Aux Id: seq11541 </p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 4.1.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The ssl application caches certificate files, it will now + invalidate cache entries if the diskfile is changed.</p> + <p> + Own Id: OTP-8965 Aux Id: seq11739 </p> + </item> + <item> + <p> + Now runs the terminate function before returning from the + call made by ssl:close/1, as before the caller of + ssl:close/1 could get problems with the reuseaddr option.</p> + <p> + Own Id: OTP-8992</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 4.1.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Correct handling of client certificate verify message + When checking the client certificate verify message the + server used the wrong algorithm identifier to determine + the signing algorithm, causing a function clause error in + the public_key application when the key-exchange + algorithm and the public key algorithm of the client + certificate happen to differ.</p> + <p> + Own Id: OTP-8897</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + For testing purposes ssl now also support some anonymous + cipher suites when explicitly configured to do so.</p> + <p> + Own Id: OTP-8870</p> + </item> + <item> + <p> + Sends an error alert instead of crashing if a crypto + function for the selected cipher suite fails.</p> + <p> + Own Id: OTP-8930 Aux Id: seq11720 </p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 4.1</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index ec272379bb..daf7b77527 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1999</year><year>2010</year> + <year>1999</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -72,10 +72,10 @@ {verify_fun, {fun(), term()}} | {fail_if_no_peer_cert, boolean()} {depth, integer()} | - {cert, der_bin()}| {certfile, path()} | - {key, der_bin()} | {keyfile, path()} | {password, string()} | - {cacerts, [der_bin()]} | {cacertfile, path()} | - |{dh, der_bin()} | {dhfile, path()} | {ciphers, ciphers()} | + {cert, der_encoded()}| {certfile, path()} | + {key, der_encoded()} | {keyfile, path()} | {password, string()} | + {cacerts, [der_encoded()]} | {cacertfile, path()} | + |{dh, der_encoded()} | {dhfile, path()} | {ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()} </c></p> @@ -95,7 +95,7 @@ <p><c>path() = string() - representing a file path.</c></p> - <p><c>der_bin() = binary() -Asn1 DER encoded entity as an erlang binary.</c></p> + <p><c>der_encoded() = binary() -Asn1 DER encoded entity as an erlang binary.</c></p> <p><c>host() = hostname() | ipaddress()</c></p> @@ -136,14 +136,14 @@ <taglist> - <tag>{cert, der_bin()}</tag> + <tag>{cert, der_encoded()}</tag> <item> The DER encoded users certificate. If this option is supplied it will override the certfile option.</item> <tag>{certfile, path()}</tag> <item>Path to a file containing the user's certificate.</item> - <tag>{key, der_bin()}</tag> + <tag>{key, der_encoded()}</tag> <item> The DER encoded users private key. If this option is supplied it will override the keyfile option.</item> @@ -158,7 +158,7 @@ Only used if the private keyfile is password protected. </item> - <tag>{cacerts, [der_bin()]}</tag> + <tag>{cacerts, [der_encoded()]}</tag> <item> The DER encoded trusted certificates. If this option is supplied it will override the cacertfile option.</item> @@ -301,7 +301,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <taglist> - <tag>{dh, der_bin()}</tag> + <tag>{dh, der_encoded()}</tag> <item>The DER encoded Diffie Hellman parameters. If this option is supplied it will override the dhfile option. </item> diff --git a/lib/ssl/src/inet_ssl_dist.erl b/lib/ssl/src/inet_ssl_dist.erl index f62aefd35a..b10aa76246 100644 --- a/lib/ssl/src/inet_ssl_dist.erl +++ b/lib/ssl/src/inet_ssl_dist.erl @@ -135,6 +135,9 @@ accept_connection(AcceptPid, Socket, MyNode, Allowed, SetupTime) -> [self(), AcceptPid, Socket, MyNode, Allowed, SetupTime]). +%% Suppress dialyzer warning, we do not really care about old ssl code +%% as we intend to remove it. +-spec(do_accept/6 :: (_,_,_,_,_,_) -> no_return()). do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) -> process_flag(priority, max), receive @@ -204,6 +207,9 @@ setup(Node, Type, MyNode, LongOrShortNames,SetupTime) -> LongOrShortNames, SetupTime]). +%% Suppress dialyzer warning, we do not really care about old ssl code +%% as we intend to remove it. +-spec(do_setup/6 :: (_,_,_,_,_,_) -> no_return()). do_setup(Kernel, Node, Type, MyNode, LongOrShortNames,SetupTime) -> process_flag(priority, max), ?trace("~p~n",[{inet_ssl_dist,self(),setup,Node}]), diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index 51c5289bd2..e6a8c557fc 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,10 +1,14 @@ %% -*- erlang -*- {"%VSN%", [ + {"4.1.2", [{restart_application, ssl}]}, + {"4.1.1", [{restart_application, ssl}]}, {"4.1", [{restart_application, ssl}]}, {"4.0.1", [{restart_application, ssl}]} ], [ + {"4.1.2", [{restart_application, ssl}]}, + {"4.1.1", [{restart_application, ssl}]}, {"4.1", [{restart_application, ssl}]}, {"4.0.1", [{restart_application, ssl}]} ]}. diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 7e5929d708..65b081937f 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-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 @@ -49,6 +49,27 @@ inet_ssl, %% inet options for internal ssl socket cb %% Callback info }). +-type option() :: socketoption() | ssloption() | transportoption(). +-type socketoption() :: [{property(), term()}]. %% See gen_tcp and inet +-type property() :: atom(). + +-type ssloption() :: {verify, verify_type()} | + {verify_fun, {fun(), InitialUserState::term()}} | + {fail_if_no_peer_cert, boolean()} | {depth, integer()} | + {cert, der_encoded()} | {certfile, path()} | {key, der_encoded()} | + {keyfile, path()} | {password, string()} | {cacerts, [der_encoded()]} | + {cacertfile, path()} | {dh, der_encoded()} | {dhfile, path()} | + {ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | + {reuse_session, fun()}. + +-type verify_type() :: verify_none | verify_peer. +-type path() :: string(). +-type ciphers() :: [erl_cipher_suite()] | + string(). % (according to old API) +-type ssl_imp() :: new | old. + +-type transportoption() :: {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom()}. + %%-------------------------------------------------------------------- -spec start() -> ok. @@ -77,9 +98,12 @@ stop() -> application:stop(ssl). %%-------------------------------------------------------------------- --spec connect(host() | port(), list()) -> {ok, #sslsocket{}}. --spec connect(host() | port(), list() | port_num(), timeout() | list()) -> {ok, #sslsocket{}}. --spec connect(host() | port(), port_num(), list(), timeout()) -> {ok, #sslsocket{}}. +-spec connect(host() | port(), [option()]) -> {ok, #sslsocket{}} | + {error, reason()}. +-spec connect(host() | port(), [option()] | port_num(), timeout() | list()) -> + {ok, #sslsocket{}} | {error, reason()}. +-spec connect(host() | port(), port_num(), list(), timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Connect to a ssl server. @@ -126,7 +150,7 @@ connect(Host, Port, Options0, Timeout) -> end. %%-------------------------------------------------------------------- --spec listen(port_num(), list()) ->{ok, #sslsocket{}} | {error, reason()}. +-spec listen(port_num(), [option()]) ->{ok, #sslsocket{}} | {error, reason()}. %% %% Description: Creates a ssl listen socket. @@ -150,8 +174,10 @@ listen(Port, Options0) -> end. %%-------------------------------------------------------------------- --spec transport_accept(#sslsocket{}) -> {ok, #sslsocket{}}. --spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}}. +-spec transport_accept(#sslsocket{}) -> {ok, #sslsocket{}} | + {error, reason()}. +-spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} | + {error, reason()}. %% %% Description: Performs transport accept on a ssl listen socket %%-------------------------------------------------------------------- @@ -189,9 +215,10 @@ transport_accept(#sslsocket{} = ListenSocket, Timeout) -> ssl_broker:transport_accept(Pid, ListenSocket, Timeout). %%-------------------------------------------------------------------- --spec ssl_accept(#sslsocket{}) -> {ok, #sslsocket{}} | {error, reason()}. --spec ssl_accept(#sslsocket{}, list() | timeout()) -> {ok, #sslsocket{}} | {error, reason()}. --spec ssl_accept(port(), list(), timeout()) -> {ok, #sslsocket{}} | {error, reason()}. +-spec ssl_accept(#sslsocket{}) -> ok | {error, reason()}. +-spec ssl_accept(#sslsocket{} | port(), timeout()| [option()]) -> + ok | {ok, #sslsocket{}} | {error, reason()}. +-spec ssl_accept(port(), [option()], timeout()) -> {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Performs accept on a ssl listen socket. e.i. performs %% ssl handshake. diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl index 2a5a7f3394..3eceefa304 100644 --- a/lib/ssl/src/ssl_certificate_db.erl +++ b/lib/ssl/src/ssl_certificate_db.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 @@ -27,7 +27,9 @@ -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/3]). + lookup_cached_certs/1, cache_pem_file/4, uncache_pem_file/2, lookup/2]). + +-type time() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}. %%==================================================================== %% Internal application API @@ -98,17 +100,35 @@ add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) -> insert(Pid, File, PidToFileDb), {ok, Ref}. %%-------------------------------------------------------------------- --spec cache_pem_file(pid(), string(), certdb_ref()) -> term(). +-spec cache_pem_file(pid(), string(), time(), certdb_ref()) -> term(). %% %% Description: Cache file as binary in DB %%-------------------------------------------------------------------- -cache_pem_file(Pid, File, [CertsDb, _FileToRefDb, PidToFileDb]) -> +cache_pem_file(Pid, File, Time, [CertsDb, _FileToRefDb, PidToFileDb]) -> {ok, PemBin} = file:read_file(File), Content = public_key:pem_decode(PemBin), - insert({file, File}, Content, CertsDb), + insert({file, File}, {Time, Content}, CertsDb), insert(Pid, File, PidToFileDb), {ok, Content}. +%-------------------------------------------------------------------- +-spec uncache_pem_file(string(), certdb_ref()) -> 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 +%% when those processes are finish the cache will be cleaned. It is +%% a rare but possible case a new ssl client/server is started with +%% a filename with the same name as previously started client/server +%% but with different content. +%% -------------------------------------------------------------------- +uncache_pem_file(File, [_CertsDb, _FileToRefDb, PidToFileDb]) -> + Pids = select(PidToFileDb, [{{'$1', File},[],['$$']}]), + lists:foreach(fun([Pid]) -> + exit(Pid, shutdown) + end, Pids). + + + %%-------------------------------------------------------------------- -spec remove_trusted_certs(pid(), certdb_ref()) -> term(). @@ -174,6 +194,22 @@ issuer_candidate(PrevCandidateKey) -> end. %%-------------------------------------------------------------------- +-spec lookup(term(), term()) -> term() | undefined. +%% +%% Description: Looks up an element in a certificat <Db>. +%%-------------------------------------------------------------------- +lookup(Key, Db) -> + case ets:lookup(Db, Key) of + [] -> + undefined; + Contents -> + Pick = fun({_, Data}) -> Data; + ({_,_,Data}) -> Data + end, + [Pick(Data) || Data <- Contents] + end. + +%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- certificate_db_name() -> @@ -191,16 +227,8 @@ ref_count(Key, Db,N) -> delete(Key, Db) -> _ = ets:delete(Db, Key). -lookup(Key, Db) -> - case ets:lookup(Db, Key) of - [] -> - undefined; - Contents -> - Pick = fun({_, Data}) -> Data; - ({_,_,Data}) -> Data - end, - [Pick(Data) || Data <- Contents] - end. +select(Db, MatchSpec)-> + ets:select(Db, MatchSpec). remove_certs(Ref, CertsDb) -> ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 6c9ac65b64..489895cf29 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.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 @@ -70,7 +70,6 @@ %% {{md5_hash, sha_hash}, {prev_md5, prev_sha}} (binary()) tls_handshake_hashes, % see above tls_cipher_texts, % list() received but not deciphered yet - own_cert, % binary() session, % #session{} from ssl_handshake.hrl session_cache, % session_cache_cb, % @@ -90,7 +89,8 @@ log_alert, % boolean() renegotiation, % {boolean(), From | internal | peer} recv_during_renegotiation, %boolean() - send_queue % queue() + send_queue, % queue() + terminated = false % }). -define(DEFAULT_DIFFIE_HELLMAN_PARAMS, @@ -304,8 +304,9 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, try ssl_init(SSLOpts0, Role) of {ok, Ref, CacheRef, OwnCert, Key, DHParams} -> + Session = State0#state.session, State = State0#state{tls_handshake_hashes = Hashes0, - own_cert = OwnCert, + session = Session#session{own_certificate = OwnCert}, cert_db_ref = Ref, session_cache = CacheRef, private_key = Key, @@ -331,14 +332,13 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, %%-------------------------------------------------------------------- hello(start, #state{host = Host, port = Port, role = client, ssl_options = SslOpts, + session = #session{own_certificate = Cert} = Session0, transport_cb = Transport, socket = Socket, connection_states = ConnectionStates, - renegotiation = {Renegotiation, _}} - = State0) -> - + renegotiation = {Renegotiation, _}} = State0) -> Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates, - SslOpts, Renegotiation), + SslOpts, Renegotiation, Cert), Version = Hello#client_hello.client_version, Hashes0 = ssl_handshake:init_hashes(), @@ -347,13 +347,13 @@ hello(start, #state{host = Host, port = Port, role = client, Transport:send(Socket, BinMsg), State1 = State0#state{connection_states = CS2, negotiated_version = Version, %% Requested version - session = - #session{session_id = Hello#client_hello.session_id, - is_resumable = false}, + session = + Session0#session{session_id = Hello#client_hello.session_id, + is_resumable = false}, tls_handshake_hashes = Hashes1}, {Record, State} = next_record(State1), next_state(hello, Record, State); - + hello(start, #state{role = server} = State0) -> {Record, State} = next_record(State0), next_state(hello, Record, State); @@ -370,7 +370,6 @@ hello(#server_hello{cipher_suite = CipherSuite, negotiated_version = ReqVersion, renegotiation = {Renegotiation, _}, ssl_options = SslOptions} = State0) -> - case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of {Version, NewId, ConnectionStates} -> {KeyAlgorithm, _, _} = @@ -396,13 +395,11 @@ hello(#server_hello{cipher_suite = CipherSuite, hello(Hello = #client_hello{client_version = ClientVersion}, State = #state{connection_states = ConnectionStates0, - port = Port, session = Session0, + port = Port, session = #session{own_certificate = Cert} = Session0, renegotiation = {Renegotiation, _}, session_cache = Cache, session_cache_cb = CacheCb, - ssl_options = SslOpts, - own_cert = Cert}) -> - + ssl_options = SslOpts}) -> case ssl_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) of {Version, {Type, Session}, ConnectionStates} -> @@ -536,7 +533,7 @@ certify(#server_hello_done{}, connection_states = ConnectionStates0, negotiated_version = Version, premaster_secret = undefined, - role = client} = State0) -> + role = client} = State0) -> case ssl_handshake:master_secret(Version, Session, ConnectionStates0, client) of {MasterSecret, ConnectionStates1} -> @@ -613,7 +610,6 @@ certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPubl diffie_hellman_params = #'DHParameter'{prime = P, base = G}, diffie_hellman_keys = {_, ServerDhPrivateKey}} = State0) -> - case dh_master_secret(crypto:mpint(P), crypto:mpint(G), ClientPublicDhKey, ServerDhPrivateKey, State0) of #state{} = State1 -> {Record, State} = next_record(State1), @@ -656,8 +652,7 @@ cipher(#finished{verify_data = Data} = Finished, role = Role, session = #session{master_secret = MasterSecret} = Session0, - tls_handshake_hashes = Hashes0} = State) -> - + tls_handshake_hashes = Hashes0} = State) -> case ssl_handshake:verify_connection(Version, Finished, opposite_role(Role), MasterSecret, Hashes0) of @@ -678,15 +673,15 @@ cipher(Msg, State) -> %%-------------------------------------------------------------------- connection(#hello_request{}, #state{host = Host, port = Port, socket = Socket, + session = #session{own_certificate = Cert}, ssl_options = SslOpts, negotiated_version = Version, transport_cb = Transport, connection_states = ConnectionStates0, renegotiation = {Renegotiation, _}, tls_handshake_hashes = Hashes0} = State0) -> - Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, - SslOpts, Renegotiation), + SslOpts, Renegotiation, Cert), {BinMsg, ConnectionStates1, Hashes1} = encode_handshake(Hello, Version, ConnectionStates0, Hashes0), @@ -777,8 +772,12 @@ handle_sync_event(start, _, connection, State) -> handle_sync_event(start, From, StateName, State) -> {next_state, StateName, State#state{from = From}}; -handle_sync_event(close, _, _StateName, State) -> - {stop, normal, ok, State}; +handle_sync_event(close, _, StateName, State) -> + %% Run terminate before returning + %% so that the reuseaddr inet-option will work + %% as intended. + (catch terminate(user_close, StateName, State)), + {stop, normal, ok, State#state{terminated = true}}; handle_sync_event({shutdown, How0}, _, StateName, #state{transport_cb = Transport, @@ -966,6 +965,11 @@ handle_info(Msg, StateName, State) -> %% necessary cleaning up. When it returns, the gen_fsm terminates with %% Reason. The return value is ignored. %%-------------------------------------------------------------------- +terminate(_, _, #state{terminated = true}) -> + %% Happens when user closes the connection using ssl:close/1 + %% we want to guarantee that Transport:close has been called + %% when ssl:close/1 returns. + ok; terminate(Reason, connection, #state{negotiated_version = Version, connection_states = ConnectionStates, transport_cb = Transport, @@ -975,14 +979,14 @@ terminate(Reason, connection, #state{negotiated_version = Version, notify_renegotiater(Renegotiate), BinAlert = terminate_alert(Reason, Version, ConnectionStates), Transport:send(Socket, BinAlert), - workaround_transport_delivery_problems(Socket, Transport), + workaround_transport_delivery_problems(Socket, Transport, Reason), Transport:close(Socket); -terminate(_Reason, _StateName, #state{transport_cb = Transport, +terminate(Reason, _StateName, #state{transport_cb = Transport, socket = Socket, send_queue = SendQueue, renegotiation = Renegotiate}) -> notify_senders(SendQueue), notify_renegotiater(Renegotiate), - workaround_transport_delivery_problems(Socket, Transport), + workaround_transport_delivery_problems(Socket, Transport, Reason), Transport:close(Socket). %%-------------------------------------------------------------------- @@ -1082,6 +1086,7 @@ init_private_key({rsa, PrivateKey}, _, _,_) -> init_private_key({dsa, PrivateKey},_,_,_) -> public_key:der_decode('DSAPrivateKey', PrivateKey). +-spec(handle_file_error/6 :: (_,_,_,_,_,_) -> no_return()). handle_file_error(Line, Error, {badmatch, Reason}, File, Throw, Stack) -> file_error(Line, Error, Reason, File, Throw, Stack); handle_file_error(Line, Error, Reason, File, Throw, Stack) -> @@ -1147,7 +1152,7 @@ certify_client(#state{client_certificate_requested = true, role = client, transport_cb = Transport, negotiated_version = Version, cert_db_ref = CertDbRef, - own_cert = OwnCert, + session = #session{own_certificate = OwnCert}, socket = Socket, tls_handshake_hashes = Hashes0} = State) -> Certificate = ssl_handshake:certificate(OwnCert, CertDbRef, client), @@ -1163,10 +1168,10 @@ verify_client_cert(#state{client_certificate_requested = true, role = client, connection_states = ConnectionStates0, transport_cb = Transport, negotiated_version = Version, - own_cert = OwnCert, socket = Socket, private_key = PrivateKey, - session = #session{master_secret = MasterSecret}, + session = #session{master_secret = MasterSecret, + own_certificate = OwnCert}, tls_handshake_hashes = Hashes0} = State) -> case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret, @@ -1332,7 +1337,7 @@ certify_server(#state{transport_cb = Transport, connection_states = ConnectionStates, tls_handshake_hashes = Hashes, cert_db_ref = CertDbRef, - own_cert = OwnCert} = State) -> + session = #session{own_certificate = OwnCert}} = State) -> case ssl_handshake:certificate(OwnCert, CertDbRef, server) of CertMsg = #certificate{} -> {BinCertMsg, NewConnectionStates, NewHashes} = @@ -1359,7 +1364,6 @@ key_exchange(#state{role = server, key_algorithm = Algo, when Algo == dhe_dss; Algo == dhe_rsa; Algo == dh_anon -> - Keys = crypto:dh_generate_key([crypto:mpint(P), crypto:mpint(G)]), ConnectionState = ssl_record:pending_connection_state(ConnectionStates0, read), @@ -1911,14 +1915,22 @@ next_state_connection(StateName, #state{send_queue = Queue0, next_state_is_connection(State) end. +%% In next_state_is_connection/1: clear tls_handshake_hashes, +%% premaster_secret and public_key_info (only needed during handshake) +%% to reduce memory foot print of a connection. next_state_is_connection(State = #state{recv_during_renegotiation = true, socket_options = #socket_options{active = false}}) -> - passive_receive(State#state{recv_during_renegotiation = false}, connection); + passive_receive(State#state{recv_during_renegotiation = false, + premaster_secret = undefined, + public_key_info = undefined, + tls_handshake_hashes = {<<>>, <<>>}}, connection); next_state_is_connection(State0) -> {Record, State} = next_record_if_active(State0), - next_state(connection, Record, State). + next_state(connection, Record, State#state{premaster_secret = undefined, + public_key_info = undefined, + tls_handshake_hashes = {<<>>, <<>>}}). register_session(_, _, _, #session{is_resumable = true} = Session) -> Session; %% Already registered @@ -2185,7 +2197,8 @@ notify_renegotiater({true, From}) when not is_atom(From) -> notify_renegotiater(_) -> ok. -terminate_alert(Reason, Version, ConnectionStates) when Reason == normal; Reason == shutdown -> +terminate_alert(Reason, Version, ConnectionStates) when Reason == normal; Reason == shutdown; + Reason == user_close -> {BinAlert, _} = encode_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), Version, ConnectionStates), BinAlert; @@ -2194,10 +2207,13 @@ terminate_alert(_, Version, ConnectionStates) -> Version, ConnectionStates), BinAlert. -workaround_transport_delivery_problems(Socket, Transport) -> +workaround_transport_delivery_problems(_,_, user_close) -> + ok; +workaround_transport_delivery_problems(Socket, Transport, _) -> %% Standard trick to try to make sure all %% data sent to to tcp port is really sent - %% before tcp port is closed. + %% before tcp port is closed so that the peer will + %% get a correct error message. inet:setopts(Socket, [{active, false}]), Transport:shutdown(Socket, write), Transport:recv(Socket, 0). diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index c7a1c4965d..1f4c44d115 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.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,7 +30,7 @@ -include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). --export([master_secret/4, client_hello/5, server_hello/4, hello/4, +-export([master_secret/4, client_hello/6, server_hello/4, hello/4, hello_request/0, certify/6, certificate/3, client_certificate_verify/5, certificate_verify/5, certificate_request/2, key_exchange/2, server_key_exchange_hash/2, @@ -49,13 +49,13 @@ %%==================================================================== %%-------------------------------------------------------------------- -spec client_hello(host(), port_num(), #connection_states{}, - #ssl_options{}, boolean()) -> #client_hello{}. + #ssl_options{}, boolean(), der_cert()) -> #client_hello{}. %% %% Description: Creates a client hello message. %%-------------------------------------------------------------------- client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions, ciphers = UserSuites} - = SslOpts, Renegotiation) -> + = SslOpts, Renegotiation, OwnCert) -> Fun = fun(Version) -> ssl_record:protocol_version(Version) @@ -65,7 +65,7 @@ client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions, SecParams = Pending#connection_state.security_parameters, Ciphers = available_suites(UserSuites, Version), - Id = ssl_manager:client_session_id(Host, Port, SslOpts), + Id = ssl_manager:client_session_id(Host, Port, SslOpts, OwnCert), #client_hello{session_id = Id, client_version = Version, @@ -194,14 +194,12 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef, {fun(OtpCert, ExtensionOrError, {SslState, UserState}) -> case ssl_certificate:validate_extension(OtpCert, ExtensionOrError, - SslState) of - {valid, _} -> - apply_user_fun(Fun, OtpCert, - ExtensionOrError, UserState, - SslState); - {fail, Reason} -> - apply_user_fun(Fun, OtpCert, Reason, UserState, - SslState); + SslState) of + {valid, NewSslState} -> + {valid, {NewSslState, UserState}}; + {fail, Reason} -> + apply_user_fun(Fun, OtpCert, Reason, UserState, + SslState); {unknown, _} -> apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState, SslState) @@ -571,7 +569,7 @@ select_session(Hello, Port, Session, Version, #ssl_options{ciphers = UserSuites} = SslOpts, Cache, CacheCb, Cert) -> SuggestedSessionId = Hello#client_hello.session_id, SessionId = ssl_manager:server_session_id(Port, SuggestedSessionId, - SslOpts), + SslOpts, Cert), Suites = available_suites(Cert, UserSuites, Version), case ssl_session:is_new(SuggestedSessionId, SessionId) of diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index 68a7802ef2..8ae4d2332e 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -36,6 +36,7 @@ -record(session, { session_id, peer_certificate, + own_certificate, compression_method, cipher_suite, master_secret, diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 43a85c2d9d..715941e3ad 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -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 @@ -19,7 +19,6 @@ %% - -ifndef(ssl_internal). -define(ssl_internal, true). diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 3b02d96562..f845b1ecc0 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -29,8 +29,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/3, - server_session_id/3, + lookup_trusted_cert/3, issuer_candidate/1, client_session_id/4, + server_session_id/4, register_session/2, register_session/3, invalidate_session/2, invalidate_session/3]). @@ -43,6 +43,7 @@ -include("ssl_handshake.hrl"). -include("ssl_internal.hrl"). +-include_lib("kernel/include/file.hrl"). -record(state, { session_cache, @@ -76,16 +77,17 @@ start_link(Opts) -> connection_init(Trustedcerts, Role) -> call({connection_init, Trustedcerts, Role}). %%-------------------------------------------------------------------- --spec cache_pem_file(string()) -> {ok, term()}. +-spec cache_pem_file(string()) -> {ok, term()} | {error, reason()}. %% -%% Description: Cach a pem file and +%% Description: Cach a pem file and return its content. %%-------------------------------------------------------------------- -cache_pem_file(File) -> - case ssl_certificate_db:lookup_cached_certs(File) of - [{_,Content}] -> - {ok, Content}; - [] -> - call({cache_pem, File}) +cache_pem_file(File) -> + try file:read_file_info(File) of + {ok, #file_info{mtime = LastWrite}} -> + cache_pem_file(File, LastWrite) + catch + _:Reason -> + {error, Reason} end. %%-------------------------------------------------------------------- -spec lookup_trusted_cert(reference(), serialnumber(), issuer()) -> @@ -106,20 +108,21 @@ lookup_trusted_cert(Ref, SerialNumber, Issuer) -> issuer_candidate(PrevCandidateKey) -> ssl_certificate_db:issuer_candidate(PrevCandidateKey). %%-------------------------------------------------------------------- --spec client_session_id(host(), port_num(), #ssl_options{}) -> session_id(). +-spec client_session_id(host(), port_num(), #ssl_options{}, + der_cert() | undefined) -> session_id(). %% %% Description: Select a session id for the client. %%-------------------------------------------------------------------- -client_session_id(Host, Port, SslOpts) -> - call({client_session_id, Host, Port, SslOpts}). +client_session_id(Host, Port, SslOpts, OwnCert) -> + call({client_session_id, Host, Port, SslOpts, OwnCert}). %%-------------------------------------------------------------------- --spec server_session_id(host(), port_num(), #ssl_options{}) -> session_id(). +-spec server_session_id(host(), port_num(), #ssl_options{}, der_cert()) -> session_id(). %% %% Description: Select a session id for the server. %%-------------------------------------------------------------------- -server_session_id(Port, SuggestedSessionId, SslOpts) -> - call({server_session_id, Port, SuggestedSessionId, SslOpts}). +server_session_id(Port, SuggestedSessionId, SslOpts, OwnCert) -> + call({server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}). %%-------------------------------------------------------------------- -spec register_session(port_num(), #session{}) -> ok. @@ -201,28 +204,35 @@ handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From, end, {reply, Result, State}; -handle_call({{client_session_id, Host, Port, SslOpts}, _}, _, +handle_call({{client_session_id, Host, Port, SslOpts, OwnCert}, _}, _, #state{session_cache = Cache, session_cache_cb = CacheCb} = State) -> - Id = ssl_session:id({Host, Port, SslOpts}, Cache, CacheCb), + Id = ssl_session:id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert), {reply, Id, State}; -handle_call({{server_session_id, Port, SuggestedSessionId, SslOpts}, _}, +handle_call({{server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}, _}, _, #state{session_cache_cb = CacheCb, session_cache = Cache, session_lifetime = LifeTime} = State) -> Id = ssl_session:id(Port, SuggestedSessionId, SslOpts, - Cache, CacheCb, LifeTime), + Cache, CacheCb, LifeTime, OwnCert), {reply, Id, State}; -handle_call({{cache_pem, File},Pid}, _, State = #state{certificate_db = Db}) -> - try ssl_certificate_db:cache_pem_file(Pid,File,Db) of +handle_call({{cache_pem, File, LastWrite}, Pid}, _, + #state{certificate_db = Db} = State) -> + try ssl_certificate_db:cache_pem_file(Pid, File, LastWrite, Db) of Result -> {reply, Result, State} catch _:Reason -> {reply, {error, Reason}, State} - end. + end; +handle_call({{recache_pem, File, LastWrite}, Pid}, From, + #state{certificate_db = Db} = State) -> + ssl_certificate_db:uncache_pem_file(File, Db), + cast({recache_pem, File, LastWrite, Pid, From}), + {noreply, State}. + %%-------------------------------------------------------------------- -spec handle_cast(msg(), #state{}) -> {noreply, #state{}}. %% Possible return values not used now. @@ -259,7 +269,21 @@ handle_cast({invalidate_session, Port, #session{session_id = ID}}, #state{session_cache = Cache, session_cache_cb = CacheCb} = State) -> CacheCb:delete(Cache, {Port, ID}), - {noreply, State}. + {noreply, State}; + +handle_cast({recache_pem, File, LastWrite, Pid, From}, + #state{certificate_db = [_, FileToRefDb, _]} = State0) -> + case ssl_certificate_db:lookup(File, FileToRefDb) of + undefined -> + {reply, Msg, State} = handle_call({{cache_pem, File, LastWrite}, Pid}, From, State0), + gen_server:reply(From, Msg), + {noreply, State}; + _ -> %% Send message to self letting cleanup messages be handled + %% first so that no reference to the old version of file + %% exists when we cache the new one. + cast({recache_pem, File, LastWrite, Pid, From}), + {noreply, State0} + end. %%-------------------------------------------------------------------- -spec handle_info(msg(), #state{}) -> {noreply, #state{}}. @@ -286,12 +310,14 @@ handle_info({'EXIT', _, _}, State) -> handle_info({'DOWN', _Ref, _Type, _Pid, ecacertfile}, State) -> {noreply, State}; +handle_info({'DOWN', _Ref, _Type, Pid, shutdown}, State) -> + handle_info({remove_trusted_certs, Pid}, State); handle_info({'DOWN', _Ref, _Type, Pid, _Reason}, State) -> erlang:send_after(?CERTIFICATE_CACHE_CLEANUP, self(), {remove_trusted_certs, Pid}), {noreply, State}; handle_info({remove_trusted_certs, Pid}, - State = #state{certificate_db = Db}) -> + #state{certificate_db = Db} = State) -> ssl_certificate_db:remove_trusted_certs(Pid, Db), {noreply, State}; @@ -362,3 +388,16 @@ session_validation({{{Host, Port}, _}, Session}, LifeTime) -> 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 + [{_, {Mtime, Content}}] -> + case LastWrite of + Mtime -> + {ok, Content}; + _ -> + call({recache_pem, File, LastWrite}) + end; + [] -> + call({cache_pem, File, LastWrite}) + end. diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index 25e7445180..dc4b7a711c 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -28,7 +28,7 @@ -include("ssl_internal.hrl"). %% Internal application API --export([is_new/2, id/3, id/6, valid_session/2]). +-export([is_new/2, id/4, id/7, valid_session/2]). -define(GEN_UNIQUE_ID_MAX_TRIES, 10). @@ -48,13 +48,14 @@ is_new(_ClientSuggestion, _ServerDecision) -> true. %%-------------------------------------------------------------------- --spec id({host(), port_num(), #ssl_options{}}, cache_ref(), atom()) -> binary(). +-spec id({host(), port_num(), #ssl_options{}}, cache_ref(), atom(), + undefined | binary()) -> binary(). %% %% Description: Should be called by the client side to get an id %% for the client hello message. %%-------------------------------------------------------------------- -id(ClientInfo, Cache, CacheCb) -> - case select_session(ClientInfo, Cache, CacheCb) of +id(ClientInfo, Cache, CacheCb, OwnCert) -> + case select_session(ClientInfo, Cache, CacheCb, OwnCert) of no_session -> <<>>; SessionId -> @@ -63,19 +64,19 @@ id(ClientInfo, Cache, CacheCb) -> %%-------------------------------------------------------------------- -spec id(port_num(), binary(), #ssl_options{}, cache_ref(), - atom(), seconds()) -> binary(). + atom(), seconds(), binary()) -> binary(). %% %% Description: Should be called by the server side to get an id %% for the server hello message. %%-------------------------------------------------------------------- -id(Port, <<>>, _, Cache, CacheCb, _) -> +id(Port, <<>>, _, Cache, CacheCb, _, _) -> new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb); id(Port, SuggestedSessionId, #ssl_options{reuse_sessions = ReuseEnabled, reuse_session = ReuseFun}, - Cache, CacheCb, SecondLifeTime) -> + Cache, CacheCb, SecondLifeTime, OwnCert) -> case is_resumable(SuggestedSessionId, Port, ReuseEnabled, - ReuseFun, Cache, CacheCb, SecondLifeTime) of + ReuseFun, Cache, CacheCb, SecondLifeTime, OwnCert) of true -> SuggestedSessionId; false -> @@ -93,19 +94,20 @@ valid_session(#session{time_stamp = TimeStamp}, LifeTime) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -select_session({HostIP, Port, SslOpts}, Cache, CacheCb) -> +select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) -> Sessions = CacheCb:select_session(Cache, {HostIP, Port}), - select_session(Sessions, SslOpts). + select_session(Sessions, SslOpts, OwnCert). -select_session([], _) -> +select_session([], _, _) -> no_session; select_session(Sessions, #ssl_options{ciphers = Ciphers, - reuse_sessions = ReuseSession}) -> + reuse_sessions = ReuseSession}, OwnCert) -> IsResumable = fun(Session) -> ReuseSession andalso (Session#session.is_resumable) andalso lists:member(Session#session.cipher_suite, Ciphers) + andalso (OwnCert == Session#session.own_certificate) end, case [Id || [Id, Session] <- Sessions, IsResumable(Session)] of [] -> @@ -140,14 +142,16 @@ new_id(Port, Tries, Cache, CacheCb) -> end. is_resumable(SuggestedSessionId, Port, ReuseEnabled, ReuseFun, Cache, - CacheCb, SecondLifeTime) -> + CacheCb, SecondLifeTime, OwnCert) -> case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of #session{cipher_suite = CipherSuite, + own_certificate = SessionOwnCert, compression_method = Compression, is_resumable = Is_resumable, peer_certificate = PeerCert} = Session -> ReuseEnabled andalso Is_resumable + andalso (OwnCert == SessionOwnCert) andalso valid_session(Session, SecondLifeTime) andalso ReuseFun(SuggestedSessionId, PeerCert, Compression, CipherSuite); diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl index c49f9f1e6d..f2926b2d2f 100644 --- a/lib/ssl/src/ssl_ssl3.erl +++ b/lib/ssl/src/ssl_ssl3.erl @@ -102,11 +102,6 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, Length, Fragment) -> %% hash(MAC_write_secret + pad_1 + seq_num + %% SSLCompressed.type + SSLCompressed.length + %% SSLCompressed.fragment)); - case Method of - ?NULL -> ok; - _ -> - ok - end, Mac = mac_hash(Method, Mac_write_secret, [<<?UINT64(Seq_num), ?BYTE(Type), ?UINT16(Length)>>, Fragment]), diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl index 3784483e9c..5f9850c386 100644 --- a/lib/ssl/src/ssl_tls1.erl +++ b/lib/ssl/src/ssl_tls1.erl @@ -128,11 +128,6 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, %% HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type + %% TLSCompressed.version + TLSCompressed.length + %% TLSCompressed.fragment)); - case Method of - ?NULL -> ok; - _ -> - ok - end, Mac = hmac_hash(Method, Mac_write_secret, [<<?UINT64(Seq_num), ?BYTE(Type), ?BYTE(Major), ?BYTE(Minor), ?UINT16(Length)>>, diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 8f9554f3ce..6a1b83d344 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.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 @@ -27,6 +27,7 @@ -include("test_server.hrl"). -include("test_server_line.hrl"). -include_lib("public_key/include/public_key.hrl"). + -include("ssl_alert.hrl"). -define('24H_in_sec', 86400). @@ -47,20 +48,23 @@ %%-------------------------------------------------------------------- init_per_suite(Config0) -> Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2), - crypto:start(), - application:start(public_key), - ssl:start(), - - %% make rsa certs using oppenssl - Result = - (catch make_certs:all(?config(data_dir, Config0), - ?config(priv_dir, Config0))), - test_server:format("Make certs ~p~n", [Result]), - - Config1 = ssl_test_lib:make_dsa_cert(Config0), - Config = ssl_test_lib:cert_options(Config1), - [{watchdog, Dog} | Config]. - + case application:start(crypto) of + ok -> + application:start(public_key), + ssl:start(), + + %% make rsa certs using oppenssl + Result = + (catch make_certs:all(?config(data_dir, Config0), + ?config(priv_dir, Config0))), + test_server:format("Make certs ~p~n", [Result]), + + Config1 = ssl_test_lib:make_dsa_cert(Config0), + Config = ssl_test_lib:cert_options(Config1), + [{watchdog, Dog} | Config]; + _ -> + {skip, "Crypto did not start"} + end. %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ %% Config - [tuple()] @@ -69,7 +73,7 @@ init_per_suite(Config0) -> %%-------------------------------------------------------------------- end_per_suite(_Config) -> ssl:stop(), - crypto:stop(). + application:stop(crypto). %%-------------------------------------------------------------------- %% Function: init_per_testcase(TestCase, Config) -> Config @@ -125,6 +129,9 @@ init_per_testcase(empty_protocol_versions, Config) -> ssl:start(), Config; +init_per_testcase(different_ca_peer_sign, Config0) -> + ssl_test_lib:make_mix_cert(Config0); + init_per_testcase(_TestCase, Config0) -> Config = lists:keydelete(watchdog, 1, Config0), Dog = test_server:timetrap(?TIMEOUT), @@ -200,12 +207,16 @@ all(suite) -> server_does_not_want_to_reuse_session, client_renegotiate, server_renegotiate, client_renegotiate_reused_session, server_renegotiate_reused_session, client_no_wrap_sequence_number, - server_no_wrap_sequence_number, extended_key_usage, + server_no_wrap_sequence_number, + extended_key_usage_verify_peer, extended_key_usage_verify_none, no_authority_key_identifier, invalid_signature_client, invalid_signature_server, cert_expired, client_with_cert_cipher_suites_handshake, unknown_server_ca_fail, der_input, unknown_server_ca_accept_verify_none, unknown_server_ca_accept_verify_peer, - unknown_server_ca_accept_backwardscompatibilty + unknown_server_ca_accept_backwardscompatibilty, + %different_ca_peer_sign, + no_reuses_session_server_restart_new_cert, + no_reuses_session_server_restart_new_cert_file, reuseaddr ]. %% Test cases starts here. @@ -321,7 +332,6 @@ basic_test(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). - %%-------------------------------------------------------------------- controlling_process(doc) -> @@ -521,9 +531,7 @@ client_closes_socket(Config) when is_list(Config) -> _Client = spawn_link(Connect), - ssl_test_lib:check_result(Server, {error,closed}), - - ssl_test_lib:close(Server). + ssl_test_lib:check_result(Server, {error,closed}). %%-------------------------------------------------------------------- @@ -738,7 +746,6 @@ socket_options(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), - ssl_test_lib:close(Client), {ok, Listen} = ssl:listen(0, ServerOpts), {ok,[{mode,list}]} = ssl:getopts(Listen, [mode]), @@ -841,6 +848,7 @@ send_recv(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- send_close(doc) -> [""]; @@ -867,8 +875,7 @@ send_close(Config) when is_list(Config) -> ok = ssl:send(SslS, "Hello world"), {ok,<<"Hello world">>} = ssl:recv(SslS, 11), gen_tcp:close(TcpS), - {error, _} = ssl:send(SslS, "Hello world"), - ssl_test_lib:close(Server). + {error, _} = ssl:send(SslS, "Hello world"). %%-------------------------------------------------------------------- close_transport_accept(doc) -> @@ -1043,8 +1050,7 @@ tcp_connect(Config) when is_list(Config) -> {Server, {error, Error}} -> test_server:format("Error ~p", [Error]) end - end, - ssl_test_lib:close(Server). + end. dummy(_Socket) -> @@ -1149,13 +1155,13 @@ ecertfile(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -ecacertfile(doc) -> +ecacertfile(doc) -> ["Test what happens with an invalid cacert file"]; -ecacertfile(suite) -> +ecacertfile(suite) -> []; -ecacertfile(Config) when is_list(Config) -> +ecacertfile(Config) when is_list(Config) -> ClientOpts = [{reuseaddr, true}|?config(client_opts, Config)], ServerBadOpts = [{reuseaddr, true}|?config(server_bad_ca, Config)], {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1532,7 +1538,7 @@ erlang_cipher_suite(Suite) -> Suite. cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> - process_flag(trap_exit, true), + %% process_flag(trap_exit, true), test_server:format("Testing CipherSuite ~p~n", [CipherSuite]), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1556,16 +1562,8 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> Result = ssl_test_lib:wait_for_result(Server, ok, Client, ok), ssl_test_lib:close(Server), - receive - {'EXIT', Server, normal} -> - ok - end, ssl_test_lib:close(Client), - receive - {'EXIT', Client, normal} -> - ok - end, - process_flag(trap_exit, false), + case Result of ok -> []; @@ -1607,7 +1605,6 @@ reuse_session(suite) -> []; reuse_session(Config) when is_list(Config) -> - process_flag(trap_exit, true), ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1615,13 +1612,13 @@ reuse_session(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, session_info_result, []}}, - {options, ServerOpts}]), + {mfa, {?MODULE, session_info_result, []}}, + {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, + {mfa, {ssl_test_lib, no_result, []}}, {from, self()}, {options, ClientOpts}]), SessionInfo = receive @@ -1629,16 +1626,16 @@ reuse_session(Config) when is_list(Config) -> Info end, - Server ! listen, + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered test_server:sleep(?SLEEP), Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {?MODULE, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), receive {Client1, SessionInfo} -> ok; @@ -1648,10 +1645,10 @@ reuse_session(Config) when is_list(Config) -> test_server:fail(session_not_reused) end, - Server ! listen, + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, Client2 = - ssl_test_lib:start_client([{node, ClientNode}, + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {mfa, {?MODULE, session_info_result, []}}, {from, self()}, {options, [{reuse_sessions, false} @@ -1665,10 +1662,6 @@ reuse_session(Config) when is_list(Config) -> end, ssl_test_lib:close(Server), - ssl_test_lib:close(Client0), - ssl_test_lib:close(Client1), - ssl_test_lib:close(Client2), - Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -1680,7 +1673,7 @@ reuse_session(Config) when is_list(Config) -> Client3 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port1}, {host, Hostname}, - {mfa, {?MODULE, session_info_result, []}}, + {mfa, {ssl_test_lib, no_result, []}}, {from, self()}, {options, ClientOpts}]), SessionInfo1 = @@ -1689,7 +1682,7 @@ reuse_session(Config) when is_list(Config) -> Info1 end, - Server1 ! listen, + Server1 ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered test_server:sleep(?SLEEP), @@ -1705,14 +1698,16 @@ reuse_session(Config) when is_list(Config) -> test_server:fail( session_reused_when_session_reuse_disabled_by_server); {Client4, _Other} -> + test_server:format("OTHER: ~p ~n", [_Other]), ok end, - + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client0), + ssl_test_lib:close(Client1), + ssl_test_lib:close(Client2), ssl_test_lib:close(Client3), - ssl_test_lib:close(Client4), - process_flag(trap_exit, false). - + ssl_test_lib:close(Client4). session_info_result(Socket) -> ssl:session_info(Socket). @@ -1725,7 +1720,6 @@ reuse_session_expired(suite) -> []; reuse_session_expired(Config) when is_list(Config) -> - process_flag(trap_exit, true), ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1746,8 +1740,8 @@ reuse_session_expired(Config) when is_list(Config) -> {Server, Info} -> Info end, - - Server ! listen, + + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered test_server:sleep(?SLEEP), @@ -1767,7 +1761,7 @@ reuse_session_expired(Config) when is_list(Config) -> end, Server ! listen, - + %% Make sure session is unregistered due to expiration test_server:sleep((?EXPIRE+1) * 1000), @@ -1782,12 +1776,12 @@ reuse_session_expired(Config) when is_list(Config) -> {Client2, _} -> ok end, - + process_flag(trap_exit, false), ssl_test_lib:close(Server), ssl_test_lib:close(Client0), ssl_test_lib:close(Client1), - ssl_test_lib:close(Client2), - process_flag(trap_exit, false). + ssl_test_lib:close(Client2). + %%-------------------------------------------------------------------- server_does_not_want_to_reuse_session(doc) -> ["Test reuse of sessions (short handshake)"]; @@ -1820,10 +1814,11 @@ server_does_not_want_to_reuse_session(Config) when is_list(Config) -> Info end, - Server ! listen, + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered test_server:sleep(?SLEEP), + ssl_test_lib:close(Client0), Client1 = ssl_test_lib:start_client([{node, ClientNode}, @@ -1836,11 +1831,9 @@ server_does_not_want_to_reuse_session(Config) when is_list(Config) -> {Client1, _Other} -> ok end, - + ssl_test_lib:close(Server), - ssl_test_lib:close(Client0), - ssl_test_lib:close(Client1), - process_flag(trap_exit, false). + ssl_test_lib:close(Client1). %%-------------------------------------------------------------------- @@ -2007,6 +2000,7 @@ server_verify_none_active_once(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). + %%-------------------------------------------------------------------- server_verify_client_once_passive(doc) -> @@ -2034,7 +2028,7 @@ server_verify_client_once_passive(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client0, ok), ssl_test_lib:close(Client0), - Server ! listen, + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, @@ -2072,7 +2066,7 @@ server_verify_client_once_active(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client0, ok), ssl_test_lib:close(Client0), - Server ! listen, + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, @@ -2083,7 +2077,6 @@ server_verify_client_once_active(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client1). - %%-------------------------------------------------------------------- server_verify_client_once_active_once(doc) -> @@ -2111,18 +2104,17 @@ server_verify_client_once_active_once(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client0, ok), ssl_test_lib:close(Client0), - Server ! listen, - + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, result_ok, []}}, - {options, [{active, once} | ClientOpts]}]), + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, result_ok, []}}, + {options, [{active, once} | ClientOpts]}]), ssl_test_lib:check_result(Client1, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client1). - + %%-------------------------------------------------------------------- server_verify_no_cacerts(doc) -> @@ -2130,9 +2122,8 @@ server_verify_no_cacerts(doc) -> server_verify_no_cacerts(suite) -> []; - server_verify_no_cacerts(Config) when is_list(Config) -> - ServerOpts = ServerOpts = ?config(server_opts, Config), + ServerOpts = ?config(server_opts, Config), {_, ServerNode, _} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, @@ -2293,8 +2284,6 @@ client_verify_none_active_once(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). - - %%-------------------------------------------------------------------- client_renegotiate(doc) -> ["Test ssl:renegotiate/1 on client."]; @@ -2303,7 +2292,6 @@ client_renegotiate(suite) -> []; client_renegotiate(Config) when is_list(Config) -> - process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -2326,11 +2314,9 @@ client_renegotiate(Config) when is_list(Config) -> {options, [{reuse_sessions, false} | ClientOpts]}]), ssl_test_lib:check_result(Client, ok, Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. + ssl_test_lib:close(Client). + %%-------------------------------------------------------------------- server_renegotiate(doc) -> ["Test ssl:renegotiate/1 on server."]; @@ -2339,7 +2325,6 @@ server_renegotiate(suite) -> []; server_renegotiate(Config) when is_list(Config) -> - process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -2362,8 +2347,7 @@ server_renegotiate(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - ok. + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- client_renegotiate_reused_session(doc) -> @@ -2373,7 +2357,6 @@ client_renegotiate_reused_session(suite) -> []; client_renegotiate_reused_session(Config) when is_list(Config) -> - process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -2396,11 +2379,8 @@ client_renegotiate_reused_session(Config) when is_list(Config) -> {options, [{reuse_sessions, true} | ClientOpts]}]), ssl_test_lib:check_result(Client, ok, Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- server_renegotiate_reused_session(doc) -> ["Test ssl:renegotiate/1 on server when the ssl session will be reused."]; @@ -2409,7 +2389,6 @@ server_renegotiate_reused_session(suite) -> []; server_renegotiate_reused_session(Config) when is_list(Config) -> - process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -2432,9 +2411,7 @@ server_renegotiate_reused_session(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - ok. - + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- client_no_wrap_sequence_number(doc) -> ["Test that erlang client will renegotiate session when", @@ -2446,7 +2423,6 @@ client_no_wrap_sequence_number(suite) -> []; client_no_wrap_sequence_number(Config) when is_list(Config) -> - process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -2471,11 +2447,8 @@ client_no_wrap_sequence_number(Config) when is_list(Config) -> {renegotiate_at, N} | ClientOpts]}]), ssl_test_lib:check_result(Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- server_no_wrap_sequence_number(doc) -> ["Test that erlang server will renegotiate session when", @@ -2487,7 +2460,6 @@ server_no_wrap_sequence_number(suite) -> []; server_no_wrap_sequence_number(Config) when is_list(Config) -> - process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -2511,17 +2483,15 @@ server_no_wrap_sequence_number(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok), ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - ok. - + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -extended_key_usage(doc) -> - ["Test cert that has a critical extended_key_usage extension"]; +extended_key_usage_verify_peer(doc) -> + ["Test cert that has a critical extended_key_usage extension in verify_peer mode"]; -extended_key_usage(suite) -> +extended_key_usage_verify_peer(suite) -> []; -extended_key_usage(Config) when is_list(Config) -> +extended_key_usage_verify_peer(Config) when is_list(Config) -> ClientOpts = ?config(client_verification_opts, Config), ServerOpts = ?config(server_verification_opts, Config), PrivDir = ?config(priv_dir, Config), @@ -2537,13 +2507,13 @@ extended_key_usage(Config) when is_list(Config) -> ServerExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']}, ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, ServerExtensions = ServerOTPTbsCert#'OTPTBSCertificate'.extensions, - NewServerOTPTbsCert = ServerOTPTbsCert#'OTPTBSCertificate'{extensions = - [ServerExtKeyUsageExt | + NewServerOTPTbsCert = ServerOTPTbsCert#'OTPTBSCertificate'{extensions = + [ServerExtKeyUsageExt | ServerExtensions]}, - NewServerDerCert = public_key:pkix_sign(NewServerOTPTbsCert, Key), + NewServerDerCert = public_key:pkix_sign(NewServerOTPTbsCert, Key), ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], - + ClientCertFile = proplists:get_value(certfile, ClientOpts), NewClientCertFile = filename:join(PrivDir, "client/new_cert.pem"), [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), @@ -2551,28 +2521,90 @@ extended_key_usage(Config) when is_list(Config) -> ClientExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-clientAuth']}, ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, ClientExtensions = ClientOTPTbsCert#'OTPTBSCertificate'.extensions, - NewClientOTPTbsCert = ClientOTPTbsCert#'OTPTBSCertificate'{extensions = + NewClientOTPTbsCert = ClientOTPTbsCert#'OTPTBSCertificate'{extensions = [ClientExtKeyUsageExt | ClientExtensions]}, - NewClientDerCert = public_key:pkix_sign(NewClientOTPTbsCert, Key), + NewClientDerCert = public_key:pkix_sign(NewClientOTPTbsCert, Key), ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, {mfa, {?MODULE, send_recv_result_active, []}}, {options, [{verify, verify_peer} | NewServerOpts]}]), Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, + {from, self()}, {mfa, {?MODULE, send_recv_result_active, []}}, {options, [{verify, verify_peer} | NewClientOpts]}]), - + ssl_test_lib:check_result(Server, ok, Client, ok), - + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +extended_key_usage_verify_none(doc) -> + ["Test cert that has a critical extended_key_usage extension in verify_none mode"]; + +extended_key_usage_verify_none(suite) -> + []; + +extended_key_usage_verify_none(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + PrivDir = ?config(priv_dir, Config), + + KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = public_key:pem_entry_decode(KeyEntry), + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + NewServerCertFile = filename:join(PrivDir, "server/new_cert.pem"), + [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), + ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), + ServerExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']}, + ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, + ServerExtensions = ServerOTPTbsCert#'OTPTBSCertificate'.extensions, + NewServerOTPTbsCert = ServerOTPTbsCert#'OTPTBSCertificate'{extensions = + [ServerExtKeyUsageExt | + ServerExtensions]}, + NewServerDerCert = public_key:pkix_sign(NewServerOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), + NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], + + ClientCertFile = proplists:get_value(certfile, ClientOpts), + NewClientCertFile = filename:join(PrivDir, "client/new_cert.pem"), + [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), + ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), + ClientExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-clientAuth']}, + ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, + ClientExtensions = ClientOTPTbsCert#'OTPTBSCertificate'.extensions, + NewClientOTPTbsCert = ClientOTPTbsCert#'OTPTBSCertificate'{extensions = + [ClientExtKeyUsageExt | + ClientExtensions]}, + NewClientDerCert = public_key:pkix_sign(NewClientOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), + NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], + + {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_active, []}}, + {options, [{verify, verify_none} | NewServerOpts]}]), + 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_active, []}}, + {options, [{verify, verify_none} | NewClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), ssl_test_lib:close(Client). @@ -2885,9 +2917,7 @@ unknown_server_ca_fail(Config) when is_list(Config) -> | ClientOpts]}]), ssl_test_lib:check_result(Server, {error,"unknown ca"}, - Client, {error, "unknown ca"}), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + Client, {error, "unknown ca"}). %%-------------------------------------------------------------------- unknown_server_ca_accept_verify_none(doc) -> @@ -3052,12 +3082,205 @@ der_input_opts(Opts) -> {Cert, {rsa, Key}, CaCerts, DHParams}. %%-------------------------------------------------------------------- +%% different_ca_peer_sign(doc) -> +%% ["Check that a CA can have a different signature algorithm than the peer cert."]; + +%% different_ca_peer_sign(suite) -> +%% []; + +%% different_ca_peer_sign(Config) when is_list(Config) -> +%% ClientOpts = ?config(client_mix_opts, Config), +%% ServerOpts = ?config(server_mix_verify_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_active_once, []}}, +%% {options, [{active, once}, +%% {verify, verify_peer} | 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_active_once, +%% []}}, +%% {options, [{active, once}, +%% {verify, verify_peer} +%% | ClientOpts]}]), + +%% ssl_test_lib:check_result(Server, ok, Client, ok), +%% ssl_test_lib:close(Server), +%% ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- +no_reuses_session_server_restart_new_cert(doc) -> + ["Check that a session is not reused if the server is restarted with a new cert."]; + +no_reuses_session_server_restart_new_cert(suite) -> + []; + +no_reuses_session_server_restart_new_cert(Config) when is_list(Config) -> + + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + DsaServerOpts = ?config(server_dsa_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, session_info_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client0 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, no_result, []}}, + {from, self()}, {options, ClientOpts}]), + SessionInfo = + receive + {Server, Info} -> + Info + end, + + %% Make sure session is registered + test_server:sleep(?SLEEP), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client0), + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, DsaServerOpts}]), + + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + receive + {Client1, SessionInfo} -> + test_server:fail(session_reused_when_server_has_new_cert); + {Client1, _Other} -> + ok + end, + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client1). + +%%-------------------------------------------------------------------- +no_reuses_session_server_restart_new_cert_file(doc) -> + ["Check that a session is not reused if a server is restarted with a new " + "cert contained in a file with the same name as the old cert."]; + +no_reuses_session_server_restart_new_cert_file(suite) -> + []; + +no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + DsaServerOpts = ?config(server_dsa_opts, Config), + PrivDir = ?config(priv_dir, Config), + + NewServerOpts = new_config(PrivDir, ServerOpts), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, session_info_result, []}}, + {options, NewServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client0 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, no_result, []}}, + {from, self()}, {options, ClientOpts}]), + SessionInfo = + receive + {Server, Info} -> + Info + end, + + %% Make sure session is registered and we get + %% new file time stamp when calling new_config! + test_server:sleep(?SLEEP* 2), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client0), + + NewServerOpts = new_config(PrivDir, DsaServerOpts), + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, NewServerOpts}]), + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + receive + {Client1, SessionInfo} -> + test_server:fail(session_reused_when_server_has_new_cert); + {Client1, _Other} -> + ok + end, + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client1). + +%%-------------------------------------------------------------------- +reuseaddr(doc) -> + [""]; + +reuseaddr(suite) -> + []; + +reuseaddr(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, {ssl_test_lib, no_result, []}}, + {options, [{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, {ssl_test_lib, no_result, []}}, + {options, [{active, false} | ClientOpts]}]), + test_server:sleep(?SLEEP), + ssl_test_lib:close(Server), + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, + {from, self()}, + {mfa, {?MODULE, send_recv_result, []}}, + {options, [{active, false} | ServerOpts]}]), + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, send_recv_result, []}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server1, ok, Client1, ok), + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client1). + +%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- erlang_ssl_receive(Socket, Data) -> receive {ssl, Socket, Data} -> - io:format("Received ~p~n",[Data]), + test_server:format("Received ~p~n",[Data]), ok; Other -> test_server:fail({unexpected_message, Other}) @@ -3087,7 +3310,6 @@ send_recv_result_active_once(Socket) -> result_ok(_Socket) -> ok. - renegotiate(Socket, Data) -> test_server:format("Renegotiating ~n", []), Result = ssl:renegotiate(Socket), @@ -3104,3 +3326,24 @@ renegotiate_reuse_session(Socket, Data) -> %% Make sure session is registerd test_server:sleep(?SLEEP), renegotiate(Socket, Data). + + +new_config(PrivDir, ServerOpts0) -> + CaCertFile = proplists:get_value(cacertfile, ServerOpts0), + CertFile = proplists:get_value(certfile, ServerOpts0), + KeyFile = proplists:get_value(keyfile, ServerOpts0), + NewCaCertFile = filename:join(PrivDir, "new_ca.pem"), + NewCertFile = filename:join(PrivDir, "new_cert.pem"), + NewKeyFile = filename:join(PrivDir, "new_key.pem"), + file:copy(CaCertFile, NewCaCertFile), + file:copy(CertFile, NewCertFile), + file:copy(KeyFile, NewKeyFile), + ServerOpts1 = proplists:delete(cacertfile, ServerOpts0), + ServerOpts2 = proplists:delete(certfile, ServerOpts1), + ServerOpts = proplists:delete(keyfile, ServerOpts2), + + {ok, PEM} = file:read_file(NewCaCertFile), + test_server:format("CA file content: ~p~n", [public_key:pem_decode(PEM)]), + + [{cacertfile, NewCaCertFile}, {certfile, NewCertFile}, + {keyfile, NewKeyFile} | ServerOpts]. diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index 88d2d99ef8..b1e585e39e 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -53,15 +53,18 @@ %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> - crypto:start(), - application:start(public_key), - ssl:start(), - Result = - (catch make_certs:all(?config(data_dir, Config), - ?config(priv_dir, Config))), - test_server:format("Make certs ~p~n", [Result]), - ssl_test_lib:cert_options(Config). - + case application:start(crypto) of + ok -> + application:start(public_key), + ssl:start(), + Result = + (catch make_certs:all(?config(data_dir, Config), + ?config(priv_dir, Config))), + test_server:format("Make certs ~p~n", [Result]), + ssl_test_lib:cert_options(Config); + _ -> + {skip, "Crypto did not start"} + end. %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ %% Config - [tuple()] @@ -70,7 +73,7 @@ init_per_suite(Config) -> %%-------------------------------------------------------------------- end_per_suite(_Config) -> ssl:stop(), - crypto:stop(). + application:stop(crypto). %%-------------------------------------------------------------------- %% Function: init_per_testcase(TestCase, Config) -> Config diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index d80df0bfbd..b19d1f286f 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -37,12 +37,15 @@ %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> - crypto:start(), - application:start(public_key), - ssl:start(), - make_certs:all(?config(data_dir, Config), ?config(priv_dir, Config)), - ssl_test_lib:cert_options(Config). - + case application:start(crypto) of + ok -> + application:start(public_key), + ssl:start(), + make_certs:all(?config(data_dir, Config), ?config(priv_dir, Config)), + ssl_test_lib:cert_options(Config); + _ -> + {skip, "Crypto did not start"} + end. %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ %% Config - [tuple()] @@ -51,7 +54,7 @@ init_per_suite(Config) -> %%-------------------------------------------------------------------- end_per_suite(_Config) -> ssl:stop(), - crypto:stop(). + application:stop(crypto). %%-------------------------------------------------------------------- %% Function: init_per_testcase(TestCase, Config) -> Config diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl index 0f39759d97..5aac9bb4e3 100644 --- a/lib/ssl/test/ssl_session_cache_SUITE.erl +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2010. All Rights Reserved. +%% Copyright Ericsson AB 2010-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 @@ -47,19 +47,23 @@ %%-------------------------------------------------------------------- init_per_suite(Config0) -> Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2), - crypto:start(), - application:start(public_key), - ssl:start(), - - %% make rsa certs using oppenssl - Result = - (catch make_certs:all(?config(data_dir, Config0), - ?config(priv_dir, Config0))), - test_server:format("Make certs ~p~n", [Result]), - - Config1 = ssl_test_lib:make_dsa_cert(Config0), - Config = ssl_test_lib:cert_options(Config1), - [{watchdog, Dog} | Config]. + case application:start(crypto) of + ok -> + application:start(public_key), + ssl:start(), + + %% make rsa certs using oppenssl + Result = + (catch make_certs:all(?config(data_dir, Config0), + ?config(priv_dir, Config0))), + test_server:format("Make certs ~p~n", [Result]), + + Config1 = ssl_test_lib:make_dsa_cert(Config0), + Config = ssl_test_lib:cert_options(Config1), + [{watchdog, Dog} | Config]; + _ -> + {skip, "Crypto did not start"} + end. %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ @@ -69,7 +73,7 @@ init_per_suite(Config0) -> %%-------------------------------------------------------------------- end_per_suite(_Config) -> ssl:stop(), - crypto:stop(). + application:stop(crypto). %%-------------------------------------------------------------------- %% Function: init_per_testcase(TestCase, Config) -> Config diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index e1e8214ed6..f6ccbe85e3 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -81,14 +81,20 @@ run_server(ListenSocket, Opts) -> no_result_msg -> ok; Msg -> - test_server:format("Msg: ~p ~n", [Msg]), + test_server:format("Server Msg: ~p ~n", [Msg]), Pid ! {self(), Msg} end, - receive + receive listen -> run_server(ListenSocket, Opts); + {listen, MFA} -> + run_server(ListenSocket, [MFA | proplists:delete(mfa, Opts)]); close -> - ok = rpc:call(Node, ssl, close, [AcceptSocket]) + test_server:format("Server closing ~p ~n", [self()]), + Result = rpc:call(Node, ssl, close, [AcceptSocket], 500), + test_server:format("Result ~p ~n", [Result]); + {ssl_closed, _} -> + ok end. %%% To enable to test with s_client -reconnect @@ -151,19 +157,30 @@ run_client(Opts) -> no_result_msg -> ok; Msg -> + test_server:format("Client Msg: ~p ~n", [Msg]), Pid ! {self(), Msg} end, - receive + receive close -> - ok = rpc:call(Node, ssl, close, [Socket]) + test_server:format("Client closing~n", []), + rpc:call(Node, ssl, close, [Socket]); + {ssl_closed, Socket} -> + ok end; {error, Reason} -> - test_server:format("Client: connection failed: ~p ~n", [Reason]), + test_server:format("Client: connection failed: ~p ~n", [Reason]), Pid ! {self(), {error, Reason}} end. close(Pid) -> - Pid ! close. + test_server:format("Close ~p ~n", [Pid]), + Monitor = erlang:monitor(process, Pid), + Pid ! close, + receive + {'DOWN', Monitor, process, Pid, Reason} -> + erlang:demonitor(Monitor), + test_server:format("Pid: ~p down due to:~p ~n", [Pid, Reason]) + end. check_result(Server, ServerMsg, Client, ClientMsg) -> receive @@ -208,47 +225,27 @@ check_result(Pid, Msg) -> test_server:fail(Reason) end. -check_result_ignore_renegotiation_reject(Pid, Msg) -> - receive - {Pid, fail_session_fatal_alert_during_renegotiation} -> - test_server:comment("Server rejected old renegotiation"), - ok; - {ssl_error, _, esslconnect} -> - test_server:comment("Server rejected old renegotiation"), - ok; - {Pid, Msg} -> - ok; - {Port, {data,Debug}} when is_port(Port) -> - io:format("openssl ~s~n",[Debug]), - check_result(Pid,Msg); - Unexpected -> - Reason = {{expected, {Pid, Msg}}, - {got, Unexpected}}, - test_server:fail(Reason) - end. - - wait_for_result(Server, ServerMsg, Client, ClientMsg) -> receive {Server, ServerMsg} -> receive {Client, ClientMsg} -> - ok; - Unexpected -> - Unexpected + ok + %% Unexpected -> + %% Unexpected end; {Client, ClientMsg} -> receive {Server, ServerMsg} -> - ok; - Unexpected -> - Unexpected + ok + %% Unexpected -> + %% Unexpected end; {Port, {data,Debug}} when is_port(Port) -> io:format("openssl ~s~n",[Debug]), - wait_for_result(Server, ServerMsg, Client, ClientMsg); - Unexpected -> - Unexpected + wait_for_result(Server, ServerMsg, Client, ClientMsg) + %% Unexpected -> + %% Unexpected end. @@ -258,9 +255,9 @@ wait_for_result(Pid, Msg) -> ok; {Port, {data,Debug}} when is_port(Port) -> io:format("openssl ~s~n",[Debug]), - wait_for_result(Pid,Msg); - Unexpected -> - Unexpected + wait_for_result(Pid,Msg) + %% Unexpected -> + %% Unexpected end. cert_options(Config) -> @@ -327,8 +324,8 @@ cert_options(Config) -> make_dsa_cert(Config) -> - {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_dsa_cert_files("server", Config), - {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_dsa_cert_files("client", Config), + {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, dsa, dsa, ""), + {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_cert_files("client", Config, dsa, dsa, ""), [{server_dsa_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, @@ -342,22 +339,41 @@ make_dsa_cert(Config) -> | Config]. - -make_dsa_cert_files(RoleStr, Config) -> - CaInfo = {CaCert, _} = erl_make_certs:make_cert([{key, dsa}]), - {Cert, CertKey} = erl_make_certs:make_cert([{key, dsa}, {issuer, CaInfo}]), +make_mix_cert(Config) -> + {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, dsa, + rsa, "mix"), + {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_cert_files("client", Config, dsa, + rsa, "mix"), + [{server_mix_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ServerCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, + {server_mix_verify_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ClientCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {verify, verify_peer}]}, + {client_mix_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ClientCaCertFile}, + {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]} + | Config]. + +make_cert_files(RoleStr, Config, Alg1, Alg2, Prefix) -> + Alg1Str = atom_to_list(Alg1), + Alg2Str = atom_to_list(Alg2), + CaInfo = {CaCert, _} = erl_make_certs:make_cert([{key, Alg1}]), + {Cert, CertKey} = erl_make_certs:make_cert([{key, Alg2}, {issuer, CaInfo}]), CaCertFile = filename:join([?config(priv_dir, Config), - RoleStr, "dsa_cacerts.pem"]), + RoleStr, Prefix ++ Alg1Str ++ "_cacerts.pem"]), CertFile = filename:join([?config(priv_dir, Config), - RoleStr, "dsa_cert.pem"]), + RoleStr, Prefix ++ Alg2Str ++ "_cert.pem"]), KeyFile = filename:join([?config(priv_dir, Config), - RoleStr, "dsa_key.pem"]), + RoleStr, Prefix ++ Alg2Str ++ "_key.pem"]), der_to_pem(CaCertFile, [{'Certificate', CaCert, not_encrypted}]), der_to_pem(CertFile, [{'Certificate', Cert, not_encrypted}]), der_to_pem(KeyFile, [CertKey]), {CaCertFile, CertFile, KeyFile}. + start_upgrade_server(Args) -> Result = spawn_link(?MODULE, run_upgrade_server, [Args]), receive @@ -395,10 +411,12 @@ run_upgrade_server(Opts) -> end, {Module, Function, Args} = proplists:get_value(mfa, Opts), Msg = rpc:call(Node, Module, Function, [SslAcceptSocket | Args]), + test_server:format("Upgrade Server Msg: ~p ~n", [Msg]), Pid ! {self(), Msg}, receive close -> - ok = rpc:call(Node, ssl, close, [SslAcceptSocket]) + test_server:format("Upgrade Server closing~n", []), + rpc:call(Node, ssl, close, [SslAcceptSocket]) end catch error:{badmatch, Error} -> Pid ! {self(), Error} @@ -428,10 +446,12 @@ run_upgrade_client(Opts) -> test_server:format("apply(~p, ~p, ~p)~n", [Module, Function, [SslSocket | Args]]), Msg = rpc:call(Node, Module, Function, [SslSocket | Args]), + test_server:format("Upgrade Client Msg: ~p ~n", [Msg]), Pid ! {self(), Msg}, receive close -> - ok = rpc:call(Node, ssl, close, [SslSocket]) + test_server:format("Upgrade Client closing~n", []), + rpc:call(Node, ssl, close, [SslSocket]) end. start_upgrade_server_error(Args) -> diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index afedeaf099..0cbaafd99c 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -50,16 +50,20 @@ init_per_suite(Config0) -> false -> {skip, "Openssl not found"}; _ -> - crypto:start(), - application:start(public_key), - ssl:start(), - Result = - (catch make_certs:all(?config(data_dir, Config0), - ?config(priv_dir, Config0))), - test_server:format("Make certs ~p~n", [Result]), - Config1 = ssl_test_lib:make_dsa_cert(Config0), - Config = ssl_test_lib:cert_options(Config1), - [{watchdog, Dog} | Config] + case application:start(crypto) of + ok -> + application:start(public_key), + ssl:start(), + Result = + (catch make_certs:all(?config(data_dir, Config0), + ?config(priv_dir, Config0))), + test_server:format("Make certs ~p~n", [Result]), + Config1 = ssl_test_lib:make_dsa_cert(Config0), + Config = ssl_test_lib:cert_options(Config1), + [{watchdog, Dog} | Config]; + _ -> + {skip, "Crypto did not start"} + end end. %%-------------------------------------------------------------------- @@ -70,7 +74,7 @@ init_per_suite(Config0) -> %%-------------------------------------------------------------------- end_per_suite(_Config) -> ssl:stop(), - crypto:stop(). + application:stop(crypto). %%-------------------------------------------------------------------- %% Function: init_per_testcase(TestCase, Config) -> Config @@ -1164,10 +1168,6 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> close_port(OpenSslPort), %% Clean close down! ssl_test_lib:close(Client), - receive - {'EXIT', Client, normal} -> - ok - end, Return = case Result of ok -> diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index ee692adb3b..a4be7bb889 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1,2 +1,2 @@ -SSL_VSN = 4.1.1 +SSL_VSN = 4.1.3 diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml index 969aff4fcb..47d64f245c 100644 --- a/lib/stdlib/doc/src/filelib.xml +++ b/lib/stdlib/doc/src/filelib.xml @@ -105,7 +105,7 @@ dirname() = filename()</code> interpreted as Unicode may be encountered, in which case the <c>fun()</c> must be prepared to handle raw file names (i.e. binaries). If the regular expression contains - codepoints beyond 255, it will not match file names that does + codepoints beyond 255, it will not match file names that do not conform to the expected character encoding (i.e. are not encoded in valid UTF-8).</p> diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index 6c618bc798..a8fe41f000 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -30,6 +30,144 @@ </header> <p>This document describes the changes made to the STDLIB application.</p> +<section><title>STDLIB 1.17.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Several type specifications for standard libraries were + wrong in the R14B01 release. This is now corrected. The + corrections concern types in re,io,filename and the + module erlang itself.</p> + <p> + Own Id: OTP-9008</p> + </item> + </list> + </section> + +</section> + +<section><title>STDLIB 1.17.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> When several clients accessed a Dets table + simultaneously, one of them calling + <c>dets:insert_new/2</c>, the Dets server could crash. + Alternatively, under the same conditions, <c>ok</c> was + sometimes returned instead of <c>true</c>. (Thanks to + John Hughes.) </p> + <p> + Own Id: OTP-8856</p> + </item> + <item> + <p> When several clients accessed a Dets table + simultaneously, inserted or updated objects were + sometimes lost due to the Dets file being truncated. + (Thanks to John Hughes.) </p> + <p> + Own Id: OTP-8898</p> + </item> + <item> + <p> When several clients accessed a Dets table + simultaneously, modifications of the Dets server's + internal state were sometimes thrown away. The symptoms + are diverse: error with reason <c>bad_object</c>; + inserted objects not returned by <c>lookup()</c>; et + cetera. (Thanks to John Hughes.) </p> + <p> + Own Id: OTP-8899</p> + </item> + <item> + <p> If a Dets table was closed after calling + <c>bchunk/2</c>, <c>match/1,3</c>, + <c>match_object/1,3</c>, or <c>select/1,3</c> and then + opened again, a subsequent call using the returned + continuation would normally return a reply. This bug has + fixed; now the call fails with reason <c>badarg</c>. </p> + <p> + Own Id: OTP-8903</p> + </item> + <item> + <p> Cover did not collect coverage data for files such as + Yecc parses containing include directives. The bug has + been fixed by modifying <c>epp</c>, the Erlang Code + Preprocessor. </p> + <p> + Own Id: OTP-8911</p> + </item> + <item> + <p> If a Dets table with fewer slots than keys was opened + and then closed after just a lookup, the contents were no + longer well-formed. This bug has been fixed. (Thanks to + Matthew Evans.) </p> + <p> + Own Id: OTP-8923</p> + </item> + <item> + <p> + In a supervisor, when it terminates a child, if that + child happens to have exited fractionally early, with + normal, the supervisor reports this as an error. This + should not be reported as an error.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-8938 Aux Id: seq11615 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The documentation filelib:wildcard/1,2 now describes the + character set syntax for wildcards.</p> + <p> + Own Id: OTP-8879 Aux Id: seq11683 </p> + </item> + <item> + <p>Buffer overflows have been prevented in <c>erlc</c>, + <c>dialyzer</c>, <c>typer</c>, <c>run_test</c>, + <c>heart</c>, <c>escript</c>, and <c>erlexec</c>.</p> + (Thanks to Michael Santos.) + <p> + Own Id: OTP-8892</p> + </item> + <item> + <p> + Using a float for the number of copies for + <c>string:copies/2</c> resulted in an infinite loop. Now + it will fail with an exception instead. (Thanks to + Michael Santos.)</p> + <p> + Own Id: OTP-8915</p> + </item> + <item> + <p> + New ETS option <c>compressed</c>, to enable a more + compact storage format at the expence of heavier table + operations. For test and evaluation, <c>erl +ec</c> can + be used to force compression on all ETS tables.</p> + <p> + Own Id: OTP-8922 Aux Id: seq11658 </p> + </item> + <item> + <p> The default maximum number of slots of a Dets table + has been changed as to be equal to the maximum number of + slots. (Thanks to Richard Carlsson.) </p> + <p> + Own Id: OTP-8959</p> + </item> + </list> + </section> + +</section> + <section><title>STDLIB 1.17.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml index c02ea3cbcb..416df1f02c 100644 --- a/lib/stdlib/doc/src/unicode_usage.xml +++ b/lib/stdlib/doc/src/unicode_usage.xml @@ -173,19 +173,19 @@ Eshell V5.7 (abort with ^G) <taglist> <tag>Mandatory Unicode file naming</tag> <item> -<p>Windows and, for most common uses, MacOSX enforces Unicode support for file names. All files created in the filesystem has names that can consistently be interpreted. In MacOSX, all file names are retrieved in UTF-8 encoding, while Windows have selected an approach where each system call handling file names has a special Unicode aware variant, giving much the same effect. There are no file names on these systems that are not Unicode file names, why the default behavior of the Erlang VM is to work in "Unicode file name translation mode", meaning that a file name can be given as a Unicode list and that will be automatically translated to the proper name encoding for the underlying operating and file system.</p> +<p>Windows and, for most common uses, MacOSX enforces Unicode support for file names. All files created in the filesystem have names that can consistently be interpreted. In MacOSX, all file names are retrieved in UTF-8 encoding, while Windows has selected an approach where each system call handling file names has a special Unicode aware variant, giving much the same effect. There are no file names on these systems that are not Unicode file names, why the default behavior of the Erlang VM is to work in "Unicode file name translation mode", meaning that a file name can be given as a Unicode list and that will be automatically translated to the proper name encoding for the underlying operating and file system.</p> <p>Doing i.e. a <c>file:list_dir/1</c> on one of these systems may return Unicode lists with codepoints beyond 255, depending on the content of the actual filesystem.</p> <p>As the feature is fairly new, you may still stumble upon non core applications that cannot handle being provided with file names containing characters with codepoints larger than 255, but the core Erlang system should have no problems with Unicode file names.</p> </item> <tag>Transparent file naming</tag> <item> <p>Most Unix operating systems have adopted a simpler approach, namely that Unicode file naming is not enforced, but by convention. Those systems usually use UTF-8 encoding for Unicode file names, but do not enforce it. On such a system, a file name containing characters having codepoints between 128 and 255 may be named either as plain ISO-latin-1 or using UTF-8 encoding. As no consistency is enforced, the Erlang VM can do no consistent translation of all file names. If the VM would automatically select encoding based on heuristics, one could get unexpected behavior on these systems, therefore file names not being encoded in UTF-8 are returned as "raw file names" if Unicode file naming support is turned on.</p> -<p>A raw file name is not a list, but a binary. Many non core applications still does not handle file names given as binaries, why such raw names are avoided by default. This means that systems having implemented Unicode file naming through transparent file systems and an UTF-8 convention, does not by default have Unicode file naming turned on. Explicitly turning Unicode file name handling on for these types of systems is considered experimental.</p> +<p>A raw file name is not a list, but a binary. Many non core applications still do not handle file names given as binaries, why such raw names are avoided by default. This means that systems having implemented Unicode file naming through transparent file systems and an UTF-8 convention, do not by default have Unicode file naming turned on. Explicitly turning Unicode file name handling on for these types of systems is considered experimental.</p> </item> </taglist> <p>The Unicode file naming support was introduced with OTP release R14B01. A VM operating in Unicode file mode can work with files having names in any language or character set (as long as it's supported by the underlying OS and file system). The Unicode character list is used to denote file or directory names and if the file system content is listed, you will also be able to get Unicode lists as return value. The support lies in the kernel and stdlib modules, why most applications (that does not explicitly require the file names to be in the ISO-latin-1 range) will benefit from the Unicode support without change.</p> -<p>On Operating systems with mandatory Unicode file names, this means that you more easily conform to the file names of other (non Erlang) applications, and you can also process file names that, at least on Windows, where completely inaccessible (due to having names that could not be represented in ISO-latin-1). Also you will avoid creating incomprehensible file names on MacOSX as the vfs layer of the OS will accept all your file names as UTF-8 and will not rewrite them.</p> +<p>On Operating systems with mandatory Unicode file names, this means that you more easily conform to the file names of other (non Erlang) applications, and you can also process file names that, at least on Windows, were completely inaccessible (due to having names that could not be represented in ISO-latin-1). Also you will avoid creating incomprehensible file names on MacOSX as the vfs layer of the OS will accept all your file names as UTF-8 and will not rewrite them.</p> <p>For most systems, turning on Unicode file name translation is no problem even if it uses transparent file naming. Very few systems have mixed file name encodings. A consistent UTF-8 named system will work perfectly in Unicode file name mode. It is still however considered experimental in R14B01. Unicode file name translation is turned on with the <c>+fnu</c> switch to the <c>erl</c> program. If the VM is started in Unicode file name translation mode, <c>file:native_name_encoding/0</c> will return the atom <c>utf8</c>.</p> @@ -197,10 +197,10 @@ Eshell V5.7 (abort with ^G) <section> <title>Notes about raw file names and automatic file name conversion</title> -<p>Raw file names is introduced together with Unicode file name support in erts-5.8.2 (OTP R14B01). The reason the "raw file names" is introduced in the system is to be able to consistently represent file names given in different encodings on the same system. Having the VM automatically translate a file name that is not in UTF-8 when to a list of Unicode characters might seem practical, but this would open up for both duplicate file names and other inconsistent behavior. Consider a directory containing a file named "bj�rn" in ISO-latin-1, while the Erlang VM is operating in Unicode file name mode (and therefore expecting UTF-8 file naming). The ISO-latin-1 name is not valid UTF-8 and one could be tempted to think that automatic conversion in for example <c>file:list_dir/1</c> is a good idea. But what would happen if we later tried to open the file and has the name as a Unicode list (magically converted from the ISO-latin-1 file name)? The VM will convert the file name given to UTF-8, as this is the encoding expected. Effectively this means trying to open the file named <<"bj�rn"/utf8>>. This file does not exist, and even if it existed it would not be the same file as the one that was listed. We could even create two files named "bj�rn", one named in the UTF-8 encoding and one not. If <c>file:list_dir/1</c> would automatically convert the ISO-latin-1 file name to a list, we would get two identical file names as the result. To avoid this, we need to differentiate between file names being properly encoded according to the Unicode file naming convention (i.e. UTF-8) and file names being invalid under the encoding. This is done by representing invalid encoding as "raw" file names, i.e. as binaries.</p> -<p>The core system of Erlang (kernel and stdlib) accept raw file names except for loadable drivers and executables invoked using <c>open_port({spawn, ...} ...)</c>. <c>open_port({spawn_executable, ...} ...)</c> however does accept them. As mentioned earlier, the arguments given in the option list to <c>open_port({spawn_executable, ...} ...)</c> undergo the same conversion as the file names, meaning that the executable will be provided with arguments in UTF-8 as well. This translation is avoided consistently with how the file names are treated, by giving the argument as a binary.</p> +<p>Raw file names is introduced together with Unicode file name support in erts-5.8.2 (OTP R14B01). The reason "raw file names" is introduced in the system is to be able to consistently represent file names given in different encodings on the same system. Having the VM automatically translate a file name that is not in UTF-8 to a list of Unicode characters might seem practical, but this would open up for both duplicate file names and other inconsistent behavior. Consider a directory containing a file named "bj�rn" in ISO-latin-1, while the Erlang VM is operating in Unicode file name mode (and therefore expecting UTF-8 file naming). The ISO-latin-1 name is not valid UTF-8 and one could be tempted to think that automatic conversion in for example <c>file:list_dir/1</c> is a good idea. But what would happen if we later tried to open the file and have the name as a Unicode list (magically converted from the ISO-latin-1 file name)? The VM will convert the file name given to UTF-8, as this is the encoding expected. Effectively this means trying to open the file named <<"bj�rn"/utf8>>. This file does not exist, and even if it existed it would not be the same file as the one that was listed. We could even create two files named "bj�rn", one named in the UTF-8 encoding and one not. If <c>file:list_dir/1</c> would automatically convert the ISO-latin-1 file name to a list, we would get two identical file names as the result. To avoid this, we need to differentiate between file names being properly encoded according to the Unicode file naming convention (i.e. UTF-8) and file names being invalid under the encoding. This is done by representing invalid encoding as "raw" file names, i.e. as binaries.</p> +<p>The core system of Erlang (kernel and stdlib) accepts raw file names except for loadable drivers and executables invoked using <c>open_port({spawn, ...} ...)</c>. <c>open_port({spawn_executable, ...} ...)</c> however does accept them. As mentioned earlier, the arguments given in the option list to <c>open_port({spawn_executable, ...} ...)</c> undergo the same conversion as the file names, meaning that the executable will be provided with arguments in UTF-8 as well. This translation is avoided consistently with how the file names are treated, by giving the argument as a binary.</p> <p>To force Unicode file name translation mode on systems where this is not the default is considered experimental in OTP R14B01 due to the raw file names possibly being a new experience to the programmer and that the non core applications of OTP are not tested for compliance with raw file names yet. Unicode file name translation is expected to be default in future releases.</p> -<p>If working with raw file names, one can still conform to the encoding convention of the Erlang VM by using the <c>file:native_name_encoding/0</c> function, which returns either the atom <c>latin1</c> or the atom <c>utf8</c> depending on the file name translation mode. On Linux, an VM started without explicitly stating the file name translation mode will default to <c>latin1</c> as the native file name encoding, why file names on the disk encoded as UTF-8 will be returned as a list of the names interpreted as ISO-latin-1. The "UTF-8 list" is not a practical type for displaying or operating on in Erlang, but it is backward compatible and usable in all functions requiring a file name. On Windows and MacOSX, the default behavior is that of file name translation, why the <c>file:native_name_encoding/0</c> by default returns <c>utf8</c> on those systems (the fact that Windows actually does not use UTF-8 on the file system level can safely be ignored by the Erlang programmer). The default behavior can be changed using the <c>+fnu</c> or <c>+fnl</c> options to the VM, see the <c>erl</c> command manual page.</p> +<p>If working with raw file names, one can still conform to the encoding convention of the Erlang VM by using the <c>file:native_name_encoding/0</c> function, which returns either the atom <c>latin1</c> or the atom <c>utf8</c> depending on the file name translation mode. On Linux, a VM started without explicitly stating the file name translation mode will default to <c>latin1</c> as the native file name encoding, why file names on the disk encoded as UTF-8 will be returned as a list of the names interpreted as ISO-latin-1. The "UTF-8 list" is not a practical type for displaying or operating on in Erlang, but it is backward compatible and usable in all functions requiring a file name. On Windows and MacOSX, the default behavior is that of file name translation, why the <c>file:native_name_encoding/0</c> by default returns <c>utf8</c> on those systems (the fact that Windows actually does not use UTF-8 on the file system level can safely be ignored by the Erlang programmer). The default behavior can be changed using the <c>+fnu</c> or <c>+fnl</c> options to the VM, see the <c>erl</c> command manual page.</p> <p>Even if you are operating without Unicode file naming translation automatically done by the VM, you can access and create files with names in UTF-8 encoding by using raw file names encoded as UTF-8. Enforcing the UTF-8 encoding regardless of the mode the Erlang VM is started in might, in some circumstances be a good idea, as the convention of using UTF-8 file names is spreading.</p> </section> <section> diff --git a/lib/stdlib/src/base64.erl b/lib/stdlib/src/base64.erl index ebef998ee1..a14a72ac6d 100644 --- a/lib/stdlib/src/base64.erl +++ b/lib/stdlib/src/base64.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. 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 @@ -114,7 +114,7 @@ decode(List) when is_list(List) -> mime_decode(Bin) when is_binary(Bin) -> mime_decode_binary(<<>>, Bin); mime_decode(List) when is_list(List) -> - list_to_binary(mime_decode_l(List)). + mime_decode(list_to_binary(List)). -spec decode_l(string()) -> string(). @@ -125,7 +125,7 @@ decode_l(List) -> -spec mime_decode_l(string()) -> string(). mime_decode_l(List) -> - L = strip_illegal(List, []), + L = strip_illegal(List, [], 0), decode(L, []). %%------------------------------------------------------------------------- @@ -198,6 +198,9 @@ decode_binary(Result, <<>>) -> true = is_binary(Result), Result. +%% Skipping pad character if not at end of string. Also liberal about +%% excess padding and skipping of other illegal (non-base64 alphabet) +%% characters. See section 3.3 of RFC4648 mime_decode_binary(Result, <<0:8,T/bits>>) -> mime_decode_binary(Result, T); mime_decode_binary(Result0, <<C:8,T/bits>>) -> @@ -205,15 +208,27 @@ mime_decode_binary(Result0, <<C:8,T/bits>>) -> Bits when is_integer(Bits) -> mime_decode_binary(<<Result0/bits,Bits:6>>, T); eq -> - case tail_contains_equal(T) of - true -> - Split = byte_size(Result0) - 1, - <<Result:Split/bytes,_:4>> = Result0, - Result; - false -> - Split = byte_size(Result0) - 1, - <<Result:Split/bytes,_:2>> = Result0, - Result + case tail_contains_more(T, false) of + {<<>>, Eq} -> + %% No more valid data. + case bit_size(Result0) rem 8 of + 0 -> + %% '====' is not uncommon. + Result0; + 4 when Eq -> + %% enforce at least one more '=' only ignoring illegals and spacing + Split = byte_size(Result0) - 1, + <<Result:Split/bytes,_:4>> = Result0, + Result; + 2 -> + %% remove 2 bits + Split = byte_size(Result0) - 1, + <<Result:Split/bytes,_:2>> = Result0, + Result + end; + {More, _} -> + %% More valid data, skip the eq as invalid + mime_decode_binary(Result0, More) end; _ -> mime_decode_binary(Result0, T) @@ -262,31 +277,63 @@ strip_ws(<<$\s,T/binary>>) -> strip_ws(T); strip_ws(T) -> T. -strip_illegal([0|Cs], A) -> - strip_illegal(Cs, A); -strip_illegal([C|Cs], A) -> +%% Skipping pad character if not at end of string. Also liberal about +%% excess padding and skipping of other illegal (non-base64 alphabet) +%% characters. See section 3.3 of RFC4648 +strip_illegal([], A, _Cnt) -> + A; +strip_illegal([0|Cs], A, Cnt) -> + strip_illegal(Cs, A, Cnt); +strip_illegal([C|Cs], A, Cnt) -> case element(C, ?DECODE_MAP) of - bad -> strip_illegal(Cs, A); - ws -> strip_illegal(Cs, A); - eq -> strip_illegal_end(Cs, [$=|A]); - _ -> strip_illegal(Cs, [C|A]) - end; -strip_illegal([], A) -> A. + bad -> + strip_illegal(Cs, A, Cnt); + ws -> + strip_illegal(Cs, A, Cnt); + eq -> + case {tail_contains_more(Cs, false), Cnt rem 4} of + {{[], _}, 0} -> + A; %% Ignore extra = + {{[], true}, 2} -> + [$=|[$=|A]]; %% 'XX==' + {{[], _}, 3} -> + [$=|A]; %% 'XXX=' + {{[H|T], _}, _} -> + %% more data, skip equals + strip_illegal(T, [H|A], Cnt+1) + end; + _ -> + strip_illegal(Cs, [C|A], Cnt+1) + end. -strip_illegal_end([0|Cs], A) -> - strip_illegal_end(Cs, A); -strip_illegal_end([C|Cs], A) -> +%% Search the tail for more valid data and remember if we saw +%% another equals along the way. +tail_contains_more([], Eq) -> + {[], Eq}; +tail_contains_more(<<>>, Eq) -> + {<<>>, Eq}; +tail_contains_more([C|T]=More, Eq) -> case element(C, ?DECODE_MAP) of - bad -> strip_illegal(Cs, A); - ws -> strip_illegal(Cs, A); - eq -> [C|A]; - _ -> strip_illegal(Cs, [C|A]) + bad -> + tail_contains_more(T, Eq); + ws -> + tail_contains_more(T, Eq); + eq -> + tail_contains_more(T, true); + _ -> + {More, Eq} end; -strip_illegal_end([], A) -> A. - -tail_contains_equal(<<$=,_/binary>>) -> true; -tail_contains_equal(<<_,T/binary>>) -> tail_contains_equal(T); -tail_contains_equal(<<>>) -> false. +tail_contains_more(<<C:8,T/bits>> =More, Eq) -> + case element(C, ?DECODE_MAP) of + bad -> + tail_contains_more(T, Eq); + ws -> + tail_contains_more(T, Eq); + eq -> + tail_contains_more(T, true); + _ -> + {More, Eq} + end. %% accessors b64e(X) -> diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl index d04d8f191f..235ea939a8 100644 --- a/lib/stdlib/src/c.erl +++ b/lib/stdlib/src/c.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. 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 @@ -42,31 +42,31 @@ -spec help() -> 'ok'. help() -> - format("bt(Pid) -- stack backtrace for a process\n" - "c(File) -- compile and load code in <File>\n" - "cd(Dir) -- change working directory\n" - "flush() -- flush any messages sent to the shell\n" - "help() -- help info\n" - "i() -- information about the system\n" - "ni() -- information about the networked system\n" - "i(X,Y,Z) -- information about pid <X,Y,Z>\n" - "l(Module) -- load or reload module\n" - "lc([File]) -- compile a list of Erlang modules\n" - "ls() -- list files in the current directory\n" - "ls(Dir) -- list files in directory <Dir>\n" - "m() -- which modules are loaded\n" - "m(Mod) -- information about module <Mod>\n" - "memory() -- memory allocation information\n" - "memory(T) -- memory allocation information of type <T>\n" - "nc(File) -- compile and load code in <File> on all nodes\n" - "nl(Module) -- load module on all nodes\n" - "pid(X,Y,Z) -- convert X,Y,Z to a Pid\n" - "pwd() -- print working directory\n" - "q() -- quit - shorthand for init:stop()\n" - "regs() -- information about registered processes\n" - "nregs() -- information about all registered processes\n" - "xm(M) -- cross reference check a module\n" - "y(File) -- generate a Yecc parser\n"). + io:put_chars(<<"bt(Pid) -- stack backtrace for a process\n" + "c(File) -- compile and load code in <File>\n" + "cd(Dir) -- change working directory\n" + "flush() -- flush any messages sent to the shell\n" + "help() -- help info\n" + "i() -- information about the system\n" + "ni() -- information about the networked system\n" + "i(X,Y,Z) -- information about pid <X,Y,Z>\n" + "l(Module) -- load or reload module\n" + "lc([File]) -- compile a list of Erlang modules\n" + "ls() -- list files in the current directory\n" + "ls(Dir) -- list files in directory <Dir>\n" + "m() -- which modules are loaded\n" + "m(Mod) -- information about module <Mod>\n" + "memory() -- memory allocation information\n" + "memory(T) -- memory allocation information of type <T>\n" + "nc(File) -- compile and load code in <File> on all nodes\n" + "nl(Module) -- load module on all nodes\n" + "pid(X,Y,Z) -- convert X,Y,Z to a Pid\n" + "pwd() -- print working directory\n" + "q() -- quit - shorthand for init:stop()\n" + "regs() -- information about registered processes\n" + "nregs() -- information about all registered processes\n" + "xm(M) -- cross reference check a module\n" + "y(File) -- generate a Yecc parser\n">>). %% c(FileName) %% Compile a file/module. diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 0c2d3db8ec..cfb9f0ca98 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. 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 @@ -60,6 +60,10 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> (_Opt, Def) -> Def end, Default, Opts). +%% The maximum number of arguments allowed for a function. + +-define(MAX_ARGUMENTS, 255). + %% The error and warning info structures, {Line,Module,Descriptor}, %% are kept in their seperate fields in the lint state record together %% with the name of the file (when a new file is entered, marked by @@ -226,6 +230,9 @@ format_error({obsolete_guard, {F, A}}) -> io_lib:format("~p/~p obsolete", [F, A]); format_error({reserved_for_future,K}) -> io_lib:format("atom ~w: future reserved keyword - rename or quote", [K]); +format_error({too_many_arguments,Arity}) -> + io_lib:format("too many arguments (~w) - " + "maximum allowed is ~w", [Arity,?MAX_ARGUMENTS]); %% --- patterns and guards --- format_error(illegal_pattern) -> "illegal pattern"; format_error(illegal_bin_pattern) -> @@ -1307,13 +1314,18 @@ define_function(Line, Name, Arity, St0) -> true -> add_error(Line, {redefine_function,NA}, St1); false -> - St2 = St1#lint{defined=gb_sets:add_element(NA, St1#lint.defined)}, - case imported(Name, Arity, St2) of - {yes,_M} -> add_error(Line, {define_import,NA}, St2); - no -> St2 + St2 = function_check_max_args(Line, Arity, St1), + St3 = St2#lint{defined=gb_sets:add_element(NA, St2#lint.defined)}, + case imported(Name, Arity, St3) of + {yes,_M} -> add_error(Line, {define_import,NA}, St3); + no -> St3 end end. +function_check_max_args(Line, Arity, St) when Arity > ?MAX_ARGUMENTS -> + add_error(Line, {too_many_arguments,Arity}, St); +function_check_max_args(_, _, St) -> St. + %% clauses([Clause], VarTable, State) -> {VarTable, State}. clauses(Cs, Vt, St) -> diff --git a/lib/stdlib/src/erl_posix_msg.erl b/lib/stdlib/src/erl_posix_msg.erl index fe981b23a7..909cc1d102 100644 --- a/lib/stdlib/src/erl_posix_msg.erl +++ b/lib/stdlib/src/erl_posix_msg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2010. 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 @@ -24,143 +24,146 @@ -spec message(atom()) -> string(). -message(e2big) -> "argument list too long"; -message(eacces) -> "permission denied"; -message(eaddrinuse) -> "address already in use"; -message(eaddrnotavail) -> "can't assign requested address"; -message(eadv) -> "advertise error"; -message(eafnosupport) -> "address family not supported by protocol family"; -message(eagain) -> "resource temporarily unavailable"; -message(ealign) -> "EALIGN"; -message(ealready) -> "operation already in progress"; -message(ebade) -> "bad exchange descriptor"; -message(ebadf) -> "bad file number"; -message(ebadfd) -> "file descriptor in bad state"; -message(ebadmsg) -> "not a data message"; -message(ebadr) -> "bad request descriptor"; -message(ebadrpc) -> "RPC structure is bad"; -message(ebadrqc) -> "bad request code"; -message(ebadslt) -> "invalid slot"; -message(ebfont) -> "bad font file format"; -message(ebusy) -> "file busy"; -message(echild) -> "no children"; -message(echrng) -> "channel number out of range"; -message(ecomm) -> "communication error on send"; -message(econnaborted) -> "software caused connection abort"; -message(econnrefused) -> "connection refused"; -message(econnreset) -> "connection reset by peer"; -message(edeadlk) -> "resource deadlock avoided"; -message(edeadlock) -> "resource deadlock avoided"; -message(edestaddrreq) -> "destination address required"; -message(edirty) -> "mounting a dirty fs w/o force"; -message(edom) -> "math argument out of range"; -message(edotdot) -> "cross mount point"; -message(edquot) -> "disk quota exceeded"; -message(eduppkg) -> "duplicate package name"; -message(eexist) -> "file already exists"; -message(efault) -> "bad address in system call argument"; -message(efbig) -> "file too large"; -message(ehostdown) -> "host is down"; -message(ehostunreach) -> "host is unreachable"; -message(eidrm) -> "identifier removed"; -message(einit) -> "initialization error"; -message(einprogress) -> "operation now in progress"; -message(eintr) -> "interrupted system call"; -message(einval) -> "invalid argument"; -message(eio) -> "I/O error"; -message(eisconn) -> "socket is already connected"; -message(eisdir) -> "illegal operation on a directory"; -message(eisnam) -> "is a name file"; -message(elbin) -> "ELBIN"; -message(el2hlt) -> "level 2 halted"; -message(el2nsync) -> "level 2 not synchronized"; -message(el3hlt) -> "level 3 halted"; -message(el3rst) -> "level 3 reset"; -message(elibacc) -> "can not access a needed shared library"; -message(elibbad) -> "accessing a corrupted shared library"; -message(elibexec) -> "can not exec a shared library directly"; -message(elibmax) -> - "attempting to link in more shared libraries than system limit"; -message(elibscn) -> ".lib section in a.out corrupted"; -message(elnrng) -> "link number out of range"; -message(eloop) -> "too many levels of symbolic links"; -message(emfile) -> "too many open files"; -message(emlink) -> "too many links"; -message(emsgsize) -> "message too long"; -message(emultihop) -> "multihop attempted"; -message(enametoolong) -> "file name too long"; -message(enavail) -> "not available"; -message(enet) -> "ENET"; -message(enetdown) -> "network is down"; -message(enetreset) -> "network dropped connection on reset"; -message(enetunreach) -> "network is unreachable"; -message(enfile) -> "file table overflow"; -message(enoano) -> "anode table overflow"; -message(enobufs) -> "no buffer space available"; -message(enocsi) -> "no CSI structure available"; -message(enodata) -> "no data available"; -message(enodev) -> "no such device"; -message(enoent) -> "no such file or directory"; -message(enoexec) -> "exec format error"; -message(enolck) -> "no locks available"; -message(enolink) -> "link has be severed"; -message(enomem) -> "not enough memory"; -message(enomsg) -> "no message of desired type"; -message(enonet) -> "machine is not on the network"; -message(enopkg) -> "package not installed"; -message(enoprotoopt) -> "bad proocol option"; -message(enospc) -> "no space left on device"; -message(enosr) -> "out of stream resources or not a stream device"; -message(enosym) -> "unresolved symbol name"; -message(enosys) -> "function not implemented"; -message(enotblk) -> "block device required"; -message(enotconn) -> "socket is not connected"; -message(enotdir) -> "not a directory"; -message(enotempty) -> "directory not empty"; -message(enotnam) -> "not a name file"; -message(enotsock) -> "socket operation on non-socket"; -message(enotsup) -> "operation not supported"; -message(enotty) -> "inappropriate device for ioctl"; -message(enotuniq) -> "name not unique on network"; -message(enxio) -> "no such device or address"; -message(eopnotsupp) -> "operation not supported on socket"; -message(eperm) -> "not owner"; -message(epfnosupport) -> "protocol family not supported"; -message(epipe) -> "broken pipe"; -message(eproclim) -> "too many processes"; -message(eprocunavail) -> "bad procedure for program"; -message(eprogmismatch) -> "program version wrong"; -message(eprogunavail) -> "RPC program not available"; -message(eproto) -> "protocol error"; -message(eprotonosupport) -> "protocol not suppored"; -message(eprototype) -> "protocol wrong type for socket"; -message(erange) -> "math result unrepresentable"; -message(erefused) -> "EREFUSED"; -message(eremchg) -> "remote address changed"; -message(eremdev) -> "remote device"; -message(eremote) -> "pathname hit remote file system"; -message(eremoteio) -> "remote i/o error"; -message(eremoterelease) -> "EREMOTERELEASE"; -message(erofs) -> "read-only file system"; -message(erpcmismatch) -> "RPC version is wrong"; -message(erremote) -> "object is remote"; -message(eshutdown) -> "can't send after socket shutdown"; -message(esocktnosupport) -> "socket type not supported"; -message(espipe) -> "invalid seek"; -message(esrch) -> "no such process"; -message(esrmnt) -> "srmount error"; -message(estale) -> "stale remote file handle"; -message(esuccess) -> "Error 0"; -message(etime) -> "timer expired"; -message(etimedout) -> "connection timed out"; -message(etoomanyrefs) -> "too many references: can't splice"; -message(etxtbsy) -> "text file or pseudo-device busy"; -message(euclean) -> "structure needs cleaning"; -message(eunatch) -> "protocol driver not attached"; -message(eusers) -> "too many users"; -message(eversion) -> "version mismatch"; -message(ewouldblock) -> "operation would block"; -message(exdev) -> "cross-domain link"; -message(exfull) -> "message tables full"; -message(nxdomain) -> "non-existing domain"; -message(_) -> "unknown POSIX error". +message(T) -> + binary_to_list(message_1(T)). + +message_1(e2big) -> <<"argument list too long">>; +message_1(eacces) -> <<"permission denied">>; +message_1(eaddrinuse) -> <<"address already in use">>; +message_1(eaddrnotavail) -> <<"can't assign requested address">>; +message_1(eadv) -> <<"advertise error">>; +message_1(eafnosupport) -> <<"address family not supported by protocol family">>; +message_1(eagain) -> <<"resource temporarily unavailable">>; +message_1(ealign) -> <<"EALIGN">>; +message_1(ealready) -> <<"operation already in progress">>; +message_1(ebade) -> <<"bad exchange descriptor">>; +message_1(ebadf) -> <<"bad file number">>; +message_1(ebadfd) -> <<"file descriptor in bad state">>; +message_1(ebadmsg) -> <<"not a data message">>; +message_1(ebadr) -> <<"bad request descriptor">>; +message_1(ebadrpc) -> <<"RPC structure is bad">>; +message_1(ebadrqc) -> <<"bad request code">>; +message_1(ebadslt) -> <<"invalid slot">>; +message_1(ebfont) -> <<"bad font file format">>; +message_1(ebusy) -> <<"file busy">>; +message_1(echild) -> <<"no children">>; +message_1(echrng) -> <<"channel number out of range">>; +message_1(ecomm) -> <<"communication error on send">>; +message_1(econnaborted) -> <<"software caused connection abort">>; +message_1(econnrefused) -> <<"connection refused">>; +message_1(econnreset) -> <<"connection reset by peer">>; +message_1(edeadlk) -> <<"resource deadlock avoided">>; +message_1(edeadlock) -> <<"resource deadlock avoided">>; +message_1(edestaddrreq) -> <<"destination address required">>; +message_1(edirty) -> <<"mounting a dirty fs w/o force">>; +message_1(edom) -> <<"math argument out of range">>; +message_1(edotdot) -> <<"cross mount point">>; +message_1(edquot) -> <<"disk quota exceeded">>; +message_1(eduppkg) -> <<"duplicate package name">>; +message_1(eexist) -> <<"file already exists">>; +message_1(efault) -> <<"bad address in system call argument">>; +message_1(efbig) -> <<"file too large">>; +message_1(ehostdown) -> <<"host is down">>; +message_1(ehostunreach) -> <<"host is unreachable">>; +message_1(eidrm) -> <<"identifier removed">>; +message_1(einit) -> <<"initialization error">>; +message_1(einprogress) -> <<"operation now in progress">>; +message_1(eintr) -> <<"interrupted system call">>; +message_1(einval) -> <<"invalid argument">>; +message_1(eio) -> <<"I/O error">>; +message_1(eisconn) -> <<"socket is already connected">>; +message_1(eisdir) -> <<"illegal operation on a directory">>; +message_1(eisnam) -> <<"is a name file">>; +message_1(elbin) -> <<"ELBIN">>; +message_1(el2hlt) -> <<"level 2 halted">>; +message_1(el2nsync) -> <<"level 2 not synchronized">>; +message_1(el3hlt) -> <<"level 3 halted">>; +message_1(el3rst) -> <<"level 3 reset">>; +message_1(elibacc) -> <<"can not access a needed shared library">>; +message_1(elibbad) -> <<"accessing a corrupted shared library">>; +message_1(elibexec) -> <<"can not exec a shared library directly">>; +message_1(elibmax) -> + <<"attempting to link in more shared libraries than system limit">>; +message_1(elibscn) -> <<".lib section in a.out corrupted">>; +message_1(elnrng) -> <<"link number out of range">>; +message_1(eloop) -> <<"too many levels of symbolic links">>; +message_1(emfile) -> <<"too many open files">>; +message_1(emlink) -> <<"too many links">>; +message_1(emsgsize) -> <<"message too long">>; +message_1(emultihop) -> <<"multihop attempted">>; +message_1(enametoolong) -> <<"file name too long">>; +message_1(enavail) -> <<"not available">>; +message_1(enet) -> <<"ENET">>; +message_1(enetdown) -> <<"network is down">>; +message_1(enetreset) -> <<"network dropped connection on reset">>; +message_1(enetunreach) -> <<"network is unreachable">>; +message_1(enfile) -> <<"file table overflow">>; +message_1(enoano) -> <<"anode table overflow">>; +message_1(enobufs) -> <<"no buffer space available">>; +message_1(enocsi) -> <<"no CSI structure available">>; +message_1(enodata) -> <<"no data available">>; +message_1(enodev) -> <<"no such device">>; +message_1(enoent) -> <<"no such file or directory">>; +message_1(enoexec) -> <<"exec format error">>; +message_1(enolck) -> <<"no locks available">>; +message_1(enolink) -> <<"link has be severed">>; +message_1(enomem) -> <<"not enough memory">>; +message_1(enomsg) -> <<"no message of desired type">>; +message_1(enonet) -> <<"machine is not on the network">>; +message_1(enopkg) -> <<"package not installed">>; +message_1(enoprotoopt) -> <<"bad proocol option">>; +message_1(enospc) -> <<"no space left on device">>; +message_1(enosr) -> <<"out of stream resources or not a stream device">>; +message_1(enosym) -> <<"unresolved symbol name">>; +message_1(enosys) -> <<"function not implemented">>; +message_1(enotblk) -> <<"block device required">>; +message_1(enotconn) -> <<"socket is not connected">>; +message_1(enotdir) -> <<"not a directory">>; +message_1(enotempty) -> <<"directory not empty">>; +message_1(enotnam) -> <<"not a name file">>; +message_1(enotsock) -> <<"socket operation on non-socket">>; +message_1(enotsup) -> <<"operation not supported">>; +message_1(enotty) -> <<"inappropriate device for ioctl">>; +message_1(enotuniq) -> <<"name not unique on network">>; +message_1(enxio) -> <<"no such device or address">>; +message_1(eopnotsupp) -> <<"operation not supported on socket">>; +message_1(eperm) -> <<"not owner">>; +message_1(epfnosupport) -> <<"protocol family not supported">>; +message_1(epipe) -> <<"broken pipe">>; +message_1(eproclim) -> <<"too many processes">>; +message_1(eprocunavail) -> <<"bad procedure for program">>; +message_1(eprogmismatch) -> <<"program version wrong">>; +message_1(eprogunavail) -> <<"RPC program not available">>; +message_1(eproto) -> <<"protocol error">>; +message_1(eprotonosupport) -> <<"protocol not suppored">>; +message_1(eprototype) -> <<"protocol wrong type for socket">>; +message_1(erange) -> <<"math result unrepresentable">>; +message_1(erefused) -> <<"EREFUSED">>; +message_1(eremchg) -> <<"remote address changed">>; +message_1(eremdev) -> <<"remote device">>; +message_1(eremote) -> <<"pathname hit remote file system">>; +message_1(eremoteio) -> <<"remote i/o error">>; +message_1(eremoterelease) -> <<"EREMOTERELEASE">>; +message_1(erofs) -> <<"read-only file system">>; +message_1(erpcmismatch) -> <<"RPC version is wrong">>; +message_1(erremote) -> <<"object is remote">>; +message_1(eshutdown) -> <<"can't send after socket shutdown">>; +message_1(esocktnosupport) -> <<"socket type not supported">>; +message_1(espipe) -> <<"invalid seek">>; +message_1(esrch) -> <<"no such process">>; +message_1(esrmnt) -> <<"srmount error">>; +message_1(estale) -> <<"stale remote file handle">>; +message_1(esuccess) -> <<"Error 0">>; +message_1(etime) -> <<"timer expired">>; +message_1(etimedout) -> <<"connection timed out">>; +message_1(etoomanyrefs) -> <<"too many references: can't splice">>; +message_1(etxtbsy) -> <<"text file or pseudo-device busy">>; +message_1(euclean) -> <<"structure needs cleaning">>; +message_1(eunatch) -> <<"protocol driver not attached">>; +message_1(eusers) -> <<"too many users">>; +message_1(eversion) -> <<"version mismatch">>; +message_1(ewouldblock) -> <<"operation would block">>; +message_1(exdev) -> <<"cross-domain link">>; +message_1(exfull) -> <<"message tables full">>; +message_1(nxdomain) -> <<"non-existing domain">>; +message_1(_) -> <<"unknown POSIX error">>. diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl index 99e454f593..7cb02afb11 100644 --- a/lib/stdlib/src/escript.erl +++ b/lib/stdlib/src/escript.erl @@ -570,9 +570,7 @@ parse_beam(S, File, HeaderSz, CheckOnly) -> forms_or_bin = Bin} end; {error, beam_lib, Reason} when is_tuple(Reason) -> - fatal(element(1, Reason)); - {error, beam_lib, Reason} -> - fatal(Reason) + fatal(element(1, Reason)) end. parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) -> diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl index e38b8957f2..24abf1e977 100644 --- a/lib/stdlib/src/filename.erl +++ b/lib/stdlib/src/filename.erl @@ -165,8 +165,6 @@ basename1([$/|[]], Tail, DirSep2) -> basename1([], Tail, DirSep2); basename1([$/|Rest], _Tail, DirSep2) -> basename1(Rest, [], DirSep2); -basename1([[_|_]=List|Rest], Tail, DirSep2) -> - basename1(List++Rest, Tail, DirSep2); basename1([DirSep2|Rest], Tail, DirSep2) when is_integer(DirSep2) -> basename1([$/|Rest], Tail, DirSep2); basename1([Char|Rest], Tail, DirSep2) when is_integer(Char) -> @@ -280,8 +278,6 @@ dirname(Name0) -> Name = flatten(Name0), dirname(Name, [], [], separators()). -dirname([[_|_]=List|Rest], Dir, File, Seps) -> - dirname(List++Rest, Dir, File, Seps); dirname([$/|Rest], Dir, File, Seps) -> dirname(Rest, File++Dir, [$/], Seps); dirname([DirSep|Rest], Dir, File, {DirSep,_}=Seps) when is_integer(DirSep) -> @@ -346,8 +342,6 @@ extension(Name) when is_binary(Name) -> [] end, case binary:matches(Name,[<<".">>]) of - nomatch -> % Bug in binary workaround :( - <<>>; [] -> <<>>; List -> @@ -479,6 +473,12 @@ maybe_remove_dirsep(Name, _) -> %% by a previous call to join/{1,2}. -spec append(file:filename(), file:name()) -> file:filename(). +append(Dir, Name) when is_binary(Dir), is_binary(Name) -> + <<Dir/binary,$/:8,Name/binary>>; +append(Dir, Name) when is_binary(Dir) -> + append(Dir,filename_string_to_binary(Name)); +append(Dir, Name) when is_binary(Name) -> + append(filename_string_to_binary(Dir),Name); append(Dir, Name) -> Dir ++ [$/|Name]. @@ -685,8 +685,6 @@ split([$/|Rest], Comp, Components, OsType) -> split(Rest, [], [lists:reverse(Comp)|Components], OsType); split([Char|Rest], Comp, Components, OsType) when is_integer(Char) -> split(Rest, [Char|Comp], Components, OsType); -split([List|Rest], Comp, Components, OsType) when is_list(List) -> - split(List++Rest, Comp, Components, OsType); split([], [], Components, _OsType) -> lists:reverse(Components); split([], Comp, Components, OsType) -> diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl index 1d0f9374bc..3efa68ca09 100644 --- a/lib/stdlib/src/io.erl +++ b/lib/stdlib/src/io.erl @@ -39,6 +39,8 @@ -type device() :: atom() | pid(). -type prompt() :: atom() | string(). +-type error_description() :: term(). % Whatever the io-server sends. +-type request_error() :: {'error',error_description()}. %% XXX: Some uses of line() in this file may need to read erl_scan:location() -type line() :: pos_integer(). @@ -53,26 +55,12 @@ to_tuple(T) when is_tuple(T) -> T; to_tuple(T) -> {T}. -%% Problem: the variables Other, Name and Args may collide with surrounding -%% ones. -%% Give extra args to macro, being the variables to use. --define(O_REQUEST(Io, Request), - case request(Io, Request) of - {error, Reason} -> - [Name | Args] = tuple_to_list(to_tuple(Request)), - erlang:error(conv_reason(Name, Reason), [Name, Io | Args]); - Other -> - Other - end). - o_request(Io, Request, Func) -> case request(Io, Request) of {error, Reason} -> [_Name | Args] = tuple_to_list(to_tuple(Request)), - {'EXIT',{undef,[_Current|Mfas]}} = (catch erlang:error(undef)), - MFA = {io, Func, [Io | Args]}, - exit({conv_reason(Func, Reason),[MFA|Mfas]}); -% erlang:error(conv_reason(Name, Reason), [Name, Io | Args]); + {'EXIT',{get_stacktrace,[_Current|Mfas]}} = (catch erlang:error(get_stacktrace)), + erlang:raise(error, conv_reason(Func, Reason), [{io, Func, [Io | Args]}|Mfas]); Other -> Other end. @@ -299,32 +287,32 @@ format(Io, Format, Args) -> %% Scanning Erlang code. --spec scan_erl_exprs(prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_exprs(prompt()) -> erl_scan:tokens_result() | request_error(). scan_erl_exprs(Prompt) -> scan_erl_exprs(default_input(), Prompt, 1). --spec scan_erl_exprs(device(), prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_exprs(device(), prompt()) -> erl_scan:tokens_result() | request_error(). scan_erl_exprs(Io, Prompt) -> scan_erl_exprs(Io, Prompt, 1). --spec scan_erl_exprs(device(), prompt(), line()) -> erl_scan:tokens_result(). +-spec scan_erl_exprs(device(), prompt(), line()) -> erl_scan:tokens_result() | request_error(). scan_erl_exprs(Io, Prompt, Pos0) -> request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). --spec scan_erl_form(prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_form(prompt()) -> erl_scan:tokens_result() | request_error(). scan_erl_form(Prompt) -> scan_erl_form(default_input(), Prompt, 1). --spec scan_erl_form(device(), prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_form(device(), prompt()) -> erl_scan:tokens_result() | request_error(). scan_erl_form(Io, Prompt) -> scan_erl_form(Io, Prompt, 1). --spec scan_erl_form(device(), prompt(), line()) -> erl_scan:tokens_result(). +-spec scan_erl_form(device(), prompt(), line()) -> erl_scan:tokens_result() | request_error(). scan_erl_form(Io, Prompt, Pos0) -> request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). @@ -335,7 +323,8 @@ scan_erl_form(Io, Prompt, Pos0) -> -type parse_ret() :: {'ok', erl_parse_expr_list(), line()} | {'eof', line()} - | {'error', erl_scan:error_info(), line()}. + | {'error', erl_scan:error_info(), line()} + | request_error(). -spec parse_erl_exprs(prompt()) -> parse_ret(). @@ -364,7 +353,8 @@ parse_erl_exprs(Io, Prompt, Pos0) -> -type parse_form_ret() :: {'ok', erl_parse_absform(), line()} | {'eof', line()} - | {'error', erl_scan:error_info(), line()}. + | {'error', erl_scan:error_info(), line()} + | request_error(). -spec parse_erl_form(prompt()) -> parse_form_ret(). diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl index 296a6b3d23..9642de17b4 100644 --- a/lib/stdlib/src/re.erl +++ b/lib/stdlib/src/re.erl @@ -208,29 +208,25 @@ replace(Subject,RE,Replacement,Options) -> process_repl_params(Options,iodata,false), FlatSubject = to_binary(Subject, Unicode), FlatReplacement = to_binary(Replacement, Unicode), - case do_replace(FlatSubject,Subject,RE,FlatReplacement,NewOpt) of - {error,_Err} -> - throw(badre); - IoList -> - case Convert of - iodata -> - IoList; - binary -> - case Unicode of - false -> - iolist_to_binary(IoList); - true -> - unicode:characters_to_binary(IoList,unicode) - end; - list -> - case Unicode of - false -> - binary_to_list(iolist_to_binary(IoList)); - true -> - unicode:characters_to_list(IoList,unicode) - end - end - end + IoList = do_replace(FlatSubject,Subject,RE,FlatReplacement,NewOpt), + case Convert of + iodata -> + IoList; + binary -> + case Unicode of + false -> + iolist_to_binary(IoList); + true -> + unicode:characters_to_binary(IoList,unicode) + end; + list -> + case Unicode of + false -> + binary_to_list(iolist_to_binary(IoList)); + true -> + unicode:characters_to_list(IoList,unicode) + end + end catch throw:badopt -> erlang:error(badarg,[Subject,RE,Replacement,Options]); diff --git a/lib/stdlib/test/base64_SUITE.erl b/lib/stdlib/test/base64_SUITE.erl index 44742063b3..d8bb2dfb60 100644 --- a/lib/stdlib/test/base64_SUITE.erl +++ b/lib/stdlib/test/base64_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. 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 @@ -18,7 +18,6 @@ %% -module(base64_SUITE). --author('[email protected]'). -include("test_server.hrl"). -include("test_server_line.hrl"). @@ -29,7 +28,7 @@ %% Test cases must be exported. -export([base64_encode/1, base64_decode/1, base64_otp_5635/1, base64_otp_6279/1, big/1, illegal/1, mime_decode/1, - roundtrip/1]). + mime_decode_to_string/1, roundtrip/1]). init_per_testcase(_, Config) -> Dog = test_server:timetrap(?t:minutes(2)), @@ -50,7 +49,7 @@ all(doc) -> all(suite) -> [base64_encode, base64_decode, base64_otp_5635, base64_otp_6279, big, illegal, mime_decode, - roundtrip]. + mime_decode_to_string, roundtrip]. %%------------------------------------------------------------------------- @@ -59,7 +58,7 @@ base64_encode(doc) -> base64_encode(suite) -> []; base64_encode(Config) when is_list(Config) -> - %% Two pads + %% Two pads <<"QWxhZGRpbjpvcGVuIHNlc2FtZQ==">> = base64:encode("Aladdin:open sesame"), %% One pad @@ -77,8 +76,8 @@ base64_decode(doc) -> base64_decode(suite) -> []; base64_decode(Config) when is_list(Config) -> - %% Two pads - <<"Aladdin:open sesame">> = + %% Two pads + <<"Aladdin:open sesame">> = base64:decode("QWxhZGRpbjpvcGVuIHNlc2FtZQ=="), %% One pad <<"Hello World">> = base64:decode(<<"SGVsbG8gV29ybGQ=">>), @@ -138,20 +137,85 @@ illegal(Config) when is_list(Config) -> {'EXIT',{function_clause, _}} = (catch base64:decode("()")), ok. %%------------------------------------------------------------------------- +%% mime_decode and mime_decode_to_string have different implementations +%% so test both with the same input separately. Both functions have +%% the same implementation for binary/string arguments. mime_decode(doc) -> ["Test base64:mime_decode/1."]; mime_decode(suite) -> []; mime_decode(Config) when is_list(Config) -> - %% Two pads - <<"Aladdin:open sesame">> = + %% Test correct padding + <<"one">> = base64:mime_decode(<<"b25l">>), + <<"on">> = base64:mime_decode(<<"b24=">>), + <<"o">> = base64:mime_decode(<<"bw==">>), + %% Test 1 extra padding + <<"one">> = base64:mime_decode(<<"b25l= =">>), + <<"on">> = base64:mime_decode(<<"b24== =">>), + <<"o">> = base64:mime_decode(<<"bw=== =">>), + %% Test 2 extra padding + <<"one">> = base64:mime_decode(<<"b25l===">>), + <<"on">> = base64:mime_decode(<<"b24====">>), + <<"o">> = base64:mime_decode(<<"bw=====">>), + %% Test misc embedded padding + <<"one">> = base64:mime_decode(<<"b2=5l===">>), + <<"on">> = base64:mime_decode(<<"b=24====">>), + <<"o">> = base64:mime_decode(<<"b=w=====">>), + %% Test misc white space and illegals with embedded padding + <<"one">> = base64:mime_decode(<<" b~2=\r\n5()l===">>), + <<"on">> = base64:mime_decode(<<"\tb =2\"�4=�= ==">>), + <<"o">> = base64:mime_decode(<<"\nb=w=====">>), + %% Two pads + <<"Aladdin:open sesame">> = base64:mime_decode("QWxhZGRpbjpvc()GVuIHNlc2FtZQ=="), - %% One pad, followed by ignored text - <<"Hello World">> = base64:mime_decode(<<"SGVsb)(G8gV29ybGQ=apa">>), + %% One pad to ignore, followed by more text + <<"Hello World!!">> = base64:mime_decode(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>), + %% No pad + <<"Aladdin:open sesam">> = + base64:mime_decode("QWxhZGRpbjpvcG�\")(VuIHNlc2Ft"), + %% Encoded base 64 strings may be divided by non base 64 chars. + %% In this cases whitespaces. + <<"0123456789!@#0^&*();:<>,. []{}">> = + base64:mime_decode( + <<"MDEy MzQ1Njc4 \tOSFAIzBeJ \nio)(oKTs6 PD4sLi \r\nBbXXt9">>), + ok. + +%%------------------------------------------------------------------------- + +%% Repeat of mime_decode() tests +mime_decode_to_string(doc) -> + ["Test base64:mime_decode_to_string/1."]; +mime_decode_to_string(suite) -> + []; +mime_decode_to_string(Config) when is_list(Config) -> + %% Test correct padding + "one" = base64:mime_decode_to_string(<<"b25l">>), + "on" = base64:mime_decode_to_string(<<"b24=">>), + "o" = base64:mime_decode_to_string(<<"bw==">>), + %% Test 1 extra padding + "one" = base64:mime_decode_to_string(<<"b25l= =">>), + "on" = base64:mime_decode_to_string(<<"b24== =">>), + "o" = base64:mime_decode_to_string(<<"bw=== =">>), + %% Test 2 extra padding + "one" = base64:mime_decode_to_string(<<"b25l===">>), + "on" = base64:mime_decode_to_string(<<"b24====">>), + "o" = base64:mime_decode_to_string(<<"bw=====">>), + %% Test misc embedded padding + "one" = base64:mime_decode_to_string(<<"b2=5l===">>), + "on" = base64:mime_decode_to_string(<<"b=24====">>), + "o" = base64:mime_decode_to_string(<<"b=w=====">>), + %% Test misc white space and illegals with embedded padding + "one" = base64:mime_decode_to_string(<<" b~2=\r\n5()l===">>), + "on" = base64:mime_decode_to_string(<<"\tb =2\"�4=�= ==">>), + "o" = base64:mime_decode_to_string(<<"\nb=w=====">>), + %% Two pads + "Aladdin:open sesame" = + base64:mime_decode_to_string("QWxhZGRpbjpvc()GVuIHNlc2FtZQ=="), + %% One pad to ignore, followed by more text + "Hello World!!" = base64:mime_decode_to_string(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>), %% No pad "Aladdin:open sesam" = base64:mime_decode_to_string("QWxhZGRpbjpvcG�\")(VuIHNlc2Ft"), - %% Encoded base 64 strings may be divided by non base 64 chars. %% In this cases whitespaces. "0123456789!@#0^&*();:<>,. []{}" = @@ -159,6 +223,7 @@ mime_decode(Config) when is_list(Config) -> <<"MDEy MzQ1Njc4 \tOSFAIzBeJ \nio)(oKTs6 PD4sLi \r\nBbXXt9">>), ok. +%%------------------------------------------------------------------------- roundtrip(Config) when is_list(Config) -> Sizes = lists:seq(1, 255) ++ lists:seq(2400-5, 2440), diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl index c60a558fa1..254ce0095d 100644 --- a/lib/stdlib/test/erl_eval_SUITE.erl +++ b/lib/stdlib/test/erl_eval_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2010. 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 @@ -38,7 +38,8 @@ otp_8133/1, funs/1, try_catch/1, - eval_expr_5/1]). + eval_expr_5/1, + zero_width/1]). %% %% Define to run outside of test server @@ -76,7 +77,8 @@ all(suite) -> [guard_1, guard_2, match_pattern, string_plusplus, pattern_expr, match_bin, guard_3, guard_4, lc, simple_cases, unary_plus, apply_atom, otp_5269, otp_6539, otp_6543, - otp_6787, otp_6977, otp_7550, otp_8133, funs, try_catch, eval_expr_5]. + otp_6787, otp_6977, otp_7550, otp_8133, funs, try_catch, eval_expr_5, + zero_width]. guard_1(doc) -> ["(OTP-2405)"]; @@ -1326,6 +1328,14 @@ eval_expr_5(Config) when is_list(Config) -> ok end. +zero_width(Config) when is_list(Config) -> + ?line check(fun() -> + {'EXIT',{badarg,_}} = (catch <<not_a_number:0>>), + ok + end, "begin {'EXIT',{badarg,_}} = (catch <<not_a_number:0>>), " + "ok end.", ok), + ok. + %% Check the string in different contexts: as is; in fun; from compiled code. check(F, String, Result) -> check1(F, String, Result), diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index d0c0d68b4a..c4158abf32 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-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 @@ -54,7 +54,8 @@ otp_7550/1, otp_8051/1, format_warn/1, - on_load/1, on_load_successful/1, on_load_failing/1 + on_load/1, on_load_successful/1, on_load_failing/1, + too_many_arguments/1 ]). % Default timetrap timeout (set in init_per_testcase). @@ -77,7 +78,7 @@ all(suite) -> otp_5362, otp_5371, otp_7227, otp_5494, otp_5644, otp_5878, otp_5917, otp_6585, otp_6885, export_all, bif_clash, behaviour_basic, behaviour_multiple, otp_7550, otp_8051, format_warn, - on_load]. + on_load,too_many_arguments]. unused_vars_warn(suite) -> [unused_vars_warn_basic, unused_vars_warn_lc, unused_vars_warn_rec, @@ -2913,6 +2914,21 @@ on_load_failing(Config) when is_list(Config) -> ?line [] = run(Config, Ts), ok. +too_many_arguments(doc) -> + "Test that too many arguments is not accepted."; +too_many_arguments(suite) -> []; +too_many_arguments(Config) when is_list(Config) -> + Ts = [{too_many_1, + <<"f(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_) -> ok.">>, + [], + {errors, + [{1,erl_lint,{too_many_arguments,256}}],[]}} + ], + + ?line [] = run(Config, Ts), + ok. + + run(Config, Tests) -> F = fun({N,P,Ws,E}, BadL) -> case catch run_test(Config, P, Ws) of diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 620848003c..4e789790f6 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -5325,7 +5325,25 @@ my_tab_to_list(_Ts,'$end_of_table', Acc) -> lists:reverse(Acc); my_tab_to_list(Ts,Key, Acc) -> my_tab_to_list(Ts,ets:next(Ts,Key),[ets:lookup(Ts, Key)| Acc]). +wait_for_all_schedulers_online_to_execute() -> + PMs = lists:map(fun (Sched) -> + spawn_opt(fun () -> ok end, + [monitor, {scheduler, Sched}]) + end, + lists:seq(1,erlang:system_info(schedulers_online))), + lists:foreach(fun ({P, M}) -> + receive + {'DOWN', M, process, P, _} -> ok + end + end, + PMs), + ok. + etsmem() -> + %% Wait until it is guaranteed that all already scheduled + %% deallocations of DbTable structures have completed. + wait_for_all_schedulers_online_to_execute(), + AllTabs = lists:map(fun(T) -> {T,ets:info(T,name),ets:info(T,size), ets:info(T,memory),ets:info(T,type)} end, ets:all()), diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index db7954af04..ac02e1f359 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -1 +1 @@ -STDLIB_VSN = 1.17.2 +STDLIB_VSN = 1.17.3 diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml index fca93a27d9..3f5eb7231e 100644 --- a/lib/syntax_tools/doc/src/notes.xml +++ b/lib/syntax_tools/doc/src/notes.xml @@ -31,6 +31,20 @@ <p>This document describes the changes made to the Syntax_Tools application.</p> +<section><title>Syntax_Tools 1.6.7</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>Miscellaneous updates</p> + <p> + Own Id: OTP-8976</p> + </item> + </list> + </section> + +</section> + <section><title>Syntax_Tools 1.6.6</title> <section><title>Improvements and New Features</title> diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk index 6051fb8e39..2e23f6aef9 100644 --- a/lib/syntax_tools/vsn.mk +++ b/lib/syntax_tools/vsn.mk @@ -1 +1 @@ -SYNTAX_TOOLS_VSN = 1.6.6 +SYNTAX_TOOLS_VSN = 1.6.7 diff --git a/lib/test_server/doc/src/notes.xml b/lib/test_server/doc/src/notes.xml index e0c4c28e44..ab329c399b 100644 --- a/lib/test_server/doc/src/notes.xml +++ b/lib/test_server/doc/src/notes.xml @@ -32,6 +32,20 @@ <file>notes.xml</file> </header> +<section><title>Test_Server 3.4.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>Miscellaneous updates</p> + <p> + Own Id: OTP-8976</p> + </item> + </list> + </section> + +</section> + <section><title>Test_Server 3.4.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl index 0176b68579..e0bf50bc43 100644 --- a/lib/test_server/src/test_server.erl +++ b/lib/test_server/src/test_server.erl @@ -935,8 +935,7 @@ spawn_fw_call(Mod,{init_per_testcase,Func},Pid,{timetrap_timeout,TVal}=Why, Skip = {skip,{failed,{Mod,init_per_testcase,Why}}}, %% if init_per_testcase fails, the test case %% should be skipped - case catch test_server_sup:framework_call( - end_tc,[?pl2a(Mod),Func,{Pid,Skip,[[]]}]) of + case catch do_end_tc_call(Mod,Func,{Pid,Skip,[[]]},Why) of {'EXIT',FwEndTCErr} -> exit({fw_notify_done,end_tc,FwEndTCErr}); _ -> @@ -955,11 +954,9 @@ spawn_fw_call(Mod,{end_per_testcase,Func},Pid,{timetrap_timeout,TVal}=Why, Conf = [{tc_status,ok}], %% if end_per_testcase fails, the test case should be %% reported successful with a warning printed as comment - case catch test_server_sup:framework_call(end_tc, - [?pl2a(Mod),Func, - {Pid, - {failed,{Mod,end_per_testcase,Why}}, - [Conf]}]) of + case catch do_end_tc_call(Mod,Func,{Pid, + {failed,{Mod,end_per_testcase,Why}}, + [Conf]}, Why) of {'EXIT',FwEndTCErr} -> exit({fw_notify_done,end_tc,FwEndTCErr}); _ -> @@ -1001,9 +998,7 @@ spawn_fw_call(Mod,Func,Pid,Error,Loc,SendTo,Comment) -> ok end, Conf = [{tc_status,{failed,timetrap_timeout}}], - case catch test_server_sup:framework_call(end_tc, - [?pl2a(Mod),Func, - {Pid,Error,[Conf]}]) of + case catch do_end_tc_call(Mod,Func,{Pid,Error,[Conf]},Error) of {'EXIT',FwEndTCErr} -> exit({fw_notify_done,end_tc,FwEndTCErr}); _ -> @@ -1069,27 +1064,27 @@ run_test_case_eval(Mod, Func, Args0, Name, Ref, RunInit, {{Time,Value},Loc,Opts} = case test_server_sup:framework_call(init_tc,[?pl2a(Mod),Func,Args0], - {ok,Args0}) of + {ok, Args0}) of {ok,Args} -> run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback); Error = {error,_Reason} -> - test_server_sup:framework_call(end_tc,[?pl2a(Mod),Func,{Error,Args0}]), - {{0,{skip,{failed,Error}}},{Mod,Func},[]}; + NewResult = do_end_tc_call(Mod,Func,{Error,Args0}, + {skip,{failed,Error}}), + {{0,NewResult},{Mod,Func},[]}; {fail,Reason} -> [Conf] = Args0, Conf1 = [{tc_status,{failed,Reason}} | Conf], fw_error_notify(Mod, Func, Conf, Reason), - test_server_sup:framework_call(end_tc,[?pl2a(Mod),Func, - {{error,Reason},[Conf1]}]), - {{0,{failed,Reason}},{Mod,Func},[]}; + NewResult = do_end_tc_call(Mod,Func, {{error,Reason},[Conf1]}, + {fail, Reason}), + {{0,NewResult},{Mod,Func},[]}; Skip = {skip,_Reason} -> - test_server_sup:framework_call(end_tc,[?pl2a(Mod),Func,{Skip,Args0}]), - {{0,Skip},{Mod,Func},[]}; + NewResult = do_end_tc_call(Mod,Func,{Skip,Args0},Skip), + {{0,NewResult},{Mod,Func},[]}; {auto_skip,Reason} -> - test_server_sup:framework_call(end_tc,[?pl2a(Mod), - Func, - {{skip,Reason},Args0}]), - {{0,{skip,{fw_auto_skip,Reason}}},{Mod,Func},[]} + NewResult = do_end_tc_call(Mod, Func, {{skip,Reason},Args0}, + {skip, {fw_auto_skip,Reason}}), + {{0,NewResult},{Mod,Func},[]} end, exit({Ref,Time,Value,Loc,Opts}). @@ -1103,14 +1098,14 @@ run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback) -> Skip = {skip,Reason} -> Line = get_loc(), Conf = [{tc_status,{skipped,Reason}}], - test_server_sup:framework_call(end_tc,[?pl2a(Mod),Func,{Skip,[Conf]}]), - {{0,{skip,Reason}},Line,[]}; + NewRes = do_end_tc_call(Mod,Func,{Skip,[Conf]}, Skip), + {{0,NewRes},Line,[]}; {skip_and_save,Reason,SaveCfg} -> Line = get_loc(), Conf = [{tc_status,{skipped,Reason}},{save_config,SaveCfg}], - test_server_sup:framework_call(end_tc,[?pl2a(Mod),Func, - {{skip,Reason},[Conf]}]), - {{0,{skip,Reason}},Line,[]}; + NewRes = do_end_tc_call(Mod, Func, {{skip, Reason}, [Conf]}, + {skip, Reason}), + {{0,NewRes},Line,[]}; {ok,NewConf} -> put(test_server_init_or_end_conf,undefined), %% call user callback function if defined @@ -1155,13 +1150,12 @@ run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback) -> {FWReturn,TSReturn,EndConf1} end, put(test_server_init_or_end_conf,undefined), - case test_server_sup:framework_call(end_tc, [?pl2a(Mod), Func, - {FWReturn1,[EndConf2]}]) of - {fail,Reason} -> - fw_error_notify(Mod, Func, EndConf2, Reason), - {{T,{failed,Reason}},{Mod,Func},[]}; - _ -> - {{T,TSReturn1},Loc,[]} + case do_end_tc_call(Mod, Func, {FWReturn1,[EndConf2]}, TSReturn1) of + {failed,Reason} = NewReturn -> + fw_error_notify(Mod,Func,EndConf2, Reason), + {{T,NewReturn},{Mod,Func},[]}; + NewReturn -> + {{T,NewReturn},Loc,[]} end end; skip_init -> @@ -1179,10 +1173,36 @@ run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback) -> {{T,Return},Loc} = {ts_tc(Mod, Func, Args2),get_loc()}, %% call user callback function if defined Return1 = user_callback(TCCallback, Mod, Func, 'end', Return), - {Return2,Opts} = process_return_val([Return1], Mod,Func,Args1, Loc, Return1), + {Return2,Opts} = process_return_val([Return1], Mod, Func, + Args1, Loc, Return1), {{T,Return2},Loc,Opts} end. +do_end_tc_call(M,F,Res,Return) -> + Ref = make_ref(), + case test_server_sup:framework_call( + end_tc, [?pl2a(M),F,Res], Ref) of + {fail,FWReason} -> + {failed,FWReason}; + Ref -> + case test_server_sup:framework_call( + end_tc, [?pl2a(M),F,Res, Return], ok) of + {fail,FWReason} -> + {failed,FWReason}; + ok -> + case Return of + {fail,Reason} -> + {failed,Reason}; + Return -> + Return + end; + NewReturn -> + NewReturn + end; + _ -> + Return + end. + %% the return value is a list and we have to check if it contains %% the result of an end conf case or if it's a Config list process_return_val([Return], M,F,A, Loc, Final) when is_list(Return) -> @@ -1197,13 +1217,13 @@ process_return_val([Return], M,F,A, Loc, Final) when is_list(Return) -> end, Return) of true -> % must be return value from end conf case process_return_val1(Return, M,F,A, Loc, Final, []); - false -> % must be Config value from init conf case - case test_server_sup:framework_call(end_tc, [?pl2a(M),F,{ok,A}]) of - {fail,FWReason} -> + false -> % must be Config value from init conf case + case do_end_tc_call(M,F,{ok,A}, Return) of + {failed, FWReason} = Failed -> fw_error_notify(M,F,A, FWReason), - {{failed,FWReason},[]}; - _ -> - {Return,[]} + {Failed, []}; + NewReturn -> + {NewReturn, []} end end; %% the return value is not a list, so it's the return value from an @@ -1211,16 +1231,16 @@ process_return_val([Return], M,F,A, Loc, Final) when is_list(Return) -> process_return_val(Return, M,F,A, Loc, Final) -> process_return_val1(Return, M,F,A, Loc, Final, []). -process_return_val1([Failed={E,TCError}|_], M,F,A=[Args], Loc, _, SaveOpts) when E=='EXIT'; - E==failed -> +process_return_val1([Failed={E,TCError}|_], M,F,A=[Args], Loc, _, SaveOpts) + when E=='EXIT'; + E==failed -> fw_error_notify(M,F,A, TCError, mod_loc(Loc)), - case test_server_sup:framework_call(end_tc, - [?pl2a(M),F,{{error,TCError}, - [[{tc_status,{failed,TCError}}|Args]]}]) of - {fail,FWReason} -> + case do_end_tc_call(M,F,{{error,TCError}, + [[{tc_status,{failed,TCError}}|Args]]}, Failed) of + {failed,FWReason} -> {{failed,FWReason},SaveOpts}; - _ -> - {Failed,SaveOpts} + NewReturn -> + {NewReturn,SaveOpts} end; process_return_val1([SaveCfg={save_config,_}|Opts], M,F,[Args], Loc, Final, SaveOpts) -> process_return_val1(Opts, M,F,[[SaveCfg|Args]], Loc, Final, SaveOpts); @@ -1234,11 +1254,11 @@ process_return_val1([RetVal={Tag,_}|Opts], M,F,A, Loc, _, SaveOpts) when Tag==sk process_return_val1([_|Opts], M,F,A, Loc, Final, SaveOpts) -> process_return_val1(Opts, M,F,A, Loc, Final, SaveOpts); process_return_val1([], M,F,A, _Loc, Final, SaveOpts) -> - case test_server_sup:framework_call(end_tc, [?pl2a(M),F,{Final,A}]) of - {fail,FWReason} -> + case do_end_tc_call(M,F,{Final,A}, Final) of + {failed,FWReason} -> {{failed,FWReason},SaveOpts}; - _ -> - {Final,lists:reverse(SaveOpts)} + NewReturn -> + {NewReturn,lists:reverse(SaveOpts)} end. user_callback(undefined, _, _, _, Args) -> @@ -1263,7 +1283,7 @@ init_per_testcase(Mod, Func, Args) -> false -> code:load_file(Mod); _ -> ok end, - %% init_per_testcase defined, returns new configuration +%% init_per_testcase defined, returns new configuration case erlang:function_exported(Mod,init_per_testcase,2) of true -> case catch my_apply(Mod, init_per_testcase, [Func|Args]) of @@ -1306,8 +1326,8 @@ init_per_testcase(Mod, Func, Args) -> {skip,{failed,{Mod,init_per_testcase,Other}}} end; false -> - %% Optional init_per_testcase not defined - %% keep quiet. +%% Optional init_per_testcase not defined +%% keep quiet. [Config] = Args, {ok, Config} end. diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl index 0c363085f4..7cd58642d0 100644 --- a/lib/test_server/src/test_server_ctrl.erl +++ b/lib/test_server/src/test_server_ctrl.erl @@ -3558,7 +3558,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where, {_,{'EXIT',Reason}} -> progress(failed, Num, Mod, Func, Loc, Reason, Time, Comment, Style); - {_, {failed, Reason}} -> + {_, {Fail, Reason}} when Fail =:= fail; Fail =:= failed -> progress(failed, Num, Mod, Func, Loc, Reason, Time, Comment, Style); {_, {Skip, Reason}} when Skip==skip; Skip==skipped -> diff --git a/lib/test_server/vsn.mk b/lib/test_server/vsn.mk index 4c3df28814..4e293b76a7 100644 --- a/lib/test_server/vsn.mk +++ b/lib/test_server/vsn.mk @@ -1,2 +1,2 @@ -TEST_SERVER_VSN = 3.4.1 +TEST_SERVER_VSN = 3.4.2 diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml index 4a71993da9..5f9cecd6e4 100644 --- a/lib/tools/doc/src/notes.xml +++ b/lib/tools/doc/src/notes.xml @@ -30,6 +30,26 @@ </header> <p>This document describes the changes made to the Tools application.</p> +<section><title>Tools 2.6.6.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>eprof: API sort mismatch has now been fixed. </p> + <p> + Own Id: OTP-8853</p> + </item> + <item> + <p> + eprof: fix division by zero in statistics</p> + <p> + Own Id: OTP-8963</p> + </item> + </list> + </section> + +</section> + <section><title>Tools 2.6.6.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk index 77b5254eaa..0c89b18065 100644 --- a/lib/tools/vsn.mk +++ b/lib/tools/vsn.mk @@ -1 +1 @@ -TOOLS_VSN = 2.6.6.1 +TOOLS_VSN = 2.6.6.2 diff --git a/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml index 8414028106..1493501ea4 100644 --- a/lib/wx/doc/src/notes.xml +++ b/lib/wx/doc/src/notes.xml @@ -31,6 +31,25 @@ <p>This document describes the changes made to the wxErlang application.</p> +<section><title>Wx 0.98.8</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>Add wxSystemSettings which was missing in the previous + release, despite previous comments.</p> <p>Fix an + external loop when stopping erlang nicely.</p> + <p>Separate OpenGL to it's own dynamic loaded library, so + other graphic libraries can reuse the gl module and it + will not waste memory if not used.</p> + <p> + Own Id: OTP-8951</p> + </item> + </list> + </section> + +</section> + <section><title>Wx 0.98.7</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk index c0cc302317..dea0678063 100644 --- a/lib/wx/vsn.mk +++ b/lib/wx/vsn.mk @@ -1 +1 @@ -WX_VSN = 0.98.7 +WX_VSN = 0.98.8 diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml index d67a622481..aa66cbec77 100644 --- a/lib/xmerl/doc/src/notes.xml +++ b/lib/xmerl/doc/src/notes.xml @@ -31,6 +31,40 @@ <p>This document describes the changes made to the Xmerl application.</p> +<section><title>Xmerl 1.2.7</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> An empty element declared as simpleContent was not + properly validated. </p> + <p> + Own Id: OTP-8599</p> + </item> + <item> + <p> Fix format_man_pages so it handles all man sections + and remove warnings/errors in various man pages. </p> + <p> + Own Id: OTP-8600</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> Fix entity checking so there are no fatal errors for + undefined entities when option skip_external_dtd is used. + </p> + <p> + Own Id: OTP-8947</p> + </item> + </list> + </section> + +</section> + <section><title>Xmerl 1.2.6</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/xmerl/src/xmerl_lib.erl b/lib/xmerl/src/xmerl_lib.erl index 7b76a76a33..1b3a7e57f0 100644 --- a/lib/xmerl/src/xmerl_lib.erl +++ b/lib/xmerl/src/xmerl_lib.erl @@ -148,9 +148,10 @@ expand_element(Element) -> expand_element(Element, Pos, Parents) -> expand_element(Element, Pos, Parents, false). -expand_element(E = #xmlElement{}, Pos, Parents, Norm) -> - Content = expand_content(E#xmlElement.content, 1, Parents, Norm), - Attrs = expand_attributes(E#xmlElement.attributes, 1, []), +expand_element(E = #xmlElement{name = N}, Pos, Parents, Norm) -> + NewParents = [{N,Pos}|Parents], + Content = expand_content(E#xmlElement.content, 1, NewParents, Norm), + Attrs = expand_attributes(E#xmlElement.attributes, 1, NewParents), E#xmlElement{pos = Pos, parents = Parents, attributes = Attrs, diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk index d85a57f447..a4d7efaee3 100644 --- a/lib/xmerl/vsn.mk +++ b/lib/xmerl/vsn.mk @@ -1 +1 @@ -XMERL_VSN = 1.2.6 +XMERL_VSN = 1.2.7 diff --git a/make/emd2exml.in b/make/emd2exml.in index ea1085cf71..16c38379d9 100644 --- a/make/emd2exml.in +++ b/make/emd2exml.in @@ -381,6 +381,8 @@ put_text(S, [$>|Cs], CTag, EmTag, Acc) when CTag /= no -> put_text(S, Cs, CTag, EmTag, [">"|Acc]); put_text(S, [$&|Cs], CTag, EmTag, Acc) when CTag /= no -> put_text(S, Cs, CTag, EmTag, ["&"|Acc]); +put_text(S, [$&, $ |Cs], CTag, EmTag, Acc) -> %Workaround for INSTALL-WIN32.md + put_text(S, Cs, CTag, EmTag, ["& "|Acc]); put_text(S, [$'|Cs], CTag, EmTag, Acc)when CTag /= no -> put_text(S, Cs, CTag, EmTag, ["'"|Acc]); put_text(S, [$<|Cs], no, EmTag, Acc) -> diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml index 2383e3cf3d..8126b93a2d 100644 --- a/system/doc/efficiency_guide/advanced.xml +++ b/system/doc/efficiency_guide/advanced.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2001</year><year>2010</year> + <year>2001</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -200,7 +200,7 @@ On 64-bit architectures: 4 words for a reference from the current local node, an <seealso marker="#ports">the maximum number of Erlang ports</seealso> available, and operating system specific settings and limits.</item> <tag><em>Number of arguments to a function or fun</em></tag> - <item>256</item> + <item>255</item> </taglist> </section> </chapter> |