Permalink
Browse files

Fixes #5

Get rid of memory leak if the enumerator is not enumerated
  • Loading branch information...
1 parent b911c09 commit 3c4b97579562f7bf29a7613665f75d6dc6386b5a Chris Scribner committed May 12, 2012
Showing with 42 additions and 21 deletions.
  1. +6 −4 benchmark/benchmark.js
  2. +2 −0 docs/api.md
  3. +1 −1 docs/overview.md
  4. +9 −1 lib/asyncblock.js
  5. +24 −15 lib/enumerator.js
View
@@ -105,9 +105,7 @@ suite.addBatch({
});
- while(enumerator.moveNext()){
-
- }
+ enumerator.end();
}
this.callback(null, new Date() - start);
@@ -332,7 +330,11 @@ suite.addBatch({
var num = enumerator.current;
}
- self.callback(null, new Date() - start);
+ var endTime = new Date() - start;
+ //Give the gc time to run
+ setTimeout(function(){
+ self.callback(null, endTime);
+ }, 3000)
});
},
View
@@ -268,6 +268,8 @@ Returns an Enumerator, which has the method moveNext and a getter named current.
Enumerators may yield results asynchronously as long as the code calling the enumerator is also in an asyncblock. If the calling code is
not in an asyncblock, the enumerator must return synchronously.
+**Warning** - If you create the enumerator and do not use it, a memory leak will occur. Make sure you call moveNext at least once, or enumerator.end().
+
One of my favorite uses of this sort of thing is graph walking code. It allows you to separate the traversal logic from the
business logic.
View
@@ -148,7 +148,7 @@ asyncblock(function(){
* The most clean and succinct syntax that asyncblock offers for cases when obtaining results from the async call
* Supports series and parallel operations
* Maintains the "this" context when calling the async function
-* Source transformation will allow asyncblock to target harmony generators when avaiable in V8, further increasing performance and removing risk associated with fibers
+* Source transformation will allow asyncblock to target harmony generators when avaiable in V8, further increasing performance and removing the dependency on fibers
### Cons
View
@@ -78,7 +78,15 @@ var asyncblock = function(fn, options) {
fiber = Fiber(fiberContents);
if(options.isGenerator){
- return enumerator.getEnumerator(flow, fiber);
+ var enumer = enumerator.getEnumerator(flow, fiber);
+
+ enumer.events.once('end', function(){
+ fn = null;
+ fiber = null;
+ flow = null;
+ });
+
+ return enumer;
} else {
fiber.run();
}
View
@@ -1,9 +1,11 @@
var Fiber = require('fibers');
var Flow = require('./flow.js').Flow;
+var EventEmitter = require('events').EventEmitter;
var Enumerator = function(exec){
this.exec = exec;
this.curr = null;
+ this.events = new EventEmitter();
};
Enumerator.prototype.__defineGetter__('current', function(){
@@ -14,7 +16,13 @@ var undef = (function(){})();
Enumerator.prototype.moveNext = function(){
this.curr = this.exec();
- return this.curr !== undef;
+ var hasMore = this.curr !== undef;
+
+ if(!hasMore){
+ this.end();
+ }
+
+ return hasMore;
};
Flow.prototype.yield = function(value){
@@ -33,15 +41,6 @@ exports.getEnumerator = function(flow, fiber){
//THe generator is initially stopped
flow._light = false;
- var runFiber = fiber.run.bind(fiber);
-
- var run = function(){
- //Fiber is done generating
- if(fiber != null){
- return runFiber();
- }
- };
-
var enumerator = function(){
//Async generator support
if(Fiber.current != null){
@@ -55,29 +54,39 @@ exports.getEnumerator = function(flow, fiber){
};
flow.on('yield', callback);
-
- //If the generator doesn't end up generating anything, we don't want to wait forever
+ //Clean up and return control when the generator returns
flow.on('end', callback);
//We need to delay the running of the generator in case it returns results without blocking
process.nextTick(function(){
- if(!flow._light){
- run();
+ if(flow && !flow._light){
+ fiber.run();
}
});
var result = outerFlow.wait(key);
+ outerFlow = null;
flow.removeListener('end', callback);
flow.removeListener('yield', callback);
return result;
} else {
- return run();
+ return fiber.run();
}
};
enumerator.__proto__ = new Enumerator(enumerator);
+ enumerator.end = function(){
+ enumerator.events.emit('end');
+
+ try { fiber.reset(); } catch(e) {}
+
+ enumerator = null;
+ run = null;
+ fiber = null;
+ flow = null;
+ };
return enumerator;
};

0 comments on commit 3c4b975

Please sign in to comment.