-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Nuke boost::thread and boost::thread_group #8631
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
Conversation
Concept ACK. Will review & test soon. The boost interruption_point paradigm is messy and can make code hard to reason about. In my code for #8464 I have also removed boost::thread and interruption points, preferring more explicit quitting. Dealing with the interruption code made working on #8464 more difficult than it had to be. One Nit, which I think is worth addressing. I would refactor the names from Interrupt to Quit, Shutdown, or even ControlledExit. Connotation of interrupt is a non-consensual break, whereas Quit or Shutdown implies being done as a normal part of control flow. |
@JeremyRubin Nearly every thread uses separate interrupt/shutdown calls for exactly that reason. Interrupt is non-consensual, and means "stop what you're doing and don't start anything else". Shutdown means "please gracefully exit now and don't return until you do". |
@theuni Ah, gotcha. Was looking at small sample; my bad. I guess my comment should instead be, we should eventually migrate to shutdown only no interrupt threads; but this is a big step in that direction and precursor to that happening. |
Concept ACK, testing. |
OS X here:
|
Compile error fixed here. |
Needs rebase. |
e25e118
to
9fb3b1c
Compare
rebased and squashed in the compile fix. |
There's another reason, and that is that interrupt is supposed to be an instant operation at the caller site, and shutdown is allowed to wait. This allows parallelism in the shutdown process:
...
Sure, the naming may not be optimal, "interrupt" is more like a hint to stop work, but I think the approach makes sense. (I'm sure this can be implemented more thoroughly with explicit shutdown-dependencies and std::futures, but the idea is the same, to make shutdown as fast as possible) Edit: re-triggered travis, the failure was a timeout, probably has nothing to do with the changes here. |
Travis does keep failing. There may be an issue after all. |
Hmm, travis was happy before the rebase. I'll try to reproduce locally, maybe something changed. |
0115d72
to
c15e11b
Compare
The travis failures were caused by the rebase after all. ab48c5e introduced a new test that relied on the interruptible semantics. I've added a commit that should fix that up. |
789eaff
to
8a3bdd5
Compare
needs rebase again :( |
Also note that boost::thread catches boost::thread_interrupted itself, but std::thread obviously won't catch thread_interrupted. So don't rethrow after catching.
Also, drop threadGroup for them and treat them individually
@@ -120,6 +120,12 @@ boost::condition_variable messageHandlerCondition; | |||
static CNodeSignals g_signals; | |||
CNodeSignals& GetNodeSignals() { return g_signals; } | |||
|
|||
static std::condition_variable net_interrupt_cond; | |||
static std::mutex cs_net_interrupt; | |||
static std::atomic<bool> net_interrupted(false); |
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.
This is OK -- initially, I was confused as to why you use an atomic var for the condition variable, as usually access is guarded by the lock so atomic would be overkill. But in this case, it's fine.
May be worth writing a profile at some point for if it is better to use a cond+atomic or to lock on writing the condition externally, or just throwing a comment in here as to why you're using an atomic.
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.
Right, that's not at all clear from the individual commits. The atomic is used as the break condition for a few condvars, as well as the loop break condition in a few places. Rather than using separate bools for each, I just lumped them all into this one.
Will add a comment.
@theuni I've gone through and I'll give you a "tentative" utack. Mostly looks fine. I'd suggest mostly:
|
@JeremyRubin Thanks for the review! To your points:
|
@theuni The only one I think you need to fix for this pr is No worries about the scriptcheck stuff, we'll cross that bridge when it comes. |
@JeremyRubin Fixing up CConditionVariable means doing lots of mutex/lock replacements all over the place. I'd really rather do that as a follow-up to keep this one minimal. Now that we're not using any boost::thread's, I don't believe there's any major functional difference. |
@theuni sounds fine, this one is nuking boost::thread not boost::condition_variable after all :) |
needs rebase |
@@ -294,6 +294,8 @@ int CommandLineRPC(int argc, char *argv[]) | |||
catch (const boost::thread_interrupted&) { |
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.
Does this still need to catch boost::thread_interrupted
? (another one below)
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.
No, see "future changes" in the OP. There are a bunch of these to get rid of. I guess it doesn't make sense to do that later, I'll add it here.
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.
Sorry, yes you mention it there. It just looked strange to me to add something instead of replace.
Concept ACK. Needs a rebase. Tagging for 0.14.0 |
There also some comments/examples about using boost thread in scheduler that could be nuked. |
Closing for now, need to get a specific chunk of this in first. |
This replaces #8023 with a solution that moves us directly to std::thread, rather than using an intermediate wrapper.
Background
std::thread is pretty much a drop-in replacement for boost::thread, except that boost::thread is interruptible. We've used those interruptions, but we'll have to drop them eventually as they didn't become part of the spec.
When an interruption point is hit, it throws an instance of boost::thread_interrupted. boost::thread catches these automatically by default.
The interruption points are defined as:
The ones relevant to us are primarily
boost::condition_variable_any::wait()
,boost::this_thread::sleep_for()
, and of courseboost::this_thread::interruption_point()
Any boost::thread will immediately throw boost::thread_interrupted if those are hit at any point. So we can't simply s/boost::thread/std::thread/g, as we would never be able to exit.
Changes
thread_interrupted
andinterruption_point
have been added.Future changes
There is lots of cleanup to be done post-merge. I was very tempted to include cleanups here, but I've elected to keep the changes minimal. Todo:
boost::thread_interrupted
)Also, I started on this by creating a base class for launching threads. Each instance would be responsible for implementing Interrupt() and Stop(). But because this is already a complex set of changes, I think it's best to do that as a separate step.