diff options
Diffstat (limited to 'erts')
-rw-r--r-- | erts/doc/src/erl_driver.xml | 4 | ||||
-rw-r--r-- | erts/doc/src/erl_nif.xml | 37 | ||||
-rw-r--r-- | erts/doc/src/erlang.xml | 2 | ||||
-rw-r--r-- | erts/doc/src/erts_alloc.xml | 2 | ||||
-rw-r--r-- | erts/emulator/Makefile.in | 10 | ||||
-rw-r--r-- | erts/emulator/beam/bif.c | 17 | ||||
-rw-r--r-- | erts/emulator/beam/erl_alloc_util.c | 10 | ||||
-rwxr-xr-x | erts/emulator/beam/erl_bif_info.c | 4 | ||||
-rw-r--r-- | erts/emulator/beam/erl_nif.c | 13 | ||||
-rw-r--r-- | erts/emulator/beam/erl_nif.h | 5 | ||||
-rw-r--r-- | erts/emulator/beam/erl_nif_api_funcs.h | 2 | ||||
-rw-r--r-- | erts/emulator/drivers/common/efile_drv.c | 2 | ||||
-rw-r--r-- | erts/emulator/sys/common/erl_mseg.c | 315 | ||||
-rw-r--r-- | erts/emulator/sys/common/erl_mseg.h | 9 | ||||
-rw-r--r-- | erts/emulator/sys/common/erl_sys_common_misc.c | 195 | ||||
-rw-r--r-- | erts/emulator/sys/common/erl_util_queue.h | 77 | ||||
-rw-r--r-- | erts/emulator/test/nif_SUITE.erl | 107 | ||||
-rw-r--r-- | erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 24 | ||||
-rw-r--r-- | erts/emulator/test/num_bif_SUITE.erl | 22 | ||||
-rwxr-xr-x | erts/emulator/utils/gen_git_version | 40 | ||||
-rw-r--r-- | erts/lib_src/common/erl_printf_format.c | 10 |
21 files changed, 644 insertions, 263 deletions
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 1212c34586..0e27f8a5bd 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -1960,9 +1960,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len emulator thread. This enables the driver to perform time-consuming, blocking operations without blocking the emulator.</p> - <p>Erlang is by default started without an async thread pool. The - number of async threads that the runtime system should use - is specified by the + <p>The async thread pool size can be set with the <seealso marker="erl#async_thread_pool_size">+A</seealso> command line argument of <seealso marker="erl">erl(1)</seealso>. If no async thread pool is available, the call is made diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index f00f7b9f46..18193d1150 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -174,9 +174,11 @@ ok millisecond has passed. This can be achieved using different approaches. If you have full control over the code that are to execute in the native function, the best approach is to divide the work into multiple chunks of - work and call the native function multiple times. This might, however, - not always be possible, e.g. when calling third party libraries. In this - case you typically want to dispatch the work to another thread, return + work and call the native function multiple times. Function + <seealso marker="#enif_consume_timeslice">enif_consume_timeslice</seealso> can be + used this facilitate such work division. In some cases, however, this might not + be possible, e.g. when calling third party libraries. Then you typically want + to dispatch the work to another thread, return from the native function, and wait for the result. The thread can send the result back to the calling thread using message passing. Information about thread primitives can be found below.</p> @@ -227,8 +229,8 @@ ok bit length have no support yet.</p> </item> <tag>Resource objects</tag> - <item><p>The use of resource objects is a way to return pointers to - native data structures from a NIF in a safe way. A resource object is + <item><p>The use of resource objects is a safe way to return pointers to + native data structures from a NIF. A resource object is just a block of memory allocated with <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>. A handle ("safe pointer") to this memory block can then be returned to Erlang by the use of @@ -581,6 +583,31 @@ typedef enum { <desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_wait">erl_drv_cond_wait</seealso>. </p></desc> </func> + <func><name><ret>int</ret><nametext>enif_consume_timeslice(ErlNifEnv *env, int percent)</nametext></name> + <fsummary></fsummary> + <desc><p>Give the runtime system a hint about how much CPU time the current NIF call has consumed + since last hint, or since the start of the NIF if no previous hint has been given. + The time is given as a <c>percent</c> of the timeslice that a process is allowed to execute Erlang + code until it may be suspended to give time for other runnable processes. + The scheduling timeslice is not an exact entity, but can usually be + approximated to about 1 millisecond.</p> + <p>Note that it is up to the runtime system to determine if and how to use this information. + Implementations on some platforms may use other means in order to determine consumed + CPU time. Lengthy NIFs should regardless of this frequently call <c>enif_consume_timeslice</c> + in order to determine if it is allowed to continue execution or not.</p> + + <p>Returns 1 if the timeslice is exhausted, or 0 otherwise. If 1 is returned the NIF should return + as soon as possible in order for the process to yield.</p> + <p>Argument <c>percent</c> must be an integer between 1 and 100. This function + must only be called from a NIF-calling thread and argument <c>env</c> must be + the environment of the calling process.</p> + <p>This function is provided to better support co-operative scheduling, improve system responsiveness, + and make it easier to prevent misbehaviors of the VM due to a NIF monopolizing a scheduler thread. + It can be used to divide <seealso marker="#lengthy_work">length work</seealso> into + a number of repeated NIF-calls without the need to create threads. + See also the <seealso marker="#WARNING">warning</seealso> text at the beginning of this document.</p> + </desc> + </func> <func><name><ret>int</ret><nametext>enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2)</nametext></name> <fsummary></fsummary> <desc><p>Same as <seealso marker="erl_driver#erl_drv_equal_tids">erl_drv_equal_tids</seealso>. diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 7804a7498e..5a63aaadc6 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -977,7 +977,7 @@ true <type> <v>Float = float()</v> <v>Options = [Option]</v> - <v>Option = {decimals, Decimals::0..249} | + <v>Option = {decimals, Decimals::0..253} | {scientific, Decimals::0..249} | compact</v> </type> diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index f0bde600ad..c73cdfd290 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -276,7 +276,7 @@ <item> Max cached segments. The maximum number of memory segments stored in the memory segment cache. Valid range is - 0-30. Default value is 5.</item> + 0-30. Default value is 10.</item> </taglist> <p>The following flags are available for configuration of <c>sys_alloc</c>:</p> diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 3e44bbb8db..7033ea0a3d 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -20,6 +20,8 @@ include $(ERL_TOP)/make/target.mk include ../vsn.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk +include $(TARGET)/gen_git_version.mk + ENABLE_ALLOC_TYPE_VARS = @ENABLE_ALLOC_TYPE_VARS@ HIPE_ENABLED=@HIPE_ENABLED@ @@ -196,7 +198,7 @@ else EMU_CC = @EMU_CC@ endif WFLAGS = @WFLAGS@ -CFLAGS = @STATIC_CFLAGS@ $(TYPE_FLAGS) $(FLAVOR_FLAGS) $(DEFS) $(WFLAGS) $(THR_DEFS) $(ARCHCFLAGS) +CFLAGS = @STATIC_CFLAGS@ $(TYPE_FLAGS) $(FLAVOR_FLAGS) $(DEFS) $(WFLAGS) $(THR_DEFS) $(ARCHCFLAGS) $(GIT_VSN) HCC = @HCC@ LD = @LD@ DEXPORT = @DEXPORT@ @@ -1006,6 +1008,12 @@ DEP_FLAGS=-MM $(MG_FLAG) $(CFLAGS) $(INCLUDES) -Idrivers/common -Idrivers/$(ERLA SYS_SRC=$(ALL_SYS_SRC) endif +.PHONY: $(TARGET)/gen_git_version.mk +$(TARGET)/gen_git_version.mk: +# We touch beam/erl_bif.info.c if we regenerated the git version to force a +# rebuild. + if $(gen_verbose)utils/gen_git_version $@; then touch beam/erl_bif_info.c; fi + .PHONY: depend ifdef VOID_EMULATOR depend: diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 46f76624a5..df084e1185 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -2963,15 +2963,14 @@ BIF_RETTYPE float_to_list_2(BIF_ALIST_2) Eterm* tp = tuple_val(arg); if (*tp == arity_two && is_small(tp[2])) { decimals = signed_val(tp[2]); - if (decimals > 0 && decimals < sizeof(fbuf) - 6 /* "X." ++ "e+YY" */) - switch (tp[1]) { - case am_decimals: - fmt_type = FMT_FIXED; - continue; - case am_scientific: - fmt_type = FMT_SCIENTIFIC; - continue; - } + switch (tp[1]) { + case am_decimals: + fmt_type = FMT_FIXED; + continue; + case am_scientific: + fmt_type = FMT_SCIENTIFIC; + continue; + } } } goto badarg; diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 187bc2b48b..ac7f420708 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -643,9 +643,9 @@ alcu_mseg_realloc(Allctr_t *allctr, void *seg, Uint old_size, Uint *new_size_p) } static ERTS_INLINE void -alcu_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size) +alcu_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags) { - erts_mseg_dealloc_opt(allctr->alloc_no, seg, size, &allctr->mseg_opt); + erts_mseg_dealloc_opt(allctr->alloc_no, seg, size, flags, &allctr->mseg_opt); INC_CC(allctr->calls.mseg_dealloc); } @@ -2276,7 +2276,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags) (void *) BLK2UMEM(old_blk), MIN(new_blk_sz, old_blk_sz) - ABLK_HDR_SZ); unlink_carrier(&allctr->sbc_list, old_crr); - alcu_mseg_dealloc(allctr, old_crr, old_crr_sz); + alcu_mseg_dealloc(allctr, old_crr, old_crr_sz, ERTS_MSEG_FLG_NONE); } else { /* Old carrier unchanged; restore stat */ @@ -2352,6 +2352,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk) Carrier_t *crr; #if HAVE_ERTS_MSEG Uint is_mseg = 0; + Uint mseg_flags = ERTS_MSEG_FLG_NONE; #endif if (IS_SBC_BLK(blk)) { @@ -2398,6 +2399,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk) is_mseg++; ASSERT(crr_sz % MSEG_UNIT_SZ == 0); STAT_MSEG_MBC_FREE(allctr, crr_sz); + mseg_flags = ERTS_MSEG_FLG_2POW; } else #endif @@ -2411,7 +2413,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk) #if HAVE_ERTS_MSEG if (is_mseg) { - alcu_mseg_dealloc(allctr, crr, crr_sz); + alcu_mseg_dealloc(allctr, crr, crr_sz, mseg_flags); } else #endif diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index a601e4fb39..8582a8954b 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -69,7 +69,11 @@ static char erts_system_version[] = ("Erlang " ERLANG_OTP_RELEASE " [no-c-stack-objects]" #endif #ifndef OTP_RELEASE +#ifdef ERLANG_GIT_VERSION + " [source-" ERLANG_GIT_VERSION "]" +#else " [source]" +#endif #endif #ifdef ARCH_64 #if HALFWORD_HEAP diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 068f904b76..d4c2b5bdcc 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1443,6 +1443,19 @@ void* enif_dlsym(void* handle, const char* symbol, return ret; } +int enif_consume_timeslice(ErlNifEnv* env, int percent) +{ + Sint reds; + + ASSERT(is_proc_bound(env) && percent >= 1 && percent <= 100); + if (percent < 1) percent = 1; + else if (percent > 100) percent = 100; + + reds = ((CONTEXT_REDS+99) / 100) * percent; + ASSERT(reds > 0 && reds <= CONTEXT_REDS); + BUMP_REDS(env->proc, reds); + return ERTS_BIF_REDS_LEFT(env->proc) == 0; +} /*************************************************************************** ** load_nif/2 ** diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 93e56332e1..62aebcab6c 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -32,10 +32,11 @@ ** 2.0: R14A ** 2.1: R14B02 "vm_variant" ** 2.2: R14B03 enif_is_exception -** 2.3: R15 enif_make_reverse_list +** 2.3: R15 enif_make_reverse_list, enif_is_number +** 2.4: R16 enif_consume_timeslice */ #define ERL_NIF_MAJOR_VERSION 2 -#define ERL_NIF_MINOR_VERSION 3 +#define ERL_NIF_MINOR_VERSION 4 #include <stdlib.h> diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index af27573433..2f841645e1 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -140,6 +140,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_make_reverse_list,(ErlNifEnv*, ERL_NIF_TERM term, ERL_NIF_API_FUNC_DECL(int,enif_is_number,(ErlNifEnv*, ERL_NIF_TERM term)); ERL_NIF_API_FUNC_DECL(void*,enif_dlopen,(const char* lib, void (*err_handler)(void*,const char*), void* err_arg)); ERL_NIF_API_FUNC_DECL(void*,enif_dlsym,(void* handle, const char* symbol, void (*err_handler)(void*,const char*), void* err_arg)); +ERL_NIF_API_FUNC_DECL(int,enif_consume_timeslice,(ErlNifEnv*, int percent)); /* ** Add new entries here to keep compatibility on Windows!!! @@ -264,6 +265,7 @@ ERL_NIF_API_FUNC_DECL(void*,enif_dlsym,(void* handle, const char* symbol, void ( # define enif_is_number ERL_NIF_API_FUNC_MACRO(enif_is_number) # define enif_dlopen ERL_NIF_API_FUNC_MACRO(enif_dlopen) # define enif_dlsym ERL_NIF_API_FUNC_MACRO(enif_dlsym) +# define enif_consume_timeslice ERL_NIF_API_FUNC_MACRO(enif_consume_timeslice) /* ** Add new entries here diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index fda6cf2e53..2279fec72a 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -2263,6 +2263,8 @@ file_stop(ErlDrvData e) desc->fd = FILE_FD_INVALID; desc->flags = 0; cq_execute(desc); + } else { + EF_FREE(desc); } } else { if (desc->fd != FILE_FD_INVALID) { diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index 538eea88d1..bd8ba82a5f 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -39,6 +39,7 @@ #include "erl_alloc.h" #include "big.h" #include "erl_thr_progress.h" +#include "erl_util_queue.h" #if HAVE_ERTS_MSEG @@ -175,6 +176,7 @@ struct cache_t_ { Uint size; void *seg; cache_t *next; + cache_t *prev; }; @@ -183,9 +185,9 @@ typedef struct ErtsMsegAllctr_t_ ErtsMsegAllctr_t; struct mem_kind_t { cache_t cache[MAX_CACHE_SIZE]; - cache_t *cache_unpowered; - cache_t *cache_area[CACHE_AREAS]; - cache_t *cache_free; + cache_t cache_unpowered_node; + cache_t cache_powered_node[CACHE_AREAS]; + cache_t cache_free; Sint cache_size; Uint cache_hits; @@ -516,67 +518,94 @@ do { \ #define ERTS_DBG_MK_CHK_THR_ACCESS(MK) #endif -/* NEW CACHE interface */ +/* Cache interface */ -static ERTS_INLINE cache_t *mseg_cache_alloc_descriptor(MemKind *mk) { - cache_t *c = mk->cache_free; - ERTS_DBG_MK_CHK_THR_ACCESS(mk); - if (c) - mk->cache_free = c->next; - - return c; -} - -static ERTS_INLINE void mseg_cache_free_descriptor(MemKind *mk, cache_t *c) { - ERTS_DBG_MK_CHK_THR_ACCESS(mk); - ASSERT(c); - - c->seg = NULL; +static ERTS_INLINE void mseg_cache_clear_node(cache_t *c) { + c->seg = NULL; c->size = 0; - c->next = mk->cache_free; - mk->cache_free = c; + c->next = c; + c->prev = c; } -static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, Uint size) { +static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, Uint size, Uint flags) { cache_t *c; ERTS_DBG_MK_CHK_THR_ACCESS(mk); - if (mk->cache_free && MAP_IS_ALIGNED(seg)) { - if (IS_2POW(size)) { + ASSERT(!MSEG_FLG_IS_2POW(flags) || (MSEG_FLG_IS_2POW(flags) && MAP_IS_ALIGNED(seg) && IS_2POW(size))); + + /* The idea is that sbc caching is prefered over mbc caching. + * Blocks are normally allocated in mb carriers and thus cached there. + * Large blocks has no such cache and it is up to mseg to cache them to speed things up. + */ + + if (!erts_circleq_is_empty(&(mk->cache_free))) { + + /* We have free slots, use one to cache the segment */ + + c = erts_circleq_head(&(mk->cache_free)); + erts_circleq_remove(c); + + c->seg = seg; + c->size = size; + + if (MSEG_FLG_IS_2POW(flags)) { int ix = SIZE_TO_CACHE_AREA_IDX(size); ASSERT(ix < CACHE_AREAS); ASSERT((1 << (ix + MSEG_ALIGN_BITS)) == size); - /* unlink from free cache list */ - c = mseg_cache_alloc_descriptor(mk); + erts_circleq_push_head(&(mk->cache_powered_node[ix]), c); - /* link to cache area */ - c->seg = seg; - c->size = size; - c->next = mk->cache_area[ix]; + } else + erts_circleq_push_head(&(mk->cache_unpowered_node), c); - mk->cache_area[ix] = c; - mk->cache_size++; + mk->cache_size++; + ASSERT(mk->cache_size <= mk->ma->max_cache_size); - ASSERT(mk->cache_size <= mk->ma->max_cache_size); + return 1; + } else if (!MSEG_FLG_IS_2POW(flags) && !erts_circleq_is_empty(&(mk->cache_unpowered_node))) { - return 1; - } else { - /* unlink from free cache list */ - c = mseg_cache_alloc_descriptor(mk); + /* No free slots. + * Evict oldest slot from unpowered cache so we can cache an unpowered (sbc) segment */ + + c = erts_circleq_tail(&(mk->cache_unpowered_node)); + erts_circleq_remove(c); + + mseg_destroy(mk->ma, mk, c->seg, c->size); + mseg_cache_clear_node(c); + + c->seg = seg; + c->size = size; + + erts_circleq_push_head(&(mk->cache_unpowered_node), c); + + return 1; + } else if (!MSEG_FLG_IS_2POW(flags)) { + + /* No free slots and no unpowered (sbc) slots. + * Evict smallest slot from powered cache so we can cache an unpowered (sbc) segment. + * Note: Though this is the wanted policy I don't think it is used significantly. + * This branch could probably be removed without serious impact. + */ + + int i; + + for( i = 0; i < CACHE_AREAS; i++) { + if (erts_circleq_is_empty(&(mk->cache_powered_node[i]))) + continue; + + c = erts_circleq_tail(&(mk->cache_powered_node[i])); + erts_circleq_remove(c); + + mseg_destroy(mk->ma, mk, c->seg, c->size); + mseg_cache_clear_node(c); - /* link to cache area */ c->seg = seg; c->size = size; - c->next = mk->cache_unpowered; - - mk->cache_unpowered = c; - mk->cache_size++; - ASSERT(mk->cache_size <= mk->ma->max_cache_size); + erts_circleq_push_head(&(mk->cache_unpowered_node), c); return 1; } @@ -585,90 +614,110 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, Uint size) { return 0; } -static ERTS_INLINE void *cache_get_segment(MemKind *mk, Uint *size_p) { +static ERTS_INLINE void *cache_get_segment(MemKind *mk, Uint *size_p, Uint flags) { Uint size = *size_p; ERTS_DBG_MK_CHK_THR_ACCESS(mk); - if (IS_2POW(size)) { + + if (MSEG_FLG_IS_2POW(flags)) { int i, ix = SIZE_TO_CACHE_AREA_IDX(size); void *seg; cache_t *c; Uint csize; + ASSERT(IS_2POW(size)); + for( i = ix; i < CACHE_AREAS; i++) { - if ((c = mk->cache_area[i]) == NULL) + if (erts_circleq_is_empty(&(mk->cache_powered_node[i]))) continue; + c = erts_circleq_head(&(mk->cache_powered_node[i])); + erts_circleq_remove(c); + ASSERT(IS_2POW(c->size)); + ASSERT(MAP_IS_ALIGNED(c->seg)); + + csize = c->size; + seg = c->seg; - /* unlink from cache area */ - csize = c->size; - seg = c->seg; - mk->cache_area[i] = c->next; - c->next = NULL; mk->cache_size--; mk->cache_hits++; /* link to free cache list */ - mseg_cache_free_descriptor(mk, c); + mseg_cache_clear_node(c); + erts_circleq_push_head(&(mk->cache_free), c); ASSERT(!(mk->cache_size < 0)); - /* divvy up the cache - if needed */ - while( i > ix) { - csize = csize >> 1; - /* try to cache half of it */ - if (!cache_bless_segment(mk, (char *)seg + csize, csize)) { - /* wouldn't cache .. destroy it instead */ - mseg_destroy(mk->ma, mk, (char *)seg + csize, csize); - } - i--; + if (csize != size) { + mseg_destroy(mk->ma, mk, (char *)seg + size, csize - size); } - ASSERT(csize == size); + return seg; } } - else if (mk->cache_unpowered) { + else if (!erts_circleq_is_empty(&(mk->cache_unpowered_node))) { void *seg; - cache_t *c, *pc; + cache_t *c; + cache_t *best = NULL; + Uint bdiff = 0; Uint csize; Uint bad_max_abs = mk->ma->abs_max_cache_bad_fit; Uint bad_max_rel = mk->ma->rel_max_cache_bad_fit; - c = mk->cache_unpowered; - pc = c; - - while (c) { + erts_circleq_foreach(c, &(mk->cache_unpowered_node)) { csize = c->size; - if (csize >= size && - ((csize - size)*100 < bad_max_rel*size) && - (csize - size) < bad_max_abs ) { + if (csize >= size) { + if (((csize - size)*100 < bad_max_rel*size) && (csize - size) < bad_max_abs ) { - /* unlink from cache area */ - seg = c->seg; + seg = c->seg; - if (pc == c) { - mk->cache_unpowered = c->next; - } else { - pc->next = c->next; - } + erts_circleq_remove(c); + + mk->cache_size--; + mk->cache_hits++; - c->next = NULL; - mk->cache_size--; - mk->cache_hits++; + mseg_cache_clear_node(c); + erts_circleq_push_head(&(mk->cache_free), c); - /* link to free cache list */ - mseg_cache_free_descriptor(mk, c); - *size_p = csize; + *size_p = csize; - return seg; + return seg; + + } else if (!best || (csize - size) < bdiff) { + best = c; + bdiff = csize - size; + } } + } + + /* No cached segment met our criteria, use the best one found and trim it */ + + if (best) { + + seg = best->seg; + csize = best->size; + + ASSERT(best->seg); + ASSERT(best->size > 0); + + mk->cache_hits++; + + /* Use current cache placement for remaining segment space */ + + best->seg = seg + size; + best->size = csize - size; + + ASSERT((size % GET_PAGE_SIZE) == 0); + ASSERT((best->size % GET_PAGE_SIZE) == 0); + + *size_p = size; + + return seg; - pc = c; - c = c->next; } } return NULL; @@ -679,20 +728,18 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, Uint *size_p) { * using callbacks from aux-work in the scheduler. */ -static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, cache_t **head) { +static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, cache_t *head) { cache_t *c = NULL; - c = *head; - - ASSERT( c != NULL ); - - *head = c->next; + c = erts_circleq_tail(head); + erts_circleq_remove(c); if (erts_mtrace_enabled) erts_mtrace_crr_free(SEGTYPE, SEGTYPE, c->seg); mseg_destroy(mk->ma, mk, c->seg, c->size); - mseg_cache_free_descriptor(mk, c); + mseg_cache_clear_node(c); + erts_circleq_push_head(&(mk->cache_free), c); mk->segments.current.watermark--; mk->cache_size--; @@ -702,30 +749,27 @@ static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, cache_t ** return mk->cache_size; } -static ERTS_INLINE Uint mseg_drop_memkind_cache_size(MemKind *mk, cache_t **head) { - cache_t *c = NULL, *next = NULL; - - c = *head; - ASSERT( c != NULL ); +static ERTS_INLINE Uint mseg_drop_memkind_cache_size(MemKind *mk, cache_t *head) { + cache_t *c = NULL; - while (c) { + while (!erts_circleq_is_empty(head)) { - next = c->next; + c = erts_circleq_tail(head); + erts_circleq_remove(c); if (erts_mtrace_enabled) erts_mtrace_crr_free(SEGTYPE, SEGTYPE, c->seg); mseg_destroy(mk->ma, mk, c->seg, c->size); - mseg_cache_free_descriptor(mk, c); + + mseg_cache_clear_node(c); + erts_circleq_push_head(&(mk->cache_free), c); mk->segments.current.watermark--; mk->cache_size--; - c = next; } - *head = NULL; - ASSERT( mk->cache_size >= 0 ); return mk->cache_size; @@ -743,12 +787,12 @@ static Uint mseg_check_memkind_cache(MemKind *mk) { ERTS_DBG_MK_CHK_THR_ACCESS(mk); for (i = 0; i < CACHE_AREAS; i++) { - if (mk->cache_area[i] != NULL) - return mseg_drop_one_memkind_cache_size(mk, &(mk->cache_area[i])); + if (!erts_circleq_is_empty(&(mk->cache_powered_node[i]))) + return mseg_drop_one_memkind_cache_size(mk, &(mk->cache_powered_node[i])); } - if (mk->cache_unpowered) - return mseg_drop_one_memkind_cache_size(mk, &(mk->cache_unpowered)); + if (!erts_circleq_is_empty(&(mk->cache_unpowered_node))) + return mseg_drop_one_memkind_cache_size(mk, &(mk->cache_unpowered_node)); return 0; } @@ -804,17 +848,17 @@ static void mseg_clear_memkind_cache(MemKind *mk) { /* drop pow2 caches */ for (i = 0; i < CACHE_AREAS; i++) { - if (mk->cache_area[i] == NULL) + if (erts_circleq_is_empty(&(mk->cache_powered_node[i]))) continue; - mseg_drop_memkind_cache_size(mk, &(mk->cache_area[i])); - ASSERT(mk->cache_area[i] == NULL); + mseg_drop_memkind_cache_size(mk, &(mk->cache_powered_node[i])); + ASSERT(erts_circleq_is_empty(&(mk->cache_powered_node[i]))); } /* drop varied caches */ - if(mk->cache_unpowered) - mseg_drop_memkind_cache_size(mk, &(mk->cache_unpowered)); + if (!erts_circleq_is_empty(&(mk->cache_unpowered_node))) + mseg_drop_memkind_cache_size(mk, &(mk->cache_unpowered_node)); - ASSERT(mk->cache_unpowered == NULL); + ASSERT(erts_circleq_is_empty(&(mk->cache_unpowered_node))); ASSERT(mk->cache_size == 0); } @@ -873,7 +917,7 @@ mseg_alloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, Uint *size_p, ma->min_seg_size = size; #endif - if (opt->cache && mk->cache_size > 0 && (seg = cache_get_segment(mk, &size)) != NULL) + if (opt->cache && mk->cache_size > 0 && (seg = cache_get_segment(mk, &size, flags)) != NULL) goto done; if ((seg = mseg_create(ma, mk, size)) == NULL) @@ -894,14 +938,13 @@ done: static void mseg_dealloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, Uint size, - const ErtsMsegOpt_t *opt) + Uint flags, const ErtsMsegOpt_t *opt) { MemKind* mk = memkind(ma, opt); - ERTS_MSEG_DEALLOC_STAT(mk,size); - if (opt->cache && cache_bless_segment(mk, seg, size)) { + if (opt->cache && cache_bless_segment(mk, seg, size, flags)) { schedule_cache_check(ma); goto done; } @@ -934,7 +977,7 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, /* Dealloc old segment if new segment is of size 0 */ if (!(*new_size_p)) { - mseg_dealloc(ma, atype, seg, old_size, opt); + mseg_dealloc(ma, atype, seg, old_size, flags, opt); DEC_CC(ma, dealloc); return NULL; } @@ -993,7 +1036,7 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, else { if (!opt->preserv) { - mseg_dealloc(ma, atype, seg, old_size, opt); + mseg_dealloc(ma, atype, seg, old_size, flags, opt); new_seg = mseg_alloc(ma, atype, &new_size, flags, opt); ASSERT(MAP_IS_ALIGNED(new_seg) || !new_seg); } @@ -1020,7 +1063,7 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, new_size = old_size; else { sys_memcpy(((char *) new_seg), ((char *) seg), MIN(new_size, old_size)); - mseg_dealloc(ma, atype, seg, old_size, opt); + mseg_dealloc(ma, atype, seg, old_size, flags, opt); } #endif } @@ -1497,19 +1540,19 @@ erts_mseg_alloc(ErtsAlcType_t atype, Uint *size_p, Uint flags) void erts_mseg_dealloc_opt(ErtsAlcType_t atype, void *seg, - Uint size, const ErtsMsegOpt_t *opt) + Uint size, Uint flags, const ErtsMsegOpt_t *opt) { ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_OPT(opt); ERTS_MSEG_LOCK(ma); ERTS_DBG_MA_CHK_THR_ACCESS(ma); - mseg_dealloc(ma, atype, seg, size, opt); + mseg_dealloc(ma, atype, seg, size, flags, opt); ERTS_MSEG_UNLOCK(ma); } void -erts_mseg_dealloc(ErtsAlcType_t atype, void *seg, Uint size) +erts_mseg_dealloc(ErtsAlcType_t atype, void *seg, Uint size, Uint flags) { - erts_mseg_dealloc_opt(atype, seg, size, &erts_mseg_default_opt); + erts_mseg_dealloc_opt(atype, seg, size, flags, &erts_mseg_default_opt); } void * @@ -1556,27 +1599,28 @@ erts_mseg_unit_size(void) return MSEG_ALIGNED_SIZE; } + static void mem_kind_init(ErtsMsegAllctr_t *ma, MemKind* mk, const char* name) { int i; + /* Clear all cache headers */ + mseg_cache_clear_node(&(mk->cache_free)); + mseg_cache_clear_node(&(mk->cache_unpowered_node)); + for (i = 0; i < CACHE_AREAS; i++) { - mk->cache_area[i] = NULL; + mseg_cache_clear_node(&(mk->cache_powered_node[i])); } - mk->cache_free = NULL; + /* Populate cache free list */ ASSERT(ma->max_cache_size <= MAX_CACHE_SIZE); for (i = 0; i < ma->max_cache_size; i++) { - mk->cache[i].seg = NULL; - mk->cache[i].size = 0; - mk->cache[i].next = mk->cache_free; - mk->cache_free = &(mk->cache[i]); + mseg_cache_clear_node(&(mk->cache[i])); + erts_circleq_push_head(&(mk->cache_free), &(mk->cache[i])); } - mk->cache_unpowered = NULL; - mk->cache_size = 0; mk->cache_hits = 0; @@ -1594,9 +1638,6 @@ static void mem_kind_init(ErtsMsegAllctr_t *ma, MemKind* mk, const char* name) ma->mk_list = mk; } - - - void erts_mseg_init(ErtsMsegInit_t *init) { @@ -1721,7 +1762,7 @@ erts_mseg_test(unsigned long op, case 0x401: return (unsigned long) erts_mseg_alloc(ERTS_ALC_A_INVALID, (Uint *) a1, (Uint) 0); case 0x402: - erts_mseg_dealloc(ERTS_ALC_A_INVALID, (void *) a1, (Uint) a2); + erts_mseg_dealloc(ERTS_ALC_A_INVALID, (void *) a1, (Uint) a2, (Uint) 0); return (unsigned long) 0; case 0x403: return (unsigned long) erts_mseg_realloc(ERTS_ALC_A_INVALID, diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h index 3d0b0f0355..3cab9e18da 100644 --- a/erts/emulator/sys/common/erl_mseg.h +++ b/erts/emulator/sys/common/erl_mseg.h @@ -74,7 +74,7 @@ typedef struct { { \ 4*1024*1024, /* amcbf: Absolute max cache bad fit */ \ 20, /* rmcbf: Relative max cache bad fit */ \ - 5, /* mcs: Max cache size */ \ + 10, /* mcs: Max cache size */ \ 1000 /* cci: Cache check interval */ \ } @@ -93,11 +93,10 @@ extern const ErtsMsegOpt_t erts_mseg_default_opt; void *erts_mseg_alloc(ErtsAlcType_t, Uint *, Uint); void *erts_mseg_alloc_opt(ErtsAlcType_t, Uint *, Uint, const ErtsMsegOpt_t *); -void erts_mseg_dealloc(ErtsAlcType_t, void *, Uint); -void erts_mseg_dealloc_opt(ErtsAlcType_t, void *, Uint, const ErtsMsegOpt_t *); +void erts_mseg_dealloc(ErtsAlcType_t, void *, Uint, Uint); +void erts_mseg_dealloc_opt(ErtsAlcType_t, void *, Uint, Uint, const ErtsMsegOpt_t *); void *erts_mseg_realloc(ErtsAlcType_t, void *, Uint, Uint *, Uint); -void *erts_mseg_realloc_opt(ErtsAlcType_t, void *, Uint, Uint *, - Uint, const ErtsMsegOpt_t *); +void *erts_mseg_realloc_opt(ErtsAlcType_t, void *, Uint, Uint *, Uint, const ErtsMsegOpt_t *); void erts_mseg_clear_cache(void); void erts_mseg_cache_check(void); Uint erts_mseg_no( const ErtsMsegOpt_t *); diff --git a/erts/emulator/sys/common/erl_sys_common_misc.c b/erts/emulator/sys/common/erl_sys_common_misc.c index a0cd4cd10a..0b31c125e5 100644 --- a/erts/emulator/sys/common/erl_sys_common_misc.c +++ b/erts/emulator/sys/common/erl_sys_common_misc.c @@ -114,9 +114,9 @@ int erts_get_native_filename_encoding(void) } /* For internal use by sys_double_to_chars_fast() */ -static char* float_first_trailing_zero(char* p) +static char* find_first_trailing_zero(char* p) { - for (--p; *p == '0' && *(p-1) == '0'; --p); + for (; *(p-1) == '0'; --p); if (*(p-1) == '.') ++p; return p; } @@ -127,34 +127,83 @@ sys_double_to_chars(double fp, char *buffer, size_t buffer_size) return sys_double_to_chars_ext(fp, buffer, buffer_size, SYS_DEFAULT_FLOAT_DECIMALS); } +/* Convert float to string using fixed point notation. + * decimals must be >= 0 + * if compact != 0, the trailing 0's will be truncated + */ int -sys_double_to_chars_fast(double f, char *outbuf, int maxlen, int decimals, int compact) +sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals, + int compact) { - enum { - FRAC_SIZE = 52 - , EXP_SIZE = 11 - , EXP_MASK = (1ll << EXP_SIZE) - 1 - , FRAC_MASK = (1ll << FRAC_SIZE) - 1 - , FRAC_MASK2 = (1ll << (FRAC_SIZE + 1)) - 1 - , MAX_FLOAT = 1ll << (FRAC_SIZE+1) + /* Note that some C compilers don't support "static const" propagation + * so we use a defines */ + #define SYS_DOUBLE_RND_CONST 0.55555555555555555 + #define FRAC_SIZE 52 + #define EXP_SIZE 11 + #define EXP_MASK ((1ll << EXP_SIZE) - 1) + #define MAX_DECIMALS (sizeof(cs_sys_double_pow10) \ + / sizeof(cs_sys_double_pow10[0])) + #define FRAC_MASK ((1ll << FRAC_SIZE) - 1) + #define FRAC_MASK2 ((1ll << (FRAC_SIZE + 1)) - 1) + #define MAX_FLOAT (1ll << (FRAC_SIZE+1)) + + static const double cs_sys_double_pow10[] = { + SYS_DOUBLE_RND_CONST / 1ll, + SYS_DOUBLE_RND_CONST / 10ll, + SYS_DOUBLE_RND_CONST / 100ll, + SYS_DOUBLE_RND_CONST / 1000ll, + SYS_DOUBLE_RND_CONST / 10000ll, + SYS_DOUBLE_RND_CONST / 100000ll, + SYS_DOUBLE_RND_CONST / 1000000ll, + SYS_DOUBLE_RND_CONST / 10000000ll, + SYS_DOUBLE_RND_CONST / 100000000ll, + SYS_DOUBLE_RND_CONST / 1000000000ll, + SYS_DOUBLE_RND_CONST / 10000000000ll, + SYS_DOUBLE_RND_CONST / 100000000000ll, + SYS_DOUBLE_RND_CONST / 1000000000000ll, + SYS_DOUBLE_RND_CONST / 10000000000000ll, + SYS_DOUBLE_RND_CONST / 100000000000000ll, + SYS_DOUBLE_RND_CONST / 1000000000000000ll, + SYS_DOUBLE_RND_CONST / 10000000000000000ll, + SYS_DOUBLE_RND_CONST / 100000000000000000ll, + SYS_DOUBLE_RND_CONST / 1000000000000000000ll }; - long long mantissa, int_part, int_part2, frac_part; + long long mantissa, int_part = 0, frac_part = 0; short exp; - int sign, i, n, m, max; - double absf; + int max; + int neg; + double fr; union { long long L; double F; } x; - char c, *p = outbuf; - int digit, roundup; + char *p = buffer; + + if (decimals < 0) + return -1; - x.F = f; + /* Round the number to given decimal places. The number of 5's in the + * SYS_DOUBLE_RND_CONST constant is chosen such that adding any more 5's doesn't + * change the double precision of the number, i.e.: + * 1> term_to_binary(0.55555555555555555, [{minor_version, 1}]). + * <<131,70,63,225,199,28,113,199,28,114>> + * 2> term_to_binary(0.5555555555555555555, [{minor_version, 1}]). + * <<131,70,63,225,199,28,113,199,28,114>> + */ + if (f >= 0) { + neg = 0; + fr = decimals < MAX_DECIMALS ? (f + cs_sys_double_pow10[decimals]) : f; + x.F = fr; + } else { + neg = 1; + fr = decimals < MAX_DECIMALS ? (f - cs_sys_double_pow10[decimals]) : f; + x.F = -fr; + } exp = (x.L >> FRAC_SIZE) & EXP_MASK; mantissa = x.L & FRAC_MASK; - sign = x.L >= 0 ? 1 : -1; + if (exp == EXP_MASK) { if (mantissa == 0) { - if (sign == -1) + if (neg) *p++ = '-'; *p++ = 'i'; *p++ = 'n'; @@ -165,101 +214,79 @@ sys_double_to_chars_fast(double f, char *outbuf, int maxlen, int decimals, int c *p++ = 'n'; } *p = '\0'; - return p - outbuf; + return p - buffer; } exp -= EXP_MASK >> 1; mantissa |= (1ll << FRAC_SIZE); - frac_part = 0; - int_part = 0; - absf = f * sign; - - /* Don't bother with optimizing too large numbers and decimals */ - if (absf > MAX_FLOAT || decimals > maxlen-17) { - int len = erts_snprintf(outbuf, maxlen, "%.*f", decimals, f); - if (len >= maxlen) + + /* Don't bother with optimizing too large numbers or too large precision */ + if (x.F > MAX_FLOAT || decimals >= MAX_DECIMALS) { + int len = erts_snprintf(buffer, buffer_size, "%.*f", decimals, f); + char* p = buffer + len; + if (len >= buffer_size) return -1; - p = outbuf + len; /* Delete trailing zeroes */ if (compact) - p = float_first_trailing_zero(outbuf + len); + p = find_first_trailing_zero(p); *p = '\0'; - return p - outbuf; - } - - if (exp >= FRAC_SIZE) + return p - buffer; + } else if (exp >= FRAC_SIZE) { int_part = mantissa << (exp - FRAC_SIZE); - else if (exp >= 0) { + } else if (exp >= 0) { int_part = mantissa >> (FRAC_SIZE - exp); frac_part = (mantissa << (exp + 1)) & FRAC_MASK2; - } - else /* if (exp < 0) */ + } else /* if (exp < 0) */ { frac_part = (mantissa & FRAC_MASK2) >> -(exp + 1); + } - if (int_part == 0) { - if (sign == -1) + if (!int_part) { + if (neg) *p++ = '-'; *p++ = '0'; } else { - int ret; + int ret, i, n; while (int_part != 0) { - int_part2 = int_part / 10; - *p++ = (char)(int_part - ((int_part2 << 3) + (int_part2 << 1)) + '0'); - int_part = int_part2; + long long j = int_part / 10; + *p++ = (char)(int_part - ((j << 3) + (j << 1)) + '0'); + int_part = j; } - if (sign == -1) + if (neg) *p++ = '-'; /* Reverse string */ - ret = p - outbuf; + ret = p - buffer; for (i = 0, n = ret/2; i < n; i++) { - int j = ret - i - 1; - c = outbuf[i]; - outbuf[i] = outbuf[j]; - outbuf[j] = c; + int j = ret - i - 1; + char c = buffer[i]; + buffer[i] = buffer[j]; + buffer[j] = c; } } - if (decimals != 0) + + if (decimals > 0) { + int i; *p++ = '.'; - max = maxlen - (p - outbuf) - 1 /* leave room for trailing '\0' */; - if (max > decimals) + max = buffer_size - (p - buffer) - 1 /* leave room for trailing '\0' */; + + if (decimals > max) + return -1; /* the number is not large enough to fit in the buffer */ + max = decimals; - for (m = 0; m < max; m++) { - /* frac_part *= 10; */ - frac_part = (frac_part << 3) + (frac_part << 1); - *p++ = (char)((frac_part >> (FRAC_SIZE + 1)) + '0'); - frac_part &= FRAC_MASK2; - } + for (i = 0; i < max; i++) { + /* frac_part *= 10; */ + frac_part = (frac_part << 3) + (frac_part << 1); - roundup = 0; - /* Rounding - look at the next digit */ - frac_part = (frac_part << 3) + (frac_part << 1); - digit = (frac_part >> (FRAC_SIZE + 1)); - if (digit > 5) - roundup = 1; - else if (digit == 5) { - frac_part &= FRAC_MASK2; - if (frac_part != 0) roundup = 1; - } - if (roundup) { - char d; - int pos = p - outbuf - 1; - do { - d = outbuf[pos]; - if (d == '-') break; - if (d == '.') continue; - if (++d != ':') { - outbuf[pos] = d; - break; - } - outbuf[pos] = '0'; - } while (--pos); + *p++ = (char)((frac_part >> (FRAC_SIZE + 1)) + '0'); + frac_part &= FRAC_MASK2; + } + + /* Delete trailing zeroes */ + if (compact) + p = find_first_trailing_zero(p); } - /* Delete trailing zeroes */ - if (compact && *(p - 1) == '0') - p = float_first_trailing_zero(--p); *p = '\0'; - return p - outbuf; + return p - buffer; } diff --git a/erts/emulator/sys/common/erl_util_queue.h b/erts/emulator/sys/common/erl_util_queue.h new file mode 100644 index 0000000000..47925e2264 --- /dev/null +++ b/erts/emulator/sys/common/erl_util_queue.h @@ -0,0 +1,77 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2013. 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 ERL_UTIL_QUEUE_H_ +#define ERL_UTIL_QUEUE_H_ + +#define erts_circleq_head(Q) ((Q)->next) +#define erts_circleq_tail(Q) ((Q)->prev) +#define erts_circleq_next(Q) ((Q)->next) +#define erts_circleq_prev(Q) ((Q)->prev) +#define erts_circleq_is_empty(Q) ((Q)->next == (void *)(Q)) + +#define erts_circleq_remove(N) \ + do { \ + (N)->next->prev = (N)->prev; \ + (N)->prev->next = (N)->next; \ + (N)->next = (N); \ + (N)->prev = (N); \ + } while(0) + +#define erts_circleq_pop_head(Q, N) \ + do { \ + (N) = (Q)->next; \ + (N)->next->prev = (N)->prev; \ + (N)->prev->next = (N)->next; \ + (N)->next = (N); \ + (N)->prev = (N); \ + } while(0) + +#define erts_circleq_pop_tail(Q, N) \ + do { \ + (N) = (Q)->prev; \ + (N)->next->prev = (N)->prev; \ + (N)->prev->next = (N)->next; \ + (N)->next = (N); \ + (N)->prev = (N); \ + } while(0) + +#define erts_circleq_push_head(Q, N) \ + do { \ + (N)->next = (Q)->next; \ + (N)->prev = (void *)(Q); \ + (Q)->next->prev = (N); \ + (Q)->next = (N); \ + } while(0) + +#define erts_circleq_push_tail(Q, N) \ + do { \ + (N)->prev = (Q)->prev; \ + (N)->next = (void *)(Q); \ + (Q)->prev->next = (N); \ + (Q)->prev = (N); \ + } while(0) + +#define erts_circleq_foreach(V, Q) \ + for ((V) = (Q)->next; (V) != (const void *)(Q); (V) = (V)->next) + +#define erts_circleq_foreach_reverse(V, Q) \ + for ((V) = (Q)->prev; (V) != (const void *)(Q); (V) = (V)->prev) + +#endif diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 6bd7361612..0a9d997c3b 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -36,7 +36,7 @@ threading/1, send/1, send2/1, send3/1, send_threaded/1, neg/1, is_checks/1, get_length/1, make_atom/1, make_string/1, reverse_list_test/1, - otp_9668/1 + otp_9668/1, consume_timeslice/1 ]). -export([many_args_100/100]). @@ -63,7 +63,7 @@ all() -> resource_takeover, threading, send, send2, send3, send_threaded, neg, is_checks, get_length, make_atom, make_string,reverse_list_test, - otp_9668 + otp_9668, consume_timeslice ]. groups() -> @@ -1259,6 +1259,108 @@ otp_9668(Config) -> ?line verify_tmpmem(TmpMem), ok. +consume_timeslice(Config) when is_list(Config) -> + CONTEXT_REDS = 2000, + Me = self(), + Go = make_ref(), + RedDiff = make_ref(), + Done = make_ref(), + DummyMFA = {?MODULE,dummy_call,1}, + P = spawn(fun () -> + receive Go -> ok end, + {reductions, R1} = process_info(self(), reductions), + 1 = consume_timeslice_nif(100, false), + dummy_call(111), + 0 = consume_timeslice_nif(90, false), + dummy_call(222), + 1 = consume_timeslice_nif(10, false), + dummy_call(333), + 0 = consume_timeslice_nif(25, false), + 0 = consume_timeslice_nif(25, false), + 0 = consume_timeslice_nif(25, false), + 1 = consume_timeslice_nif(25, false), + 0 = consume_timeslice_nif(25, false), + + ok = case consume_timeslice_nif(1, true) of + Cnt when Cnt > 70, Cnt < 80 -> ok; + Other -> Other + end, + dummy_call(444), + + {reductions, R2} = process_info(self(), reductions), + Me ! {RedDiff, R2 - R1}, + exit(Done) + end), + erlang:yield(), + + erlang:trace_pattern(DummyMFA, [], [local]), + ?line 1 = erlang:trace(P, true, [call, running, procs, {tracer, self()}]), + + P ! Go, + + %% receive Go -> ok end, + ?line {trace, P, in, _} = next_tmsg(P), + + %% consume_timeslice_nif(100), + %% dummy_call(111) + ?line {trace, P, out, _} = next_tmsg(P), + ?line {trace, P, in, _} = next_tmsg(P), + ?line {trace, P, call, {?MODULE,dummy_call,[111]}} = next_tmsg(P), + + %% consume_timeslice_nif(90), + %% dummy_call(222) + ?line {trace, P, call, {?MODULE,dummy_call,[222]}} = next_tmsg(P), + + %% consume_timeslice_nif(10), + %% dummy_call(333) + ?line {trace, P, out, _} = next_tmsg(P), + ?line {trace, P, in, _} = next_tmsg(P), + ?line {trace, P, call, {?MODULE,dummy_call,[333]}} = next_tmsg(P), + + %% 25,25,25,25, 25 + ?line {trace, P, out, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P), + ?line {trace, P, in, {?MODULE,consume_timeslice_nif,2}} = next_tmsg(P), + + %% consume_timeslice(1,true) + %% dummy_call(444) + ?line {trace, P, out, DummyMFA} = next_tmsg(P), + ?line {trace, P, in, DummyMFA} = next_tmsg(P), + ?line {trace, P, call, {?MODULE,dummy_call,[444]}} = next_tmsg(P), + + %% exit(Done) + ?line {trace, P, exit, Done} = next_tmsg(P), + + ExpReds = (100 + 90 + 10 + 25*5 + 75) * CONTEXT_REDS div 100, + receive + {RedDiff, Reductions} when Reductions < (ExpReds + 10), Reductions > (ExpReds - 10) -> + io:format("Reductions = ~p~n", [Reductions]), + ok; + {RedDiff, Reductions} -> + ?t:fail({unexpected_reduction_count, Reductions}) + end, + + none = next_msg(P), + + ok. + +next_msg(Pid) -> + receive + M -> M + after 100 -> + none + end. + +next_tmsg(Pid) -> + receive TMsg when is_tuple(TMsg), + element(1, TMsg) == trace, + element(2, TMsg) == Pid -> + TMsg + after 100 -> + none + end. + +dummy_call(_) -> + ok. tmpmem() -> case erlang:system_info({allocator,temp_alloc}) of @@ -1370,6 +1472,7 @@ reverse_list(_) -> ?nif_stub. echo_int(_) -> ?nif_stub. type_sizes() -> ?nif_stub. otp_9668_nif(_) -> ?nif_stub. +consume_timeslice_nif(_,_) -> ?nif_stub. nif_stub_error(Line) -> exit({nif_not_loaded,module,?MODULE,line,Line}). diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 03092fef5e..2504d24b51 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1456,6 +1456,27 @@ static ERL_NIF_TERM otp_9668_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar return atom_ok; } +static ERL_NIF_TERM consume_timeslice_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int percent; + char atom[10]; + int do_repeat; + + if (!enif_get_int(env, argv[0], &percent) || + !enif_get_atom(env, argv[1], atom, sizeof(atom), ERL_NIF_LATIN1)) { + return enif_make_badarg(env); + } + if (strcmp(atom , "true") == 0) { + int cnt = 1; + while (enif_consume_timeslice(env, percent) == 0 && cnt < 200) + cnt++; + return enif_make_int(env, cnt); + } + else { + return enif_make_int(env, enif_consume_timeslice(env, percent)); + } +} + static ErlNifFunc nif_funcs[] = { {"lib_version", 0, lib_version}, @@ -1504,7 +1525,8 @@ static ErlNifFunc nif_funcs[] = {"reverse_list",1, reverse_list}, {"echo_int", 1, echo_int}, {"type_sizes", 0, type_sizes}, - {"otp_9668_nif", 1, otp_9668_nif} + {"otp_9668_nif", 1, otp_9668_nif}, + {"consume_timeslice_nif", 2, consume_timeslice_nif} }; ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload) diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl index bf33c337ee..c625e655ce 100644 --- a/erts/emulator/test/num_bif_SUITE.erl +++ b/erts/emulator/test/num_bif_SUITE.erl @@ -128,10 +128,17 @@ t_float_to_list(Config) when is_list(Config) -> "-1.00000000000000000000e+00" = float_to_list(-1.0, []), "-1.00000000000000000000" = float_to_list(-1.0, [{decimals, 20}]), {'EXIT', {badarg, _}} = (catch float_to_list(1.0, [{decimals, -1}])), - {'EXIT', {badarg, _}} = (catch float_to_list(1.0, [{decimals, 250}])), + {'EXIT', {badarg, _}} = (catch float_to_list(1.0, [{decimals, 254}])), + {'EXIT', {badarg, _}} = (catch float_to_list(1.0, [{scientific, 250}])), {'EXIT', {badarg, _}} = (catch float_to_list(1.0e+300, [{decimals, 1}])), "1.0e+300" = float_to_list(1.0e+300, [{scientific, 1}]), - "1.0" = float_to_list(1.0, [{decimals, 249}, compact]), + "1.0" = float_to_list(1.0, [{decimals, 249}, compact]), + "1" = float_to_list(1.0, [{decimals, 0}]), + "2" = float_to_list(1.9, [{decimals, 0}]), + "123456789012345680.0" = erlang:float_to_list( + 123456789012345678.0, [{decimals, 236}, compact]), + {'EXIT', {badarg, _}} = (catch float_to_list( + 123456789012345678.0, [{decimals, 237}])), Expected = "1." ++ string:copies("0", 249) ++ "e+00", Expected = float_to_list(1.0, [{scientific, 249}, compact]), @@ -139,7 +146,8 @@ t_float_to_list(Config) when is_list(Config) -> X2 = float_to_list(1.0, [{scientific, 20}]), X1 = X2, "1.000e+00" = float_to_list(1.0, [{scientific, 3}]), - "1.000" = float_to_list(1.0, [{decimals, 3}]), + "1.000" = float_to_list(1.0, [{decimals, 3}]), + "1.0" = float_to_list(1.0, [{decimals, 1}]), "1.0" = float_to_list(1.0, [{decimals, 3}, compact]), "1.12" = float_to_list(1.123, [{decimals, 2}]), "1.123" = float_to_list(1.123, [{decimals, 3}]), @@ -148,6 +156,14 @@ t_float_to_list(Config) when is_list(Config) -> "1.12300" = float_to_list(1.123, [{decimals, 5}]), "1.123" = float_to_list(1.123, [{decimals, 5}, compact]), "1.1234" = float_to_list(1.1234,[{decimals, 6}, compact]), + "1.01" = float_to_list(1.005, [{decimals, 2}]), + "-1.01" = float_to_list(-1.005,[{decimals, 2}]), + "0.999" = float_to_list(0.999, [{decimals, 3}]), + "-0.999" = float_to_list(-0.999,[{decimals, 3}]), + "1.0" = float_to_list(0.999, [{decimals, 2}, compact]), + "-1.0" = float_to_list(-0.999,[{decimals, 2}, compact]), + "0.5" = float_to_list(0.5, [{decimals, 1}]), + "-0.5" = float_to_list(-0.5, [{decimals, 1}]), "2.333333" = erlang:float_to_list(7/3, [{decimals, 6}, compact]), "2.333333" = erlang:float_to_list(7/3, [{decimals, 6}]), "0.00000000000000000000e+00" = float_to_list(0.0, [compact]), diff --git a/erts/emulator/utils/gen_git_version b/erts/emulator/utils/gen_git_version new file mode 100755 index 0000000000..d93a97cbbb --- /dev/null +++ b/erts/emulator/utils/gen_git_version @@ -0,0 +1,40 @@ +#!/bin/sh + +OUTPUT_FILE=$1 + +if command -v git 2>&1 >/dev/null && + test -d $ERL_TOP/.git -o -f $ERL_TOP/.git +then + VSN=`git describe --match "OTP_R[0-9][0-9][A-B]*" HEAD` + case "$VSN" in + OTP_R*-g*) + VSN=`echo $VSN | sed -e 's/.*-g\\(.*\\)/\\1/g'` ;; + *) VSN="na" ;; + esac +else + VSN="na" +fi + + +# Only update the file if there has been a change to +# the version number. +if test -r $OUTPUT_FILE +then + VC=`sed -n -e 's/^.*"\\\\"\\(.*\\)\\\\"".*/\\1/p' < $OUTPUT_FILE` +else + VC=unset +fi +echo "VSN = $VSN" +echo "VC = $VC" +if test "$VSN" != "$VC" +then + echo "# Automatically generated by $0 - DO NOT EDIT." > $OUTPUT_FILE + if test "$VSN" = "na" + then + echo "# GIT_VSN=-DERLANG_GIT_VERSION=\"\\\"$VSN\\\"\"" >> $OUTPUT_FILE + else + echo "GIT_VSN=-DERLANG_GIT_VERSION=\"\\\"$VSN\\\"\"" >> $OUTPUT_FILE + fi + exit 0 +fi +exit 1
\ No newline at end of file diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c index 384b1b1ad7..00df3f068f 100644 --- a/erts/lib_src/common/erl_printf_format.c +++ b/erts/lib_src/common/erl_printf_format.c @@ -335,7 +335,7 @@ static int fmt_double(fmtfn_t fn,void*arg,double val, int fi = 0; char format_str[7]; char sbuf[32]; - char *bufp; + char *bufp = sbuf; double dexp; int exp; size_t max_size = 1; @@ -425,12 +425,12 @@ static int fmt_double(fmtfn_t fn,void*arg,double val, max_size++; /* '\0' */ - if (max_size < sizeof(sbuf)) - bufp = sbuf; - else { + if (max_size >= sizeof(sbuf)) { bufp = (char *) malloc(sizeof(char)*max_size); if (!bufp) { res = -ENOMEM; + /* Make sure not to trigger free */ + bufp = sbuf; goto out; } } @@ -448,10 +448,10 @@ static int fmt_double(fmtfn_t fn,void*arg,double val, res = fmt_fld(fn, arg, bufp, size, 0, width, 0, new_fmt, count); + out: if (bufp != sbuf) free((void *) bufp); - out: if (erts_printf_unblock_fpe) (*erts_printf_unblock_fpe)(fpe_was_unmasked); return res; |