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
common: consolidate spinlocks #15816
Conversation
61d31f5
to
85d1b94
Compare
87cdee1
to
786653d
Compare
src/include/spinlock.h
Outdated
void lock() const { | ||
ceph::spin_lock(spinlock); | ||
} | ||
/// release spinlock |
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.
Why is this const? (Not that it's wrong, it just seems odd.)
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.
It just tells the compiler and reader that this member function causes no side-effects on the object itself. In addition to expressing intent, it can enable some extra optimizations.
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.
But the only thing this class does is wrap the actual spin_lock, which is for some reason declared mutable. What optimizations could it possibly perform that are worth that confusion? Or are we grabbing non-mutable locks in chat functions that require this chicanery?
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.
@gregsfortytwo The answer to that very good question is "that's how Spinlock.h did it" and I wanted to avoid breaking any code that was declaring const locks (which make no sense to me, either!). I think this whole thing can go away by using std::unique_lock<>, which I'm very happy to consider as either an extension of this PR or follow-up PR.
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.
Oh. This must be because in the mists of time, declaring locks mutable was annoying. I wonder if I did that. o_0
Follow-up PR sounds good to me!
src/include/spinlock.h
Outdated
// Scoped control of a Spinlock: | ||
class Locker { | ||
const Spinlock& spinlock; | ||
public: |
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.
Rather than adding our own Locker, couldn't we use unique_lock with the spinlock class?
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 believe that we definitely could! After the current changeset, let's either consider that as a next-step of this PR, or as the "next PR".
src/include/spinlock.h
Outdated
|
||
class Spinlock { | ||
mutable std::atomic_flag spinlock { 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.
Shouldn't this be ATOMIC_FLAG_INIT? I didn't think any other way was guaranteed to initialize it properly.
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.
...aaaand, you would be right! I thought I'd found a way around that with aggregate initialization, which does indeed /work/-- but, it turns out that it's a non-standard library extension (N3690, 29.7). I'll correct it and also look through recent atomic code to see if anything using atomic flag is initialized in this way. Thanks for catching this!
src/common/ceph_context.cc
Outdated
@@ -285,11 +294,11 @@ class CephContextObs : public md_config_obs_t { | |||
const std::set <std::string> &changed) override { | |||
if (changed.count( | |||
"enable_experimental_unrecoverable_data_corrupting_features")) { | |||
ceph_spin_lock(&cct->_feature_lock); | |||
ceph::spin_lock(&cct->_feature_lock); | |||
get_str_set( |
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'd be tempted to replace all of these calls to direct lock/unlock with scoped lockers.
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.
That thought's crossed my mind, too-- seemed out of scope (hah-hah!) for this PR. That said, I'm happy to do it (and things should be easy to search for now). One of the big benefits is that scoped locks are safe in the face of any thrown exceptions, and if we happen to have any case where we've missed those might "instantly" fix a bug or two.
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 don't want to PILE WORK ON YOU that could in principle be done later, but if you're willing to do it I'd love to see us use scoped lockers instead of explicit locks/unlocks..
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.
As with std::lock_guard, I'm absolutely happy to do it-- but it may be a better "next" PR than this one (or, alternatively, it may be perfect for this one). I guess the question is whether the cycles are better spent here, or on trying to fix things for Luminous.
eb258e9
to
35e1d3d
Compare
If folks are starting to feel good about this, I propose one of these two next steps:
...I'm ok with either of these, obviously 1a will take a bit longer, where as 1b could be ready for testing "now". |
35e1d3d
to
0141c7d
Compare
I would prefer 1a, simply on the theory that if we're going to have to touch every lock/unlock call anyway we may as well unique_lockify them now. However since you're doing the work I'd defer to whichever judgment you mentioned. Though since the weird const/mutable stuff is a historical accident I'd strongly prefer we get rid of that in either case. |
@adamemerson I'll ponder it a bit-- 1a makes sense because now that the locking is indeed "consolidated", it's easier to see how the standard library can be applied. Same with the idea of a lock that's const. Let me look and see what it would take from this point. |
ac236df
to
04d7f08
Compare
src/common/buffer.cc
Outdated
@@ -44,8 +44,8 @@ using namespace ceph; | |||
|
|||
#ifdef BUFFER_DEBUG | |||
static std::atomic_flag buffer_debug_lock = ATOMIC_FLAG_INIT; | |||
# define bdout { simple_spin_lock(&buffer_debug_lock); std::cout | |||
# define bendl std::endl; simple_spin_unlock(&buffer_debug_lock); } | |||
# define bdout { ceph::spin_lock(&buffer_debug_lock); std::cout |
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 ends up being the one place I keep the bare spin_lock() calls... who knows what that macro does? (Would it be safe to use the scope-guard version..?)
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.
It should be since every bdout has to be paired with a bendl to close the scope, analogous to dout/dendl.
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.
Well, actually, if it /doesn't/ match I guess we'll get a compiler error, so will-do!
Have to put the pen down for a bit-- but, unfortunately std::atomic_flag does not model a /simple lockable/ type, so direct use of std::lock_guard<> and std::unique_lock<> won't work for it. But, it looks like there are several ways around this. I've started in, though I'm beginning to have second thoughts about trying to get it into this PR-- I'll pick up with a fresher mind! |
Is there any reason to have/use a bare std::atomic_flag in Ceph as opposed to just using the ceph::spn_lock class? I'd think from the perspective of both clarity (YES, I know this thing IS a Spinlock...not that std::atomic flag is good for much else, but still.) And from the perspective of substitutability (i.e. if I decide I want a spinlock somewhere I have a std::mutex or vice versa I can just change the member type and not every lock/unlock call, at least assuming people using the lock factories.) |
@adamemerson I'll mull it over a bit more, but I don't think there's any reason to use the bare spinlock (we /may/ want to provide a method to expose it, but I see no need for that right now), and there are at least two additional advantages-- you can't forget to initialize the std::atomic_flag, and you can get a /simple lockable/ model. I think what I'll probably wind up doing is implementing a new spinlock class (or two, for different semantics) that will work with the standard library. I'll see if I can make some headway, but I have vacation coming up and that's starting to nudge me toward wanting to make it a separate PR again. There are also some other cleanups, like bare pthreads locks, that should happen. |
@chardan That sounds good. And, happy vacating. |
Was just chatting in irc and I trust you guys to get this right so definitely not doing a detailed review — but we definitely don't want to condition "switching to the standard library" on "making everything use unique_lock/Locker variants". There are (perhaps unfortunately) places where that's not feasible with our locking constraints and trying to identify and switch all of them before we migrate our new stuff is just going to drag everybody down into misery. |
@gregsfortytwo Oh, sure. Where it's not appropriate I agree completely. I know of at least ONE place where it doesn't make sense off the top of my head. It's just one of those things that I /prefer/ to do whenever it's reasonable and possible without vast restructuring since it eliminates a bunch of potential bugs in error pathways and whatnot. |
674fa52
to
c7754e9
Compare
Sound good! Looks like the next step is code review? Most cases did indeed work out to require only RAII guards! A powerful idiom. |
f9578ee
to
18aef06
Compare
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 a fine and good patch.
Launch Grand Thaumaturgic Miracles
src/include/spinlock.h
Outdated
* Foundation. See file COPYING. | ||
* | ||
* @author Sage Weil <sage@inktank.com> | ||
*/ |
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.
Credit yourself in the file you wrote :)
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.
Not to mention the copyright date! Thanks for mentioning this.
std::lock_guard<Spinlock> lock(random_lock); | ||
static std::mutex random_lock; | ||
std::lock_guard<std::mutex> lock(random_lock); | ||
|
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 assume this goes away when your random PR gets pulled in.
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.
Hmm... yep, looks like that whole block of code can likely vanish! Thanks for the reminder that I should check for cases where we use <random>
as well as calls to rand().
18aef06
to
b5b454b
Compare
@liewegas ping... does this look all right to you? |
Yep! Let's merge this post-luminous, though. I won't want to be hunting down some weird regression due to a typo here. |
(Note: I'm adding a versioning namespace, up shortly.) |
@liewegas Ok, thank you! |
Signed-off-by: Jesse Williamson <jwilliamson@suse.de>
Signed-off-by: Jesse Williamson <jwilliamson@suse.de>
Signed-off-by: Jesse Williamson <jwilliamson@suse.de>
ceph::spinlock models /BasicLockable/, and works with standard library types like std::lock_guard<>. Signed-off-by: Jesse Williamson <jwilliamson@suse.de>
Signed-off-by: Jesse Williamson <jwilliamson@suse.de>
Signed-off-by: Jesse Williamson <jwilliamson@suse.de>
Signed-off-by: Jesse Williamson <jwilliamson@suse.de>
Signed-off-by: Jesse Williamson <jwilliamson@suse.de>
Signed-off-by: Jesse Williamson <jwilliamson@suse.de>
Signed-off-by: Jesse Williamson <jwilliamson@suse.de>
Signed-off-by: Jesse Williamson <jwilliamson@suse.de>
3922b1d
to
7415375
Compare
Since we've forked luminous, is there anything this is waiting on before it can be merged? |
@liewegas (You have to imagine Rod Serling saying this) "Submitted for your approval." |
http://pulpito.ceph.com/sage-2017-08-31_16:29:40-rados-wip-sage-testing2-2017-08-31-0908-distro-basic-smithi/ |
Sounds good-- let me know how I can help! |
\o/ |
Yay! |
Completes migration away from SpinLock.h to wrappers around std::atomic_flag.
simple_spin_lock() and ceph_spin_lock() are now consolidated as ceph::spin_lock(), any remaining instances of spinlock_t move to std::atomic_flag.
Refer-to:
#14337
See-also minibenchmark here (short version: the standard library is fast):
https://github.com/chardan/ceph-spinlock-minibench