Skip to content

Commit 5e39741

Browse files
committed
create only one timer per thread
1 parent e1f9d34 commit 5e39741

File tree

5 files changed

+114
-67
lines changed

5 files changed

+114
-67
lines changed

Zend/zend.c

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "zend_attributes.h"
3636
#include "zend_observer.h"
3737
#include "zend_fibers.h"
38+
#include "zend_timer.h"
3839
#include "Optimizer/zend_optimizer.h"
3940

4041
static size_t global_map_ptr_last = 0;
@@ -817,10 +818,60 @@ static void executor_globals_dtor(zend_executor_globals *executor_globals) /* {{
817818
}
818819
/* }}} */
819820

821+
# ifdef ZEND_TIMER
822+
static void zend_timer_create() /* {{{ */
823+
{
824+
struct sigevent sev;
825+
sev.sigev_notify = SIGEV_THREAD_ID;
826+
sev.sigev_value.sival_ptr = &EG(timer);
827+
// The chosen signal must:
828+
// 1. not be used internally by libc
829+
// 2. be allowed to happen spuriously without consequences
830+
// 3. not be commonly used by applications, this excludes SIGALRM, SIGUSR1 and SIGUSR2
831+
// 4. not be used by profilers, this excludes SIGPROF
832+
// 5. not be used internally by runtimes of programs that can embed PHP, this excludes SIGURG, which is used by Go
833+
sev.sigev_signo = SIGIO;
834+
sev.sigev_notify_thread_id = (pid_t) syscall(SYS_gettid);
835+
836+
int errn = timer_create(CLOCK_THREAD_CPUTIME_ID, &sev, &EG(timer));
837+
if (errn != 0) {
838+
EG(timer) = 0;
839+
840+
zend_strerror_noreturn(E_ERROR, errn, "Could not create timer");
841+
}
842+
843+
# ifdef TIMER_DEBUG
844+
fprintf(stderr, "Timer %#jx created on thread %d\n", (uintmax_t) EG(timer), sev.sigev_notify_thread_id);
845+
# endif
846+
}
847+
/* }}} */
848+
# endif
849+
820850
static void zend_new_thread_end_handler(THREAD_T thread_id) /* {{{ */
821851
{
822852
zend_copy_ini_directives();
823853
zend_ini_refresh_caches(ZEND_INI_STAGE_STARTUP);
854+
#ifdef ZEND_TIMER
855+
zend_timer_create();
856+
#endif
857+
}
858+
/* }}} */
859+
860+
static void zend_thread_shutdown_handler(void) { /* {{{ */
861+
zend_interned_strings_dtor();
862+
863+
# ifdef ZEND_TIMER
864+
timer_t timer = EG(timer);
865+
866+
if (timer == 0) return;
867+
868+
int errn = timer_delete(EG(timer));
869+
if (errn != 0) zend_strerror_noreturn(E_ERROR, errn, "Could not delete timer");
870+
871+
# ifdef TIMER_DEBUG
872+
fprintf(stderr, "Timer %#jx deleted on thread %d\n", (uintmax_t) EG(timer), (pid_t) syscall(SYS_gettid));
873+
# endif
874+
# endif
824875
}
825876
/* }}} */
826877
#endif
@@ -1026,7 +1077,7 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
10261077

10271078
#ifdef ZTS
10281079
tsrm_set_new_thread_end_handler(zend_new_thread_end_handler);
1029-
tsrm_set_shutdown_handler(zend_interned_strings_dtor);
1080+
tsrm_set_shutdown_handler(zend_thread_shutdown_handler);
10301081
#endif
10311082
}
10321083
/* }}} */
@@ -1092,6 +1143,10 @@ zend_result zend_post_startup(void) /* {{{ */
10921143
executor_globals_ctor(executor_globals);
10931144
global_persistent_list = &EG(persistent_list);
10941145
zend_copy_ini_directives();
1146+
1147+
# ifdef ZEND_TIMER
1148+
zend_timer_create();
1149+
# endif
10951150
#else
10961151
global_map_ptr_last = CG(map_ptr_last);
10971152
#endif
@@ -1612,6 +1667,15 @@ ZEND_API ZEND_COLD ZEND_NORETURN void zend_error_noreturn(int type, const char *
16121667
abort();
16131668
}
16141669

1670+
ZEND_API ZEND_COLD void zend_strerror_noreturn(int type, int errn, const char *message)
1671+
{
1672+
char buf[1024];
1673+
if (!strerror_r(errn, buf, sizeof(buf)))
1674+
zend_error_noreturn(type, "%s: %d", message, errn);
1675+
1676+
zend_error_noreturn(type, "%s: %s (%d)", message, buf, errn);
1677+
}
1678+
16151679
ZEND_API ZEND_COLD void zend_error_zstr(int type, zend_string *message) {
16161680
zend_string *filename;
16171681
uint32_t lineno;

Zend/zend.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,9 @@ ZEND_API ZEND_COLD void zend_value_error(const char *format, ...) ZEND_ATTRIBUTE
353353

354354
ZEND_COLD void zenderror(const char *error);
355355

356+
/* For internal C errors */
357+
ZEND_API ZEND_COLD void zend_strerror_noreturn(int type, int errn, const char *message);
358+
356359
/* The following #define is used for code duality in PHP for Engine 1 & 2 */
357360
#define ZEND_STANDARD_CLASS_DEF_PTR zend_standard_class_def
358361
extern ZEND_API zend_class_entry *zend_standard_class_def;

Zend/zend_execute_API.c

Lines changed: 26 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,7 @@
3737
#include "zend_weakrefs.h"
3838
#include "zend_inheritance.h"
3939
#include "zend_observer.h"
40-
#ifdef HAVE_SYS_TIME_H
41-
#include <sys/time.h>
42-
#endif
43-
#ifdef HAVE_UNISTD_H
44-
#include <unistd.h>
45-
#endif
46-
#if defined(ZTS) && defined(HAVE_TIMER_CREATE)
47-
#include <time.h>
48-
#include <sys/syscall.h>
49-
#include <sys/types.h>
50-
// Musl Libc defines this macro, glibc does not
51-
// According to "man 2 timer_create" this field should always be available, but it's not
52-
# ifndef sigev_notify_thread_id
53-
# define sigev_notify_thread_id _sigev_un._tid
54-
# endif
55-
#endif
40+
#include "zend_timer.h"
5641

5742
ZEND_API void (*zend_execute_ex)(zend_execute_data *execute_data);
5843
ZEND_API void (*zend_execute_internal)(zend_execute_data *execute_data, zval *return_value);
@@ -181,23 +166,6 @@ void init_executor(void) /* {{{ */
181166
EG(vm_interrupt) = 0;
182167
EG(timed_out) = 0;
183168

184-
#if defined(ZTS) && defined(HAVE_TIMER_CREATE)
185-
struct sigevent sev;
186-
sev.sigev_notify = SIGEV_THREAD_ID;
187-
sev.sigev_value.sival_ptr = &EG(timer);
188-
sev.sigev_signo = SIGIO;
189-
sev.sigev_notify_thread_id = (pid_t) syscall(SYS_gettid);
190-
191-
if (timer_create(CLOCK_THREAD_CPUTIME_ID, &sev, &EG(timer)) != 0) {
192-
# ifdef TIMER_DEBUG
193-
fprintf(stderr, "error %d while creating timer %#jx on thread %d\n", errno, (uintmax_t) EG(timer), (pid_t) syscall(SYS_gettid));
194-
# endif
195-
}
196-
# ifdef TIMER_DEBUG
197-
else fprintf(stderr, "timer %#jx created on thread %d\n", (uintmax_t) EG(timer), syscall(SYS_gettid));
198-
# endif
199-
#endif
200-
201169
EG(exception) = NULL;
202170
EG(prev_exception) = NULL;
203171

@@ -413,6 +381,26 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown)
413381
zend_objects_store_free_object_storage(&EG(objects_store), fast_shutdown);
414382
}
415383

384+
#ifdef ZEND_TIMER
385+
static void zend_timer_settime(zend_long seconds) /* {{{ }*/
386+
{
387+
timer_t timer = EG(timer);
388+
if (timer == 0) zend_error_noreturn(E_ERROR, "Timer not created");
389+
390+
struct itimerspec its;
391+
its.it_value.tv_sec = seconds;
392+
its.it_value.tv_nsec = its.it_interval.tv_sec = its.it_interval.tv_nsec = 0;
393+
394+
int errn = timer_settime(timer, 0, &its, NULL);
395+
if (errn != 0) zend_strerror_noreturn(E_ERROR, errn, "Could not set timer");
396+
397+
# ifdef TIMER_DEBUG
398+
fprintf(stderr, "Timer %#jx set on thread %d (%ld seconds)\n", (uintmax_t) timer, (pid_t) syscall(SYS_gettid), seconds);
399+
# endif
400+
}
401+
/* }}} */
402+
#endif
403+
416404
void shutdown_executor(void) /* {{{ */
417405
{
418406
zend_string *key;
@@ -423,17 +411,6 @@ void shutdown_executor(void) /* {{{ */
423411
bool fast_shutdown = is_zend_mm() && !EG(full_tables_cleanup);
424412
#endif
425413

426-
#if defined(ZTS) && defined(HAVE_TIMER_CREATE)
427-
if (timer_delete(EG(timer)) != 0) {
428-
# ifdef TIMER_DEBUG
429-
fprintf(stderr, "error %d while deleting timer %#jx on thread %d\n", errno, (uintmax_t) EG(timer), (pid_t) syscall(SYS_gettid));
430-
# endif
431-
}
432-
# ifdef TIMER_DEBUG
433-
else fprintf(stderr, "timer %#jx deleted on thread %d\n", (uintmax_t) EG(timer), (pid_t) syscall(SYS_gettid));
434-
# endif
435-
#endif
436-
437414
zend_try {
438415
zend_stream_shutdown();
439416
} zend_end_try();
@@ -1352,7 +1329,7 @@ ZEND_API ZEND_NORETURN void ZEND_FASTCALL zend_timeout(void) /* {{{ */
13521329
/* }}} */
13531330

13541331
#ifndef ZEND_WIN32
1355-
# if defined(ZTS) && defined(HAVE_TIMER_CREATE)
1332+
# ifdef ZEND_TIMER
13561333
static void zend_timeout_handler(int dummy, siginfo_t *si, void *uc) /* {{{ */
13571334
{
13581335
if (si->si_value.sival_ptr != &EG(timer)) {
@@ -1465,25 +1442,8 @@ static void zend_set_timeout_ex(zend_long seconds, bool reset_signals) /* {{{ */
14651442
zend_error_noreturn(E_ERROR, "Could not queue new timer");
14661443
return;
14671444
}
1468-
#elif defined(ZTS) && defined(HAVE_TIMER_CREATE)
1469-
timer_t timer = EG(timer);
1470-
struct itimerspec its;
1471-
1472-
its.it_value.tv_sec = seconds;
1473-
its.it_value.tv_nsec = 0;
1474-
its.it_interval.tv_sec = 0;
1475-
its.it_interval.tv_nsec = 0;
1476-
1477-
if (timer_settime(timer, 0, &its, NULL) != 0) {
1478-
#ifdef TIMER_DEBUG
1479-
fprintf(stderr, "unable to set timer %#jx on thread %d\n", (uintmax_t) timer, (pid_t) syscall(SYS_gettid));
1480-
#endif
1481-
1482-
return;
1483-
}
1484-
# ifdef TIMER_DEBUG
1485-
else fprintf(stderr, "timer %#jx set on thread %d (%ld seconds)\n", (uintmax_t) timer, (pid_t) syscall(SYS_gettid), seconds);
1486-
# endif
1445+
#elif defined(ZEND_TIMER)
1446+
zend_timer_settime(seconds);
14871447

14881448
if (reset_signals) {
14891449
sigset_t sigset;
@@ -1562,6 +1522,8 @@ void zend_unset_timeout(void) /* {{{ */
15621522
}
15631523
tq_timer = NULL;
15641524
}
1525+
#elif ZEND_TIMER
1526+
zend_timer_settime(0);
15651527
#elif defined(HAVE_SETITIMER)
15661528
if (EG(timeout_seconds)) {
15671529
struct itimerval no_timeout;

Zend/zend_globals.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323

2424
#include <setjmp.h>
25-
#if defined(ZTS) && defined(HAVE_TIMER_CREATE)
25+
#ifdef ZEND_TIMER
2626
#include <time.h>
2727
#endif
2828

@@ -269,7 +269,7 @@ struct _zend_executor_globals {
269269
uint32_t num_errors;
270270
zend_error_info **errors;
271271

272-
#if defined(ZTS) && defined(HAVE_TIMER_CREATE)
272+
#ifndef ZEND_TIMER
273273
timer_t timer;
274274
#endif
275275

Zend/zend_timer.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#if !defined(ZEND_TIMER_H) && defined(ZTS) && defined(__linux__) && defined(HAVE_TIMER_CREATE)
2+
#define ZEND_TIMER_H
3+
4+
#define ZEND_TIMER 1
5+
6+
#include <signal.h>
7+
#include <time.h>
8+
#include <unistd.h>
9+
#include <sys/syscall.h>
10+
#include <sys/types.h>
11+
12+
// Musl Libc defines this macro, glibc does not
13+
// According to "man 2 timer_create" this field should always be available, but it's not: https://sourceware.org/bugzilla/show_bug.cgi?id=27417
14+
# ifndef sigev_notify_thread_id
15+
# define sigev_notify_thread_id _sigev_un._tid
16+
# endif
17+
18+
#endif

0 commit comments

Comments
 (0)