@@ -1246,9 +1246,14 @@ ZEND_API zend_result zend_eval_string_ex(const char *str, zval *retval_ptr, cons
1246
1246
/* }}} */
1247
1247
1248
1248
static void zend_set_timeout_ex (zend_long seconds , bool reset_signals );
1249
+ static void zend_set_wall_timeout_ex (zend_long seconds , bool reset_signals );
1249
1250
1250
1251
ZEND_API ZEND_NORETURN void ZEND_FASTCALL zend_timeout (void ) /* {{{ */
1251
1252
{
1253
+ zend_long original_timed_out = EG (timed_out );
1254
+ zend_long original_timeout_seconds = EG (timeout_seconds );
1255
+ zend_long original_wall_timeout_seconds = EG (wall_timeout_seconds );
1256
+
1252
1257
#if defined(PHP_WIN32 )
1253
1258
# ifndef ZTS
1254
1259
/* No action is needed if we're timed out because zero seconds are
@@ -1264,10 +1269,18 @@ ZEND_API ZEND_NORETURN void ZEND_FASTCALL zend_timeout(void) /* {{{ */
1264
1269
# endif
1265
1270
#else
1266
1271
EG (timed_out ) = 0 ;
1267
- zend_set_timeout_ex (0 , 1 );
1272
+ if (original_timed_out == 1 ) {
1273
+ zend_set_timeout_ex (0 , 1 );
1274
+ } else {
1275
+ zend_set_wall_timeout_ex (0 , 1 );
1276
+ }
1268
1277
#endif
1269
1278
1270
- zend_error_noreturn (E_ERROR , "Maximum execution time of " ZEND_LONG_FMT " second%s exceeded" , EG (timeout_seconds ), EG (timeout_seconds ) == 1 ? "" : "s" );
1279
+ if (original_timed_out == 1 ) {
1280
+ zend_error_noreturn (E_ERROR , "Maximum execution time of " ZEND_LONG_FMT " second%s exceeded" , original_timeout_seconds , original_timeout_seconds == 1 ? "" : "s" );
1281
+ } else {
1282
+ zend_error_noreturn (E_ERROR , "Maximum execution wall-time of " ZEND_LONG_FMT " second%s exceeded" , original_wall_timeout_seconds , original_wall_timeout_seconds == 1 ? "" : "s" );
1283
+ }
1271
1284
}
1272
1285
/* }}} */
1273
1286
@@ -1323,6 +1336,57 @@ static void zend_timeout_handler(int dummy) /* {{{ */
1323
1336
/* }}} */
1324
1337
#endif
1325
1338
1339
+ #ifndef ZEND_WIN32
1340
+ static void zend_wall_timeout_handler (int dummy ) /* {{{ */
1341
+ {
1342
+ #ifndef ZTS
1343
+ if (EG (timed_out )) {
1344
+ /* Die on hard timeout */
1345
+ const char * error_filename = NULL ;
1346
+ uint32_t error_lineno = 0 ;
1347
+ char log_buffer [2048 ];
1348
+ int output_len = 0 ;
1349
+
1350
+ if (zend_is_compiling ()) {
1351
+ error_filename = ZSTR_VAL (zend_get_compiled_filename ());
1352
+ error_lineno = zend_get_compiled_lineno ();
1353
+ } else if (zend_is_executing ()) {
1354
+ error_filename = zend_get_executed_filename ();
1355
+ if (error_filename [0 ] == '[' ) { /* [no active file] */
1356
+ error_filename = NULL ;
1357
+ error_lineno = 0 ;
1358
+ } else {
1359
+ error_lineno = zend_get_executed_lineno ();
1360
+ }
1361
+ }
1362
+ if (!error_filename ) {
1363
+ error_filename = "Unknown" ;
1364
+ }
1365
+
1366
+ output_len = snprintf (log_buffer , sizeof (log_buffer ), "\nFatal error: Maximum execution wall-time of " ZEND_LONG_FMT "+" ZEND_LONG_FMT " seconds exceeded (terminated) in %s on line %d\n" , EG (wall_timeout_seconds ), EG (hard_timeout ), error_filename , error_lineno );
1367
+ if (output_len > 0 ) {
1368
+ zend_quiet_write (2 , log_buffer , MIN (output_len , sizeof (log_buffer )));
1369
+ }
1370
+ _exit (124 );
1371
+ }
1372
+ #endif
1373
+
1374
+ if (zend_on_timeout ) {
1375
+ zend_on_timeout (EG (wall_timeout_seconds ));
1376
+ }
1377
+
1378
+ EG (timed_out ) = 2 ;
1379
+ EG (vm_interrupt ) = 1 ;
1380
+
1381
+ #ifndef ZTS
1382
+ if (EG (hard_timeout )) {
1383
+ zend_set_wall_timeout_ex (EG (hard_timeout ), 1 );
1384
+ }
1385
+ #endif
1386
+ }
1387
+ /* }}} */
1388
+ #endif
1389
+
1326
1390
#ifdef ZEND_WIN32
1327
1391
VOID CALLBACK tq_timer_cb (PVOID arg , BOOLEAN timed_out )
1328
1392
{
@@ -1417,15 +1481,89 @@ static void zend_set_timeout_ex(zend_long seconds, bool reset_signals) /* {{{ */
1417
1481
}
1418
1482
/* }}} */
1419
1483
1420
- void zend_set_timeout (zend_long seconds , bool reset_signals ) /* {{{ */
1484
+ static void zend_set_wall_timeout_ex (zend_long seconds , bool reset_signals ) /* {{{ */
1421
1485
{
1486
+ #ifdef ZEND_WIN32
1487
+ zend_executor_globals * eg ;
1488
+
1489
+ if (!seconds ) {
1490
+ return ;
1491
+ }
1492
+
1493
+ /* Don't use ChangeTimerQueueTimer() as it will not restart an expired
1494
+ * timer, so we could end up with just an ignored timeout. Instead
1495
+ * delete and recreate. */
1496
+ if (NULL != tq_timer ) {
1497
+ if (!DeleteTimerQueueTimer (NULL , tq_timer , INVALID_HANDLE_VALUE )) {
1498
+ tq_timer = NULL ;
1499
+ zend_error_noreturn (E_ERROR , "Could not delete queued timer" );
1500
+ return ;
1501
+ }
1502
+ tq_timer = NULL ;
1503
+ }
1504
+
1505
+ /* XXX passing NULL means the default timer queue provided by the system is used */
1506
+ eg = ZEND_MODULE_GLOBALS_BULK (executor );
1507
+ if (!CreateTimerQueueTimer (& tq_timer , NULL , (WAITORTIMERCALLBACK )tq_timer_cb , (VOID * )eg , seconds * 1000 , 0 , WT_EXECUTEONLYONCE )) {
1508
+ tq_timer = NULL ;
1509
+ zend_error_noreturn (E_ERROR , "Could not queue new timer" );
1510
+ return ;
1511
+ }
1512
+ #elif defined(HAVE_SETITIMER )
1513
+ {
1514
+ struct itimerval t_r ; /* timeout requested */
1515
+ int signo ;
1516
+
1517
+ if (seconds ) {
1518
+ t_r .it_value .tv_sec = seconds ;
1519
+ t_r .it_value .tv_usec = t_r .it_interval .tv_sec = t_r .it_interval .tv_usec = 0 ;
1520
+
1521
+ setitimer (ITIMER_REAL , & t_r , NULL );
1522
+ }
1523
+ signo = SIGALRM ;
1422
1524
1525
+ if (reset_signals ) {
1526
+
1527
+ # ifdef ZEND_SIGNALS
1528
+ zend_signal (signo , zend_wall_timeout_handler );
1529
+ # else
1530
+ sigset_t sigset ;
1531
+ # ifdef HAVE_SIGACTION
1532
+ struct sigaction act ;
1533
+
1534
+ act .sa_handler = zend_wall_timeout_handler ;
1535
+ sigemptyset (& act .sa_mask );
1536
+ act .sa_flags = SA_RESETHAND | SA_NODEFER ;
1537
+ sigaction (signo , & act , NULL );
1538
+ # else
1539
+ signal (signo , zend_wall_timeout_handler );
1540
+ # endif /* HAVE_SIGACTION */
1541
+ sigemptyset (& sigset );
1542
+ sigaddset (& sigset , signo );
1543
+ sigprocmask (SIG_UNBLOCK , & sigset , NULL );
1544
+ # endif /* ZEND_SIGNALS */
1545
+ }
1546
+ }
1547
+ #endif /* HAVE_SETITIMER */
1548
+ }
1549
+ /* }}} */
1550
+
1551
+ void zend_set_timeout (zend_long seconds , bool reset_signals ) /* {{{ */
1552
+ {
1423
1553
EG (timeout_seconds ) = seconds ;
1424
1554
zend_set_timeout_ex (seconds , reset_signals );
1425
1555
EG (timed_out ) = 0 ;
1426
1556
}
1427
1557
/* }}} */
1428
1558
1559
+ void zend_set_wall_timeout (zend_long seconds , bool reset_signals ) /* {{{ */
1560
+ {
1561
+ EG (wall_timeout_seconds ) = seconds ;
1562
+ zend_set_wall_timeout_ex (seconds , reset_signals );
1563
+ EG (timed_out ) = 0 ;
1564
+ }
1565
+ /* }}} */
1566
+
1429
1567
void zend_unset_timeout (void ) /* {{{ */
1430
1568
{
1431
1569
#ifdef ZEND_WIN32
@@ -1455,6 +1593,31 @@ void zend_unset_timeout(void) /* {{{ */
1455
1593
}
1456
1594
/* }}} */
1457
1595
1596
+ void zend_unset_wall_timeout (void ) /* {{{ */
1597
+ {
1598
+ #ifdef ZEND_WIN32
1599
+ if (NULL != tq_timer ) {
1600
+ if (!DeleteTimerQueueTimer (NULL , tq_timer , INVALID_HANDLE_VALUE )) {
1601
+ EG (timed_out ) = 0 ;
1602
+ tq_timer = NULL ;
1603
+ zend_error_noreturn (E_ERROR , "Could not delete queued timer" );
1604
+ return ;
1605
+ }
1606
+ tq_timer = NULL ;
1607
+ }
1608
+ #elif defined(HAVE_SETITIMER )
1609
+ if (EG (wall_timeout_seconds )) {
1610
+ struct itimerval no_timeout ;
1611
+
1612
+ no_timeout .it_value .tv_sec = no_timeout .it_value .tv_usec = no_timeout .it_interval .tv_sec = no_timeout .it_interval .tv_usec = 0 ;
1613
+
1614
+ setitimer (ITIMER_REAL , & no_timeout , NULL );
1615
+ }
1616
+ #endif
1617
+ EG (timed_out ) = 0 ;
1618
+ }
1619
+ /* }}} */
1620
+
1458
1621
zend_class_entry * zend_fetch_class (zend_string * class_name , int fetch_type ) /* {{{ */
1459
1622
{
1460
1623
zend_class_entry * ce , * scope ;
0 commit comments