Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

P/Invoke cleanup #60

Closed
wants to merge 76 commits into from
Closed

Conversation

Tragetaschen
Copy link
Contributor

This PR cleans up large portions of the P/Invoke handling of Kestrel to libuv. I know these are a lot of changes, but I have kept the single commits for easier following.

The highlights are:

  • Use DllImport and Mono's DllMap feature instead of manually loading and binding a platform-specific library
  • The various SafeHandles now do all the required allocation work in their constructors
  • No more unsafe
  • Working shutdown

I have tested on both Windows and Linux, but could not do so on a Mac simply because I don't have access to one. My test setup included a web application with an active SignalR connection. I was not able to reproduce any of the shutdown issues again (yet?)

@ghost
Copy link

ghost commented Feb 8, 2015

Hi @Tragetaschen, I'm your friendly neighborhood Microsoft Open Technologies, Inc. Pull Request Bot (You can call me MSOTBOT). Thanks for your contribution!
You've already signed the contribution license agreement. Thanks!

The agreement was validated by Microsoft Open Technologies, Inc. and real humans are currently evaluating your PR.

TTYL, MSOTBOT;

@ghost ghost added the cla-not-required label Feb 8, 2015
@lodejard
Copy link
Contributor

lodejard commented Feb 8, 2015

Wow, that's a lot of work! The individual commits are very helpful and they sound like good ideas based on the descriptions. We'll take a closer look soon. Thank you very much!

@Tragetaschen
Copy link
Contributor Author

As a final step (well, let's see about that):

  • UvHandles are not SafeHandles anymore

Since they have to clean both loop resource as well as the unmanaged memory, the SafeHandle pattern does not really work: By the time the Finalizer runs, the loop might have been long brought down and posting to it won't work. The new base class implements a working UvHandle pattern and warns when a finalizer does the cleanup (I don't get the warning).

@Tragetaschen
Copy link
Contributor Author

Indirectly, I know that OSx won't work, but it's close. I have modified my Linux environment so it looks similar to how OSx will behave (basically removed libuv* from /usr/lib64 and placed the files in various folders).

The reason it worked on Linux so far is that mono is quite smart with DllImports. "libuv.dll" is mapped to "libuv.so" and then searched for in the standard folders. Because I have a link libuv.so -> libuv.so.1.0.0 in /usr/lib64, everything Just Works (tm). On OSx, a compatible file is not in the standard folders, though.

I'll revert to the manual binding :-/

{
IsDarwin = PlatformApis.IsDarwin();
#if ASPNETCORE50
return true;
Copy link
Contributor

Choose a reason for hiding this comment

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

While this is true now, it won't be true in the future... what would be the correct code once Core CLR runs on non-Windows?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question.

This code is the same as before, I just took it as is. However, I spent a few thoughts and decided I don't know enough to fix it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah OK we'll figure this out in the future, then.

@Tragetaschen Tragetaschen force-pushed the pinvoke-cleanup branch 3 times, most recently from 3b1443e to 1ee6990 Compare February 9, 2015 19:35
private static void WriteCallback(UvWriteReq req, int status, Exception error, object state)
{
((ThisWriteReq)state).OnWrite(req, status, error);
}

SocketOutput _self;
ArraySegment<byte> _buffer;
byte[] _buffer;
Copy link
Member

Choose a reason for hiding this comment

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

Why?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The only code path into the Contextualize currently creates a copy of the incoming ArraySegment. I figured I can simplify the code down to the P/Invoke.

Copy link
Contributor

Choose a reason for hiding this comment

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

That part of the server hasn't been optimized at all really (which is why it's copying the array segment every time) - so I wouldn't worry too much about fixing that code path before it's been fine-tuned.

@Tragetaschen
Copy link
Contributor Author

  • No more manual writing in unmanaged memory
  • Clean shutdown logic (no more uv_stop)

@Tragetaschen
Copy link
Contributor Author

I will now stop shuffeling code around :)

@Eilon
Copy link
Contributor

Eilon commented Feb 20, 2015

@lodejard can you try this out locally and merge?

@Tragetaschen
Copy link
Contributor Author

I'm close to fixing most of the bugs I found so far (see the -beta2 branch in my fork). For stability reasons, I'd prefer that additional work to be included

@Tragetaschen
Copy link
Contributor Author

  • Connections are now closed cleanly on shutdown as opposed to through the Finalizer
  • Keep-Alive working
  • Connections closed by the client working

I'm not quite convinced of the last commit. Not that I couldn't defend its practical correctness, but some code paths down the MessageBody.Consume implementations are not that clean in their handling of RemoteIntakeFin and Buffer.Count == 0, and these aren't triggered anymore through the Frame.

The leaking SignalR connections currently trigger a Console warning during shutdown.

@Tragetaschen
Copy link
Contributor Author

I've done a fair amount of testing including ab stress testing with half a million requests and I don't expect to have introduced any regressions while fixing a couple of issues.

@Tragetaschen
Copy link
Contributor Author

There was a race between closing the socket client-wise on the one hand and still writing from the application's end. The write request could throw an ObjectDisposedException on its on-loop-part that would bring down the loop.

Instead of catching this exception, I changed the Write to WriteAsync. Now, the exception still occurs, but is handled by whomever is currently handling the request.

@Tragetaschen
Copy link
Contributor Author

When a connection with an active write request is closed, the write request's callback gets -UV_ECANCELED as error code. This is now handled as an expected error.

@Tragetaschen
Copy link
Contributor Author

Sure

{
KestrelTrace.Log.ConnectionReadFin(_connectionId);
SocketInput.RemoteIntakeFin = true;
_socket.ReadStop();
if (status != -4095)
Copy link
Member

Choose a reason for hiding this comment

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

Can you define a descriptive constant for this? The same goes for the ones above.

@dnfclas
Copy link

dnfclas commented May 7, 2015

@Tragetaschen, Thanks for signing the contribution license agreement so quickly! Actual humans will now validate the agreement and then evaluate the PR.

Thanks, DNFBOT;

@Tragetaschen
Copy link
Contributor Author

Watching last week's Community Standup made me really, really sad :(

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants