Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Zend/zend.c
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,11 @@ static void executor_globals_dtor(zend_executor_globals *executor_globals) /* {{
zend_hash_destroy(executor_globals->zend_constants);
free(executor_globals->zend_constants);
}

/* Every thread pools its own Bigints, and zend_shutdown_strtod() reaches only
* the main thread's: without this a thread that ever formatted or parsed a
* double leaves them behind when its storage goes. */
zend_strtod_state_dtor(&executor_globals->strtod_state);
}
/* }}} */

Expand Down
35 changes: 25 additions & 10 deletions Zend/zend_strtod.c
Original file line number Diff line number Diff line change
Expand Up @@ -545,8 +545,8 @@ Bigint {
static Bigint *freelist[Kmax+1];
#endif

static void destroy_freelist(void);
static void free_p5s(void);
static void destroy_freelist(zend_strtod_state *state);
static void free_p5s(zend_strtod_state *state);

#ifdef MULTIPLE_THREADS
static MUTEX_T dtoa_mutex;
Expand All @@ -555,11 +555,21 @@ static MUTEX_T pow5mult_mutex;

ZEND_API int zend_shutdown_strtod(void) /* {{{ */
{
destroy_freelist();
free_p5s();
zend_strtod_state_dtor(&EG(strtod_state));

return 1;
}
/* }}} */

/* Frees the Bigints one thread pooled. Bfree recycles rather than releasing, so
* every thread that formats or parses a double keeps a handful for its life. The
* state travels as an argument because TSRM runs a thread's storage dtor from
* whichever thread performs the shutdown, and EG() there is somebody else's. */
ZEND_API void zend_strtod_state_dtor(zend_strtod_state *state) /* {{{ */
{
destroy_freelist(state);
free_p5s(state);
}
/* }}} */

static Bigint *
Expand Down Expand Up @@ -4605,33 +4615,38 @@ ZEND_API char *zend_gcvt(double value, int ndigit, char dec_point, char exponent
return (buf);
}

static void destroy_freelist(void)
/* The two pools are reached through a thread's own state from here on, so the
* EG() shorthands above must not swallow the field names. */
#undef freelist
#undef p5s

static void destroy_freelist(zend_strtod_state *state)
{
int i;
Bigint *tmp;

ACQUIRE_DTOA_LOCK(0)
for (i = 0; i <= Kmax; i++) {
Bigint **listp = &freelist[i];
Bigint **listp = &state->freelist[i];
while ((tmp = *listp) != NULL) {
*listp = tmp->next;
FREE(tmp);
}
freelist[i] = NULL;
state->freelist[i] = NULL;
}
FREE_DTOA_LOCK(0)
}

static void free_p5s(void)
static void free_p5s(zend_strtod_state *state)
{
Bigint **listp, *tmp;

ACQUIRE_DTOA_LOCK(1)
listp = &p5s;
listp = &state->p5s;
while ((tmp = *listp) != NULL) {
*listp = tmp->next;
FREE(tmp);
}
p5s = NULL;
state->p5s = NULL;
FREE_DTOA_LOCK(1)
}
3 changes: 3 additions & 0 deletions Zend/zend_strtod.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ ZEND_API double zend_hex_strtod(const char *str, const char **endptr);
ZEND_API double zend_oct_strtod(const char *str, const char **endptr);
ZEND_API double zend_bin_strtod(const char *str, const char **endptr);
ZEND_API int zend_shutdown_strtod(void);
/* Releases what one thread pooled. Called for every thread's state, not only the
* one zend_shutdown_strtod() reaches. */
ZEND_API void zend_strtod_state_dtor(zend_strtod_state *state);
END_EXTERN_C()

/* double limits */
Expand Down
Loading