-
Notifications
You must be signed in to change notification settings - Fork 270
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
Clean up once code #359
base: master
Are you sure you want to change the base?
Clean up once code #359
Conversation
f32a5df
to
e19209d
Compare
@@ -673,6 +672,80 @@ mod fuzz_dummy { | |||
fn rustsecp256k1_v0_4_1_context_preallocated_clone(cx: *const Context, prealloc: *mut c_void) -> *mut Context; | |||
} | |||
|
|||
mod once { |
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 needs some kind of descriptive comment describing why its here, that it's only used in fuzzing, and that we don't really care too much...................all that said, maybe we should care a bit more, I realized yesterday that global-context-less-secure
relies on std
, even though the whole point of it is to avoid relying on std (it predates no-std so that's expected, but still). It only actually relies on std::sync::Once
, so maybe we should use this there as well. Not sure how to get it across both crates, though, maybe just make a single file and symlink them across the two crates to avoid making it 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.
In that case I think it should condinionaly use Once
from std
as suggested in #352
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.
The whole point of the global-context-less-secure
feature is to work on no-std
, and sadly Once
is not no-std
as it requires thread-parking support.
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.
Since cargo features are additive it's still useful to avoid performance issues if two crates turn on these features. Although maybe they should've been --cfg
parameters, not features...
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, sorry, I misread your comment again lol. Yes, conditionally using Once
if std
is turned on is fine, but we still need to somehow have this once
available in secp256k1
if we want to fix global-context-less-secure
not building with no-std
.
This was originally intended to just change `swap` to `compare_exchange` however other changes were needed too: * Load with `Release` ordering seemed wrong because if it loaded `DONE` before while loop it wouldn't synchronize with the storing thread. * There's no point in repeatedly checking if the value is `NONE` so this check was moved about while loop * Due to a mistake I wanted to inspect using `miri` but couldn't because of FFI call. Separating the once implementation from consumer allowed writing a test in pure Rust and inspect via `miri`. * The initializing code now skips loading the value again because it already knows it's initialized. * Orderings are now explained in the comments
e19209d
to
36c96a9
Compare
This caches the initialization state of preallocated context in a thread local variable to avoid atomics in subsequent accesses.
Accessing `std::mem` instead of `core::mem` caused fuzzing to break without `std`.
I also fixed broken no_std fuzzing. Would've done in a separate PR but not worth conflict fixing I think. |
This reverts commit 5cbf5ee.
Are you planning on fixing global-context-less-secure + no-std here? |
Oh, didn't notice it's broken. Is there an advantage of doing it here? |
I mentioned it in one of the discussions further up, but those were a mess, it's issue is use of std Once. I don't know if this PR is worth bothering unless we also bother refactoring it to make it available in both crates (I guess a symlink to a common module?) and fix global contact with it. |
OK, will try to find the time to look into 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.
A few minor suggestions.
if have_ctx == NONE { | ||
// Ordering: on success we're only signalling to other thread that work is in progress | ||
// without transferring other data, so it should be Relaxed, on failure the value may be DONE, | ||
// so we want to Acquire to safely proceed in that case. |
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.
Should this be 'so we want Acquire to safely proceed ...'?
// without transferring other data, so it should be Relaxed, on failure the value may be DONE, | ||
// so we want to Acquire to safely proceed in that case. | ||
// However compare_exchange doesn't allow failure ordering to be stronger than success | ||
// so both are 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.
nit, this whole comment might look better like this (includes change above):
// Ordering: on success we're only signalling to other thread that work is in
// progress without transferring other data, so it should be Relaxed, on failure
// the value may be DONE, so we want Acquire to safely proceed in that case.
// However compare_exchange doesn't allow failure ordering to be stronger than
// success so both are Acquire.
} | ||
} | ||
while have_ctx != DONE { | ||
// Another thread is building, just busy-loop until they're done. |
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 refers to the whole loop, right? Perhaps clearer if its outside the loop (above the while
line).
assert_eq!(rustsecp256k1_v0_4_1_context_preallocated_create( | ||
PREALLOCATED_CONTEXT[..].as_ptr() as *mut c_void, | ||
SECP256K1_START_SIGN | SECP256K1_START_VERIFY), | ||
PREALLOCATED_CONTEXT[..].as_ptr() as *mut Context); |
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.
Can we assign this pointer to a local variable?
let ptr = PREALLOCATED_CONTEXT[..].as_ptr() as *mut c_void;
assert_eq!(rustsecp256k1_v0_4_1_context_preallocated_create(ptr, SECP256K1_START_SIGN | SECP256K1_START_VERIFY), ptr);
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.
They are different types. Which is strange. It'd still be shorter to cast one of them but would be better to look if we can unify them.
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 dear, I missed the type difference :(
// This thread will read whatever the other thread wrote so this needs to be Acquire. | ||
have_ctx = self.0.load(Ordering::Acquire); | ||
#[cfg(feature = "std")] | ||
::std::thread::yield_now(); |
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.
FTR yield_now
is not great. Futex would be the best, cond var as a fallback.
assert_eq!(rustsecp256k1_v0_4_1_context_preallocated_create( | ||
PREALLOCATED_CONTEXT[..].as_ptr() as *mut c_void, | ||
SECP256K1_START_SIGN | SECP256K1_START_VERIFY), | ||
PREALLOCATED_CONTEXT[..].as_ptr() as *mut Context); |
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.
They are different types. Which is strange. It'd still be shorter to cast one of them but would be better to look if we can unify them.
const HAVE_CONTEXT_NONE: usize = 0; | ||
const HAVE_CONTEXT_WORKING: usize = 1; | ||
const HAVE_CONTEXT_DONE: usize = 2; | ||
static HAVE_PREALLOCATED_CONTEXT: once::Once = once::Once::INIT; | ||
static mut PREALLOCATED_CONTEXT: [u8; CTX_SIZE] = [0; CTX_SIZE]; |
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.
Just realized this variable is very wrong due to alignment
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's what we have AlignedType
for: https://docs.rs/secp256k1-sys/latest/secp256k1_sys/types/struct.AlignedType.html
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.
Ah, so the fix should be [AlignedType; CTX_SIZE / mem::size_of::<AlignedType>()]
, right? Although maybe we should have a separate type for that.
Now looking at ffi::Context
and it's an int
. WTF?
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.
idk what CTX_SIZE
is and how was it calculated.
we can't have a type for that because we don't know the size of the context in compile time.
although everything here won't be needed when we start supporting the static contexts
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.
we don't know the size of the context in compile time.
I think we can know the maximum and guard against it changing by calling secp256k1_context_preallocated_size
Whats the status of this PR @Kixunil, are there more things to be done or would you like it re-reviewed as is? |
Didn't have much time for it but meanwhile I came to conclusion that I really don't like spin locks for reasons that were mentioned elsewhere. I will probably still finish the PR to fix UB but then I want to make another one fixing the spin lock issue. |
This was originally intended to just change
swap
tocompare_exchange
however other changes were needed too:
Release
ordering seemed wrong because if it loadedDONE
before while loop it wouldn't synchronize with the storing thread.
NONE
so thischeck was moved about while loop
miri
but couldn't becauseof FFI call. Separating the once implementation from consumer allowed
writing a test in pure Rust and inspect via
miri
.already knows it's initialized.
Closes #351