Skip to content

Commit 62918c2

Browse files
author
Martijn Otto
committed
Run foreground tasks on the isolate thread they should run on, cancel tasks scheduled in the future when the isolate is destructed
1 parent 09c9929 commit 62918c2

File tree

4 files changed

+107
-7
lines changed

4 files changed

+107
-7
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -148,5 +148,5 @@ install:
148148
${CP} ${INI} ${INI_DIR}
149149

150150
clean:
151-
${RM} ${EXTENSION} ${OBJECTS}
151+
${RM} ${EXTENSION} ${OBJECTS} ${DEPENDENCIES}
152152

isolate.cpp

+79-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
#include "isolate.h"
2222
#include <cstring>
2323
#include <cstdlib>
24-
#include <iostream>
2524

2625
/**
2726
* Start namespace
@@ -69,6 +68,10 @@ Isolate::Isolate()
6968
// create the actual isolate
7069
_isolate = v8::Isolate::New(params);
7170

71+
// associate ourselves with this isolate
72+
// so that we can find it back from the pointer
73+
_isolate->SetData(0, this);
74+
7275
// and enter it
7376
_isolate->Enter();
7477
}
@@ -78,13 +81,85 @@ Isolate::Isolate()
7881
*/
7982
Isolate::~Isolate()
8083
{
84+
// run all tasks still awaiting executing
85+
runTasks();
86+
87+
/**
88+
* the tasks that are still there are scheduled
89+
* somewhere in the future, we just delete these
90+
* - this seems like a strange thing to do, but
91+
* executing them now seems to throw v8 off guard
92+
* and results in either a deadlock or some weird
93+
* error message about garbage collection running
94+
* in some old "space" or something equally weird
95+
* and confusing.
96+
*/
97+
_tasks.clear();
98+
8199
// leave the isolate scope
82100
_isolate->Exit();
83101

84102
// clean it up
85103
_isolate->Dispose();
86104
}
87105

106+
/**
107+
* Perform all waiting tasks for this isolate
108+
*/
109+
void Isolate::runTasks()
110+
{
111+
// no tasks? then we're done fast!
112+
if (_tasks.empty()) return;
113+
114+
// determine the current time
115+
auto now = std::chrono::system_clock::now();
116+
117+
// loop over all the tasks
118+
for (auto iter = _tasks.begin(); iter != _tasks.end(); ++iter)
119+
{
120+
// is the execution time still in the future?
121+
if (now < iter->first)
122+
{
123+
// first task? then don't remove anything
124+
if (iter == _tasks.begin()) return;
125+
126+
// remove from the beginning up until the task
127+
// since we already moved past the last task we
128+
// need to go back one so we don't remove a task
129+
// we have not actually executed yet
130+
_tasks.erase(_tasks.begin(), --iter);
131+
132+
// tasks executed and removed
133+
return;
134+
}
135+
136+
// execute the task
137+
iter->second->Run();
138+
}
139+
140+
// we ran through all the tasks and executed all of them
141+
_tasks.clear();
142+
}
143+
144+
/**
145+
* Schedule a task to be executed
146+
*
147+
* @param isolate The isolate to execute the task under
148+
* @param task The task to execute
149+
* @param delay Number of seconds to wait before executing
150+
*/
151+
void Isolate::scheduleTask(v8::Isolate *isolate, v8::Task *task, double delay)
152+
{
153+
// first retrieve the isolate to schedule it under
154+
auto *real = static_cast<Isolate*>(isolate->GetData(0));
155+
156+
// determine the time at which the task should be executed
157+
auto expire = std::chrono::system_clock::now() + std::chrono::microseconds{ static_cast<int64_t>(delay * 1000000) };
158+
159+
// schedule the task to be executed
160+
real->_tasks.emplace(std::make_pair(expire, std::unique_ptr<v8::Task>{ task }));
161+
}
162+
88163
/**
89164
* Get the isolate for this thread
90165
*
@@ -95,6 +170,9 @@ v8::Isolate *Isolate::get()
95170
// do we still have to create the isolate?
96171
if (!isolate) isolate.reset(new Isolate);
97172

173+
// execute tasks for this isolate
174+
isolate->runTasks();
175+
98176
// return the isolate
99177
return *isolate;
100178
}

isolate.h

+23
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
/**
2323
* Dependencies
2424
*/
25+
#include <v8-platform.h>
26+
#include <chrono>
2527
#include <v8.h>
28+
#include <map>
2629

2730
/**
2831
* Start namespace
@@ -40,6 +43,17 @@ class Isolate final
4043
* @var v8::Isolate*
4144
*/
4245
v8::Isolate *_isolate;
46+
47+
/**
48+
* List of tasks to execute
49+
* @var std::multimap<std::chrono::system_clock::time_point, std::unique_ptr<v8::Task>>
50+
*/
51+
std::multimap<std::chrono::system_clock::time_point, std::unique_ptr<v8::Task>> _tasks;
52+
53+
/**
54+
* Perform all waiting tasks for this isolate
55+
*/
56+
void runTasks();
4357
public:
4458
/**
4559
* Constructor
@@ -51,6 +65,15 @@ class Isolate final
5165
*/
5266
~Isolate();
5367

68+
/**
69+
* Schedule a task to be executed
70+
*
71+
* @param isolate The isolate to schedule the task under
72+
* @param task The task to execute
73+
* @param delay Number of seconds to wait before executing
74+
*/
75+
static void scheduleTask(v8::Isolate *isolate, v8::Task *task, double delay);
76+
5477
/**
5578
* Get the isolate for this thread
5679
*

platform.cpp

+4-5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*/
1414
#include <v8.h>
1515
#include "platform.h"
16+
#include "isolate.h"
1617
#include <atomic>
1718
#include <time.h>
1819
#include <mutex>
@@ -185,7 +186,7 @@ void Platform::shutdown()
185186
* all PHP engines to be destructed, but we actually do
186187
* stay loaded in memory. So we have to make sure that
187188
* this variable is correctly set to a nullptr, so that
188-
* the get() method creates a new instance.
189+
* the create() method creates a new instance.
189190
*/
190191

191192
// retrieve the current platform
@@ -314,10 +315,8 @@ void Platform::CallOnForegroundThread(v8::Isolate *isolate, v8::Task *task)
314315
*/
315316
void Platform::CallDelayedOnForegroundThread(v8::Isolate *isolate, v8::Task *task, double delay_in_seconds)
316317
{
317-
// we simply call the CallOnBackgroundThread method which will queue our task, but before that
318-
// we turn it into a DelayedTask. The ExpectedRuntime here doesn't matter as our implementation
319-
// of CallOnBackgroundThread doesn't do anything with it anyway
320-
CallOnBackgroundThread(new DelayedTask(task, delay_in_seconds), ExpectedRuntime::kShortRunningTask);
318+
// schedule this task to be executed in the isolate
319+
Isolate::scheduleTask(isolate, task, delay_in_seconds);
321320
}
322321

323322
/**

0 commit comments

Comments
 (0)