Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Again) Attempt to implement Locker for Isolate #1620

Closed
wants to merge 6 commits into from

Conversation

linrongbin16
Copy link

Resolve conflicts in #1411.

cc @Earthmark

@CLAassistant
Copy link

CLAassistant commented Sep 13, 2024

CLA assistant check
All committers have signed the CLA.

src/locker.rs Outdated

#[repr(C)]
#[derive(Debug)]
struct LockerHandle(Opaque);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: Remove the unused LockerHandle.

src/locker.rs Outdated

impl<'a> Locker<'a> {
/// Claims the isolate, this should only be used from a shared isolate.
pub(crate) fn new(isolate: &'a mut Isolate) -> Self {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this is UB, the locker should be acquired before a mutable reference is created.

Copy link
Author

@linrongbin16 linrongbin16 Sep 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: set parameter to isolate: &Isolate (use unsafe cast to &mut Isolate in this new function).

src/locker.rs Outdated

#[repr(C)]
#[derive(Debug)]
pub(super) struct Locker([MaybeUninit<usize>; 2]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with src/binding.hpp and crate::binding you can use the actual C++ definition instead of something that happens to hopefully be the correct size.

Copy link
Author

@linrongbin16 linrongbin16 Sep 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: Use binding to set Locker size.

(When build in my local machine, there's an error report cannot find the v8__Locker__SIZE I newly added in binding.hpp, I'm trying to build it from source again)

src/isolate.rs Outdated

impl Drop for SharedIsolate {
fn drop(&mut self) {
let isolate = self.internal_unsafe_isolate_mut();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might be better of SharedIsolate just contains an OwnedIsolate, since the drop logic needs to stay in sync anyway.

Copy link
Author

@linrongbin16 linrongbin16 Sep 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update:

  1. Make SharedIsolate just contains an OwnedIsolate.
  2. Remove the Drop trait for SharedIsolate, because I think SharedIsolate can automatically/implicitly implement it when it only contains a UnsafeCell<OwnedIsolate>.

NOTE: Actually I found this PR is out of my capabilities, i.e. I'm not 100% sure about what some code is doing. So please be careful about it.

@@ -212,6 +212,10 @@ pub struct Global<T> {
isolate_handle: IsolateHandle,
}

// Globals can go between threads as long as a locker was used to acquire a SharedIsolate.
unsafe impl<T> Send for Global<T> {}
unsafe impl<T> Sync for Global<T> {}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In @Earthmark 's previous PR #1411 , there is a comment saying, simply add Send and Sync trait to Global can be risk.

Could you also please give comments?

Copy link
Author

@linrongbin16 linrongbin16 Sep 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found there is a mutex with isolate inside the IsolateAnnex, see:

isolate_mutex: Mutex<()>,

This code looks it wants to keep the data race on the isolate, makes me feel like using the Locker C++ api is the correct way to implement this part. Once this done, seems a Isolate can natively support Send and Sync traits, i.e. safely be sent between threads.

Correct?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely forgot I had this change out here, thank you for picking it back up.

Let me know if you have questions about what I was trying to do.

I'm pretty sure the comment on the original pr about globals stands.

Globals moving between threads is a bit scary if lockers are not used, although it's kind of a landmine either way because the global needs to be made into a local with the same isolate that made it.

If lockers are not used, the global can only be used on the isolate that made it, which will be single threaded.

This usage constraint kind of enforces that the global is single threaded, but a global being send means it could be moved to another thread accidently (like if it's bundled in an async method), then dropped, which I think I vaguely recall could be a problem. (I haven't looked at this in months, and I'm writing this on my phone).

I think there are two paths with globals:

verify that dropping a global on the wrong thread won't explode (even in the lockers-not-used case)

OR

Make some other thread safe wrapper for globals that locked isolates can produce, that has whatever mechanisms are needed to not explode.

That being said, haven't looked at this in months.

I'm not sure if it's acceptable to make every isolate internally use lockers, they are re-entrant, but adding lockers to every API may add an unacceptable performance cost for users who are not using lockers.

I'm pretty sure lockers block as well, which is a bit nasty if someone wasn't expecting a hard block from an API call.

Copy link
Author

@linrongbin16 linrongbin16 Sep 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, too many API (in rusty_v8) are designed to run in a single thread at the beginning. And JavaScript language itself doesn't support such concepts like multi-thread and mutex.

So just keep it !Send and !Sync is more compatible with V8 and JavaScript language.

The best practice of using v8 along with multi-thread or async runtime should be: put v8 engine inside a single thread, and communicate with outer world with channels.

I close this PR now.

@linrongbin16 linrongbin16 changed the title (Resolved) Attempt to implement Locker for Isolate (Again) Attempt to implement Locker for Isolate Sep 14, 2024
@linrongbin16
Copy link
Author

linrongbin16 commented Sep 15, 2024

The CI failure looks like a network issue when building V8 engine, not related to my code change.

But I cannot re-run it, somebody help me?

@linrongbin16 linrongbin16 reopened this Sep 16, 2024
@linrongbin16 linrongbin16 deleted the locker_attempt branch September 19, 2024 00:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants