Skip to content

Commit 5801720

Browse files
committed
move EG() and CG() to __thread storage
1 parent 425cd3d commit 5801720

5 files changed

Lines changed: 100 additions & 8 deletions

File tree

TSRM/TSRM.c

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ typedef struct {
4242
ts_allocate_ctor ctor;
4343
ts_allocate_dtor dtor;
4444
size_t fast_offset;
45+
/* When set, storage comes from __thread memory instead of being allocated by TSRM. */
46+
void *(*tls_addr)(void);
4547
int done;
4648
} tsrm_resource_type;
4749

@@ -163,14 +165,19 @@ TSRM_API bool tsrm_startup(int expected_threads, int expected_resources, int deb
163165

164166
static void ts_free_resources(tsrm_tls_entry *thread_resources)
165167
{
168+
bool own_thread = thread_resources->thread_id == tsrm_thread_id();
169+
166170
/* Need to destroy in reverse order to respect dependencies. */
167171
for (int i = thread_resources->count - 1; i >= 0; i--) {
168172
if (!resource_types_table[i].done) {
173+
if (resource_types_table[i].tls_addr && !own_thread) {
174+
continue;
175+
}
169176
if (resource_types_table[i].dtor) {
170177
resource_types_table[i].dtor(thread_resources->storage[i]);
171178
}
172179

173-
if (!resource_types_table[i].fast_offset) {
180+
if (!resource_types_table[i].fast_offset && !resource_types_table[i].tls_addr) {
174181
free(thread_resources->storage[i]);
175182
}
176183
}
@@ -256,7 +263,10 @@ static void tsrm_update_active_threads(void)
256263

257264
p->storage = (void *) realloc(p->storage, sizeof(void *)*id_count);
258265
for (j=p->count; j<id_count; j++) {
259-
if (resource_types_table[j].fast_offset) {
266+
if (resource_types_table[j].tls_addr) {
267+
TSRM_ASSERT(p->thread_id == tsrm_thread_id());
268+
p->storage[j] = resource_types_table[j].tls_addr();
269+
} else if (resource_types_table[j].fast_offset) {
260270
p->storage[j] = (void *) (((char*)p) + resource_types_table[j].fast_offset);
261271
} else {
262272
p->storage[j] = (void *) malloc(resource_types_table[j].size);
@@ -301,6 +311,7 @@ TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate
301311
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
302312
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
303313
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = 0;
314+
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].tls_addr = NULL;
304315
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;
305316

306317
tsrm_update_active_threads();
@@ -359,6 +370,7 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz
359370
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
360371
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
361372
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = *offset;
373+
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].tls_addr = NULL;
362374
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;
363375

364376
tsrm_update_active_threads();
@@ -368,6 +380,41 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz
368380
return *rsrc_id;
369381
}/*}}}*/
370382

383+
/* allocates a resource id whose per-thread storage is a native __thread block */
384+
TSRM_API ts_rsrc_id ts_allocate_tls_id(ts_rsrc_id *rsrc_id, void *(*tls_addr)(void), size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
385+
{/*{{{*/
386+
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new TLS resource id, %d bytes", size));
387+
388+
tsrm_mutex_lock(tsmm_mutex);
389+
390+
*rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++);
391+
392+
if (resource_types_table_size < id_count) {
393+
tsrm_resource_type *_tmp;
394+
_tmp = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);
395+
if (!_tmp) {
396+
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource"));
397+
*rsrc_id = 0;
398+
tsrm_mutex_unlock(tsmm_mutex);
399+
return 0;
400+
}
401+
resource_types_table = _tmp;
402+
resource_types_table_size = id_count;
403+
}
404+
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size;
405+
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
406+
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
407+
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = 0;
408+
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].tls_addr = tls_addr;
409+
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;
410+
411+
tsrm_update_active_threads();
412+
tsrm_mutex_unlock(tsmm_mutex);
413+
414+
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new TLS resource id %d", *rsrc_id));
415+
return *rsrc_id;
416+
}/*}}}*/
417+
371418
static void set_thread_local_storage_resource_to(tsrm_tls_entry *thread_resource)
372419
{
373420
tsrm_tls_set(thread_resource);
@@ -397,7 +444,9 @@ static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_
397444
if (resource_types_table[i].done) {
398445
(*thread_resources_ptr)->storage[i] = NULL;
399446
} else {
400-
if (resource_types_table[i].fast_offset) {
447+
if (resource_types_table[i].tls_addr) {
448+
(*thread_resources_ptr)->storage[i] = resource_types_table[i].tls_addr();
449+
} else if (resource_types_table[i].fast_offset) {
401450
(*thread_resources_ptr)->storage[i] = (void *) (((char*)(*thread_resources_ptr)) + resource_types_table[i].fast_offset);
402451
} else {
403452
(*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size);
@@ -485,7 +534,8 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)
485534
/* In case that extensions don't use the pointer passed from the dtor, but incorrectly
486535
* use the global pointer, we need to setup the global pointer temporarily here. */
487536
set_thread_local_storage_resource_to(thread_resources);
488-
/* Free up the old resource from the old thread instance */
537+
/* Dead thread, recycled id: already freed, so just zero it. */
538+
thread_resources->thread_id = 0;
489539
ts_free_resources(thread_resources);
490540
free(thread_resources);
491541
/* Allocate a new resource at the same point in the linked list, and relink the next pointer */
@@ -559,7 +609,7 @@ void ts_free_id(ts_rsrc_id id)
559609
if (resource_types_table[rsrc_id].dtor) {
560610
resource_types_table[rsrc_id].dtor(p->storage[rsrc_id]);
561611
}
562-
if (!resource_types_table[rsrc_id].fast_offset) {
612+
if (!resource_types_table[rsrc_id].fast_offset && !resource_types_table[rsrc_id].tls_addr) {
563613
free(p->storage[rsrc_id]);
564614
}
565615
}

TSRM/TSRM.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate
9393
/* Fast resource in reserved (pre-allocated) space */
9494
TSRM_API void tsrm_reserve(size_t size);
9595
TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor);
96+
/* Must be called at startup before any other thread exists. */
97+
TSRM_API ts_rsrc_id ts_allocate_tls_id(ts_rsrc_id *rsrc_id, void *(*tls_addr)(void), size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor);
9698

9799
/* fetches the requested resource for the current thread */
98100
TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id);
@@ -155,7 +157,7 @@ TSRM_API bool tsrm_is_managed_thread(void);
155157
#if !__has_attribute(tls_model) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__MUSL__) || defined(__HAIKU__)
156158
# define TSRM_TLS_MODEL_ATTR
157159
# define TSRM_TLS_MODEL_DEFAULT
158-
#elif __PIC__
160+
#elif __PIC__ && !defined(__PIE__)
159161
# define TSRM_TLS_MODEL_ATTR __attribute__((tls_model("initial-exec")))
160162
# define TSRM_TLS_MODEL_INITIAL_EXEC
161163
#else

Zend/Zend.m4

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,23 @@ AC_MSG_RESULT([$ZEND_ZTS])
178178
AS_VAR_IF([ZEND_ZTS], [yes], [
179179
AC_DEFINE([ZTS], [1], [Define to 1 if thread safety (ZTS) is enabled.])
180180
AS_VAR_APPEND([CFLAGS], [" -DZTS"])
181+
182+
AC_CACHE_CHECK([for __thread support], [php_cv_have_thread_local], [
183+
AC_LINK_IFELSE([AC_LANG_PROGRAM(
184+
[[static __thread int tls_var;]],
185+
[[tls_var = 1; return tls_var;]])],
186+
[php_cv_have_thread_local=yes], [php_cv_have_thread_local=no])
187+
])
188+
AS_VAR_IF([php_cv_have_thread_local], [yes], [
189+
AC_DEFINE([ZEND_EG_TLS], [1],
190+
[Define to hold EG()/CG() in a __thread variable under ZTS.])
191+
AS_VAR_APPEND([CFLAGS], [" -DZEND_EG_TLS"])
192+
193+
dnl -mtls-size=12 drops the dead high-bits offset add from TLS access,
194+
dnl valid while the thread-local block stays under 4 KiB.
195+
AX_CHECK_COMPILE_FLAG([-mtls-size=12],
196+
[AS_VAR_APPEND([CFLAGS], [" -mtls-size=12"])])
197+
])
181198
])
182199
183200
AC_MSG_CHECKING([whether to enable Zend debugging])

Zend/zend.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ ZEND_API int compiler_globals_id;
5454
ZEND_API int executor_globals_id;
5555
ZEND_API size_t compiler_globals_offset;
5656
ZEND_API size_t executor_globals_offset;
57+
# ifdef ZEND_EG_TLS
58+
ZEND_API TSRM_TLS TSRM_TLS_MODEL_ATTR zend_executor_globals executor_globals_tls;
59+
ZEND_API TSRM_TLS TSRM_TLS_MODEL_ATTR zend_compiler_globals compiler_globals_tls;
60+
/* ts_allocate_tls_id takes a callback so each thread resolves its own block.
61+
* A plain &..._tls would capture only the registering thread's address. */
62+
static void *executor_globals_tls_addr(void) { return &executor_globals_tls; }
63+
static void *compiler_globals_tls_addr(void) { return &compiler_globals_tls; }
64+
# endif
5765
static HashTable *global_function_table = NULL;
5866
static HashTable *global_class_table = NULL;
5967
static HashTable *global_constants_table = NULL;
@@ -1019,8 +1027,13 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
10191027
zend_init_rsrc_list_dtors();
10201028

10211029
#ifdef ZTS
1030+
#ifdef ZEND_EG_TLS
1031+
ts_allocate_tls_id(&compiler_globals_id, compiler_globals_tls_addr, sizeof(zend_compiler_globals), (ts_allocate_ctor) compiler_globals_ctor, (ts_allocate_dtor) compiler_globals_dtor);
1032+
ts_allocate_tls_id(&executor_globals_id, executor_globals_tls_addr, sizeof(zend_executor_globals), (ts_allocate_ctor) executor_globals_ctor, (ts_allocate_dtor) executor_globals_dtor);
1033+
#else
10221034
ts_allocate_fast_id(&compiler_globals_id, &compiler_globals_offset, sizeof(zend_compiler_globals), (ts_allocate_ctor) compiler_globals_ctor, (ts_allocate_dtor) compiler_globals_dtor);
10231035
ts_allocate_fast_id(&executor_globals_id, &executor_globals_offset, sizeof(zend_executor_globals), (ts_allocate_ctor) executor_globals_ctor, (ts_allocate_dtor) executor_globals_dtor);
1036+
#endif
10241037
ts_allocate_fast_id(&language_scanner_globals_id, &language_scanner_globals_offset, sizeof(zend_php_scanner_globals), (ts_allocate_ctor) php_scanner_globals_ctor, NULL);
10251038
ts_allocate_fast_id(&ini_scanner_globals_id, &ini_scanner_globals_offset, sizeof(zend_ini_scanner_globals), (ts_allocate_ctor) ini_scanner_globals_ctor, NULL);
10261039
compiler_globals = ts_resource(compiler_globals_id);

Zend/zend_globals_macros.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ BEGIN_EXTERN_C()
3030

3131
/* Compiler */
3232
#ifdef ZTS
33-
# define CG(v) ZEND_TSRMG_FAST(compiler_globals_offset, zend_compiler_globals *, v)
33+
# ifdef ZEND_EG_TLS
34+
extern ZEND_API TSRM_TLS TSRM_TLS_MODEL_ATTR zend_compiler_globals compiler_globals_tls;
35+
# define CG(v) (compiler_globals_tls.v)
36+
# else
37+
# define CG(v) ZEND_TSRMG_FAST(compiler_globals_offset, zend_compiler_globals *, v)
38+
# endif
3439
#else
3540
# define CG(v) (compiler_globals.v)
3641
extern ZEND_API struct _zend_compiler_globals compiler_globals;
@@ -40,7 +45,12 @@ ZEND_API int zendparse(void);
4045

4146
/* Executor */
4247
#ifdef ZTS
43-
# define EG(v) ZEND_TSRMG_FAST(executor_globals_offset, zend_executor_globals *, v)
48+
# ifdef ZEND_EG_TLS
49+
extern ZEND_API TSRM_TLS TSRM_TLS_MODEL_ATTR zend_executor_globals executor_globals_tls;
50+
# define EG(v) (executor_globals_tls.v)
51+
# else
52+
# define EG(v) ZEND_TSRMG_FAST(executor_globals_offset, zend_executor_globals *, v)
53+
# endif
4454
#else
4555
# define EG(v) (executor_globals.v)
4656
extern ZEND_API zend_executor_globals executor_globals;

0 commit comments

Comments
 (0)