Skip to content

Commit

Permalink
Added memory allocation routines for arrays, that we somehow suddenly…
Browse files Browse the repository at this point in the history
… need and created a Platform implementation that we need for garbage collection to function properly
  • Loading branch information
Martijn Otto committed Jul 13, 2015
1 parent 337dcad commit 1de0e4d
Show file tree
Hide file tree
Showing 3 changed files with 324 additions and 2 deletions.
62 changes: 60 additions & 2 deletions isolate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
* Dependencies
*/
#include "isolate.h"
#include "platform.h"
#include <cstring>
#include <cstdlib>

/**
* Start namespace
Expand All @@ -27,14 +30,62 @@ namespace JS {
/**
* Private class
*/
class Isolate final
class Isolate final : private v8::ArrayBuffer::Allocator
{
private:
/**
* The "platform" we run on
* @var Platform
*/
Platform _platform;

/**
* The isolate creation parameters
* @var v8::Isolate::CreateParams
*/
v8::Isolate::CreateParams _params;

/**
* The underlying isolate
* @var v8::Isolate*
*/
v8::Isolate *_isolate;

/**
* Allocate a range of memory, zero-initialized.
*
* @param size The number of bytes to allocate
* @return Pointer to the allocated memory, or a nullptr
*/
void *Allocate(size_t size) override
{
// request some cleared memory
return std::calloc(size, 1);
}

/**
* Allocate a range of memory, uninitialized.
*
* @param size The number of bytes to allocate
* @return Pointer to the allocated memory, or a nullptr
*/
void *AllocateUninitialized(size_t size) override
{
// request some uninitialized memory
return std::malloc(size);
}

/**
* Free some allocated memory
*
* @param data Pointer to memory to free
* @param size Number of bytes to free
*/
void Free(void *data, size_t size) override
{
// free the allocated memory
std::free(data);
}
public:
/**
* Constructor
Expand All @@ -43,10 +94,14 @@ class Isolate final
{
// initialize the ICU and v8 engine
v8::V8::InitializeICU();
v8::V8::InitializePlatform(&_platform);
v8::V8::Initialize();

// initialize the parameters
_params.array_buffer_allocator = this;

// create the actual isolate
_isolate = v8::Isolate::New();
_isolate = v8::Isolate::New(_params);

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

// and shut down the engine (this also takes care of the ICU)
v8::V8::Dispose();

// finally shut down the platform
v8::V8::ShutdownPlatform();
}

/**
Expand Down
150 changes: 150 additions & 0 deletions platform.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/**
* platform.cpp
*
* A "platform" as needed by the v8 engine, implementing
* functionality required for, amongst other things,
* garbage collection.
*
* @copyright 2015 Copernica B.V.
*/

/**
* Dependencies
*/
#include "platform.h"
#include <time.h>

/**
* Start namespace
*/
namespace JS {

/**
* Constructor
*/
Platform::Platform() :
_running(true),
_worker(&Platform::run, this)
{}

/**
* Destructor
*/
Platform::~Platform()
{
// we are no longer running
_running = false;

// signal the thread in case it is waiting for input
_condition.notify_one();

// and wait for the thread to stop
_worker.join();
}

/**
* Execute some work, this is called
* by the worker thread to keep going
*/
void Platform::run()
{
// keep going indefinitely
while (true)
{
// lock the work queue
std::unique_lock<std::mutex> lock(_mutex);

// wait for new work to arrive
while (_tasks.empty() && _running) _condition.wait(lock);

// still no tasks? we must be done
if (!_running) return;

// retrieve the new task
auto *task = _tasks.front();

// remove it from the queue
_tasks.pop_front();

// unlock the queue again
lock.unlock();

// execute the task
task->Run();

// and clean it up
delete task;
}
}

/**
* Schedule a task to be executed on a background thread.
*
* The platform takes responsibility for the task, and will
* free it when the task has finished executing.
*
* @param task The task to execute
* @param time The expected run time
*/
void Platform::CallOnBackgroundThread(v8::Task *task, ExpectedRuntime time)
{
// lock the mutex
_mutex.lock();

// add the task to the list
_tasks.push_back(task);

// unlock the mutex again
_mutex.unlock();

// signal the worker that work is ready
_condition.notify_one();
}

/**
* Schedule a task to be executed on a foreground thread.
*
* @param isolate The isolate the task belongs to
* @param task The task to execute
*/
void Platform::CallOnForegroundThread(v8::Isolate *isolate, v8::Task *task)
{
/**
* We are _required_ to implement this function, as it is a pure-virtual
* in the v8::Platform class. It is, however, never called, unless one
* calls the PumpMessageLoop method, in which case it would not work
* either, since that function blindly casts it to a DefaultPlatform
* object and executes the PumpMessageLoop on this object.
*
* Besides the fact that the cast would fail, and the function call
* thus ends in a segfault, there is no way to fix this even without
* this weird casting, since the PumpMessageLoop is not a virtual
* function.
*
* Because of this I hereby declare this function to be utterly
* useless. It can never be called, but we can't compile without.
*/
}

/**
* Retrieve the monotonically increasing time. The starting point
* is not relevant, but it must return at least millisecond-precision
*
* @return time in seconds since an unspecified time
*/
double Platform::MonotonicallyIncreasingTime()
{
// the result structure
struct timespec time;

// retrieve the current time
clock_gettime(CLOCK_MONOTONIC_RAW, &time);

// convert the result to a double with millisecond precision
return static_cast<double>(time.tv_sec) + static_cast<double>(time.tv_nsec) / 1000000000;
}

/**
* End namespace
*/
}
114 changes: 114 additions & 0 deletions platform.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* platform.h
*
* A "platform" as needed by the v8 engine, implementing
* functionality required for, amongst other things,
* garbage collection.
*
* @copyright 2015 Copernica B.V.
*/

/**
* Include guard
*/
#pragma once

/**
* Dependencies
*/
#include <v8-platform.h>
#include <condition_variable>
#include <atomic>
#include <thread>
#include <mutex>
#include <deque>

/**
* Start namespace
*/
namespace JS {

/**
* Class definition
*/
class Platform : public v8::Platform
{
private:
/**
* The list of tasks to execute
* @var std::queue<Task*>
*/
std::deque<v8::Task*> _tasks;

/**
* Are we still supposed to be running?
* @var std::atomic<bool>
*/
std::atomic<bool> _running;

/**
* Mutex to protect the task list
* @var std::mutex
*/
std::mutex _mutex;

/**
* Condition variable to signal new work arriving
* @var std::condition_variable
*/
std::condition_variable _condition;

/**
* The worker thread
* @var std::thread
*/
std::thread _worker;

/**
* Execute some work, this is called
* by the worker thread to keep going
*/
void run();
public:
/**
* Constructor
*/
Platform();

/**
* Destructor
*/
virtual ~Platform();

/**
* Schedule a task to be executed on a background thread.
*
* The platform takes responsibility for the task, and will
* free it when the task has finished executing.
*
* @param task The task to execute
* @param time The expected run time
*/
void CallOnBackgroundThread(v8::Task *task, ExpectedRuntime time) override;

/**
* Schedule a task to be executed on a foreground thread.
*
* @param isolate The isolate the task belongs to
* @param task The task to execute
*/
void CallOnForegroundThread(v8::Isolate *isolate, v8::Task *task) override;

/**
* Retrieve the monotonically increasing time. The starting point
* is not relevant, but it must return at least millisecond-precision
*
* @return time in milliseconds since an unspecified time
*/
double MonotonicallyIncreasingTime() override;
};

/**
* End namespace
*/
}

0 comments on commit 1de0e4d

Please sign in to comment.