Skip to content

Eliminate promise polling#854

Merged
johnhaley81 merged 4 commits into
nodegit:masterfrom
srajko:eliminate-promise-polling
Jan 15, 2016
Merged

Eliminate promise polling#854
johnhaley81 merged 4 commits into
nodegit:masterfrom
srajko:eliminate-promise-polling

Conversation

@srajko
Copy link
Copy Markdown
Collaborator

@srajko srajko commented Jan 6, 2016

Eliminates the polling of promises when executing javascript callbacks from within async libgit2 calls. Replaces with a system that forwards the promise result / rejection reason to the native layer directly.

This should remove our dependency on the nodegit-promise library.

@johnhaley81
Copy link
Copy Markdown
Collaborator

This looks fantastic! A+ man. Can you switch the clones tests over to using another library to confirm that we work with other promise implementations? Bluebird or then would be good.

@tbranyen
Copy link
Copy Markdown
Member

tbranyen commented Jan 6, 2016

👯 !

@maxkorp
Copy link
Copy Markdown
Collaborator

maxkorp commented Jan 6, 2016

I would also test vanilla node promises (since they exist in 0.12+, which is where our support starts).

@srajko srajko force-pushed the eliminate-promise-polling branch from b1fb458 to 3865ab2 Compare January 7, 2016 14:15
@srajko
Copy link
Copy Markdown
Collaborator Author

srajko commented Jan 7, 2016

Good call! I tested with native promises since that was the easiest, and got it to work. On the native side I just needed to replace a Nan::Get with a Nan::GetRealNamedProperty, and on the test side I had to modify some uses of Promise.resolve and Promise.reject.

It turns out (at least in node 4.2.3 that I was testing with) that native Promises don't let you call Promise.resolve or Promise.reject without the Promise context. That is, if you try:

var resolve = Promise.resolve;
resolve(1);

you get a crash. And we had test code like this:

    return Remote.delete(repository, "origin3")
      .then(function() {
        return Remote.lookup(repository, "origin3");
      })
      .then(Promise.reject, Promise.resolve);

The last line results in the same crash. Use cases like this just need to be modified to pass Promise.reject.bind(Promise) and Promise.resolve.bind(Promise) instead. That or we Promise.resolve = Promise.resolve.bind(Promise).

Would you like me to replace var Promise = require('nodegit-promise') with var Promise = global.Promise || require('nodegit-promise') as part of this PR (and fixup the uses of Promise.resolve and Promise.reject described above) - at least in tests?

@srajko
Copy link
Copy Markdown
Collaborator Author

srajko commented Jan 7, 2016

Just FYI, the last thing I am working on is ensuring that PromiseCompletion objects get freed correctly. I don't think they are currently - even if I force garbage collection, I never seem to get a hit in the destructor.

@srajko
Copy link
Copy Markdown
Collaborator Author

srajko commented Jan 7, 2016

Actually, I may have been wrong about needing GetRealNamedProperty instead of Get...

@srajko
Copy link
Copy Markdown
Collaborator Author

srajko commented Jan 7, 2016

Yeah... best I can tell, GetRealNamedProperty is like Get except it doesn't run interceptors. Going back to Get.

@srajko srajko force-pushed the eliminate-promise-polling branch 2 times, most recently from 334f8b6 to 1849693 Compare January 7, 2016 21:47
@srajko
Copy link
Copy Markdown
Collaborator Author

srajko commented Jan 7, 2016

Found and fixed the memory leak - the majority of the problem was with the fact that when using a FunctionTemplate, "The lifetime of the created function is equal to the lifetime of the context". I was creating templates and functions willy-nilly, and the way I was doing it was keeping handles on the PromiseCompletion instances.

Taking off the WIP, but someone better at Nan/v8 than me might want to take a close look at the code.

@srajko srajko changed the title [WIP] Eliminate promise polling Eliminate promise polling Jan 7, 2016
@srajko srajko force-pushed the eliminate-promise-polling branch 3 times, most recently from 966222b to 25a9ba2 Compare January 8, 2016 20:05
@johnhaley81
Copy link
Copy Markdown
Collaborator

@srajko require('nodegit-promise') is scattered throughout the application in both the/liband the/test` folder. We should remove all of those and then make sure that the tests still run. I think that would be sufficient for this PR to be considered finished.

@srajko srajko force-pushed the eliminate-promise-polling branch from 93bd8d2 to 8dae154 Compare January 13, 2016 03:18
@johnhaley81
Copy link
Copy Markdown
Collaborator

AppVeyor timed out. I'll re-run

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I feel like these should bind to result and not thisHandle. They are instance properties of result and we're rebinding the context of them.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

promiseFulfilled and promiseRejected are instance properties of PromiseCompletion objects - https://github.com/srajko/nodegit/blob/8dae154aa00b5c23f482b746ba4efcfb524a812d/generate/templates/manual/src/promise_completion.cc#L13-L14 https://github.com/srajko/nodegit/blob/8dae154aa00b5c23f482b746ba4efcfb524a812d/generate/templates/manual/src/promise_completion.cc#L97-L103

We need to bind thisHandle to them so that they can access the PromiseCompletion instance here - https://github.com/srajko/nodegit/blob/8dae154aa00b5c23f482b746ba4efcfb524a812d/generate/templates/manual/src/promise_completion.cc#L92-L94

There might be a better way to do the binding though - using the JS Function.bind method was the best I could come up with, short of leaking memory by creating a function template for each instance of PromiseCompletion :-)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

So I was mistaken about what was going on. I thought this was related to how the Promise.resolve functions passed in now were being bound to themselves in JS. Apparently that is a requirement of Chromium and is Promise library implementation specific. The actual then library does not have this requirement and that was the backing lib for nodegit-promise which is why we didn't need to bind the functions being passed in during tests. With that resolved I'm good with this PR.

@johnhaley81
Copy link
Copy Markdown
Collaborator

https://github.com/nodegit/nodegit/pull/854/files#r49623812 might fix the binding issue?

johnhaley81 added a commit that referenced this pull request Jan 15, 2016
@johnhaley81 johnhaley81 merged commit 57a34e2 into nodegit:master Jan 15, 2016
@srajko srajko deleted the eliminate-promise-polling branch May 9, 2016 22:17
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.

4 participants