Skip to content

Commit

Permalink
Interned strings unification for TS/NTS
Browse files Browse the repository at this point in the history
Hereby, interned strings are supported in thread safe PHP. The patch
implements two types of interned strings

- interning per process, strings are not freed till process end
- interning per request, strings are freed at request end

There is no runtime interning.

With Opcache, all the permanent iterned strings are copied into SHM on
startup, additional copying into SHM might happen on demand.
  • Loading branch information
weltling committed Mar 4, 2017
1 parent a07272e commit c698299
Show file tree
Hide file tree
Showing 28 changed files with 440 additions and 477 deletions.
31 changes: 27 additions & 4 deletions TSRM/TSRM.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ static int resource_types_table_size;
static MUTEX_T tsmm_mutex; /* thread-safe memory manager mutex */

/* New thread handlers */
static tsrm_thread_begin_func_t tsrm_new_thread_begin_handler;
static tsrm_thread_end_func_t tsrm_new_thread_end_handler;
static tsrm_thread_begin_func_t tsrm_new_thread_begin_handler = NULL;
static tsrm_thread_end_func_t tsrm_new_thread_end_handler = NULL;
static tsrm_shutdown_func_t tsrm_shutdown_handler = NULL;

/* Debug support */
int tsrm_error(int level, const char *format, ...);
Expand Down Expand Up @@ -120,6 +121,8 @@ static int32 tls_key;
# warning tsrm_set_interpreter_context is probably broken on this platform
#endif

TSRM_TLS uint8_t in_main_thread = 0;

/* Startup TSRM (call once for the entire process) */
TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, char *debug_filename)
{
Expand All @@ -136,6 +139,9 @@ TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debu
tls_key = tls_allocate();
#endif

/* ensure singleton */
in_main_thread = 1;

tsrm_error_file = stderr;
tsrm_error_set(debug_level, debug_filename);
tsrm_tls_table_size = expected_threads;
Expand All @@ -158,8 +164,6 @@ TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debu

tsmm_mutex = tsrm_mutex_alloc();

tsrm_new_thread_begin_handler = tsrm_new_thread_end_handler = NULL;

TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Started up TSRM, %d expected threads, %d expected resources", expected_threads, expected_resources));
return 1;
}
Expand All @@ -170,6 +174,11 @@ TSRM_API void tsrm_shutdown(void)
{
int i;

if (!in_main_thread) {
/* ensure singleton */
return;
}

if (tsrm_tls_table) {
for (i=0; i<tsrm_tls_table_size; i++) {
tsrm_tls_entry *p = tsrm_tls_table[i], *next_p;
Expand Down Expand Up @@ -212,6 +221,12 @@ TSRM_API void tsrm_shutdown(void)
#elif defined(TSRM_WIN32)
TlsFree(tls_key);
#endif
if (tsrm_shutdown_handler) {
tsrm_shutdown_handler();
}
tsrm_new_thread_begin_handler = NULL;
tsrm_new_thread_end_handler = NULL;
tsrm_shutdown_handler = NULL;
}


Expand Down Expand Up @@ -740,6 +755,14 @@ TSRM_API void *tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread
}


TSRM_API void *tsrm_set_shutdown_handler(tsrm_shutdown_func_t shutdown_handler)
{
void *retval = (void *) tsrm_shutdown_handler;

tsrm_shutdown_handler = shutdown_handler;
return retval;
}


/*
* Debug support
Expand Down
2 changes: 2 additions & 0 deletions TSRM/TSRM.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ TSRM_API void ts_free_id(ts_rsrc_id id);

typedef void (*tsrm_thread_begin_func_t)(THREAD_T thread_id);
typedef void (*tsrm_thread_end_func_t)(THREAD_T thread_id);
typedef void (*tsrm_shutdown_func_t)(void);


TSRM_API int tsrm_error(int level, const char *format, ...);
Expand All @@ -145,6 +146,7 @@ TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset);

TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler);
TSRM_API void *tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler);
TSRM_API void *tsrm_set_shutdown_handler(tsrm_shutdown_func_t shutdown_handler);

/* these 3 APIs should only be used by people that fully understand the threading model
* used by PHP/Zend and the selected SAPI. */
Expand Down
14 changes: 3 additions & 11 deletions Zend/zend.c
Original file line number Diff line number Diff line change
Expand Up @@ -580,12 +580,6 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals) /* {{
compiler_globals->static_members_table = NULL;
}
compiler_globals->script_encoding_list = NULL;

compiler_globals->empty_string = zend_zts_interned_string_init("", sizeof("")-1);

memset(compiler_globals->one_char_string, 0, sizeof(compiler_globals->one_char_string));

zend_known_interned_strings_init(&compiler_globals->known_strings, &compiler_globals->known_strings_count);
}
/* }}} */

Expand All @@ -610,11 +604,6 @@ static void compiler_globals_dtor(zend_compiler_globals *compiler_globals) /* {{
pefree((char*)compiler_globals->script_encoding_list, 1);
}
compiler_globals->last_static_member = 0;

zend_zts_interned_string_free(&compiler_globals->empty_string);

compiler_globals->known_strings = NULL;
compiler_globals->known_strings_count = 0;
}
/* }}} */

Expand Down Expand Up @@ -856,6 +845,7 @@ int zend_startup(zend_utility_functions *utility_functions, char **extensions) /

#ifdef ZTS
tsrm_set_new_thread_end_handler(zend_new_thread_end_handler);
tsrm_set_shutdown_handler(zend_interned_strings_dtor);
#endif

return SUCCESS;
Expand Down Expand Up @@ -967,7 +957,9 @@ void zend_shutdown(void) /* {{{ */
#endif
zend_destroy_rsrc_list_dtors();

#ifndef ZTS
zend_interned_strings_dtor();
#endif
}
/* }}} */

Expand Down
4 changes: 4 additions & 0 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -2019,6 +2019,7 @@ ZEND_API zend_module_entry* zend_register_module_ex(zend_module_entry *module) /
lcname = zend_string_alloc(name_len, 1);
zend_str_tolower_copy(ZSTR_VAL(lcname), module->name, name_len);

lcname = zend_new_interned_string(lcname);
if ((module_ptr = zend_hash_add_mem(&module_registry, lcname, module, sizeof(zend_module_entry))) == NULL) {
zend_error(E_CORE_WARNING, "Module '%s' already loaded", module->name);
zend_string_release(lcname);
Expand Down Expand Up @@ -3816,6 +3817,9 @@ ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name,
int ret;

zend_string *key = zend_string_init(name, name_length, ce->type & ZEND_INTERNAL_CLASS);
if (ce->type == ZEND_INTERNAL_CLASS) {
key = zend_new_interned_string(key);
}
ret = zend_declare_class_constant_ex(ce, key, value, ZEND_ACC_PUBLIC, NULL);
zend_string_release(key);
return ret;
Expand Down
52 changes: 26 additions & 26 deletions Zend/zend_builtin_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ ZEND_FUNCTION(each)
if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry);
}
zend_hash_index_add_new(Z_ARRVAL_P(return_value), 1, entry);
zend_hash_add_new(Z_ARRVAL_P(return_value), CG(known_strings)[ZEND_STR_VALUE], entry);
zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_VALUE), entry);

/* add the key elements */
if (zend_hash_get_current_key(target_hash, &key, &num_key) == HASH_KEY_IS_STRING) {
Expand All @@ -773,7 +773,7 @@ ZEND_FUNCTION(each)
ZVAL_LONG(&tmp, num_key);
}
zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp);
zend_hash_add_new(Z_ARRVAL_P(return_value), CG(known_strings)[ZEND_STR_KEY], &tmp);
zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_KEY), &tmp);
zend_hash_move_forward(target_hash);
}
/* }}} */
Expand All @@ -797,7 +797,7 @@ ZEND_FUNCTION(error_reporting)
zend_ini_entry *p = EG(error_reporting_ini_entry);

if (!p) {
p = zend_hash_find_ptr(EG(ini_directives), CG(known_strings)[ZEND_STR_ERROR_REPORTING]);
p = zend_hash_find_ptr(EG(ini_directives), ZSTR_KNOWN(ZEND_STR_ERROR_REPORTING));
if (p) {
EG(error_reporting_ini_entry) = p;
} else {
Expand All @@ -809,7 +809,7 @@ ZEND_FUNCTION(error_reporting)
ALLOC_HASHTABLE(EG(modified_ini_directives));
zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
}
if (EXPECTED(zend_hash_add_ptr(EG(modified_ini_directives), CG(known_strings)[ZEND_STR_ERROR_REPORTING], p) != NULL)) {
if (EXPECTED(zend_hash_add_ptr(EG(modified_ini_directives), ZSTR_KNOWN(ZEND_STR_ERROR_REPORTING), p) != NULL)) {
p->orig_value = p->value;
p->orig_modifiable = p->modifiable;
p->modified = 1;
Expand Down Expand Up @@ -2659,9 +2659,9 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
lineno = skip->opline->lineno;
}
ZVAL_STR_COPY(&tmp, filename);
zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_FILE], &tmp);
zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
ZVAL_LONG(&tmp, lineno);
zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_LINE], &tmp);
zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_LINE), &tmp);

/* try to fetch args only if an FCALL was just made - elsewise we're in the middle of a function
* and debug_baktrace() might have been called by the error_handler. in this case we don't
Expand All @@ -2679,9 +2679,9 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
}
if (prev->func && ZEND_USER_CODE(prev->func->common.type)) {
ZVAL_STR_COPY(&tmp, prev->func->op_array.filename);
zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_FILE], &tmp);
zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
ZVAL_LONG(&tmp, prev->opline->lineno);
zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_LINE], &tmp);
zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
break;
}
prev_call = prev;
Expand All @@ -2707,7 +2707,7 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int

if (function_name) {
ZVAL_STR_COPY(&tmp, function_name);
zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_FUNCTION], &tmp);
zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp);

if (object) {
if (func->common.scope) {
Expand All @@ -2717,27 +2717,27 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
} else {
ZVAL_STR(&tmp, object->handlers->get_class_name(object));
}
zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_CLASS], &tmp);
zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_CLASS), &tmp);
if ((options & DEBUG_BACKTRACE_PROVIDE_OBJECT) != 0) {
ZVAL_OBJ(&tmp, object);
zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_OBJECT], &tmp);
zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_OBJECT), &tmp);
Z_ADDREF(tmp);
}

ZVAL_INTERNED_STR(&tmp, CG(known_strings)[ZEND_STR_OBJECT_OPERATOR]);
zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_TYPE], &tmp);
ZVAL_INTERNED_STR(&tmp, ZSTR_KNOWN(ZEND_STR_OBJECT_OPERATOR));
zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_TYPE), &tmp);
} else if (func->common.scope) {
ZVAL_STR_COPY(&tmp, func->common.scope->name);
zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_CLASS], &tmp);
ZVAL_INTERNED_STR(&tmp, CG(known_strings)[ZEND_STR_PAAMAYIM_NEKUDOTAYIM]);
zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_TYPE], &tmp);
zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_CLASS), &tmp);
ZVAL_INTERNED_STR(&tmp, ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM));
zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_TYPE), &tmp);
}

if ((options & DEBUG_BACKTRACE_IGNORE_ARGS) == 0 &&
func->type != ZEND_EVAL_CODE) {

debug_backtrace_get_args(call, &tmp);
zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_ARGS], &tmp);
zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_ARGS), &tmp);
}
} else {
/* i know this is kinda ugly, but i'm trying to avoid extra cycles in the main execution loop */
Expand All @@ -2746,30 +2746,30 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int

if (!ptr->func || !ZEND_USER_CODE(ptr->func->common.type) || ptr->opline->opcode != ZEND_INCLUDE_OR_EVAL) {
/* can happen when calling eval from a custom sapi */
pseudo_function_name = CG(known_strings)[ZEND_STR_UNKNOWN];
pseudo_function_name = ZSTR_KNOWN(ZEND_STR_UNKNOWN);
build_filename_arg = 0;
} else
switch (ptr->opline->extended_value) {
case ZEND_EVAL:
pseudo_function_name = CG(known_strings)[ZEND_STR_EVAL];
pseudo_function_name = ZSTR_KNOWN(ZEND_STR_EVAL);
build_filename_arg = 0;
break;
case ZEND_INCLUDE:
pseudo_function_name = CG(known_strings)[ZEND_STR_INCLUDE];
pseudo_function_name = ZSTR_KNOWN(ZEND_STR_INCLUDE);
break;
case ZEND_REQUIRE:
pseudo_function_name = CG(known_strings)[ZEND_STR_REQUIRE];
pseudo_function_name = ZSTR_KNOWN(ZEND_STR_REQUIRE);
break;
case ZEND_INCLUDE_ONCE:
pseudo_function_name = CG(known_strings)[ZEND_STR_INCLUDE_ONCE];
pseudo_function_name = ZSTR_KNOWN(ZEND_STR_INCLUDE_ONCE);
break;
case ZEND_REQUIRE_ONCE:
pseudo_function_name = CG(known_strings)[ZEND_STR_REQUIRE_ONCE];
pseudo_function_name = ZSTR_KNOWN(ZEND_STR_REQUIRE_ONCE);
break;
default:
/* this can actually happen if you use debug_backtrace() in your error_handler and
* you're in the top-scope */
pseudo_function_name = CG(known_strings)[ZEND_STR_UNKNOWN];
pseudo_function_name = ZSTR_KNOWN(ZEND_STR_UNKNOWN);
build_filename_arg = 0;
break;
}
Expand All @@ -2785,11 +2785,11 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int

ZVAL_STR_COPY(&tmp, include_filename);
zend_hash_next_index_insert_new(Z_ARRVAL(arg_array), &tmp);
zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_ARGS], &arg_array);
zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_ARGS), &arg_array);
}

ZVAL_INTERNED_STR(&tmp, pseudo_function_name);
zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_FUNCTION], &tmp);
zend_hash_add_new(Z_ARRVAL(stack_frame), ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp);
}

zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &stack_frame);
Expand Down
6 changes: 3 additions & 3 deletions Zend/zend_closures.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* {
invoke->internal_function.handler = ZEND_MN(Closure___invoke);
invoke->internal_function.module = 0;
invoke->internal_function.scope = zend_ce_closure;
invoke->internal_function.function_name = CG(known_strings)[ZEND_STR_MAGIC_INVOKE];
invoke->internal_function.function_name = ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE);
return invoke;
}
/* }}} */
Expand Down Expand Up @@ -509,12 +509,12 @@ static HashTable *zend_closure_get_debug_info(zval *object, int *is_temp) /* {{{
if (closure->func.type == ZEND_USER_FUNCTION && closure->func.op_array.static_variables) {
HashTable *static_variables = closure->func.op_array.static_variables;
ZVAL_ARR(&val, zend_array_dup(static_variables));
zend_hash_update(debug_info, CG(known_strings)[ZEND_STR_STATIC], &val);
zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_STATIC), &val);
}

if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
Z_ADDREF(closure->this_ptr);
zend_hash_update(debug_info, CG(known_strings)[ZEND_STR_THIS], &closure->this_ptr);
zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_THIS), &closure->this_ptr);
}

if (arg_info &&
Expand Down
14 changes: 2 additions & 12 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -3467,13 +3467,7 @@ int zend_compile_func_chr(znode *result, zend_ast_list *args) /* {{{ */
zend_long c = Z_LVAL_P(zend_ast_get_zval(args->child[0])) & 0xff;

result->op_type = IS_CONST;
if (CG(one_char_string)[c]) {
ZVAL_INTERNED_STR(&result->u.constant, CG(one_char_string)[c]);
} else {
ZVAL_NEW_STR(&result->u.constant, zend_string_alloc(1, 0));
Z_STRVAL_P(&result->u.constant)[0] = (char)c;
Z_STRVAL_P(&result->u.constant)[1] = '\0';
}
ZVAL_INTERNED_STR(&result->u.constant, ZSTR_CHAR(c));
return SUCCESS;
} else {
return FAILURE;
Expand Down Expand Up @@ -8216,11 +8210,7 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
return;
}
c = (zend_uchar) Z_STRVAL_P(container)[offset];
if (CG(one_char_string)[c]) {
ZVAL_INTERNED_STR(&result, CG(one_char_string)[c]);
} else {
ZVAL_NEW_STR(&result, zend_string_init((char *) &c, 1, 0));
}
ZVAL_INTERNED_STR(&result, ZSTR_CHAR(c));
} else if (Z_TYPE_P(container) <= IS_FALSE) {
ZVAL_NULL(&result);
} else {
Expand Down
4 changes: 4 additions & 0 deletions Zend/zend_constants.c
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,10 @@ ZEND_API int zend_register_constant(zend_constant *c)
printf("Registering constant for module %d\n", c->module_number);
#endif

if (c->module_number != PHP_USER_CONSTANT) {
c->name = zend_new_interned_string(c->name);
}

if (!(c->flags & CONST_CS)) {
lowercase_name = zend_string_alloc(ZSTR_LEN(c->name), c->flags & CONST_PERSISTENT);
zend_str_tolower_copy(ZSTR_VAL(lowercase_name), ZSTR_VAL(c->name), ZSTR_LEN(c->name));
Expand Down
Loading

0 comments on commit c698299

Please sign in to comment.