Skip to content

Commit 0dccadf

Browse files
committed
wip
1 parent 2ccc554 commit 0dccadf

File tree

3 files changed

+99
-38
lines changed

3 files changed

+99
-38
lines changed

src/binding.cc

Lines changed: 35 additions & 26 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-
// Callback type: called from any thread when a foreground task is posted
3016-
// for a given isolate. The void* is the raw isolate pointer. delay_in_seconds
3017-
// is 0.0 for immediate tasks, or the delay before the task should be executed.
3018-
using ForegroundTaskPostedCallback = void (*)(void* isolate_ptr,
3019-
double delay_in_seconds);
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+
}
30203022

30213023
// TaskRunner wrapper that intercepts all PostTask* calls and notifies
3022-
// the embedder before delegating to the real runner.
3024+
// the embedder (via a Rust trait) before delegating to the real runner.
30233025
class NotifyingTaskRunner final : public v8::TaskRunner {
30243026
public:
3025-
NotifyingTaskRunner(std::shared_ptr<v8::TaskRunner> wrapped,
3026-
ForegroundTaskPostedCallback callback,
3027+
NotifyingTaskRunner(std::shared_ptr<v8::TaskRunner> wrapped, void* context,
30273028
v8::Isolate* isolate)
3028-
: wrapped_(std::move(wrapped)), callback_(callback), isolate_(isolate) {}
3029+
: wrapped_(std::move(wrapped)), context_(context), isolate_(isolate) {}
30293030

30303031
bool IdleTasksEnabled() override { return wrapped_->IdleTasksEnabled(); }
30313032
bool NonNestableTasksEnabled() const override {
@@ -3039,50 +3040,59 @@ class NotifyingTaskRunner final : public v8::TaskRunner {
30393040
void PostTaskImpl(std::unique_ptr<v8::Task> task,
30403041
const v8::SourceLocation& location) override {
30413042
wrapped_->PostTask(std::move(task), location);
3042-
callback_(static_cast<void*>(isolate_), 0.0);
3043+
v8__Platform__NotifyingPlatform__onForegroundTaskPosted(
3044+
context_, static_cast<void*>(isolate_), 0.0);
30433045
}
30443046
void PostNonNestableTaskImpl(std::unique_ptr<v8::Task> task,
30453047
const v8::SourceLocation& location) override {
30463048
wrapped_->PostNonNestableTask(std::move(task), location);
3047-
callback_(static_cast<void*>(isolate_), 0.0);
3049+
v8__Platform__NotifyingPlatform__onForegroundTaskPosted(
3050+
context_, static_cast<void*>(isolate_), 0.0);
30483051
}
30493052
void PostDelayedTaskImpl(std::unique_ptr<v8::Task> task,
30503053
double delay_in_seconds,
30513054
const v8::SourceLocation& location) override {
30523055
wrapped_->PostDelayedTask(std::move(task), delay_in_seconds, location);
3053-
callback_(static_cast<void*>(isolate_),
3054-
delay_in_seconds > 0 ? delay_in_seconds : 0.0);
3056+
v8__Platform__NotifyingPlatform__onForegroundTaskPosted(
3057+
context_, static_cast<void*>(isolate_),
3058+
delay_in_seconds > 0 ? delay_in_seconds : 0.0);
30553059
}
30563060
void PostNonNestableDelayedTaskImpl(
30573061
std::unique_ptr<v8::Task> task, double delay_in_seconds,
30583062
const v8::SourceLocation& location) override {
30593063
wrapped_->PostNonNestableDelayedTask(std::move(task), delay_in_seconds,
30603064
location);
3061-
callback_(static_cast<void*>(isolate_),
3062-
delay_in_seconds > 0 ? delay_in_seconds : 0.0);
3065+
v8__Platform__NotifyingPlatform__onForegroundTaskPosted(
3066+
context_, static_cast<void*>(isolate_),
3067+
delay_in_seconds > 0 ? delay_in_seconds : 0.0);
30633068
}
30643069
void PostIdleTaskImpl(std::unique_ptr<v8::IdleTask> task,
30653070
const v8::SourceLocation& location) override {
30663071
wrapped_->PostIdleTask(std::move(task), location);
3067-
callback_(static_cast<void*>(isolate_), 0.0);
3072+
v8__Platform__NotifyingPlatform__onForegroundTaskPosted(
3073+
context_, static_cast<void*>(isolate_), 0.0);
30683074
}
30693075

30703076
private:
30713077
std::shared_ptr<v8::TaskRunner> wrapped_;
3072-
ForegroundTaskPostedCallback callback_;
3078+
void* context_;
30733079
v8::Isolate* isolate_;
30743080
};
30753081

30763082
// Platform wrapper that intercepts GetForegroundTaskRunner to return
3077-
// NotifyingTaskRunner instances, notifying the embedder of foreground tasks.
3083+
// NotifyingTaskRunner instances, dispatching to a Rust trait object.
30783084
class NotifyingPlatform : public v8::platform::DefaultPlatform {
30793085
using IdleTaskSupport = v8::platform::IdleTaskSupport;
30803086

30813087
public:
30823088
NotifyingPlatform(int thread_pool_size, IdleTaskSupport idle_task_support,
3083-
ForegroundTaskPostedCallback callback)
3089+
void* context)
30843090
: DefaultPlatform(thread_pool_size, idle_task_support),
3085-
callback_(callback) {}
3091+
context_(context) {}
3092+
3093+
~NotifyingPlatform() override {
3094+
v8__Platform__NotifyingPlatform__dropContext(context_);
3095+
}
30863096

30873097
std::shared_ptr<v8::TaskRunner> GetForegroundTaskRunner(
30883098
v8::Isolate* isolate, v8::TaskPriority priority) override {
@@ -3095,12 +3105,12 @@ class NotifyingPlatform : public v8::platform::DefaultPlatform {
30953105
if (runner) return runner;
30963106
}
30973107
auto notifying =
3098-
std::make_shared<NotifyingTaskRunner>(original, callback_, isolate);
3108+
std::make_shared<NotifyingTaskRunner>(original, context_, isolate);
30993109
runners_[key] = notifying;
31003110
return notifying;
31013111
}
31023112

3103-
void NotifyIsolateShutdown(v8::Isolate* isolate) override {
3113+
void NotifyIsolateShutdown(v8::Isolate* isolate) {
31043114
{
31053115
std::lock_guard<std::mutex> lock(mutex_);
31063116
for (auto it = runners_.begin(); it != runners_.end();) {
@@ -3119,7 +3129,7 @@ class NotifyingPlatform : public v8::platform::DefaultPlatform {
31193129
}
31203130

31213131
private:
3122-
ForegroundTaskPostedCallback callback_;
3132+
void* context_;
31233133
std::mutex mutex_;
31243134
std::map<std::pair<v8::Isolate*, v8::TaskPriority>,
31253135
std::weak_ptr<NotifyingTaskRunner>>
@@ -3181,8 +3191,7 @@ v8::Platform* v8__Platform__NewSingleThreadedDefaultPlatform(
31813191

31823192
v8::Platform* v8__Platform__NewNotifyingPlatform(int thread_pool_size,
31833193
bool idle_task_support,
3184-
void (*callback)(void*,
3185-
double)) {
3194+
void* context) {
31863195
if (thread_pool_size < 1) {
31873196
thread_pool_size = std::thread::hardware_concurrency();
31883197
}
@@ -3191,7 +3200,7 @@ v8::Platform* v8__Platform__NewNotifyingPlatform(int thread_pool_size,
31913200
thread_pool_size,
31923201
idle_task_support ? v8::platform::IdleTaskSupport::kEnabled
31933202
: v8::platform::IdleTaskSupport::kDisabled,
3194-
callback)
3203+
context)
31953204
.release();
31963205
}
31973206

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ pub use isolate_create_params::CreateParams;
136136
pub use microtask::MicrotaskQueue;
137137
pub use module::*;
138138
pub use object::*;
139+
pub use platform::ForegroundTaskCallback;
139140
pub use platform::Platform;
140141
pub use platform::new_default_platform;
141142
pub use platform::new_notifying_platform;

src/platform.rs

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ unsafe extern "C" {
2525
fn v8__Platform__NewNotifyingPlatform(
2626
thread_pool_size: int,
2727
idle_task_support: bool,
28-
callback: unsafe extern "C" fn(*mut std::ffi::c_void, f64),
28+
context: *mut std::ffi::c_void,
2929
) -> *mut Platform;
3030
fn v8__Platform__DELETE(this: *mut Platform);
3131

@@ -65,6 +65,51 @@ 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.
71+
///
72+
/// This follows the trait-based pattern used by the inspector API
73+
/// (`V8InspectorClientImpl`, `ChannelImpl`).
74+
pub trait ForegroundTaskCallback: Send + Sync {
75+
/// Called when a foreground task has been posted for the given isolate.
76+
///
77+
/// `isolate_ptr` is the raw `v8::Isolate*` pointer of the target isolate.
78+
/// `delay_in_seconds` is 0.0 for immediate tasks, or the delay before the
79+
/// task should be executed. For delayed tasks, the embedder should schedule
80+
/// a wake-up after the given delay (e.g. via a timer in tokio).
81+
///
82+
/// This may be called from ANY thread (V8 background threads, etc.).
83+
fn on_foreground_task_posted(
84+
&self,
85+
isolate_ptr: *mut std::ffi::c_void,
86+
delay_in_seconds: f64,
87+
);
88+
}
89+
90+
// FFI callbacks called from C++ NotifyingPlatform/NotifyingTaskRunner.
91+
// `context` is a raw pointer to a `Box<dyn ForegroundTaskCallback>`.
92+
93+
#[unsafe(no_mangle)]
94+
unsafe extern "C" fn v8__Platform__NotifyingPlatform__onForegroundTaskPosted(
95+
context: *mut std::ffi::c_void,
96+
isolate: *mut std::ffi::c_void,
97+
delay_in_seconds: f64,
98+
) {
99+
let callback =
100+
unsafe { &*(context as *const Box<dyn ForegroundTaskCallback>) };
101+
callback.on_foreground_task_posted(isolate, delay_in_seconds);
102+
}
103+
104+
#[unsafe(no_mangle)]
105+
unsafe extern "C" fn v8__Platform__NotifyingPlatform__dropContext(
106+
context: *mut std::ffi::c_void,
107+
) {
108+
unsafe {
109+
let _ = Box::from_raw(context as *mut Box<dyn ForegroundTaskCallback>);
110+
}
111+
}
112+
68113
/// Returns a new instance of the default v8::Platform implementation.
69114
///
70115
/// |thread_pool_size| is the number of worker threads to allocate for
@@ -116,10 +161,9 @@ pub fn new_single_threaded_default_platform(
116161
Platform::new_single_threaded(idle_task_support)
117162
}
118163

119-
/// Creates a NotifyingPlatform that wraps DefaultPlatform and calls `callback`
120-
/// whenever a foreground task is posted for any isolate. The callback receives
121-
/// the raw `v8::Isolate*` pointer (as `*mut c_void`) and `delay_in_seconds`
122-
/// (0.0 for immediate tasks, or the delay before the task should run).
164+
/// Creates a NotifyingPlatform that wraps DefaultPlatform and calls the
165+
/// provided [`ForegroundTaskCallback`] whenever a foreground task is posted
166+
/// for any isolate.
123167
///
124168
/// This allows embedders to wake their event loop when V8 background threads
125169
/// complete work and post foreground continuations (e.g. background compilation
@@ -134,7 +178,7 @@ pub fn new_single_threaded_default_platform(
134178
pub fn new_notifying_platform(
135179
thread_pool_size: u32,
136180
idle_task_support: bool,
137-
callback: unsafe extern "C" fn(*mut std::ffi::c_void, f64),
181+
callback: impl ForegroundTaskCallback + 'static,
138182
) -> UniqueRef<Platform> {
139183
Platform::new_notifying(thread_pool_size, idle_task_support, callback)
140184
}
@@ -204,21 +248,28 @@ impl Platform {
204248
}
205249
}
206250

207-
/// Creates a NotifyingPlatform (subclass of DefaultPlatform) that calls
208-
/// `callback` whenever a foreground task is posted for an isolate.
209-
/// The callback receives the isolate pointer and delay in seconds (0.0 for
210-
/// immediate tasks).
251+
/// Creates a NotifyingPlatform (subclass of DefaultPlatform) that dispatches
252+
/// to the provided [`ForegroundTaskCallback`] whenever a foreground task is
253+
/// posted for an isolate.
254+
///
255+
/// The callback trait object is owned by the platform and will be dropped
256+
/// when the platform is destroyed.
211257
#[inline(always)]
212258
pub fn new_notifying(
213259
thread_pool_size: u32,
214260
idle_task_support: bool,
215-
callback: unsafe extern "C" fn(*mut std::ffi::c_void, f64),
261+
callback: impl ForegroundTaskCallback + 'static,
216262
) -> UniqueRef<Self> {
263+
// Double-box: inner Box<dyn> is a fat pointer, outer Box gives us a
264+
// thin pointer we can pass through C++ void*.
265+
let boxed: Box<dyn ForegroundTaskCallback> = Box::new(callback);
266+
let context =
267+
Box::into_raw(Box::new(boxed)) as *mut std::ffi::c_void;
217268
unsafe {
218269
UniqueRef::from_raw(v8__Platform__NewNotifyingPlatform(
219270
thread_pool_size.min(16) as i32,
220271
idle_task_support,
221-
callback,
272+
context,
222273
))
223274
}
224275
}

0 commit comments

Comments
 (0)