Skip to content

Comments

Eliminate static shared this in std.socket#5813

Closed
andralex wants to merge 4 commits intodlang:masterfrom
andralex:nomoresharedzisinsocketlastoneyay-redux
Closed

Eliminate static shared this in std.socket#5813
andralex wants to merge 4 commits intodlang:masterfrom
andralex:nomoresharedzisinsocketlastoneyay-redux

Conversation

@andralex
Copy link
Member

A fresh approach based loosely on #5472.

std/socket.d Outdated
auto ws2Lib = GetModuleHandleA("ws2_32.dll");
if (ws2Lib)
{
atomicStore(getnameinfoPointer, cast(typeof(getnameinfoPointer))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yah, that's what @CyberShadow said too. Then we had linking problems so I backed off of that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

???
Why do you put a TLS flag around the double-checked locking in initOnce?

if (!atomicLoad!(MemoryOrder.acq)(flag))

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wanted to make initialize cheap to call in all instances.

Copy link
Member

@MartinNowak MartinNowak Oct 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem, glad to remove.

@andralex andralex force-pushed the nomoresharedzisinsocketlastoneyay-redux branch from ac40755 to 0cc4fbc Compare October 27, 2017 18:10
@dlang-bot
Copy link
Contributor

dlang-bot commented Oct 27, 2017

Thanks for your pull request, @andralex!

Bugzilla references

Your 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 locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "master + phobos#5813"

@andralex
Copy link
Member Author

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(); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually quite nice that dmd mangles nested C functions, no?

Copy link
Member

@MartinNowak MartinNowak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those don't need to be atomic either since in case it really did initialize, the mutex has the necessary memory barriers.

@MartinNowak
Copy link
Member

Looks like you still need to wrap gethostbyname and such functions not loaded from the DLL.

@andralex andralex force-pushed the nomoresharedzisinsocketlastoneyay-redux branch from 2052ee0 to 50350e2 Compare October 28, 2017 13:37
@andralex
Copy link
Member Author

@MartinNowak thanks. I've made the updates prompted by your review save for calls to initialize. That'd be difficult to do without a Windows host. If you can get to this great, otherwise I'll return to this later.

@quickfur
Copy link
Member

ping @andralex

Are we moving forward with this? Is it just a matter of a rebase?

@andralex
Copy link
Member Author

@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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CircleCi failure is due to the redundant usage of private:

same visibility attribute used as defined on line 329. 

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@dejlek
Copy link

dejlek commented Feb 4, 2019

So... where are we with this PR?

@andralex andralex force-pushed the nomoresharedzisinsocketlastoneyay-redux branch from 98218fb to 824bd7d Compare June 14, 2020 21:58
@andralex
Copy link
Member Author

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);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm considering aborting the application here. It pretty much means the Windows installation is messed up. Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like an horrible idea for GUI applications. Why?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, there is an alternative: simply return and leave the pointers to functions to be null. Then nothing will work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add getaddrinfoSupported and getnameinfoSupported and not just make getnameInfoPointer / getaddrinfoPointer lazily-initialized property functions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but, why not just keep comparing pointers to null?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because you need to call initialize before comparing them to null.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ...);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, yeah, two functions are better than four.

shared static ~this() @system nothrow @nogc
{
version (Windows)
void initialize() @trusted nothrow
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, will fix. Thanks.

@andralex
Copy link
Member Author

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 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants