aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/sys/common
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/sys/common')
-rw-r--r--erts/emulator/sys/common/erl_check_io.c4
-rw-r--r--erts/emulator/sys/common/erl_mmap.c103
-rw-r--r--erts/emulator/sys/common/erl_mmap.h16
-rw-r--r--erts/emulator/sys/common/erl_mseg.c10
-rw-r--r--erts/emulator/sys/common/erl_mseg.h2
-rw-r--r--erts/emulator/sys/common/erl_osenv.c396
-rw-r--r--erts/emulator/sys/common/erl_osenv.h121
-rw-r--r--erts/emulator/sys/common/erl_poll.c18
-rw-r--r--erts/emulator/sys/common/erl_sys_common_misc.c195
9 files changed, 639 insertions, 226 deletions
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index 8928529984..3e77dce1cd 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2006-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2006-2018. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -885,7 +885,7 @@ enif_select(ErlNifEnv* env,
ErtsDrvSelectDataState *free_select = NULL;
ErtsNifSelectDataState *free_nif = NULL;
- ASSERT(!(resource->monitors && resource->monitors->is_dying));
+ ASSERT(!resource->monitors);
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
if (!grow_drv_ev_state(fd)) {
diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c
index a9c6e72c5f..5dadd8a5a6 100644
--- a/erts/emulator/sys/common/erl_mmap.c
+++ b/erts/emulator/sys/common/erl_mmap.c
@@ -296,11 +296,10 @@ typedef struct {
}ErtsFreeSegMap;
struct ErtsMemMapper_ {
- int (*reserve_physical)(char *, UWord, int exec);
+ int (*reserve_physical)(char *, UWord);
void (*unreserve_physical)(char *, UWord);
int supercarrier;
int no_os_mmap;
- int executable; /* is client a native code allocator? */
/*
* Super unaligned area is located above super aligned
* area. That is, `sa.bot` is beginning of the super
@@ -358,10 +357,6 @@ char* erts_literals_start;
UWord erts_literals_size;
#endif
-#ifdef ERTS_HAVE_EXEC_MMAPPER
-ErtsMemMapper erts_exec_mmapper;
-#endif
-
#define ERTS_MMAP_SIZE_SC_SA_INC(SZ) \
do { \
@@ -1240,7 +1235,6 @@ Eterm build_free_seg_list(Process* p, ErtsFreeSegMap* map)
#if HAVE_MMAP
# define ERTS_MMAP_PROT (PROT_READ|PROT_WRITE)
-# define ERTS_MMAP_PROT_EXEC (PROT_READ|PROT_WRITE|PROT_EXEC)
# if defined(MAP_ANONYMOUS)
# define ERTS_MMAP_FLAGS (MAP_ANON|MAP_PRIVATE)
# define ERTS_MMAP_FD (-1)
@@ -1254,26 +1248,24 @@ Eterm build_free_seg_list(Process* p, ErtsFreeSegMap* map)
#endif
static ERTS_INLINE void *
-os_mmap(void *hint_ptr, UWord size, int try_superalign, int executable)
+os_mmap(void *hint_ptr, UWord size, int try_superalign)
{
#if HAVE_MMAP
- const int prot = executable ? ERTS_MMAP_PROT_EXEC : ERTS_MMAP_PROT;
void *res;
#ifdef MAP_ALIGN
if (try_superalign)
- res = mmap((void *) ERTS_SUPERALIGNED_SIZE, size, prot,
+ res = mmap((void *) ERTS_SUPERALIGNED_SIZE, size, ERTS_MMAP_PROT,
ERTS_MMAP_FLAGS|MAP_ALIGN, ERTS_MMAP_FD, 0);
else
#endif
- res = mmap((void *) hint_ptr, size, prot,
+ res = mmap((void *) hint_ptr, size, ERTS_MMAP_PROT,
ERTS_MMAP_FLAGS, ERTS_MMAP_FD, 0);
if (res == MAP_FAILED)
return NULL;
return res;
#elif HAVE_VIRTUALALLOC
- const DWORD prot = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
return (void *) VirtualAlloc(NULL, (SIZE_T) size,
- MEM_COMMIT|MEM_RESERVE, prot);
+ MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
#else
# error "missing mmap() or similar"
#endif
@@ -1330,7 +1322,6 @@ os_mremap(void *ptr, UWord old_size, UWord new_size, int try_superalign)
#if HAVE_MMAP
#define ERTS_MMAP_RESERVE_PROT (ERTS_MMAP_PROT)
-#define ERTS_MMAP_RESERVE_PROT_EXEC (ERTS_MMAP_PROT_EXEC)
#define ERTS_MMAP_RESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_FIXED)
#define ERTS_MMAP_UNRESERVE_PROT (PROT_NONE)
#if defined(__FreeBSD__)
@@ -1346,10 +1337,9 @@ os_mremap(void *ptr, UWord old_size, UWord new_size, int try_superalign)
#endif /* __FreeBSD__ */
static int
-os_reserve_physical(char *ptr, UWord size, int exec)
+os_reserve_physical(char *ptr, UWord size)
{
- const int prot = exec ? ERTS_MMAP_RESERVE_PROT_EXEC : ERTS_MMAP_RESERVE_PROT;
- void *res = mmap((void *) ptr, (size_t) size, prot,
+ void *res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_RESERVE_PROT,
ERTS_MMAP_RESERVE_FLAGS, ERTS_MMAP_FD, 0);
if (res == (void *) MAP_FAILED)
return 0;
@@ -1366,37 +1356,11 @@ os_unreserve_physical(char *ptr, UWord size)
}
static void *
-os_mmap_virtual(char *ptr, UWord size, int exec)
+os_mmap_virtual(char *ptr, UWord size)
{
int flags = ERTS_MMAP_VIRTUAL_FLAGS;
void* res;
-#ifdef ERTS_ALC_A_EXEC
- if (exec) {
- ASSERT(!ptr);
- /* OTP-19.0: Nice hack below cut-and-pasted from hipe_amd64.c */
-
-# ifdef MAP_32BIT
- /* If we got MAP_32BIT (Linux), then use that to ask for low memory */
- flags |= MAP_32BIT;
-# else
- /* FreeBSD doesn't have MAP_32BIT, and it doesn't respect
- a plain map_hint (returns high mappings even though the
- hint refers to a free area), so we have to use both map_hint
- and MAP_FIXED to get addresses below the 2GB boundary.
- This is even worse than the Linux/ppc64 case.
- Similarly, Solaris 10 doesn't have MAP_32BIT,
- and it doesn't respect a plain map_hint. */
- ptr = (char*)(512*1024*1024); /* 0.5GB */
-
-# if defined(__FreeBSD__) || defined(__sun__)
- flags |= MAP_FIXED;
-# endif
-# endif /* !MAP_32BIT */
- }
-#else /* !ERTS_ALC_A_EXEC */
- ASSERT(!exec);
-#endif
res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_VIRTUAL_PROT,
flags, ERTS_MMAP_FD, 0);
if (res == (void *) MAP_FAILED)
@@ -1411,7 +1375,7 @@ os_mmap_virtual(char *ptr, UWord size, int exec)
#endif /* ERTS_HAVE_OS_MMAP */
-static int reserve_noop(char *ptr, UWord size, int exec)
+static int reserve_noop(char *ptr, UWord size)
{
#ifdef ERTS_MMAP_DEBUG_FILL_AREAS
Uint32 *uip, *end = (Uint32 *) (ptr + size);
@@ -1454,7 +1418,7 @@ alloc_desc_insert_free_seg(ErtsMemMapper* mm,
#if ERTS_HAVE_OS_MMAP
if (!mm->no_os_mmap) {
- ptr = os_mmap(mm->desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0, 0);
+ ptr = os_mmap(mm->desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0);
if (ptr) {
mm->desc.new_area_hint = ptr+ERTS_PAGEALIGNED_SIZE;
ERTS_MMAP_SIZE_OS_INC(ERTS_PAGEALIGNED_SIZE);
@@ -1473,7 +1437,7 @@ alloc_desc_insert_free_seg(ErtsMemMapper* mm,
da_map = &mm->sua.map;
desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE);
if (desc) {
- if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE, 0))
+ if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE))
ERTS_MMAP_SIZE_SC_SUA_INC(ERTS_PAGEALIGNED_SIZE);
else
desc = NULL;
@@ -1483,7 +1447,7 @@ alloc_desc_insert_free_seg(ErtsMemMapper* mm,
da_map = &mm->sa.map;
desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE);
if (desc) {
- if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE, 0))
+ if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE))
ERTS_MMAP_SIZE_SC_SA_INC(ERTS_PAGEALIGNED_SIZE);
else
desc = NULL;
@@ -1544,7 +1508,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
if (desc) {
seg = desc->start;
end = seg+asize;
- if (!mm->reserve_physical(seg, asize, mm->executable))
+ if (!mm->reserve_physical(seg, asize))
goto supercarrier_reserve_failure;
if (desc->end == end) {
delete_free_seg(&mm->sua.map, desc);
@@ -1559,8 +1523,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
}
if (asize <= mm->sua.bot - mm->sa.top) {
- if (!mm->reserve_physical(mm->sua.bot - asize, asize,
- mm->executable))
+ if (!mm->reserve_physical(mm->sua.bot - asize, asize))
goto supercarrier_reserve_failure;
mm->sua.bot -= asize;
seg = mm->sua.bot;
@@ -1576,8 +1539,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
char *start = seg = desc->start;
seg = (char *) ERTS_SUPERALIGNED_CEILING(seg);
end = seg+asize;
- if (!mm->reserve_physical(start, (UWord) (end - start),
- mm->executable))
+ if (!mm->reserve_physical(start, (UWord) (end - start)))
goto supercarrier_reserve_failure;
ERTS_MMAP_SIZE_SC_SA_INC(asize);
if (desc->end == end) {
@@ -1609,8 +1571,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
if (asize + (seg - start) <= mm->sua.bot - start) {
end = seg + asize;
- if (!mm->reserve_physical(start, (UWord) (end - start),
- mm->executable))
+ if (!mm->reserve_physical(start, (UWord) (end - start)))
goto supercarrier_reserve_failure;
mm->sa.top = end;
ERTS_MMAP_SIZE_SC_SA_INC(asize);
@@ -1632,8 +1593,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
seg = (char *) ERTS_SUPERALIGNED_CEILING(org_start);
end = seg + asize;
- if (!mm->reserve_physical(seg, (UWord) (org_end - seg),
- mm->executable))
+ if (!mm->reserve_physical(seg, (UWord) (org_end - seg)))
goto supercarrier_reserve_failure;
ERTS_MMAP_SIZE_SC_SUA_INC(asize);
if (org_start != seg) {
@@ -1666,13 +1626,13 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
/* Map using OS primitives */
if (!(ERTS_MMAPFLG_SUPERCARRIER_ONLY & flags) && !mm->no_os_mmap) {
if (!(ERTS_MMAPFLG_SUPERALIGNED & flags)) {
- seg = os_mmap(NULL, asize, 0, mm->executable);
+ seg = os_mmap(NULL, asize, 0);
if (!seg)
goto failure;
}
else {
asize = ERTS_SUPERALIGNED_CEILING(*sizep);
- seg = os_mmap(NULL, asize, 1, mm->executable);
+ seg = os_mmap(NULL, asize, 1);
if (!seg)
goto failure;
@@ -1682,8 +1642,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep)
os_munmap(seg, asize);
- ptr = os_mmap(NULL, asize + ERTS_SUPERALIGNED_SIZE, 1,
- mm->executable);
+ ptr = os_mmap(NULL, asize + ERTS_SUPERALIGNED_SIZE, 1);
if (!ptr)
goto failure;
@@ -2018,8 +1977,7 @@ erts_mremap(ErtsMemMapper* mm,
if (next && new_end <= next->end) {
if (!mm->reserve_physical(((char *) ptr) + old_size,
- asize - old_size,
- mm->executable))
+ asize - old_size))
goto supercarrier_reserve_failure;
if (new_end < next->end)
resize_free_seg(&mm->sua.map, next, new_end, next->end);
@@ -2037,8 +1995,7 @@ erts_mremap(ErtsMemMapper* mm,
if (end == mm->sa.top) {
if (new_end <= mm->sua.bot) {
if (!mm->reserve_physical(((char *) ptr) + old_size,
- asize - old_size,
- mm->executable))
+ asize - old_size))
goto supercarrier_reserve_failure;
mm->sa.top = new_end;
new_ptr = ptr;
@@ -2050,8 +2007,7 @@ erts_mremap(ErtsMemMapper* mm,
adjacent_free_seg(&mm->sa.map, start, end, &prev, &next);
if (next && new_end <= next->end) {
if (!mm->reserve_physical(((char *) ptr) + old_size,
- asize - old_size,
- mm->executable))
+ asize - old_size))
goto supercarrier_reserve_failure;
if (new_end < next->end)
resize_free_seg(&mm->sa.map, next, new_end, next->end);
@@ -2171,7 +2127,7 @@ static void hard_dbg_mseg_init(void);
#endif
void
-erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
+erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init)
{
static int is_first_call = 1;
int virtual_map = 0;
@@ -2203,7 +2159,6 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
mm->supercarrier = 0;
mm->reserve_physical = reserve_noop;
mm->unreserve_physical = unreserve_noop;
- mm->executable = executable;
#if HAVE_MMAP && !defined(MAP_ANON)
mm->mmap_fd = open("/dev/zero", O_RDWR);
@@ -2225,7 +2180,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
ptr = (char *) ERTS_PAGEALIGNED_CEILING(init->virtual_range.start);
end = (char *) ERTS_PAGEALIGNED_FLOOR(init->virtual_range.end);
sz = end - ptr;
- start = os_mmap_virtual(ptr, sz, executable);
+ start = os_mmap_virtual(ptr, sz);
if (!start || start > ptr || start >= end)
erts_exit(1,
"erts_mmap: Failed to create virtual range for super carrier\n");
@@ -2250,7 +2205,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
sz = ERTS_PAGEALIGNED_CEILING(init->scs);
#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
if (!init->scrpm) {
- start = os_mmap_virtual(NULL, sz, executable);
+ start = os_mmap_virtual(NULL, sz);
mm->reserve_physical = os_reserve_physical;
mm->unreserve_physical = os_unreserve_physical;
virtual_map = 1;
@@ -2262,7 +2217,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
* The whole supercarrier will by physically
* reserved all the time.
*/
- start = os_mmap(NULL, sz, 1, executable);
+ start = os_mmap(NULL, sz, 1);
}
if (!start)
erts_exit(1,
@@ -2336,7 +2291,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
mm->sua.top -= ERTS_PAGEALIGNED_SIZE;
mm->size.supercarrier.used.total += ERTS_PAGEALIGNED_SIZE;
#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
- if (!virtual_map || os_reserve_physical(mm->sua.top, ERTS_PAGEALIGNED_SIZE, 0))
+ if (!virtual_map || os_reserve_physical(mm->sua.top, ERTS_PAGEALIGNED_SIZE))
#endif
add_free_desc_area(mm, mm->sua.top, end);
mm->desc.reserved += (end - mm->sua.top) / sizeof(ErtsFreeSegDesc);
@@ -2349,7 +2304,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable)
* will be used for free segment descritors.
*/
#ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
- if (virtual_map && !os_reserve_physical(start, mm->sa.bot - start, 0))
+ if (virtual_map && !os_reserve_physical(start, mm->sa.bot - start))
erts_exit(1, "erts_mmap: Failed to reserve physical memory for descriptors\n");
#endif
mm->desc.unused_start = start;
diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h
index 2a07d93c8c..c1f9668b18 100644
--- a/erts/emulator/sys/common/erl_mmap.h
+++ b/erts/emulator/sys/common/erl_mmap.h
@@ -93,11 +93,6 @@ typedef struct {
#define ERTS_MMAP_INIT_LITERAL_INITER \
{{NULL, NULL}, {NULL, NULL}, ERTS_LITERAL_VIRTUAL_AREA_SIZE, 1, (1 << 10), 0}
-#define ERTS_HIPE_EXEC_VIRTUAL_AREA_SIZE (UWORD_CONSTANT(512)*1024*1024)
-
-#define ERTS_MMAP_INIT_HIPE_EXEC_INITER \
- {{NULL, NULL}, {NULL, NULL}, ERTS_HIPE_EXEC_VIRTUAL_AREA_SIZE, 1, (1 << 10), 0}
-
#define ERTS_SUPERALIGNED_SIZE \
(1 << ERTS_MMAP_SUPERALIGNED_BITS)
@@ -140,7 +135,7 @@ void *erts_mmap(ErtsMemMapper*, Uint32 flags, UWord *sizep);
void erts_munmap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord size);
void *erts_mremap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord old_size, UWord *sizep);
int erts_mmap_in_supercarrier(ErtsMemMapper*, void *ptr);
-void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*, int executable);
+void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*);
struct erts_mmap_info_struct
{
UWord sizes[6];
@@ -165,15 +160,6 @@ extern ErtsMemMapper erts_dflt_mmapper;
extern ErtsMemMapper erts_literal_mmapper;
# endif
-# if defined(ERTS_ALC_A_EXEC) && defined(__x86_64__)
- /*
- * On x86_64, exec_alloc employs its own super carrier 'erts_exec_mmaper'
- * to ensure low memory for HiPE AMD64 small code model.
- */
-# define ERTS_HAVE_EXEC_MMAPPER
-extern ErtsMemMapper erts_exec_mmapper;
-# endif
-
# endif /* ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION */
#endif /* ERTS_WANT_MEM_MAPPERS */
diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c
index bf6de9b13a..ced3d61525 100644
--- a/erts/emulator/sys/common/erl_mseg.c
+++ b/erts/emulator/sys/common/erl_mseg.c
@@ -1406,15 +1406,9 @@ erts_mseg_init(ErtsMsegInit_t *init)
erts_mtx_init(&init_atoms_mutex, "mseg_init_atoms", NIL,
ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
-#ifdef ERTS_HAVE_EXEC_MMAPPER
- /* Initialize erts_exec_mapper *FIRST*, to increase probability
- * of getting low memory for HiPE AMD64's small code model.
- */
- erts_mmap_init(&erts_exec_mmapper, &init->exec_mmap, 1);
-#endif
- erts_mmap_init(&erts_dflt_mmapper, &init->dflt_mmap, 0);
+ erts_mmap_init(&erts_dflt_mmapper, &init->dflt_mmap);
#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
- erts_mmap_init(&erts_literal_mmapper, &init->literal_mmap, 0);
+ erts_mmap_init(&erts_literal_mmapper, &init->literal_mmap);
#endif
if (!IS_2POW(GET_PAGE_SIZE))
diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h
index bba0dec499..af275c18be 100644
--- a/erts/emulator/sys/common/erl_mseg.h
+++ b/erts/emulator/sys/common/erl_mseg.h
@@ -61,7 +61,6 @@ typedef struct {
Uint nos;
ErtsMMapInit dflt_mmap;
ErtsMMapInit literal_mmap;
- ErtsMMapInit exec_mmap;
} ErtsMsegInit_t;
#define ERTS_MSEG_INIT_DEFAULT_INITIALIZER \
@@ -72,7 +71,6 @@ typedef struct {
1000, /* cci: Cache check interval */ \
ERTS_MMAP_INIT_DEFAULT_INITER, \
ERTS_MMAP_INIT_LITERAL_INITER, \
- ERTS_MMAP_INIT_HIPE_EXEC_INITER \
}
typedef struct {
diff --git a/erts/emulator/sys/common/erl_osenv.c b/erts/emulator/sys/common/erl_osenv.c
new file mode 100644
index 0000000000..9f54d1dff0
--- /dev/null
+++ b/erts/emulator/sys/common/erl_osenv.c
@@ -0,0 +1,396 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2017. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#include "erl_osenv.h"
+
+#include "global.h"
+#include "erl_alloc.h"
+#include "erl_process.h"
+
+#define STACKBUF_SIZE (512)
+
+typedef struct __env_rbtnode_t {
+ struct __env_rbtnode_t *parent;
+ struct __env_rbtnode_t *left;
+ struct __env_rbtnode_t *right;
+
+ int is_red;
+
+ erts_osenv_data_t key;
+ erts_osenv_data_t value;
+} env_rbtnode_t;
+
+#define ERTS_RBT_PREFIX env
+#define ERTS_RBT_T env_rbtnode_t
+#define ERTS_RBT_KEY_T erts_osenv_data_t
+#define ERTS_RBT_FLAGS_T int
+#define ERTS_RBT_INIT_EMPTY_TNODE(T) \
+ do { \
+ (T)->parent = NULL; \
+ (T)->left = NULL; \
+ (T)->right = NULL; \
+ (T)->is_red = 0; \
+ } while(0)
+#define ERTS_RBT_IS_RED(T) ((T)->is_red)
+#define ERTS_RBT_SET_RED(T) ((T)->is_red = 1)
+#define ERTS_RBT_IS_BLACK(T) (!ERTS_RBT_IS_RED(T))
+#define ERTS_RBT_SET_BLACK(T) ((T)->is_red = 0)
+#define ERTS_RBT_GET_FLAGS(T) ((T)->is_red)
+#define ERTS_RBT_SET_FLAGS(T, F) ((T)->is_red = F)
+#define ERTS_RBT_GET_PARENT(T) ((T)->parent)
+#define ERTS_RBT_SET_PARENT(T, P) ((T)->parent = P)
+#define ERTS_RBT_GET_RIGHT(T) ((T)->right)
+#define ERTS_RBT_SET_RIGHT(T, R) ((T)->right = (R))
+#define ERTS_RBT_GET_LEFT(T) ((T)->left)
+#define ERTS_RBT_SET_LEFT(T, L) ((T)->left = (L))
+#define ERTS_RBT_GET_KEY(T) ((T)->key)
+#define ERTS_RBT_IS_LT(KX, KY) (compare_env_keys(KX, KY) < 0)
+#define ERTS_RBT_IS_EQ(KX, KY) (compare_env_keys(KX, KY) == 0)
+#define ERTS_RBT_WANT_FOREACH_DESTROY
+#define ERTS_RBT_WANT_FOREACH
+#define ERTS_RBT_WANT_REPLACE
+#define ERTS_RBT_WANT_DELETE
+#define ERTS_RBT_WANT_INSERT
+#define ERTS_RBT_WANT_LOOKUP
+
+static int compare_env_keys(const erts_osenv_data_t a, const erts_osenv_data_t b);
+
+#include "erl_rbtree.h"
+
+static int compare_env_keys(const erts_osenv_data_t a, const erts_osenv_data_t b) {
+ int relation = sys_memcmp(a.data, b.data, MIN(a.length, b.length));
+
+ if(relation != 0) {
+ return relation;
+ }
+
+ if(a.length < b.length) {
+ return -1;
+ } else if(a.length == b.length) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static void *convert_value_to_native(Eterm term, char *stackbuf,
+ int stackbuf_size, Sint *length) {
+ int encoding;
+ void *result;
+
+ if(is_atom(term)) {
+ return NULL;
+ }
+
+ encoding = erts_get_native_filename_encoding();
+ *length = erts_native_filename_need(term, encoding);
+
+ if(*length < 0) {
+ return NULL;
+ } else if(*length >= stackbuf_size) {
+ result = erts_alloc(ERTS_ALC_T_TMP, *length);
+ } else {
+ result = stackbuf;
+ }
+
+ erts_native_filename_put(term, encoding, (byte*)result);
+
+ return result;
+}
+
+static void *convert_key_to_native(Eterm term, char *stackbuf,
+ int stackbuf_size, Sint *length) {
+ byte *name_iterator, *name_end;
+ void *result;
+ int encoding;
+
+ result = convert_value_to_native(term, stackbuf, stackbuf_size, length);
+
+ if(result == NULL || length == 0) {
+ return NULL;
+ }
+
+ encoding = erts_get_native_filename_encoding();
+
+ name_iterator = (byte*)result;
+ name_end = &name_iterator[*length];
+
+#ifdef __WIN32__
+ /* Windows stores per-drive working directories as variables starting with
+ * '=', so we skip the first character to tolerate that. */
+ name_iterator = erts_raw_env_next_char(name_iterator, encoding);
+#endif
+
+ while(name_iterator < name_end) {
+ if(erts_raw_env_char_is_7bit_ascii_char('=', name_iterator, encoding)) {
+ if(result != stackbuf) {
+ erts_free(ERTS_ALC_T_TMP, result);
+ }
+
+ return NULL;
+ }
+
+ name_iterator = erts_raw_env_next_char(name_iterator, encoding);
+ }
+
+ return result;
+}
+
+void erts_osenv_init(erts_osenv_t *env) {
+ env->variable_count = 0;
+ env->content_size = 0;
+ env->tree = NULL;
+}
+
+static void destroy_foreach(env_rbtnode_t *node, void *_state) {
+ erts_free(ERTS_ALC_T_ENVIRONMENT, node);
+ (void)_state;
+}
+
+void erts_osenv_clear(erts_osenv_t *env) {
+ env_rbt_foreach_destroy(&env->tree, &destroy_foreach, NULL);
+ erts_osenv_init(env);
+}
+
+struct __env_merge {
+ int overwrite_existing;
+ erts_osenv_t *env;
+};
+
+static void merge_foreach(env_rbtnode_t *node, void *_state) {
+ struct __env_merge *state = (struct __env_merge*)(_state);
+ env_rbtnode_t *existing_node;
+
+ existing_node = env_rbt_lookup(state->env->tree, node->key);
+
+ if(existing_node == NULL || state->overwrite_existing) {
+ erts_osenv_put_native(state->env, &node->key, &node->value);
+ }
+}
+
+void erts_osenv_merge(erts_osenv_t *env, const erts_osenv_t *with, int overwrite) {
+ struct __env_merge merge_state;
+
+ merge_state.overwrite_existing = overwrite;
+ merge_state.env = env;
+
+ env_rbt_foreach(with->tree, merge_foreach, &merge_state);
+}
+
+struct __env_foreach_term {
+ erts_osenv_foreach_term_cb_t user_callback;
+ struct process *process;
+ void *user_state;
+};
+
+static void foreach_term_wrapper(env_rbtnode_t *node, void *_state) {
+ struct __env_foreach_term *state = (struct __env_foreach_term*)_state;
+ Eterm key, value;
+
+ key = erts_convert_native_to_filename(state->process,
+ node->key.length, (byte*)node->key.data);
+ value = erts_convert_native_to_filename(state->process,
+ node->value.length, (byte*)node->value.data);
+
+ state->user_callback(state->process, state->user_state, key, value);
+}
+
+void erts_osenv_foreach_term(const erts_osenv_t *env, struct process *process,
+ void *state, erts_osenv_foreach_term_cb_t callback) {
+ struct __env_foreach_term wrapper_state;
+
+ wrapper_state.user_callback = callback;
+ wrapper_state.user_state = state;
+ wrapper_state.process = process;
+
+ env_rbt_foreach(env->tree, foreach_term_wrapper, &wrapper_state);
+}
+
+int erts_osenv_get_term(const erts_osenv_t *env, Process *process,
+ Eterm key_term, Eterm *out_term) {
+ char key_stackbuf[STACKBUF_SIZE];
+ erts_osenv_data_t key;
+ int result;
+
+ key.data = convert_key_to_native(key_term, key_stackbuf,
+ STACKBUF_SIZE, &key.length);
+ result = -1;
+
+ if(key.data != NULL) {
+ env_rbtnode_t *node;
+
+ node = env_rbt_lookup(env->tree, key);
+ result = 0;
+
+ if(node != NULL) {
+ (*out_term) = erts_convert_native_to_filename(process,
+ node->value.length, (byte*)node->value.data);
+ result = 1;
+ }
+
+ if(key.data != key_stackbuf) {
+ erts_free(ERTS_ALC_T_TMP, key.data);
+ }
+ }
+
+ return result;
+}
+
+int erts_osenv_put_term(erts_osenv_t *env, Eterm key_term, Eterm value_term) {
+ char key_stackbuf[STACKBUF_SIZE], value_stackbuf[STACKBUF_SIZE];
+ erts_osenv_data_t key, value;
+ int result;
+
+ key.data = convert_key_to_native(key_term, key_stackbuf,
+ STACKBUF_SIZE, &key.length);
+ value.data = convert_value_to_native(value_term, value_stackbuf,
+ STACKBUF_SIZE, &value.length);
+ result = -1;
+
+ if(value.data != NULL && key.data != NULL) {
+ result = erts_osenv_put_native(env, &key, &value);
+ }
+
+ if(value.data != NULL && value.data != value_stackbuf) {
+ erts_free(ERTS_ALC_T_TMP, value.data);
+ }
+
+ if(key.data != NULL && key.data != key_stackbuf) {
+ erts_free(ERTS_ALC_T_TMP, key.data);
+ }
+
+ return result;
+}
+
+int erts_osenv_unset_term(erts_osenv_t *env, Eterm key_term) {
+ char key_stackbuf[STACKBUF_SIZE];
+ erts_osenv_data_t key;
+ int result;
+
+ key.data = convert_key_to_native(key_term, key_stackbuf,
+ STACKBUF_SIZE, &key.length);
+ result = -1;
+
+ if(key.data != NULL) {
+ result = erts_osenv_unset_native(env, &key);
+
+ if(key.data != key_stackbuf) {
+ erts_free(ERTS_ALC_T_TMP, key.data);
+ }
+ }
+
+ return result;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct __env_foreach_native {
+ erts_osenv_foreach_native_cb_t user_callback;
+ void *user_state;
+};
+
+static void foreach_native_wrapper(env_rbtnode_t *node, void *_state) {
+ struct __env_foreach_native *state = (struct __env_foreach_native*)_state;
+
+ state->user_callback(state->user_state, &node->key, &node->value);
+}
+
+void erts_osenv_foreach_native(const erts_osenv_t *env, void *state,
+ erts_osenv_foreach_native_cb_t callback) {
+ struct __env_foreach_native wrapper_state;
+
+ wrapper_state.user_callback = callback;
+ wrapper_state.user_state = state;
+
+ env_rbt_foreach(env->tree, foreach_native_wrapper, &wrapper_state);
+}
+
+int erts_osenv_get_native(const erts_osenv_t *env,
+ const erts_osenv_data_t *key,
+ erts_osenv_data_t *value) {
+ env_rbtnode_t *node = env_rbt_lookup(env->tree, *key);
+
+ if(node != NULL) {
+ if(value != NULL) {
+ if(node->value.length > value->length) {
+ return -1;
+ }
+
+ sys_memcpy(value->data, node->value.data, node->value.length);
+ value->length = node->value.length;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int erts_osenv_put_native(erts_osenv_t *env, const erts_osenv_data_t *key,
+ const erts_osenv_data_t *value) {
+ env_rbtnode_t *old_node, *new_node;
+
+ new_node = erts_alloc(ERTS_ALC_T_ENVIRONMENT, sizeof(env_rbtnode_t) +
+ key->length + value->length);
+
+ new_node->key.data = (char*)(&new_node[1]);
+ new_node->key.length = key->length;
+ new_node->value.data = &((char*)new_node->key.data)[key->length];
+ new_node->value.length = value->length;
+
+ sys_memcpy(new_node->key.data, key->data, key->length);
+ sys_memcpy(new_node->value.data, value->data, value->length);
+
+ old_node = env_rbt_lookup(env->tree, *key);
+
+ if(old_node != NULL) {
+ env->content_size -= old_node->value.length;
+ env->content_size -= old_node->key.length;
+ env_rbt_replace(&env->tree, old_node, new_node);
+ } else {
+ env_rbt_insert(&env->tree, new_node);
+ env->variable_count++;
+ }
+
+ env->content_size += new_node->value.length;
+ env->content_size += new_node->key.length;
+
+ if(old_node != NULL) {
+ erts_free(ERTS_ALC_T_ENVIRONMENT, old_node);
+ }
+
+ return 1;
+}
+
+int erts_osenv_unset_native(erts_osenv_t *env, const erts_osenv_data_t *key) {
+ env_rbtnode_t *old_node = env_rbt_lookup(env->tree, *key);
+
+ if(old_node != NULL) {
+ env->content_size -= old_node->value.length;
+ env->content_size -= old_node->key.length;
+ env->variable_count -= 1;
+
+ env_rbt_delete(&env->tree, old_node);
+ erts_free(ERTS_ALC_T_ENVIRONMENT, old_node);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/erts/emulator/sys/common/erl_osenv.h b/erts/emulator/sys/common/erl_osenv.h
new file mode 100644
index 0000000000..4777f2148a
--- /dev/null
+++ b/erts/emulator/sys/common/erl_osenv.h
@@ -0,0 +1,121 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2017. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/* This is a replacement for getenv(3) and friends, operating on instances so
+ * we can keep a common implementation for both the global and local (per-port)
+ * environments.
+ *
+ * The instances are not thread-safe on their own but unlike getenv(3) we're
+ * guaranteed to be the only user, so placing locks around all our accesses
+ * will suffice.
+ *
+ * Use erts_sys_rwlock_global_osenv to access the global environment. */
+
+#ifndef __ERL_OSENV_H__
+#define __ERL_OSENV_H__
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+typedef struct __erts_osenv_data_t erts_osenv_data_t;
+
+typedef struct __erts_osenv_t {
+ struct __env_rbtnode_t *tree;
+ int variable_count;
+ int content_size;
+} erts_osenv_t;
+
+#include "sys.h"
+
+struct __erts_osenv_data_t {
+ Sint length;
+ void *data;
+};
+
+void erts_osenv_init(erts_osenv_t *env);
+void erts_osenv_clear(erts_osenv_t *env);
+
+/* @brief Merges \c with into \c env
+ *
+ * @param overwrite Whether to overwrite existing entries or keep them as they
+ * are. */
+void erts_osenv_merge(erts_osenv_t *env, const erts_osenv_t *with, int overwrite);
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/* @brief Copies env[key] into \c value
+ *
+ * @return 1 on success, 0 if the key couldn't be found, and -1 if the input
+ * was invalid. */
+int erts_osenv_get_term(const erts_osenv_t *env, struct process *process,
+ Eterm key, Eterm *value);
+
+/* @brief Copies \c value into \c env[key]
+ *
+ * @return 1 on success, -1 if the input was invalid. */
+int erts_osenv_put_term(erts_osenv_t *env, Eterm key, Eterm value);
+
+/* @brief Removes \c env[key]
+ *
+ * @return 1 on success, 0 if the key couldn't be found, and -1 if the input
+ * was invalid. */
+int erts_osenv_unset_term(erts_osenv_t *env, Eterm key);
+
+/* @brief Copies env[key] into \c value
+ *
+ * @param value [in,out] The buffer to copy the value into, may be NULL if you
+ * only wish to query presence.
+ *
+ * @return 1 on success, 0 if the key couldn't be found, and -1 if if the value
+ * didn't fit into the buffer. */
+int erts_osenv_get_native(const erts_osenv_t *env, const erts_osenv_data_t *key,
+ erts_osenv_data_t *value);
+
+/* @brief Copies \c value into \c env[key]
+ *
+ * @return 1 on success, -1 on failure. */
+int erts_osenv_put_native(erts_osenv_t *env, const erts_osenv_data_t *key,
+ const erts_osenv_data_t *value);
+
+/* @brief Removes \c key from the env.
+ *
+ * @return 1 on success, 0 if the key couldn't be found. */
+int erts_osenv_unset_native(erts_osenv_t *env, const erts_osenv_data_t *key);
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+typedef void (*erts_osenv_foreach_term_cb_t)(struct process *process,
+ void *state, Eterm key, Eterm value);
+
+typedef void (*erts_osenv_foreach_native_cb_t)(void *state,
+ const erts_osenv_data_t *key,
+ const erts_osenv_data_t *value);
+
+/* @brief Walks through all environment variables, calling \c callback for each
+ * one. It's unsafe to modify \c env within the callback. */
+void erts_osenv_foreach_term(const erts_osenv_t *env, struct process *process,
+ void *state, erts_osenv_foreach_term_cb_t callback);
+
+/* @copydoc erts_osenv_foreach_term */
+void erts_osenv_foreach_native(const erts_osenv_t *env, void *state,
+ erts_osenv_foreach_native_cb_t callback);
+
+#endif
diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c
index 30a595c17a..ced8a4a2a7 100644
--- a/erts/emulator/sys/common/erl_poll.c
+++ b/erts/emulator/sys/common/erl_poll.c
@@ -782,10 +782,14 @@ update_pollset(ErtsPollSet *ps, int fd, ErtsPollOp op, ErtsPollEvents events)
struct kevent evts[2];
struct timespec ts = {0, 0};
-#ifdef EV_DISPATCH
- /* If we have EV_DISPATCH we use it. The kevent descriptions for both
- read and write are added on OP_ADD and removed on OP_DEL. And then
- after than only EV_ENABLE|EV_DISPATCH are used.
+#if defined(EV_DISPATCH) && !defined(__OpenBSD__)
+ /* If we have EV_DISPATCH we use it, unless we are on OpenBSD as the
+ behavior of EV_EOF seems to be edge triggered there and we need it
+ to be level triggered.
+
+ The kevent descriptions for both read and write are added on OP_ADD
+ and removed on OP_DEL. And then after than only EV_ENABLE|EV_DISPATCH
+ are used.
It could be possible to not modify the pollset when disabling and/or
deleting events, but that may cause the poll threads to be awoken
@@ -2059,7 +2063,8 @@ uint32_t epoll_events(int kp_fd, int fd)
int ev_fd;
uint32_t events;
uint64_t data;
- if (fscanf(f,"tfd:%d events:%x data:%lx\n", &ev_fd, &events, &data) != 3) {
+ if (fscanf(f,"tfd:%d events:%x data:%llx\n", &ev_fd, &events,
+ (unsigned long long*)&data) != 3) {
fprintf(stderr,"failed to parse file %s on line %d, errno = %d\n", fname,
line,
errno);
@@ -2125,7 +2130,8 @@ ERTS_POLL_EXPORT(erts_poll_get_selected_events)(ErtsPollSet *ps,
int fd;
uint32_t events;
uint64_t data;
- if (fscanf(f,"tfd:%d events:%x data:%lx\n", &fd, &events, &data) != 3) {
+ if (fscanf(f,"tfd:%d events:%x data:%llx\n", &fd, &events,
+ (unsigned long long*)&data) != 3) {
fprintf(stderr,"failed to parse file %s on line %d, errno = %d\n",
fname, line, errno);
ASSERT(0);
diff --git a/erts/emulator/sys/common/erl_sys_common_misc.c b/erts/emulator/sys/common/erl_sys_common_misc.c
index 420138ff0a..41a6fcb7e1 100644
--- a/erts/emulator/sys/common/erl_sys_common_misc.c
+++ b/erts/emulator/sys/common/erl_sys_common_misc.c
@@ -142,7 +142,16 @@ 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.
+
+#if SIZEOF_LONG == 8
+# define round_int64 lround
+#elif SIZEOF_LONG_LONG == 8
+# define round_int64 llround
+#else
+# error "No 64-bit integer type?"
+#endif
+
+/* Convert float to string
* decimals must be >= 0
* if compact != 0, the trailing 0's will be truncated
*/
@@ -150,93 +159,39 @@ int
sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
int compact)
{
- /* Note that some C compilers don't support "static const" propagation
- * so we use a defines */
- #define SYS_DOUBLE_RND_CONST 0.55555555555555555
+ #define SYS_DOUBLE_RND_CONST 0.5
#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
+ #define EXP_MASK (((Uint64)1 << EXP_SIZE) - 1)
+ #define MAX_DECIMALS (sizeof(pow10v) / sizeof(pow10v[0]))
+ #define FRAC_MASK (((Uint64)1 << FRAC_SIZE) - 1)
+ #define FRAC_MASK2 (((Uint64)1 << (FRAC_SIZE + 1)) - 1)
+ #define MAX_FLOAT ((Uint64)1 << (FRAC_SIZE+1))
+
+ static const double pow10v[] = {
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18
};
- long long mantissa, int_part = 0, frac_part = 0;
- short exp;
- int max;
+ double af;
+ Uint64 int_part, frac_part;
int neg;
- double fr;
- union { long long L; double F; } x;
char *p = buffer;
if (decimals < 0)
return -1;
- /* 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 {
+ if (f < 0) {
neg = 1;
- fr = decimals < MAX_DECIMALS ? (f - cs_sys_double_pow10[decimals]) : f;
- x.F = -fr;
+ af = -f;
}
-
- exp = (x.L >> FRAC_SIZE) & EXP_MASK;
- mantissa = x.L & FRAC_MASK;
-
- if (exp == EXP_MASK) {
- if (mantissa == 0) {
- if (neg)
- *p++ = '-';
- *p++ = 'i';
- *p++ = 'n';
- *p++ = 'f';
- } else {
- *p++ = 'n';
- *p++ = 'a';
- *p++ = 'n';
- }
- *p = '\0';
- return p - buffer;
+ else {
+ neg = 0;
+ af = f;
}
- exp -= EXP_MASK >> 1;
- mantissa |= (1ll << FRAC_SIZE);
-
/* Don't bother with optimizing too large numbers or too large precision */
- if (x.F > MAX_FLOAT || decimals >= MAX_DECIMALS) {
+ if (af > MAX_FLOAT || decimals >= MAX_DECIMALS) {
int len = erts_snprintf(buffer, buffer_size, "%.*f", decimals, f);
char* p = buffer + len;
if (len >= buffer_size)
@@ -246,62 +201,64 @@ sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
p = find_first_trailing_zero(p);
*p = '\0';
return p - buffer;
- } else if (exp >= FRAC_SIZE) {
- int_part = mantissa << (exp - FRAC_SIZE);
- } else if (exp >= 0) {
- int_part = mantissa >> (FRAC_SIZE - exp);
- frac_part = (mantissa << (exp + 1)) & FRAC_MASK2;
- } else /* if (exp < 0) */ {
- frac_part = (mantissa & FRAC_MASK2) >> -(exp + 1);
}
- if (!int_part) {
- if (neg)
- *p++ = '-';
- *p++ = '0';
- } else {
- int ret, i, n;
- while (int_part != 0) {
- long long j = int_part / 10;
- *p++ = (char)(int_part - ((j << 3) + (j << 1)) + '0');
- int_part = j;
- }
- if (neg)
- *p++ = '-';
- /* Reverse string */
- ret = p - buffer;
- for (i = 0, n = ret/2; i < n; i++) {
- int j = ret - i - 1;
- char c = buffer[i];
- buffer[i] = buffer[j];
- buffer[j] = c;
- }
- }
-
- if (decimals > 0) {
- int i;
- *p++ = '.';
+ if (decimals) {
+ double int_f = floor(af);
+ double frac_f = round((af - int_f) * pow10v[decimals]);
- max = buffer_size - (p - buffer) - 1 /* leave room for trailing '\0' */;
+ int_part = (Uint64)int_f;
+ frac_part = (Uint64)frac_f;
- if (decimals > max)
- return -1; /* the number is not large enough to fit in the buffer */
+ if (frac_f >= pow10v[decimals]) {
+ /* rounding overflow carry into int_part */
+ int_part++;
+ frac_part = 0;
+ }
- max = decimals;
+ do {
+ Uint64 n;
+ if (!frac_part) {
+ do {
+ *p++ = '0';
+ } while (--decimals);
+ break;
+ }
+ n = frac_part / 10;
+ *p++ = (char)((frac_part - n*10) + '0');
+ frac_part = n;
+ } while (--decimals);
- for (i = 0; i < max; i++) {
- /* frac_part *= 10; */
- frac_part = (frac_part << 3) + (frac_part << 1);
+ *p++ = '.';
+ }
+ else
+ int_part = (Uint64)round_int64(af);
- *p++ = (char)((frac_part >> (FRAC_SIZE + 1)) + '0');
- frac_part &= FRAC_MASK2;
+ if (!int_part) {
+ *p++ = '0';
+ } else {
+ do {
+ Uint64 n = int_part / 10;
+ *p++ = (char)((int_part - n*10) + '0');
+ int_part = n;
+ } while (int_part);
+ }
+ if (neg)
+ *p++ = '-';
+
+ {/* Reverse string */
+ int i = 0;
+ int j = p - buffer - 1;
+ for ( ; i < j; i++, j--) {
+ char tmp = buffer[i];
+ buffer[i] = buffer[j];
+ buffer[j] = tmp;
}
-
- /* Delete trailing zeroes */
- if (compact)
- p = find_first_trailing_zero(p);
}
+ /* Delete trailing zeroes */
+ if (compact)
+ p = find_first_trailing_zero(p);
*p = '\0';
return p - buffer;
}