Skip to content

Commit 1de0e4d

Browse files
author
Martijn Otto
committed
Added memory allocation routines for arrays, that we somehow suddenly need and created a Platform implementation that we need for garbage collection to function properly
1 parent 337dcad commit 1de0e4d

File tree

3 files changed

+324
-2
lines changed

3 files changed

+324
-2
lines changed

isolate.cpp

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
* Dependencies
1919
*/
2020
#include "isolate.h"
21+
#include "platform.h"
22+
#include <cstring>
23+
#include <cstdlib>
2124

2225
/**
2326
* Start namespace
@@ -27,14 +30,62 @@ namespace JS {
2730
/**
2831
* Private class
2932
*/
30-
class Isolate final
33+
class Isolate final : private v8::ArrayBuffer::Allocator
3134
{
3235
private:
36+
/**
37+
* The "platform" we run on
38+
* @var Platform
39+
*/
40+
Platform _platform;
41+
42+
/**
43+
* The isolate creation parameters
44+
* @var v8::Isolate::CreateParams
45+
*/
46+
v8::Isolate::CreateParams _params;
47+
3348
/**
3449
* The underlying isolate
3550
* @var v8::Isolate*
3651
*/
3752
v8::Isolate *_isolate;
53+
54+
/**
55+
* Allocate a range of memory, zero-initialized.
56+
*
57+
* @param size The number of bytes to allocate
58+
* @return Pointer to the allocated memory, or a nullptr
59+
*/
60+
void *Allocate(size_t size) override
61+
{
62+
// request some cleared memory
63+
return std::calloc(size, 1);
64+
}
65+
66+
/**
67+
* Allocate a range of memory, uninitialized.
68+
*
69+
* @param size The number of bytes to allocate
70+
* @return Pointer to the allocated memory, or a nullptr
71+
*/
72+
void *AllocateUninitialized(size_t size) override
73+
{
74+
// request some uninitialized memory
75+
return std::malloc(size);
76+
}
77+
78+
/**
79+
* Free some allocated memory
80+
*
81+
* @param data Pointer to memory to free
82+
* @param size Number of bytes to free
83+
*/
84+
void Free(void *data, size_t size) override
85+
{
86+
// free the allocated memory
87+
std::free(data);
88+
}
3889
public:
3990
/**
4091
* Constructor
@@ -43,10 +94,14 @@ class Isolate final
4394
{
4495
// initialize the ICU and v8 engine
4596
v8::V8::InitializeICU();
97+
v8::V8::InitializePlatform(&_platform);
4698
v8::V8::Initialize();
4799

100+
// initialize the parameters
101+
_params.array_buffer_allocator = this;
102+
48103
// create the actual isolate
49-
_isolate = v8::Isolate::New();
104+
_isolate = v8::Isolate::New(_params);
50105

51106
// and enter it
52107
_isolate->Enter();
@@ -65,6 +120,9 @@ class Isolate final
65120

66121
// and shut down the engine (this also takes care of the ICU)
67122
v8::V8::Dispose();
123+
124+
// finally shut down the platform
125+
v8::V8::ShutdownPlatform();
68126
}
69127

70128
/**

platform.cpp

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/**
2+
* platform.cpp
3+
*
4+
* A "platform" as needed by the v8 engine, implementing
5+
* functionality required for, amongst other things,
6+
* garbage collection.
7+
*
8+
* @copyright 2015 Copernica B.V.
9+
*/
10+
11+
/**
12+
* Dependencies
13+
*/
14+
#include "platform.h"
15+
#include <time.h>
16+
17+
/**
18+
* Start namespace
19+
*/
20+
namespace JS {
21+
22+
/**
23+
* Constructor
24+
*/
25+
Platform::Platform() :
26+
_running(true),
27+
_worker(&Platform::run, this)
28+
{}
29+
30+
/**
31+
* Destructor
32+
*/
33+
Platform::~Platform()
34+
{
35+
// we are no longer running
36+
_running = false;
37+
38+
// signal the thread in case it is waiting for input
39+
_condition.notify_one();
40+
41+
// and wait for the thread to stop
42+
_worker.join();
43+
}
44+
45+
/**
46+
* Execute some work, this is called
47+
* by the worker thread to keep going
48+
*/
49+
void Platform::run()
50+
{
51+
// keep going indefinitely
52+
while (true)
53+
{
54+
// lock the work queue
55+
std::unique_lock<std::mutex> lock(_mutex);
56+
57+
// wait for new work to arrive
58+
while (_tasks.empty() && _running) _condition.wait(lock);
59+
60+
// still no tasks? we must be done
61+
if (!_running) return;
62+
63+
// retrieve the new task
64+
auto *task = _tasks.front();
65+
66+
// remove it from the queue
67+
_tasks.pop_front();
68+
69+
// unlock the queue again
70+
lock.unlock();
71+
72+
// execute the task
73+
task->Run();
74+
75+
// and clean it up
76+
delete task;
77+
}
78+
}
79+
80+
/**
81+
* Schedule a task to be executed on a background thread.
82+
*
83+
* The platform takes responsibility for the task, and will
84+
* free it when the task has finished executing.
85+
*
86+
* @param task The task to execute
87+
* @param time The expected run time
88+
*/
89+
void Platform::CallOnBackgroundThread(v8::Task *task, ExpectedRuntime time)
90+
{
91+
// lock the mutex
92+
_mutex.lock();
93+
94+
// add the task to the list
95+
_tasks.push_back(task);
96+
97+
// unlock the mutex again
98+
_mutex.unlock();
99+
100+
// signal the worker that work is ready
101+
_condition.notify_one();
102+
}
103+
104+
/**
105+
* Schedule a task to be executed on a foreground thread.
106+
*
107+
* @param isolate The isolate the task belongs to
108+
* @param task The task to execute
109+
*/
110+
void Platform::CallOnForegroundThread(v8::Isolate *isolate, v8::Task *task)
111+
{
112+
/**
113+
* We are _required_ to implement this function, as it is a pure-virtual
114+
* in the v8::Platform class. It is, however, never called, unless one
115+
* calls the PumpMessageLoop method, in which case it would not work
116+
* either, since that function blindly casts it to a DefaultPlatform
117+
* object and executes the PumpMessageLoop on this object.
118+
*
119+
* Besides the fact that the cast would fail, and the function call
120+
* thus ends in a segfault, there is no way to fix this even without
121+
* this weird casting, since the PumpMessageLoop is not a virtual
122+
* function.
123+
*
124+
* Because of this I hereby declare this function to be utterly
125+
* useless. It can never be called, but we can't compile without.
126+
*/
127+
}
128+
129+
/**
130+
* Retrieve the monotonically increasing time. The starting point
131+
* is not relevant, but it must return at least millisecond-precision
132+
*
133+
* @return time in seconds since an unspecified time
134+
*/
135+
double Platform::MonotonicallyIncreasingTime()
136+
{
137+
// the result structure
138+
struct timespec time;
139+
140+
// retrieve the current time
141+
clock_gettime(CLOCK_MONOTONIC_RAW, &time);
142+
143+
// convert the result to a double with millisecond precision
144+
return static_cast<double>(time.tv_sec) + static_cast<double>(time.tv_nsec) / 1000000000;
145+
}
146+
147+
/**
148+
* End namespace
149+
*/
150+
}

platform.h

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* platform.h
3+
*
4+
* A "platform" as needed by the v8 engine, implementing
5+
* functionality required for, amongst other things,
6+
* garbage collection.
7+
*
8+
* @copyright 2015 Copernica B.V.
9+
*/
10+
11+
/**
12+
* Include guard
13+
*/
14+
#pragma once
15+
16+
/**
17+
* Dependencies
18+
*/
19+
#include <v8-platform.h>
20+
#include <condition_variable>
21+
#include <atomic>
22+
#include <thread>
23+
#include <mutex>
24+
#include <deque>
25+
26+
/**
27+
* Start namespace
28+
*/
29+
namespace JS {
30+
31+
/**
32+
* Class definition
33+
*/
34+
class Platform : public v8::Platform
35+
{
36+
private:
37+
/**
38+
* The list of tasks to execute
39+
* @var std::queue<Task*>
40+
*/
41+
std::deque<v8::Task*> _tasks;
42+
43+
/**
44+
* Are we still supposed to be running?
45+
* @var std::atomic<bool>
46+
*/
47+
std::atomic<bool> _running;
48+
49+
/**
50+
* Mutex to protect the task list
51+
* @var std::mutex
52+
*/
53+
std::mutex _mutex;
54+
55+
/**
56+
* Condition variable to signal new work arriving
57+
* @var std::condition_variable
58+
*/
59+
std::condition_variable _condition;
60+
61+
/**
62+
* The worker thread
63+
* @var std::thread
64+
*/
65+
std::thread _worker;
66+
67+
/**
68+
* Execute some work, this is called
69+
* by the worker thread to keep going
70+
*/
71+
void run();
72+
public:
73+
/**
74+
* Constructor
75+
*/
76+
Platform();
77+
78+
/**
79+
* Destructor
80+
*/
81+
virtual ~Platform();
82+
83+
/**
84+
* Schedule a task to be executed on a background thread.
85+
*
86+
* The platform takes responsibility for the task, and will
87+
* free it when the task has finished executing.
88+
*
89+
* @param task The task to execute
90+
* @param time The expected run time
91+
*/
92+
void CallOnBackgroundThread(v8::Task *task, ExpectedRuntime time) override;
93+
94+
/**
95+
* Schedule a task to be executed on a foreground thread.
96+
*
97+
* @param isolate The isolate the task belongs to
98+
* @param task The task to execute
99+
*/
100+
void CallOnForegroundThread(v8::Isolate *isolate, v8::Task *task) override;
101+
102+
/**
103+
* Retrieve the monotonically increasing time. The starting point
104+
* is not relevant, but it must return at least millisecond-precision
105+
*
106+
* @return time in milliseconds since an unspecified time
107+
*/
108+
double MonotonicallyIncreasingTime() override;
109+
};
110+
111+
/**
112+
* End namespace
113+
*/
114+
}

0 commit comments

Comments
 (0)