@@ -1260,9 +1260,14 @@ ZEND_API zend_result zend_eval_string_ex(const char *str, zval *retval_ptr, cons
1260
1260
/* }}} */
1261
1261
1262
1262
static void zend_set_timeout_ex (zend_long seconds , bool reset_signals );
1263
+ static void zend_set_wall_timeout_ex (zend_long seconds , bool reset_signals );
1263
1264
1264
1265
ZEND_API ZEND_NORETURN void ZEND_FASTCALL zend_timeout (void ) /* {{{ */
1265
1266
{
1267
+ zend_long original_timed_out = EG (timed_out );
1268
+ zend_long original_timeout_seconds = EG (timeout_seconds );
1269
+ zend_long original_wall_timeout_seconds = EG (wall_timeout_seconds );
1270
+
1266
1271
#if defined(PHP_WIN32 )
1267
1272
# ifndef ZTS
1268
1273
/* No action is needed if we're timed out because zero seconds are
@@ -1278,10 +1283,18 @@ ZEND_API ZEND_NORETURN void ZEND_FASTCALL zend_timeout(void) /* {{{ */
1278
1283
# endif
1279
1284
#else
1280
1285
EG (timed_out ) = 0 ;
1281
- zend_set_timeout_ex (0 , 1 );
1286
+ if (original_timed_out == 1 ) {
1287
+ zend_set_timeout_ex (0 , 1 );
1288
+ } else {
1289
+ zend_set_wall_timeout_ex (0 , 1 );
1290
+ }
1282
1291
#endif
1283
1292
1284
- zend_error_noreturn (E_ERROR , "Maximum execution time of " ZEND_LONG_FMT " second%s exceeded" , EG (timeout_seconds ), EG (timeout_seconds ) == 1 ? "" : "s" );
1293
+ if (original_timed_out == 1 ) {
1294
+ zend_error_noreturn (E_ERROR , "Maximum execution time of " ZEND_LONG_FMT " second%s exceeded" , original_timeout_seconds , original_timeout_seconds == 1 ? "" : "s" );
1295
+ } else {
1296
+ 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" );
1297
+ }
1285
1298
}
1286
1299
/* }}} */
1287
1300
@@ -1337,6 +1350,57 @@ static void zend_timeout_handler(int dummy) /* {{{ */
1337
1350
/* }}} */
1338
1351
#endif
1339
1352
1353
+ #ifndef ZEND_WIN32
1354
+ static void zend_wall_timeout_handler (int dummy ) /* {{{ */
1355
+ {
1356
+ #ifndef ZTS
1357
+ if (EG (timed_out )) {
1358
+ /* Die on hard timeout */
1359
+ const char * error_filename = NULL ;
1360
+ uint32_t error_lineno = 0 ;
1361
+ char log_buffer [2048 ];
1362
+ int output_len = 0 ;
1363
+
1364
+ if (zend_is_compiling ()) {
1365
+ error_filename = ZSTR_VAL (zend_get_compiled_filename ());
1366
+ error_lineno = zend_get_compiled_lineno ();
1367
+ } else if (zend_is_executing ()) {
1368
+ error_filename = zend_get_executed_filename ();
1369
+ if (error_filename [0 ] == '[' ) { /* [no active file] */
1370
+ error_filename = NULL ;
1371
+ error_lineno = 0 ;
1372
+ } else {
1373
+ error_lineno = zend_get_executed_lineno ();
1374
+ }
1375
+ }
1376
+ if (!error_filename ) {
1377
+ error_filename = "Unknown" ;
1378
+ }
1379
+
1380
+ 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 );
1381
+ if (output_len > 0 ) {
1382
+ zend_quiet_write (2 , log_buffer , MIN (output_len , sizeof (log_buffer )));
1383
+ }
1384
+ _exit (124 );
1385
+ }
1386
+ #endif
1387
+
1388
+ if (zend_on_timeout ) {
1389
+ zend_on_timeout (EG (wall_timeout_seconds ));
1390
+ }
1391
+
1392
+ EG (timed_out ) = 2 ;
1393
+ EG (vm_interrupt ) = 1 ;
1394
+
1395
+ #ifndef ZTS
1396
+ if (EG (hard_timeout )) {
1397
+ zend_set_wall_timeout_ex (EG (hard_timeout ), 1 );
1398
+ }
1399
+ #endif
1400
+ }
1401
+ /* }}} */
1402
+ #endif
1403
+
1340
1404
#ifdef ZEND_WIN32
1341
1405
VOID CALLBACK tq_timer_cb (PVOID arg , BOOLEAN timed_out )
1342
1406
{
@@ -1431,15 +1495,89 @@ static void zend_set_timeout_ex(zend_long seconds, bool reset_signals) /* {{{ */
1431
1495
}
1432
1496
/* }}} */
1433
1497
1434
- void zend_set_timeout (zend_long seconds , bool reset_signals ) /* {{{ */
1498
+ static void zend_set_wall_timeout_ex (zend_long seconds , bool reset_signals ) /* {{{ */
1435
1499
{
1500
+ #ifdef ZEND_WIN32
1501
+ zend_executor_globals * eg ;
1502
+
1503
+ if (!seconds ) {
1504
+ return ;
1505
+ }
1506
+
1507
+ /* Don't use ChangeTimerQueueTimer() as it will not restart an expired
1508
+ * timer, so we could end up with just an ignored timeout. Instead
1509
+ * delete and recreate. */
1510
+ if (NULL != tq_timer ) {
1511
+ if (!DeleteTimerQueueTimer (NULL , tq_timer , INVALID_HANDLE_VALUE )) {
1512
+ tq_timer = NULL ;
1513
+ zend_error_noreturn (E_ERROR , "Could not delete queued timer" );
1514
+ return ;
1515
+ }
1516
+ tq_timer = NULL ;
1517
+ }
1518
+
1519
+ /* XXX passing NULL means the default timer queue provided by the system is used */
1520
+ eg = ZEND_MODULE_GLOBALS_BULK (executor );
1521
+ if (!CreateTimerQueueTimer (& tq_timer , NULL , (WAITORTIMERCALLBACK )tq_timer_cb , (VOID * )eg , seconds * 1000 , 0 , WT_EXECUTEONLYONCE )) {
1522
+ tq_timer = NULL ;
1523
+ zend_error_noreturn (E_ERROR , "Could not queue new timer" );
1524
+ return ;
1525
+ }
1526
+ #elif defined(HAVE_SETITIMER )
1527
+ {
1528
+ struct itimerval t_r ; /* timeout requested */
1529
+ int signo ;
1530
+
1531
+ if (seconds ) {
1532
+ t_r .it_value .tv_sec = seconds ;
1533
+ t_r .it_value .tv_usec = t_r .it_interval .tv_sec = t_r .it_interval .tv_usec = 0 ;
1534
+
1535
+ setitimer (ITIMER_REAL , & t_r , NULL );
1536
+ }
1537
+ signo = SIGALRM ;
1436
1538
1539
+ if (reset_signals ) {
1540
+
1541
+ # ifdef ZEND_SIGNALS
1542
+ zend_signal (signo , zend_wall_timeout_handler );
1543
+ # else
1544
+ sigset_t sigset ;
1545
+ # ifdef HAVE_SIGACTION
1546
+ struct sigaction act ;
1547
+
1548
+ act .sa_handler = zend_wall_timeout_handler ;
1549
+ sigemptyset (& act .sa_mask );
1550
+ act .sa_flags = SA_RESETHAND | SA_NODEFER ;
1551
+ sigaction (signo , & act , NULL );
1552
+ # else
1553
+ signal (signo , zend_wall_timeout_handler );
1554
+ # endif /* HAVE_SIGACTION */
1555
+ sigemptyset (& sigset );
1556
+ sigaddset (& sigset , signo );
1557
+ sigprocmask (SIG_UNBLOCK , & sigset , NULL );
1558
+ # endif /* ZEND_SIGNALS */
1559
+ }
1560
+ }
1561
+ #endif /* HAVE_SETITIMER */
1562
+ }
1563
+ /* }}} */
1564
+
1565
+ void zend_set_timeout (zend_long seconds , bool reset_signals ) /* {{{ */
1566
+ {
1437
1567
EG (timeout_seconds ) = seconds ;
1438
1568
zend_set_timeout_ex (seconds , reset_signals );
1439
1569
EG (timed_out ) = 0 ;
1440
1570
}
1441
1571
/* }}} */
1442
1572
1573
+ void zend_set_wall_timeout (zend_long seconds , bool reset_signals ) /* {{{ */
1574
+ {
1575
+ EG (wall_timeout_seconds ) = seconds ;
1576
+ zend_set_wall_timeout_ex (seconds , reset_signals );
1577
+ EG (timed_out ) = 0 ;
1578
+ }
1579
+ /* }}} */
1580
+
1443
1581
void zend_unset_timeout (void ) /* {{{ */
1444
1582
{
1445
1583
#ifdef ZEND_WIN32
@@ -1469,6 +1607,31 @@ void zend_unset_timeout(void) /* {{{ */
1469
1607
}
1470
1608
/* }}} */
1471
1609
1610
+ void zend_unset_wall_timeout (void ) /* {{{ */
1611
+ {
1612
+ #ifdef ZEND_WIN32
1613
+ if (NULL != tq_timer ) {
1614
+ if (!DeleteTimerQueueTimer (NULL , tq_timer , INVALID_HANDLE_VALUE )) {
1615
+ EG (timed_out ) = 0 ;
1616
+ tq_timer = NULL ;
1617
+ zend_error_noreturn (E_ERROR , "Could not delete queued timer" );
1618
+ return ;
1619
+ }
1620
+ tq_timer = NULL ;
1621
+ }
1622
+ #elif defined(HAVE_SETITIMER )
1623
+ if (EG (wall_timeout_seconds )) {
1624
+ struct itimerval no_timeout ;
1625
+
1626
+ 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 ;
1627
+
1628
+ setitimer (ITIMER_REAL , & no_timeout , NULL );
1629
+ }
1630
+ #endif
1631
+ EG (timed_out ) = 0 ;
1632
+ }
1633
+ /* }}} */
1634
+
1472
1635
zend_class_entry * zend_fetch_class (zend_string * class_name , int fetch_type ) /* {{{ */
1473
1636
{
1474
1637
zend_class_entry * ce , * scope ;
0 commit comments