v8js_timer.cc 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2013 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | http://www.opensource.org/licenses/mit-license.php MIT License |
  8. +----------------------------------------------------------------------+
  9. | Author: Jani Taskinen <[email protected]> |
  10. | Author: Patrick Reilly <[email protected]> |
  11. +----------------------------------------------------------------------+
  12. */
  13. #ifdef HAVE_CONFIG_H
  14. #include "config.h"
  15. #endif
  16. extern "C" {
  17. #include "php.h"
  18. #include "ext/date/php_date.h"
  19. #include "ext/standard/php_string.h"
  20. #include "zend_interfaces.h"
  21. #include "zend_closures.h"
  22. #include "ext/spl/spl_exceptions.h"
  23. #include "zend_exceptions.h"
  24. }
  25. #include "php_v8js_macros.h"
  26. #include "v8js_v8.h"
  27. #include "v8js_exceptions.h"
  28. static void v8js_timer_interrupt_handler(v8::Isolate *isolate, void *data) { /* {{{ */
  29. #ifdef ZTS
  30. TSRMLS_D = (void ***) data;
  31. #endif
  32. if (!V8JSG(timer_stack).size()) {
  33. return;
  34. }
  35. v8::Locker locker(isolate);
  36. v8::HeapStatistics hs;
  37. bool send_notification = false;
  38. bool has_sent_notification = false;
  39. do {
  40. if (send_notification) {
  41. #if PHP_V8_API_VERSION >= 3028036
  42. isolate->LowMemoryNotification();
  43. #else
  44. v8::V8::LowMemoryNotification();
  45. #endif
  46. has_sent_notification = true;
  47. }
  48. isolate->GetHeapStatistics(&hs);
  49. V8JSG(timer_mutex).lock();
  50. for (std::deque< v8js_timer_ctx* >::iterator it = V8JSG(timer_stack).begin();
  51. it != V8JSG(timer_stack).end(); it ++) {
  52. v8js_timer_ctx *timer_ctx = *it;
  53. v8js_ctx *c = timer_ctx->ctx;
  54. if(c->isolate != isolate || timer_ctx->killed) {
  55. continue;
  56. }
  57. if (timer_ctx->memory_limit > 0 && hs.used_heap_size() > timer_ctx->memory_limit) {
  58. if (has_sent_notification) {
  59. timer_ctx->killed = true;
  60. v8::V8::TerminateExecution(c->isolate);
  61. c->memory_limit_hit = true;
  62. } else {
  63. // force garbage collection, then check again
  64. send_notification = true;
  65. break;
  66. }
  67. }
  68. }
  69. V8JSG(timer_mutex).unlock();
  70. } while(send_notification != has_sent_notification);
  71. }
  72. /* }}} */
  73. void v8js_timer_thread(TSRMLS_D) /* {{{ */
  74. {
  75. while (!V8JSG(timer_stop)) {
  76. V8JSG(timer_mutex).lock();
  77. if (V8JSG(timer_stack).size()) {
  78. v8js_timer_ctx *timer_ctx = V8JSG(timer_stack).front();
  79. v8js_ctx *c = timer_ctx->ctx;
  80. std::chrono::time_point<std::chrono::high_resolution_clock> now = std::chrono::high_resolution_clock::now();
  81. if(timer_ctx->killed) {
  82. /* execution already terminated, nothing to check anymore,
  83. * but wait for caller to pop this timer context. */
  84. }
  85. else if(timer_ctx->time_limit > 0 && now > timer_ctx->time_point) {
  86. timer_ctx->killed = true;
  87. v8::V8::TerminateExecution(c->isolate);
  88. c->time_limit_hit = true;
  89. }
  90. else if (timer_ctx->memory_limit > 0) {
  91. /* If a memory_limit is set, we need to interrupt execution
  92. * and check heap size within the callback. We must *not*
  93. * directly call GetHeapStatistics here, since we don't have
  94. * a v8::Locker on the isolate, but are expected to hold one,
  95. * and cannot aquire it as v8 is executing the script ... */
  96. void *data = NULL;
  97. #ifdef ZTS
  98. data = (void *) TSRMLS_C;
  99. #endif
  100. c->isolate->RequestInterrupt(v8js_timer_interrupt_handler, data);
  101. }
  102. }
  103. V8JSG(timer_mutex).unlock();
  104. // Sleep for 10ms
  105. #ifdef _WIN32
  106. concurrency::wait(10);
  107. #else
  108. std::chrono::milliseconds duration(10);
  109. std::this_thread::sleep_for(duration);
  110. #endif
  111. }
  112. }
  113. /* }}} */
  114. void v8js_timer_push(long time_limit, long memory_limit, v8js_ctx *c TSRMLS_DC) /* {{{ */
  115. {
  116. V8JSG(timer_mutex).lock();
  117. // Create context for this timer
  118. v8js_timer_ctx *timer_ctx = (v8js_timer_ctx *)emalloc(sizeof(v8js_timer_ctx));
  119. // Calculate the time point when the time limit is exceeded
  120. std::chrono::milliseconds duration(time_limit);
  121. std::chrono::time_point<std::chrono::high_resolution_clock> from = std::chrono::high_resolution_clock::now();
  122. // Push the timer context
  123. timer_ctx->time_limit = time_limit;
  124. timer_ctx->memory_limit = memory_limit;
  125. timer_ctx->time_point = from + duration;
  126. timer_ctx->ctx = c;
  127. timer_ctx->killed = false;
  128. V8JSG(timer_stack).push_front(timer_ctx);
  129. V8JSG(timer_mutex).unlock();
  130. }
  131. /* }}} */
  132. /*
  133. * Local variables:
  134. * tab-width: 4
  135. * c-basic-offset: 4
  136. * indent-tabs-mode: t
  137. * End:
  138. * vim600: noet sw=4 ts=4 fdm=marker
  139. * vim<600: noet sw=4 ts=4
  140. */