Browse Source

Use isolate->RequestInterrupt to get heap size

Stefan Siegl 10 years ago
parent
commit
2252169a98
2 changed files with 58 additions and 24 deletions
  1. 4 4
      tests/set_time_limit_004.phpt
  2. 54 20
      v8js.cc

+ 4 - 4
tests/set_time_limit_004.phpt

@@ -7,9 +7,9 @@ Test V8::setTimeLimit() : Time limit can be prolonged
 
 $JS = <<< EOT
 var text = "abcdefghijklmnopqrstuvwyxz0123456789";
-/* Spend 20 * >10ms in the loop, i.e. at least 200ms; hence
+/* Spend 30 * >10ms in the loop, i.e. at least 300ms; hence
  * it should be killed if prolonging doesn't work. */
-for (var j = 0; j < 20; ++j) {
+for (var j = 0; j < 30; ++j) {
     PHP.prolongTimeLimit();
     var start = (new Date()).getTime();
     var encoded = encodeURI(text);
@@ -22,10 +22,10 @@ for (var j = 0; j < 20; ++j) {
 EOT;
 
 $v8 = new V8Js();
-$v8->setTimeLimit(25);
+$v8->setTimeLimit(100);
 
 $v8->prolongTimeLimit = function() use ($v8) {
-    $v8->setTimeLimit(25);
+    $v8->setTimeLimit(100);
 };
 
 $v8->executeString($JS);

+ 54 - 20
v8js.cc

@@ -1081,40 +1081,74 @@ static void php_v8js_terminate_execution(php_v8js_ctx *c TSRMLS_DC)
 	// This timer will be removed from stack by the parent thread.
 }
 
+static void php_v8js_timer_interrupt_handler(v8::Isolate *isolate, void *data) { /* {{{ */
+#ifdef ZTS
+	TSRMLS_D = (void ***) data;
+#endif
+
+	if (!V8JSG(timer_stack).size()) {
+		return;
+	}
+
+	v8::Locker locker(isolate);
+	v8::HeapStatistics hs;
+	isolate->GetHeapStatistics(&hs);
+
+	V8JSG(timer_mutex).lock();
+
+	for (std::deque< php_v8js_timer_ctx* >::iterator it = V8JSG(timer_stack).begin();
+		 it != V8JSG(timer_stack).end(); it ++) {
+		php_v8js_timer_ctx *timer_ctx = *it;
+		php_v8js_ctx *c = timer_ctx->v8js_ctx;
+
+		if(c->isolate != isolate || timer_ctx->killed) {
+			continue;
+		}
+
+		if (timer_ctx->memory_limit > 0 && hs.used_heap_size() > timer_ctx->memory_limit) {
+			timer_ctx->killed = true;
+			php_v8js_terminate_execution(c TSRMLS_CC);
+			c->memory_limit_hit = true;
+		}
+	}
+
+	V8JSG(timer_mutex).unlock();
+}
+/* }}} */
+
 static void php_v8js_timer_thread(TSRMLS_D)
 {
 	while (!V8JSG(timer_stop)) {
-		std::chrono::time_point<std::chrono::high_resolution_clock> now = std::chrono::high_resolution_clock::now();
-		v8::HeapStatistics hs;
 
+		V8JSG(timer_mutex).lock();
 		if (V8JSG(timer_stack).size()) {
-			// Get the current timer context
 			php_v8js_timer_ctx *timer_ctx = V8JSG(timer_stack).front();
 			php_v8js_ctx *c = timer_ctx->v8js_ctx;
+			std::chrono::time_point<std::chrono::high_resolution_clock> now = std::chrono::high_resolution_clock::now();
 
-			// Get memory usage statistics for the isolate
-			c->isolate->GetHeapStatistics(&hs);
-
-			if (timer_ctx->time_limit > 0 && now > timer_ctx->time_point &&
-				!timer_ctx->killed) {
+			if(timer_ctx->killed) {
+				/* execution already terminated, nothing to check anymore,
+				 * but wait for caller to pop this timer context. */
+			}
+			else if(timer_ctx->time_limit > 0 && now > timer_ctx->time_point) {
 				timer_ctx->killed = true;
 				php_v8js_terminate_execution(c TSRMLS_CC);
-
-				V8JSG(timer_mutex).lock();
 				c->time_limit_hit = true;
-				V8JSG(timer_mutex).unlock();
 			}
-
-			if (timer_ctx->memory_limit > 0 && hs.used_heap_size() > timer_ctx->memory_limit &&
-				!timer_ctx->killed) {
-				timer_ctx->killed = true;
-				php_v8js_terminate_execution(c TSRMLS_CC);
-
-				V8JSG(timer_mutex).lock();
-				c->memory_limit_hit = true;
-				V8JSG(timer_mutex).unlock();
+			else if (timer_ctx->memory_limit > 0) {
+				/* If a memory_limit is set, we need to interrupt execution
+				 * and check heap size within the callback.  We must *not*
+				 * directly call GetHeapStatistics here, since we don't have
+				 * a v8::Locker on the isolate, but are expected to hold one,
+				 * and cannot aquire it as v8 is executing the script ... */
+				void *data = NULL;
+#ifdef ZTS
+				data = (void *) TSRMLS_C;
+#endif
+				c->isolate->RequestInterrupt(php_v8js_timer_interrupt_handler, data);
 			}
 		}
+		V8JSG(timer_mutex).unlock();
 
 		// Sleep for 10ms
 #ifdef _WIN32