Eliminate static shared this in std.socket#5813
Eliminate static shared this in std.socket#5813andralex wants to merge 4 commits intodlang:masterfrom
Conversation
std/socket.d
Outdated
| auto ws2Lib = GetModuleHandleA("ws2_32.dll"); | ||
| if (ws2Lib) | ||
| { | ||
| atomicStore(getnameinfoPointer, cast(typeof(getnameinfoPointer)) |
There was a problem hiding this comment.
If I read this correctly, it's supported since WinXP.
https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520(v=vs.85).aspx#SUPPORT_FOR_GETADDRINFO_ON_WINDOWS_2000_AND_OLDER_VERSIONS_
There was a problem hiding this comment.
Yah, that's what @CyberShadow said too. Then we had linking problems so I backed off of that.
There was a problem hiding this comment.
Just seeing those linking issues myself, it's an outed ws2_32.lib (OMF import lib for optlink) shipping with dmd, so much to that rotten format.
There was a problem hiding this comment.
Yah, that's what @CyberShadow said too. Then we had linking problems so I backed off of that.
I just fixed those linking issues, dlang/installer#264, after 6 years :o, but it'll take a while till those libs arrive at the testers.
std/socket.d
Outdated
| { | ||
| WSACleanup(); | ||
| static bool tlsInitialized; | ||
| if (tlsInitialized) return; |
There was a problem hiding this comment.
???
Why do you put a TLS flag around the double-checked locking in initOnce?
Line 2487 in 27072f8
Considering how slow name lookups are, performance shouldn't be a hindrance. And even then you'd better spent some time to optimize initOnce for dmd than to add more and more workarounds.
There was a problem hiding this comment.
Just wanted to make initialize cheap to call in all instances.
There was a problem hiding this comment.
It's already cheap to call initOnce, that's the purpose of it. I understand that dmd performs bad inlining with it, but that doesn't matter here.
And besides a TLS access isn't actually free either.
There was a problem hiding this comment.
I'm a bit worried about cargo cult here.
That TLS locking pattern came up as alternative to "double-checked locking", only that double-checked locking works fine with atomic acquire&release primitives (which are free on X86).
So in total you have the overhead of a TLS access against a normal X86 load instruction.
Now in this PR you added both to be even faster.?
There was a problem hiding this comment.
No problem, glad to remove.
ac40755 to
0cc4fbc
Compare
|
Thanks for your pull request, @andralex! 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 run digger -- build "master + phobos#5813" |
|
Fixed typo on Windows. |
| const val = WSAStartup(0x2020, &wd); | ||
| if (val) // Request Winsock 2.2 for IPv6. | ||
| throw new SocketOSException("Unable to initialize socket library", val); | ||
| static extern(C) void cleanup() { WSACleanup(); } |
There was a problem hiding this comment.
Actually quite nice that dmd mangles nested C functions, no?
MartinNowak
left a comment
There was a problem hiding this comment.
The name resolution test is failing, complaining that WSA isn't initialized.
std.socket.AddressException@std\socket.d(1600): Unable to resolve host 'www.dlang.org': Either the application has not called WSAStartup, or WSAStartup failed.
----------------
I might just have a look since my Win VirtualBox is just on.
std/socket.d
Outdated
| { | ||
| atomicStore(getnameinfoPointer, cast(typeof(getnameinfoPointer)) | ||
| GetProcAddress(ws2Lib, "getnameinfo")); | ||
| atomicStore(getaddrinfoPointer, cast(typeof(getaddrinfoPointer)) |
There was a problem hiding this comment.
Those don't need to be atomic, since initOnce is holding a lock for you.
std/socket.d
Outdated
| getaddrinfoPointer = &getaddrinfo; | ||
| freeaddrinfoPointer = &freeaddrinfo; | ||
| initialize; | ||
| return atomicLoad(getaddrinfoPointer)(a); |
There was a problem hiding this comment.
Those don't need to be atomic either since in case it really did initialize, the mutex has the necessary memory barriers.
|
Looks like you still need to wrap |
2052ee0 to
50350e2
Compare
|
@MartinNowak thanks. I've made the updates prompted by your review save for calls to |
|
ping @andralex Are we moving forward with this? Is it just a matter of a rebase? |
|
@quickfur yes I'll get to this soon thanks for the prod |
std/socket.d
Outdated
| shared static ~this() @system nothrow @nogc | ||
| { | ||
| version(Windows) | ||
| private void initialize() @trusted |
There was a problem hiding this comment.
CircleCi failure is due to the redundant usage of private:
same visibility attribute used as defined on line 329.
|
So... where are we with this PR? |
98218fb to
824bd7d
Compare
|
OK, I rebased. Let's see how it goes now. |
std/socket.d
Outdated
| WSADATA wd; | ||
| const val = WSAStartup(0x2020, &wd); | ||
| if (val) // Request Winsock 2.2 for IPv6. | ||
| throw new SocketOSException("Unable to initialize socket library", val); |
There was a problem hiding this comment.
I'm considering aborting the application here. It pretty much means the Windows installation is messed up. Thoughts?
There was a problem hiding this comment.
Sounds like an horrible idea for GUI applications. Why?
There was a problem hiding this comment.
There's just no realistic scenario that warrants throwing an exception (and forcing all transitive clients of this function to mind it). Last time WSAStartup() failed was on Windows 7 or whatever: https://www.youtube.com/watch?v=MWc6fGipU28.
Normally I wouldn't mind much, but it's time to acknowledge that throwing exceptions is painful for programmatic clients and we should only throw them when it makes sense. We need to tighten the screws on this stuff.
There was a problem hiding this comment.
Oh, there is an alternative: simply return and leave the pointers to functions to be null. Then nothing will work.
There was a problem hiding this comment.
OK, I think this is nice: if the lib doesn't load, no networking functionality is supported. There was code before that accounted for that anyway.
| } | ||
|
|
||
| // Returns true iff getnameinfo is supported on this platform. | ||
| version(Windows) private @property bool getnameinfoSupported() |
There was a problem hiding this comment.
Why add getaddrinfoSupported and getnameinfoSupported and not just make getnameInfoPointer / getaddrinfoPointer lazily-initialized property functions?
There was a problem hiding this comment.
That's not new to this PR - old code handled the cases when the pointers were null and used workaround solutions.
So, old code:
- try to fetch the pointers at startup
- compare the pointers against null in code
New code:
- define xxxSupported and xxx, both doing initialization on first call
- use xxxSupported instead of comparing pointers to null
There was a problem hiding this comment.
Right, but, why not just keep comparing pointers to null?
There was a problem hiding this comment.
Because you need to call initialize before comparing them to null.
There was a problem hiding this comment.
Yes, so, why not turn the function pointer variables into function pointer returning property functions, in the same way that getnameinfoSupported are in the current version, but returning the pointer directly instead of a bool?
There was a problem hiding this comment.
That would be an alternative, but it seems more roundabout. To make sure I understand:
@property auto our_getnameinfoPtr() {
initialize;
return &getNameInfoPtr;
}
... using code ...
if (our_getnameinfoPtr)
{
...
our_getnameinfoPtr(... arguments ...);
}Is this the idea? Mine was to not use pointers at all, but instead have the simple "if xxxSupported returns true, you may call xxx" convention. It seems yours could be used to avoid two calls to initialize by using local variables:
... using code ...
auto gni = our_getnameinfoPtr;
if (gni)
{
...
gni(... arguments ...);
}There was a problem hiding this comment.
That would be an alternative, but it seems more roundabout.
I don't see how that would be more roundabout. If anything, adding an additional pair of functions would better fit the description of "roundabout".
It seems yours could be used to avoid two calls to initialize by using local variables:
I don't see the need to avoid performing two calls. The compiler ought to inline the function, at which point it can trivially conclude that under the if branch, the pointer cannot possibly be null, and eliminate the second comparison.
There was a problem hiding this comment.
Cool, yeah, two functions are better than four.
| shared static ~this() @system nothrow @nogc | ||
| { | ||
| version (Windows) | ||
| void initialize() @trusted nothrow |
There was a problem hiding this comment.
If I'm reading this correctly, this function needs to be called in a lot more places now (every single bit of public code that eventually calls WSA functions, so that it calls WSAStartup), and I don't see this patch doing it.
There was a problem hiding this comment.
Got it, will fix. Thanks.
|
Folks, I'll need to give up on this. There's too much work for Windows (albeit fun) but I don't have the time to set up Windows right now. Eliminating static cdtors from druntime and phobos is key to lean and mean executables. Any function called from a static cdtors ends up linked in the program, transitively with all other functions it calls. And you know Phobos... you link one, you link'em all :) |
A fresh approach based loosely on #5472.