Extract atomic platform specific implementation into an implementation file#2739
Extract atomic platform specific implementation into an implementation file#2739thewilsonator merged 5 commits intodlang:masterfrom
Conversation
|
Thanks for your pull request and interest in making D better, @TurkeyMan! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please see CONTRIBUTING.md for more information. If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment. Bugzilla referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog. Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub fetch digger
dub run digger -- build "master + druntime#2739" |
|
Why can't it find the file? What have I missed? |
ff83f8c to
a78c156
Compare
|
This is green, good to go? |
|
I'm just gonna add another patch, rather than open another PR. |
|
Almost there... and I think this'll be the last of my atomic mischief. |
|
|
Okay, I'm done... when it's green it's finished. |
Hmm, phobos problems. |
|
It's weird; why doesn't it show the instantiation context :/ |
|
I mean, that just looks like a bug in phobos that I've exposed... how can anyone expect to call atomicStore on a const object? |
|
Why is that being inferred as const at all? The offending line |
|
Mmmm, I can't find any evidence of const feeding into this :/ |
|
Indeed, it looks very strange. |
|
I seem to have made it worse... I have no idea what's going on :/ |
|
I think it might have something to do with this: |
|
Seems likely. |
|
Everything always has to be so hard! >_< |
I might have a fix: dlang/dmd#10317 |
src/core/atomic.d
Outdated
| * The value of 'val'. | ||
| */ | ||
| TailShared!T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)( ref const shared T val ) pure nothrow @nogc @trusted | ||
| inout(TailShared!T) atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)( ref inout shared T val ) pure nothrow @nogc @trusted |
There was a problem hiding this comment.
As far as I can tell, this inout stuff isn't necessary.
Going from const T to T is fine if it's a copy. Only the (copied) head of the type is made mutable.
Example:
import core.atomic;
shared const int* x;
pragma(msg, typeof(atomicLoad(x))); /* shared(const(int))* */Note that the referenced int is still const.
There was a problem hiding this comment.
I'd like to think that's true, I didn't write this and it's a huge mess, but if I change anything at all it all breaks spectacularly.
I think some of the downstream code is maladjusted to this complexity, so it would be good to tidy up generally, but that's a different PR.
There was a problem hiding this comment.
The worst part here is that atomics shouldn't be shared at all... unless the user passed a shared thing, in which case it should work properly with that.
There was a problem hiding this comment.
I didn't write this and it's a huge mess, but if I change anything at all it all breaks spectacularly.
You added inout which is what I'm commenting on. I'm saying you should be able to leave it as it is (const instead of inout).
I expect that the DMD bug you filed is the real issue, and I hope that we can fix it.
There was a problem hiding this comment.
The T in the code should contain and propagate the qualifiers, I'd like to get the explicit qualifiers out of the code.
There was a problem hiding this comment.
This should just return a copy of T, whatever T is, and comply with whether T is copyable or not.
Theres this whole TailShared machine in core.atomic; I have no idea what it does, or why it's there, but it's very complicated and seems to make problems.
There was a problem hiding this comment.
I'm the author of TailShared. It's supposed to be an improvement over the previous HeadUnshared which would strip shared to eagerly (#1605).
I wouldn't mind if you got rid of it, changing the return type to simply T. But changing return types is always risky business, and shared is still underspecified, as far as I know. So that's probably not going to be easy.
There was a problem hiding this comment.
Oh perfect!
Well, I need to understand what it does and why it's there before removing it... changing API in here breaks lots of client code, so I haven't been able to make much simplification.
But I also think some of the unittests have bugs which are there to hide some bugs in the implementation... like a double-negative situation.
I had trouble with it because it wraps the type and changes the calling convention. It generates extra code in loadAtomic by doing a copy from one calling convention to another.
Slice/delegate returns in RAX;RDX, but TailShared returns by stack, so you can see atomicLoad has that thunk in the return statement which is there so that it transfers the return value from one ABI to the other. It would be better if it wasn't there... but like I say, I don't understand the problem it was trying to solve.
I've been working with Andrei and Walter and friends on what shared needs to be in the future... we should talk, do you have IM?
Have you been contributing to core.atomic a lot? (Your github profile gives me no idea who you are)
There was a problem hiding this comment.
Well, I need to understand what it does and why it's there before removing it... changing API in here breaks lots of client code, so I haven't been able to make much simplification.
TailShared does (or is supposed to do) what it says in the comment: "Construct a type with a shared tail, and if possible with an unshared head."
shared(T*) becomes shared(T)*. As for why that's done, I suppose it's because dealing with shared is cumbersome, so it's removed as much as possible. But I didn't add that, I just refined it. I can't say for sure what the original motivation was.
T* also becomes shared(T)*. If I remember correctly, this was just easier to implement and/or use, because atomicLoad and friends always operate on shared types anyway.
shared SomeClass remains shared SomeClass. This is the whole improvement over HeadUnshared which would remove shared.
But I also think some of the unittests have bugs which are there to hide some bugs in the implementation... like a double-negative situation.
Which tests do you think are wrong?
I had trouble with it because it wraps the type and changes the calling convention. It generates extra code in loadAtomic by doing a copy from one calling convention to another.
Slice/delegate returns in RAX;RDX, but TailShared returns by stack, so you can seeatomicLoadhas that thunk in the return statement which is there so that it transfers the return value from one ABI to the other. It would be better if it wasn't there... but like I say, I don't understand the problem it was trying to solve.
Being a type template, TailShared doesn't return anything in the sense of calling conventions. And (unfortunately) I can't shed light on what atomicLoad does. I didn't write it and I know next to nothing about atomic operations, memory fences, etc. Sorry.
I've been working with Andrei and Walter and friends on what shared needs to be in the future... we should talk, do you have IM?
You can email me at gmail, but I don't have anything to offer when it comes to technical details of dealing with shared data. At best I can make amateurish comments on the type system side of things.
Have you been contributing to
core.atomica lot?
No. I'm pretty sure that turning HeadUnshared into TailShared was my most significant contribution here. Might actually be the only one. And that required no actual knowledge of atomics, just of D's type system.
Sorry again if I got your hopes up. I'm not actually knowledgeable about most of this.
There was a problem hiding this comment.
I had trouble with it because it wraps the type and changes the calling convention. It generates extra code in loadAtomic by doing a copy from one calling convention to another.
Slice/delegate returns in RAX;RDX, but TailShared returns by stack, so you can seeatomicLoadhas that thunk in the return statement which is there so that it transfers the return value from one ABI to the other. It would be better if it wasn't there... but like I say, I don't understand the problem it was trying to solve.Being a type template,
TailShareddoesn't return anything in the sense of calling conventions.
Well it does, because the function T fun(), and TailShared!T fun() have a different calling convention in some cases... which is problematic when there's thunk's all over the code. I'm trying to remove all the ugly shit as best I can.
I've made some changes that isolate TailShared to a single overload which operates on that exact case, and tried to narrow it down.
I think it's better now, and the tests pass, but what I think might be better if the user called the function with a TailShared!T manually. They can use it when they want it.
e14218d to
395f97a
Compare
|
FFS, someone needs to kill |
|
WHY was this merged with a failing buildkite check??? https://github.com/vibe-d/vibe.d/blob/master/crypto/vibe/crypto/cryptorand.d#L173 |
|
The buildkite/dmd error: The code https://github.com/vibe-d/vibe.d/blob/master/crypto/vibe/crypto/cryptorand.d |
| is(T : U[], U) || | ||
| is(T : R delegate(A), R, A...) || | ||
| (is(T == struct) && __traits(isPOD, T) && | ||
| T.sizeof <= size_t.sizeof*2 && // no more than 2 words |
There was a problem hiding this comment.
This introduced a regression from the original:
T.sizeof <= 16
In that now atomics fail on ix86 and x32 platforms where 128-bit atomics is supported in a libatomic library (or natively with a lock cmpxchg16b on x32)

Pulled all the noise out into
core.internal.atomic, which GDC/LDC can easily replace with intrinsics, and can be a reference for DMD intrinsics if that work happens.core.atomicbecomes platform agnostic.