Skip to content

Commit c512ea4

Browse files
committed
PlatformImpl
1 parent 70228eb commit c512ea4

File tree

3 files changed

+93
-75
lines changed

3 files changed

+93
-75
lines changed

src/binding.cc

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3012,20 +3012,21 @@ v8::StartupData v8__SnapshotCreator__CreateBlob(
30123012
return self->CreateBlob(function_code_handling);
30133013
}
30143014

3015-
// Rust-side callbacks for the trait-based NotifyingPlatform.
3016-
// `context` is a pointer to a Rust Box<dyn ForegroundTaskCallback>.
3017-
extern "C" {
3018-
void v8__Platform__NotifyingPlatform__onForegroundTaskPosted(
3019-
void* context, void* isolate, double delay_in_seconds);
3020-
void v8__Platform__NotifyingPlatform__dropContext(void* context);
3021-
}
3022-
3023-
// TaskRunner wrapper that intercepts all PostTask* calls and notifies
3024-
// the embedder (via a Rust trait) before delegating to the real runner.
3025-
class NotifyingTaskRunner final : public v8::TaskRunner {
3015+
// Rust-side callbacks for trait-based CustomPlatform (PlatformImpl trait).
3016+
// `this` is a pointer to the CustomPlatform instance, used by Rust to
3017+
// recover the Box<dyn PlatformImpl> stored at the same offset.
3018+
void v8__Platform__CustomPlatform__BASE__onForegroundTaskPosted(
3019+
void* this_, void* isolate, double delay_in_seconds);
3020+
void v8__Platform__CustomPlatform__BASE__onIsolateShutdown(void* this_,
3021+
void* isolate);
3022+
void v8__Platform__CustomPlatform__BASE__DROP(void* this_);
3023+
3024+
// TaskRunner wrapper that intercepts all PostTask* calls and dispatches
3025+
// to the Rust PlatformImpl trait via the CustomPlatform context.
3026+
class CustomTaskRunner final : public v8::TaskRunner {
30263027
public:
3027-
NotifyingTaskRunner(std::shared_ptr<v8::TaskRunner> wrapped, void* context,
3028-
v8::Isolate* isolate)
3028+
CustomTaskRunner(std::shared_ptr<v8::TaskRunner> wrapped, void* context,
3029+
v8::Isolate* isolate)
30293030
: wrapped_(std::move(wrapped)), context_(context), isolate_(isolate) {}
30303031

30313032
bool IdleTasksEnabled() override { return wrapped_->IdleTasksEnabled(); }
@@ -3040,20 +3041,20 @@ class NotifyingTaskRunner final : public v8::TaskRunner {
30403041
void PostTaskImpl(std::unique_ptr<v8::Task> task,
30413042
const v8::SourceLocation& location) override {
30423043
wrapped_->PostTask(std::move(task), location);
3043-
v8__Platform__NotifyingPlatform__onForegroundTaskPosted(
3044+
v8__Platform__CustomPlatform__BASE__onForegroundTaskPosted(
30443045
context_, static_cast<void*>(isolate_), 0.0);
30453046
}
30463047
void PostNonNestableTaskImpl(std::unique_ptr<v8::Task> task,
30473048
const v8::SourceLocation& location) override {
30483049
wrapped_->PostNonNestableTask(std::move(task), location);
3049-
v8__Platform__NotifyingPlatform__onForegroundTaskPosted(
3050+
v8__Platform__CustomPlatform__BASE__onForegroundTaskPosted(
30503051
context_, static_cast<void*>(isolate_), 0.0);
30513052
}
30523053
void PostDelayedTaskImpl(std::unique_ptr<v8::Task> task,
30533054
double delay_in_seconds,
30543055
const v8::SourceLocation& location) override {
30553056
wrapped_->PostDelayedTask(std::move(task), delay_in_seconds, location);
3056-
v8__Platform__NotifyingPlatform__onForegroundTaskPosted(
3057+
v8__Platform__CustomPlatform__BASE__onForegroundTaskPosted(
30573058
context_, static_cast<void*>(isolate_),
30583059
delay_in_seconds > 0 ? delay_in_seconds : 0.0);
30593060
}
@@ -3062,14 +3063,14 @@ class NotifyingTaskRunner final : public v8::TaskRunner {
30623063
const v8::SourceLocation& location) override {
30633064
wrapped_->PostNonNestableDelayedTask(std::move(task), delay_in_seconds,
30643065
location);
3065-
v8__Platform__NotifyingPlatform__onForegroundTaskPosted(
3066+
v8__Platform__CustomPlatform__BASE__onForegroundTaskPosted(
30663067
context_, static_cast<void*>(isolate_),
30673068
delay_in_seconds > 0 ? delay_in_seconds : 0.0);
30683069
}
30693070
void PostIdleTaskImpl(std::unique_ptr<v8::IdleTask> task,
30703071
const v8::SourceLocation& location) override {
30713072
wrapped_->PostIdleTask(std::move(task), location);
3072-
v8__Platform__NotifyingPlatform__onForegroundTaskPosted(
3073+
v8__Platform__CustomPlatform__BASE__onForegroundTaskPosted(
30733074
context_, static_cast<void*>(isolate_), 0.0);
30743075
}
30753076

@@ -3079,19 +3080,19 @@ class NotifyingTaskRunner final : public v8::TaskRunner {
30793080
v8::Isolate* isolate_;
30803081
};
30813082

3082-
// Platform wrapper that intercepts GetForegroundTaskRunner to return
3083-
// NotifyingTaskRunner instances, dispatching to a Rust trait object.
3084-
class NotifyingPlatform : public v8::platform::DefaultPlatform {
3083+
// Generic Platform subclass that delegates virtual method overrides to a
3084+
// Rust PlatformImpl trait object, following the inspector API pattern.
3085+
class CustomPlatform : public v8::platform::DefaultPlatform {
30853086
using IdleTaskSupport = v8::platform::IdleTaskSupport;
30863087

30873088
public:
3088-
NotifyingPlatform(int thread_pool_size, IdleTaskSupport idle_task_support,
3089-
void* context)
3089+
CustomPlatform(int thread_pool_size, IdleTaskSupport idle_task_support,
3090+
void* context)
30903091
: DefaultPlatform(thread_pool_size, idle_task_support),
30913092
context_(context) {}
30923093

3093-
~NotifyingPlatform() override {
3094-
v8__Platform__NotifyingPlatform__dropContext(context_);
3094+
~CustomPlatform() override {
3095+
v8__Platform__CustomPlatform__BASE__DROP(context_);
30953096
}
30963097

30973098
std::shared_ptr<v8::TaskRunner> GetForegroundTaskRunner(
@@ -3104,10 +3105,10 @@ class NotifyingPlatform : public v8::platform::DefaultPlatform {
31043105
auto runner = it->second.lock();
31053106
if (runner) return runner;
31063107
}
3107-
auto notifying =
3108-
std::make_shared<NotifyingTaskRunner>(original, context_, isolate);
3109-
runners_[key] = notifying;
3110-
return notifying;
3108+
auto custom =
3109+
std::make_shared<CustomTaskRunner>(original, context_, isolate);
3110+
runners_[key] = custom;
3111+
return custom;
31113112
}
31123113

31133114
void NotifyIsolateShutdown(v8::Isolate* isolate) {
@@ -3121,6 +3122,8 @@ class NotifyingPlatform : public v8::platform::DefaultPlatform {
31213122
}
31223123
}
31233124
}
3125+
v8__Platform__CustomPlatform__BASE__onIsolateShutdown(
3126+
context_, static_cast<void*>(isolate));
31243127
DefaultPlatform::NotifyIsolateShutdown(isolate);
31253128
}
31263129

@@ -3132,7 +3135,7 @@ class NotifyingPlatform : public v8::platform::DefaultPlatform {
31323135
void* context_;
31333136
std::mutex mutex_;
31343137
std::map<std::pair<v8::Isolate*, v8::TaskPriority>,
3135-
std::weak_ptr<NotifyingTaskRunner>>
3138+
std::weak_ptr<CustomTaskRunner>>
31363139
runners_;
31373140
};
31383141

@@ -3189,14 +3192,14 @@ v8::Platform* v8__Platform__NewSingleThreadedDefaultPlatform(
31893192
.release();
31903193
}
31913194

3192-
v8::Platform* v8__Platform__NewNotifyingPlatform(int thread_pool_size,
3193-
bool idle_task_support,
3194-
void* context) {
3195+
v8::Platform* v8__Platform__NewCustomPlatform(int thread_pool_size,
3196+
bool idle_task_support,
3197+
void* context) {
31953198
if (thread_pool_size < 1) {
31963199
thread_pool_size = std::thread::hardware_concurrency();
31973200
}
31983201
thread_pool_size = std::max(std::min(thread_pool_size, 16), 1);
3199-
return std::make_unique<NotifyingPlatform>(
3202+
return std::make_unique<CustomPlatform>(
32003203
thread_pool_size,
32013204
idle_task_support ? v8::platform::IdleTaskSupport::kEnabled
32023205
: v8::platform::IdleTaskSupport::kDisabled,

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,10 @@ pub use isolate_create_params::CreateParams;
136136
pub use microtask::MicrotaskQueue;
137137
pub use module::*;
138138
pub use object::*;
139-
pub use platform::ForegroundTaskCallback;
140139
pub use platform::Platform;
140+
pub use platform::PlatformImpl;
141+
pub use platform::new_custom_platform;
141142
pub use platform::new_default_platform;
142-
pub use platform::new_notifying_platform;
143143
pub use platform::new_single_threaded_default_platform;
144144
pub use platform::new_unprotected_default_platform;
145145
pub use primitives::*;

src/platform.rs

Lines changed: 54 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ unsafe extern "C" {
2222
fn v8__Platform__NewSingleThreadedDefaultPlatform(
2323
idle_task_support: bool,
2424
) -> *mut Platform;
25-
fn v8__Platform__NewNotifyingPlatform(
25+
fn v8__Platform__NewCustomPlatform(
2626
thread_pool_size: int,
2727
idle_task_support: bool,
2828
context: *mut std::ffi::c_void,
@@ -65,15 +65,21 @@ unsafe extern "C" {
6565
#[derive(Debug)]
6666
pub struct Platform(Opaque);
6767

68-
/// Trait for receiving notifications when foreground tasks are posted to an
69-
/// isolate's task runner. Implementations must be thread-safe as callbacks
70-
/// can fire from any V8 background thread.
68+
/// Trait for customizing platform behavior, following the same pattern as
69+
/// [`V8InspectorClientImpl`](crate::inspector::V8InspectorClientImpl).
7170
///
72-
/// This follows the trait-based pattern used by the inspector API
73-
/// (`V8InspectorClientImpl`, `ChannelImpl`).
74-
pub trait ForegroundTaskCallback: Send + Sync {
71+
/// Implement this trait to receive callbacks for platform virtual method
72+
/// overrides. The C++ `CustomPlatform` base class delegates to these methods.
73+
/// All methods have default no-op implementations; override only what you need.
74+
///
75+
/// Implementations must be `Send + Sync` as callbacks may fire from any thread.
76+
#[allow(unused_variables)]
77+
pub trait PlatformImpl: Send + Sync {
7578
/// Called when a foreground task has been posted for the given isolate.
7679
///
80+
/// This corresponds to intercepted calls on the isolate's `TaskRunner`
81+
/// (`PostTask`, `PostDelayedTask`, `PostIdleTask`, etc.).
82+
///
7783
/// `isolate_ptr` is the raw `v8::Isolate*` pointer of the target isolate.
7884
/// `delay_in_seconds` is 0.0 for immediate tasks, or the delay before the
7985
/// task should be executed. For delayed tasks, the embedder should schedule
@@ -84,29 +90,44 @@ pub trait ForegroundTaskCallback: Send + Sync {
8490
&self,
8591
isolate_ptr: *mut std::ffi::c_void,
8692
delay_in_seconds: f64,
87-
);
93+
) {
94+
}
95+
96+
/// Called when an isolate is about to be shut down.
97+
///
98+
/// This corresponds to the `NotifyIsolateShutdown` virtual method.
99+
/// The default `DefaultPlatform` cleanup runs after this callback returns.
100+
fn on_isolate_shutdown(&self, isolate_ptr: *mut std::ffi::c_void) {}
88101
}
89102

90-
// FFI callbacks called from C++ NotifyingPlatform/NotifyingTaskRunner.
91-
// `context` is a raw pointer to a `Box<dyn ForegroundTaskCallback>`.
103+
// FFI callbacks called from C++ CustomPlatform/CustomTaskRunner.
104+
// `context` is a raw pointer to a `Box<dyn PlatformImpl>`.
92105

93106
#[unsafe(no_mangle)]
94-
unsafe extern "C" fn v8__Platform__NotifyingPlatform__onForegroundTaskPosted(
107+
unsafe extern "C" fn v8__Platform__CustomPlatform__BASE__onForegroundTaskPosted(
95108
context: *mut std::ffi::c_void,
96109
isolate: *mut std::ffi::c_void,
97110
delay_in_seconds: f64,
98111
) {
99-
let callback =
100-
unsafe { &*(context as *const Box<dyn ForegroundTaskCallback>) };
101-
callback.on_foreground_task_posted(isolate, delay_in_seconds);
112+
let imp = unsafe { &*(context as *const Box<dyn PlatformImpl>) };
113+
imp.on_foreground_task_posted(isolate, delay_in_seconds);
102114
}
103115

104116
#[unsafe(no_mangle)]
105-
unsafe extern "C" fn v8__Platform__NotifyingPlatform__dropContext(
117+
unsafe extern "C" fn v8__Platform__CustomPlatform__BASE__onIsolateShutdown(
118+
context: *mut std::ffi::c_void,
119+
isolate: *mut std::ffi::c_void,
120+
) {
121+
let imp = unsafe { &*(context as *const Box<dyn PlatformImpl>) };
122+
imp.on_isolate_shutdown(isolate);
123+
}
124+
125+
#[unsafe(no_mangle)]
126+
unsafe extern "C" fn v8__Platform__CustomPlatform__BASE__DROP(
106127
context: *mut std::ffi::c_void,
107128
) {
108129
unsafe {
109-
let _ = Box::from_raw(context as *mut Box<dyn ForegroundTaskCallback>);
130+
let _ = Box::from_raw(context as *mut Box<dyn PlatformImpl>);
110131
}
111132
}
112133

@@ -161,26 +182,21 @@ pub fn new_single_threaded_default_platform(
161182
Platform::new_single_threaded(idle_task_support)
162183
}
163184

164-
/// Creates a NotifyingPlatform that wraps DefaultPlatform and calls the
165-
/// provided [`ForegroundTaskCallback`] whenever a foreground task is posted
166-
/// for any isolate.
167-
///
168-
/// This allows embedders to wake their event loop when V8 background threads
169-
/// complete work and post foreground continuations (e.g. background compilation
170-
/// finishing, Atomics.waitAsync resolving). For delayed tasks, the embedder
171-
/// should schedule a wake-up after `delay_in_seconds` (e.g. via a timer).
185+
/// Creates a custom platform backed by `DefaultPlatform` that delegates
186+
/// virtual method overrides to the provided [`PlatformImpl`] trait object.
172187
///
173-
/// The callback may be invoked from ANY thread (V8 background threads, etc.)
174-
/// and must be safe to call concurrently.
188+
/// This follows the same pattern as
189+
/// [`V8InspectorClient::new`](crate::inspector::V8InspectorClient::new).
175190
///
176-
/// Thread-isolated allocations are disabled (same as `new_unprotected_default_platform`).
191+
/// Thread-isolated allocations are disabled (same as
192+
/// `new_unprotected_default_platform`).
177193
#[inline(always)]
178-
pub fn new_notifying_platform(
194+
pub fn new_custom_platform(
179195
thread_pool_size: u32,
180196
idle_task_support: bool,
181-
callback: impl ForegroundTaskCallback + 'static,
197+
platform_impl: impl PlatformImpl + 'static,
182198
) -> UniqueRef<Platform> {
183-
Platform::new_notifying(thread_pool_size, idle_task_support, callback)
199+
Platform::new_custom(thread_pool_size, idle_task_support, platform_impl)
184200
}
185201

186202
impl Platform {
@@ -248,24 +264,23 @@ impl Platform {
248264
}
249265
}
250266

251-
/// Creates a NotifyingPlatform (subclass of DefaultPlatform) that dispatches
252-
/// to the provided [`ForegroundTaskCallback`] whenever a foreground task is
253-
/// posted for an isolate.
267+
/// Creates a custom platform backed by `DefaultPlatform` that delegates
268+
/// virtual method overrides to the provided [`PlatformImpl`] trait object.
254269
///
255-
/// The callback trait object is owned by the platform and will be dropped
256-
/// when the platform is destroyed.
270+
/// The trait object is owned by the platform and will be dropped when the
271+
/// platform is destroyed.
257272
#[inline(always)]
258-
pub fn new_notifying(
273+
pub fn new_custom(
259274
thread_pool_size: u32,
260275
idle_task_support: bool,
261-
callback: impl ForegroundTaskCallback + 'static,
276+
platform_impl: impl PlatformImpl + 'static,
262277
) -> UniqueRef<Self> {
263278
// Double-box: inner Box<dyn> is a fat pointer, outer Box gives us a
264279
// thin pointer we can pass through C++ void*.
265-
let boxed: Box<dyn ForegroundTaskCallback> = Box::new(callback);
280+
let boxed: Box<dyn PlatformImpl> = Box::new(platform_impl);
266281
let context = Box::into_raw(Box::new(boxed)) as *mut std::ffi::c_void;
267282
unsafe {
268-
UniqueRef::from_raw(v8__Platform__NewNotifyingPlatform(
283+
UniqueRef::from_raw(v8__Platform__NewCustomPlatform(
269284
thread_pool_size.min(16) as i32,
270285
idle_task_support,
271286
context,

0 commit comments

Comments
 (0)