return K();
}
static void DisposeCallbackData(WeakCallbackDataType* data) {}
+ static void OnWeakCallback(
+ const WeakCallbackInfo<WeakCallbackDataType>& data) {}
static void Dispose(Isolate* isolate, Global<V> value, K key) {}
// This is a second pass callback, so SetSecondPassCallback cannot be called.
static void DisposeWeak(const WeakCallbackInfo<WeakCallbackDataType>& data) {}
: WeakCallbackType::kParameter;
Local<V> value(Local<V>::New(this->isolate(), *persistent));
persistent->template SetWeak<typename Traits::WeakCallbackDataType>(
- Traits::WeakCallbackParameter(this, key, value), FirstWeakCallback,
+ Traits::WeakCallbackParameter(this, key, value), OnWeakCallback,
callback_type);
}
PersistentContainerValue old_value =
}
private:
- static void FirstWeakCallback(
+ static void OnWeakCallback(
const WeakCallbackInfo<typename Traits::WeakCallbackDataType>& data) {
if (Traits::kCallbackType != kNotWeak) {
auto map = Traits::MapFromWeakCallbackInfo(data);
K key = Traits::KeyFromWeakCallbackInfo(data);
map->RemoveWeak(key);
+ Traits::OnWeakCallback(data);
data.SetSecondPassCallback(SecondWeakCallback);
}
}
};
-bool ProcessEntries(HttpRequestProcessor* processor, int count,
- StringHttpRequest* reqs) {
+bool ProcessEntries(v8::Platform* platform, HttpRequestProcessor* processor,
+ int count, StringHttpRequest* reqs) {
for (int i = 0; i < count; i++) {
- if (!processor->Process(&reqs[i]))
- return false;
+ bool result = processor->Process(&reqs[i]);
+ while (v8::platform::PumpMessageLoop(platform, Isolate::GetCurrent()))
+ continue;
+ if (!result) return false;
}
return true;
}
fprintf(stderr, "Error initializing processor.\n");
return 1;
}
- if (!ProcessEntries(&processor, kSampleSize, kSampleRequests))
+ if (!ProcessEntries(platform, &processor, kSampleSize, kSampleRequests))
return 1;
PrintMap(&output);
}
v8::Local<v8::Context> CreateShellContext(v8::Isolate* isolate);
-void RunShell(v8::Local<v8::Context> context);
-int RunMain(v8::Isolate* isolate, int argc, char* argv[]);
+void RunShell(v8::Local<v8::Context> context, v8::Platform* platform);
+int RunMain(v8::Isolate* isolate, v8::Platform* platform, int argc,
+ char* argv[]);
bool ExecuteString(v8::Isolate* isolate, v8::Local<v8::String> source,
v8::Local<v8::Value> name, bool print_result,
bool report_exceptions);
return 1;
}
v8::Context::Scope context_scope(context);
- result = RunMain(isolate, argc, argv);
- if (run_shell) RunShell(context);
+ result = RunMain(isolate, platform, argc, argv);
+ if (run_shell) RunShell(context, platform);
}
isolate->Dispose();
v8::V8::Dispose();
// Process remaining command line arguments and execute files
-int RunMain(v8::Isolate* isolate, int argc, char* argv[]) {
+int RunMain(v8::Isolate* isolate, v8::Platform* platform, int argc,
+ char* argv[]) {
for (int i = 1; i < argc; i++) {
const char* str = argv[i];
if (strcmp(str, "--shell") == 0) {
.ToLocal(&source)) {
return 1;
}
- if (!ExecuteString(isolate, source, file_name, false, true)) return 1;
+ bool success = ExecuteString(isolate, source, file_name, false, true);
+ while (v8::platform::PumpMessageLoop(platform, isolate)) continue;
+ if (!success) return 1;
} else {
// Use all other arguments as names of files to load and run.
v8::Local<v8::String> file_name =
fprintf(stderr, "Error reading '%s'\n", str);
continue;
}
- if (!ExecuteString(isolate, source, file_name, false, true)) return 1;
+ bool success = ExecuteString(isolate, source, file_name, false, true);
+ while (v8::platform::PumpMessageLoop(platform, isolate)) continue;
+ if (!success) return 1;
}
}
return 0;
// The read-eval-execute loop of the shell.
-void RunShell(v8::Local<v8::Context> context) {
+void RunShell(v8::Local<v8::Context> context, v8::Platform* platform) {
fprintf(stderr, "V8 version %s [sample shell]\n", v8::V8::GetVersion());
static const int kBufferSize = 256;
// Enter the execution environment before evaluating any code.
v8::String::NewFromUtf8(context->GetIsolate(), str,
v8::NewStringType::kNormal).ToLocalChecked(),
name, true, true);
+ while (v8::platform::PumpMessageLoop(platform, context->GetIsolate()))
+ continue;
}
fprintf(stderr, "\n");
}
return false;
}
result = script->Run();
+ EmptyMessageQueues(isolate);
data->realm_current_ = data->realm_switch_;
}
if (result.IsEmpty()) {
}
+void Shell::EmptyMessageQueues(Isolate* isolate) {
+ while (v8::platform::PumpMessageLoop(g_platform, isolate)) continue;
+}
+
+
#ifndef V8_SHARED
bool Shell::SerializeValue(Isolate* isolate, Handle<Value> value,
const ObjectList& to_transfer,
static void Exit(int exit_code);
static void OnExit(Isolate* isolate);
static void CollectGarbage(Isolate* isolate);
+ static void EmptyMessageQueues(Isolate* isolate);
#ifndef V8_SHARED
// TODO(binji): stupid implementation for now. Is there an easy way to hash an
DISALLOW_COPY_AND_ASSIGN(NodeIterator);
};
+class GlobalHandles::PendingPhantomCallbacksSecondPassTask : public v8::Task {
+ public:
+ // Takes ownership of the contents of pending_phantom_callbacks, leaving it in
+ // the same state it would be after a call to Clear().
+ PendingPhantomCallbacksSecondPassTask(
+ List<PendingPhantomCallback>* pending_phantom_callbacks, Isolate* isolate)
+ : isolate_(isolate) {
+ pending_phantom_callbacks_.Swap(pending_phantom_callbacks);
+ }
+
+ ~PendingPhantomCallbacksSecondPassTask() override {}
+
+ void Run() override {
+ InvokeSecondPassPhantomCallbacks(&pending_phantom_callbacks_, isolate_);
+ }
+
+ private:
+ List<PendingPhantomCallback> pending_phantom_callbacks_;
+ Isolate* isolate_;
+
+ DISALLOW_COPY_AND_ASSIGN(PendingPhantomCallbacksSecondPassTask);
+};
+
GlobalHandles::GlobalHandles(Isolate* isolate)
: isolate_(isolate),
}
+void GlobalHandles::InvokeSecondPassPhantomCallbacks(
+ List<PendingPhantomCallback>* callbacks, Isolate* isolate) {
+ while (callbacks->length() != 0) {
+ auto callback = callbacks->RemoveLast();
+ DCHECK(callback.node() == nullptr);
+ // No second pass callback required.
+ if (callback.callback() == nullptr) continue;
+ // Fire second pass callback
+ callback.Invoke(isolate);
+ }
+}
+
+
int GlobalHandles::PostScavengeProcessing(
const int initial_post_gc_processing_count) {
int freed_nodes = 0;
}
-int GlobalHandles::DispatchPendingPhantomCallbacks() {
+int GlobalHandles::DispatchPendingPhantomCallbacks(
+ bool synchronous_second_pass) {
int freed_nodes = 0;
{
// The initial pass callbacks must simply clear the nodes.
freed_nodes++;
}
}
- // The second pass empties the list.
- while (pending_phantom_callbacks_.length() != 0) {
- auto callback = pending_phantom_callbacks_.RemoveLast();
- DCHECK(callback.node() == nullptr);
- // No second pass callback required.
- if (callback.callback() == nullptr) continue;
- // Fire second pass callback.
- callback.Invoke(isolate());
+ if (pending_phantom_callbacks_.length() > 0) {
+ if (synchronous_second_pass) {
+ InvokeSecondPassPhantomCallbacks(&pending_phantom_callbacks_, isolate());
+ } else {
+ auto* task = new PendingPhantomCallbacksSecondPassTask(
+ &pending_phantom_callbacks_, isolate());
+ V8::GetCurrentPlatform()->CallOnForegroundThread(
+ reinterpret_cast<v8::Isolate*>(isolate()), task);
+ }
}
pending_phantom_callbacks_.Clear();
return freed_nodes;
}
-int GlobalHandles::PostGarbageCollectionProcessing(GarbageCollector collector) {
+int GlobalHandles::PostGarbageCollectionProcessing(
+ GarbageCollector collector, const v8::GCCallbackFlags gc_callback_flags) {
// Process weak global handle callbacks. This must be done after the
// GC is completely done, because the callbacks may invoke arbitrary
// API functions.
DCHECK(isolate_->heap()->gc_state() == Heap::NOT_IN_GC);
const int initial_post_gc_processing_count = ++post_gc_processing_count_;
int freed_nodes = 0;
- freed_nodes += DispatchPendingPhantomCallbacks();
+ bool synchronous_second_pass =
+ (gc_callback_flags & kGCCallbackFlagForced) != 0;
+ freed_nodes += DispatchPendingPhantomCallbacks(synchronous_second_pass);
if (initial_post_gc_processing_count != post_gc_processing_count_) {
// If the callbacks caused a nested GC, then return. See comment in
// PostScavengeProcessing.
// Process pending weak handles.
// Returns the number of freed nodes.
- int PostGarbageCollectionProcessing(GarbageCollector collector);
+ int PostGarbageCollectionProcessing(
+ GarbageCollector collector, const v8::GCCallbackFlags gc_callback_flags);
// Iterates over all strong handles.
void IterateStrongRoots(ObjectVisitor* v);
// don't assign any initial capacity.
static const int kObjectGroupConnectionsCapacity = 20;
+ class PendingPhantomCallback;
+
// Helpers for PostGarbageCollectionProcessing.
+ static void InvokeSecondPassPhantomCallbacks(
+ List<PendingPhantomCallback>* callbacks, Isolate* isolate);
int PostScavengeProcessing(int initial_post_gc_processing_count);
int PostMarkSweepProcessing(int initial_post_gc_processing_count);
- int DispatchPendingPhantomCallbacks();
+ int DispatchPendingPhantomCallbacks(bool synchronous_second_pass);
void UpdateListOfNewSpaceNodes();
// Internal node structures.
class Node;
class NodeBlock;
class NodeIterator;
- class PendingPhantomCallback;
+ class PendingPhantomCallbacksSecondPassTask;
Isolate* isolate_;
AllowHeapAllocation allow_allocation;
GCTracer::Scope scope(tracer(), GCTracer::Scope::EXTERNAL);
freed_global_handles =
- isolate_->global_handles()->PostGarbageCollectionProcessing(collector);
+ isolate_->global_handles()->PostGarbageCollectionProcessing(
+ collector, gc_callback_flags);
}
gc_post_processing_depth_--;
return false;
}
+template <typename T, class P>
+void List<T, P>::Swap(List<T, P>* list) {
+ std::swap(data_, list->data_);
+ std::swap(length_, list->length_);
+ std::swap(capacity_, list->capacity_);
+}
template<typename T, class P>
void List<T, P>::Allocate(int length, P allocator) {
#ifndef V8_LIST_H_
#define V8_LIST_H_
+#include <algorithm>
+
#include "src/checks.h"
#include "src/utils.h"
// Drop the last 'count' elements from the list.
INLINE(void RewindBy(int count)) { Rewind(length_ - count); }
+ // Swaps the contents of the two lists.
+ INLINE(void Swap(List<T, AllocationPolicy>* list));
+
// Halve the capacity if fill level is less than a quarter.
INLINE(void Trim(AllocationPolicy allocator = AllocationPolicy()));
}
callback_();
if (initialize_) {
+ EmptyMessageQueues(isolate_);
isolate_->Exit();
}
}
#ifndef CCTEST_H_
#define CCTEST_H_
+#include "include/libplatform/libplatform.h"
#include "src/v8.h"
#ifndef TEST
static inline void DisableDebugger() { v8::Debug::SetDebugEventListener(NULL); }
+static inline void EmptyMessageQueues(v8::Isolate* isolate) {
+ while (v8::platform::PumpMessageLoop(v8::internal::V8::GetCurrentPlatform(),
+ isolate))
+ ;
+}
+
+
// Helper class for new allocations tracking and checking.
// To use checking of JS allocations tracking in a test,
// just create an instance of this class.
}
CHECK_EQ(static_cast<int>(kLength), instance_counter);
CcTest::heap()->CollectAllGarbage();
+ EmptyMessageQueues(isolate);
CHECK_EQ(0, instance_counter);
}
array[15]->MarkTriggerGc();
CHECK_EQ(static_cast<int>(kLength), instance_counter);
CcTest::heap()->CollectAllGarbage();
+ EmptyMessageQueues(isolate);
CHECK_EQ(0, instance_counter);
}
CHECK_EQ(IntKeyToVoidPointer(key),
v8::Object::GetAlignedPointerFromInternalField(value, 0));
}
+ static void OnWeakCallback(
+ const v8::WeakCallbackInfo<WeakCallbackDataType>&) {}
static void DisposeWeak(
const v8::WeakCallbackInfo<WeakCallbackDataType>& info) {
K key = KeyFromWeakCallbackInfo(info);
v8::WeakCallbackType::kParameter);
object.handle.MarkIndependent();
invoke_gc[outer_gc]();
+ EmptyMessageQueues(isolate);
CHECK(object.flag);
}
}
handle3.SetWeak(&handle3, HandleCreatingCallback1,
v8::WeakCallbackType::kParameter);
CcTest::heap()->CollectAllGarbage();
+ EmptyMessageQueues(isolate);
}
#include "test/unittests/test-utils.h"
+#include "include/libplatform/libplatform.h"
#include "src/base/platform/time.h"
#include "src/debug.h"
#include "src/flags.h"
#include "src/isolate.h"
+#include "src/v8.h"
namespace v8 {
// static
void TestWithIsolate::TearDownTestCase() {
ASSERT_TRUE(isolate_ != NULL);
+ v8::Platform* platform = internal::V8::GetCurrentPlatform();
+ ASSERT_TRUE(platform != NULL);
+ while (platform::PumpMessageLoop(platform, isolate_)) continue;
isolate_->Dispose();
isolate_ = NULL;
delete array_buffer_allocator_;