php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
zend_max_execution_timer.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Kévin Dunglas <kevin@dunglas.dev> |
14 +----------------------------------------------------------------------+
15 */
16
17#ifdef ZEND_MAX_EXECUTION_TIMERS
18
19#include <stdio.h>
20#include <signal.h>
21#include <time.h>
22#include <unistd.h>
23#include <errno.h>
24#include <sys/syscall.h>
25#include <sys/types.h>
26# ifdef __FreeBSD__
27# include <pthread_np.h>
28# endif
29
30#include "zend.h"
31#include "zend_globals.h"
32#include "zend_portability.h"
33
34#if __has_feature(memory_sanitizer)
35# include <sanitizer/msan_interface.h>
36#endif
37
38// Musl Libc defines this macro, glibc does not
39// 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
40# ifndef sigev_notify_thread_id
41# define sigev_notify_thread_id _sigev_un._tid
42# endif
43
44// FreeBSD doesn't support CLOCK_BOOTTIME
45# ifdef __FreeBSD__
46# define ZEND_MAX_EXECUTION_TIMERS_CLOCK CLOCK_MONOTONIC
47# else
48# define ZEND_MAX_EXECUTION_TIMERS_CLOCK CLOCK_BOOTTIME
49# endif
50
51ZEND_API void zend_max_execution_timer_init(void) /* {{{ */
52{
53 pid_t pid = getpid();
54
55 if (EG(pid) == pid) {
56 return;
57 }
58
59 struct sigevent sev;
60 sev.sigev_notify = SIGEV_THREAD_ID;
61 sev.sigev_value.sival_ptr = &EG(max_execution_timer_timer);
62 sev.sigev_signo = SIGRTMIN;
63# ifdef __FreeBSD__
64 sev.sigev_notify_thread_id = pthread_getthreadid_np();
65# else
66 sev.sigev_notify_thread_id = (pid_t) syscall(SYS_gettid);
67# endif
68
69#if __has_feature(memory_sanitizer)
70 /* MSan does not intercept timer_create() */
71 __msan_unpoison(&EG(max_execution_timer_timer),
72 sizeof(EG(max_execution_timer_timer)));
73#endif
74
75 // Measure wall time instead of CPU time as originally planned now that it is possible https://github.com/php/php-src/pull/6504#issuecomment-1370303727
76 if (timer_create(ZEND_MAX_EXECUTION_TIMERS_CLOCK, &sev, &EG(max_execution_timer_timer)) != 0) {
77 zend_strerror_noreturn(E_ERROR, errno, "Could not create timer");
78 }
79
80 EG(pid) = pid;
81
82# ifdef MAX_EXECUTION_TIMERS_DEBUG
83 fprintf(stderr, "Timer %#jx created on thread %d\n", (uintmax_t) EG(max_execution_timer_timer), sev.sigev_notify_thread_id);
84# endif
85
86 sigaction(sev.sigev_signo, NULL, &EG(oldact));
87}
88/* }}} */
89
90void zend_max_execution_timer_settime(zend_long seconds) /* {{{ }*/
91{
92 /* Timer not initialized or shutdown. */
93 if (!EG(pid)) {
94 return;
95 }
96
97 timer_t timer = EG(max_execution_timer_timer);
98
99 // Prevent EINVAL error
100 if (seconds < 0 || seconds > 999999999) {
101 seconds = 0;
102 }
103
104 struct itimerspec its;
105 its.it_value.tv_sec = seconds;
106 its.it_value.tv_nsec = its.it_interval.tv_sec = its.it_interval.tv_nsec = 0;
107
108# ifdef MAX_EXECUTION_TIMERS_DEBUG
109 fprintf(stderr, "Setting timer %#jx on thread %d (%ld seconds)...\n", (uintmax_t) timer, (pid_t) syscall(SYS_gettid), seconds);
110# endif
111
112 if (timer_settime(timer, 0, &its, NULL) != 0) {
113 zend_strerror_noreturn(E_ERROR, errno, "Could not set timer");
114 }
115}
116/* }}} */
117
118void zend_max_execution_timer_shutdown(void) /* {{{ */
119{
120 /* Don't try to delete a timer created before a call to fork() */
121 if (EG(pid) != getpid()) {
122 return;
123 }
124
125 EG(pid) = 0;
126
127 timer_t timer = EG(max_execution_timer_timer);
128
129# ifdef MAX_EXECUTION_TIMERS_DEBUG
130 fprintf(stderr, "Deleting timer %#jx on thread %d...\n", (uintmax_t) timer, (pid_t) syscall(SYS_gettid));
131# endif
132
133 int err = timer_delete(timer);
134 if (err != 0) {
135 zend_strerror_noreturn(E_ERROR, errno, "Could not delete timer");
136 }
137}
138/* }}}} */
139
140#endif
fprintf($stream, string $format, mixed ... $values)
char * err
Definition ffi.c:3029
#define NULL
Definition gdcache.h:45
const SIGRTMIN
#define errno
ZEND_API ZEND_COLD ZEND_NORETURN void zend_strerror_noreturn(int type, int errn, const char *message)
Definition zend.c:1713
#define ZEND_API
#define E_ERROR
Definition zend_errors.h:23
#define EG(v)
int32_t zend_long
Definition zend_long.h:42
#define zend_max_execution_timer_shutdown()
#define zend_max_execution_timer_settime(seconds)
#define zend_max_execution_timer_init()