-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
src: refactor thread stopping mechanism #26757
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -104,7 +104,7 @@ Worker::Worker(Environment* env, | |
| bool Worker::is_stopped() const { | ||
| Mutex::ScopedLock lock(mutex_); | ||
| if (env_ != nullptr) | ||
| return env_->GetAsyncRequest()->IsStopped(); | ||
| return env_->is_stopping(); | ||
| return stopped_; | ||
| } | ||
|
|
||
|
|
@@ -222,7 +222,7 @@ void Worker::Run() { | |
| stopped_ = true; | ||
| this->env_ = nullptr; | ||
| } | ||
| env_->GetAsyncRequest()->SetStopped(true); | ||
| env_->thread_stopper()->set_stopped(true); | ||
| env_->stop_sub_worker_contexts(); | ||
| env_->RunCleanup(); | ||
| RunAtExit(env_.get()); | ||
|
|
@@ -381,7 +381,8 @@ void Worker::OnThreadStopped() { | |
| Worker::~Worker() { | ||
| Mutex::ScopedLock lock(mutex_); | ||
|
|
||
| CHECK(stopped_ || env_ == nullptr || env_->GetAsyncRequest()->IsStopped()); | ||
| CHECK(stopped_); | ||
| CHECK_NULL(env_); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIRC, there was a control flow that takes to Worker destructor without nullifying
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gireeshpunathil I think that would be a bug – the child thread is not allowed to exist at this point (and the next |
||
| CHECK(thread_joined_); | ||
|
|
||
| Debug(this, "Worker %llu destroyed", thread_id_); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so: the heavy usage of muexes around the worker code where multi-thread data access was expected, was almost always to ensure data consistency by flushing cache lines? (in other words, writes in one thread is made visible to other threads instantly). If so,
std::memory_order_relaxedconstraint is insufficient to ensure that? we might need at leastmemory_order_acquire?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I retract above question - when I tested with a small code, I see
mfenceorsyncinstruction being added withmemory_order_relaxeditself; so pls ignore.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So … my line of thinking was that the syscalls behind
uv_async_send()would themselves present full memory barriers. I’ll check later and verify that that’s indeed correct.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@addaleax - that may be true; but we do have
set_stoppedis_stoppedcalls that the threads can call but do not involve syscalls? however:this is what I tested and this is what I see in the generated code:
please note that
mfenceat0x400587that settles the matter?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On x64 it does, yes – to be honest, I don’t know how the different memory order modes are implemented on different platforms? It seems like this disassembled implementation simply ignores the order argument?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@addaleax - if you look at the continuity of the instructions, looks like these (the atomic* helpers) are not static APIs, but compiler-generated code, on the fly; so it is possible that only necessary code was generated, on a per compilation unit basis?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Libuv doesn't promise that.
uv_async_send()can (at least in theory) elide the system call.I'm kind of surprised the compiler emits an mfence. It's not needed on x64 (nor any other architecture, I think?) because aligned loads and stores are always atomic. It might just be a compiler bug; I wouldn't depend on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gireeshpunathil I wouldn’t think so, the linker should be able to elide multiple copies of that variable into a single one.
@bnoordhuis Yeah, thanks. I’ve removed the
memory_order_relaxedbit.