From ea2f9bcb727a8482d907e6eef89eacff4fe96bfd Mon Sep 17 00:00:00 2001
From: isaacs <i@izs.me>
Date: Sun, 7 Nov 2010 19:09:32 -0800
Subject: [PATCH] Demonstrating that this is not GC.

---
 lib/fs.js                     |    4 +-
 src/node_file.cc              |   39 +++++++++++++++++++++++++++++------
 test/pummel/writealot-fast.js |   30 +++++++++++++++++++++++++++
 test/pummel/writealot-wait.js |   44 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 108 insertions(+), 9 deletions(-)
 create mode 100644 test/pummel/writealot-fast.js
 create mode 100644 test/pummel/writealot-wait.js

diff --git a/lib/fs.js b/lib/fs.js
index ecbe013..0a1fc32 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -234,8 +234,8 @@ fs.write = function (fd, buffer, offset, length, position, callback) {
     }
     return;
   }
-
-  binding.write(fd, buffer, offset, length, position, callback || noop);
+  function wrapcb () { return (callback || noop).apply(this, arguments) }
+  binding.write(fd, buffer, offset, length, position, wrapcb);
 };
 
 fs.writeSync = function (fd, buffer, offset, length, position) {
diff --git a/src/node_file.cc b/src/node_file.cc
index 2a5be60..03b477f 100644
--- a/src/node_file.cc
+++ b/src/node_file.cc
@@ -36,10 +36,32 @@ static Persistent<String> errno_symbol;
 // file-level rather than method-level to avoid excess stack usage.
 static char getbuf[PATH_MAX + 1];
 
+static inline Persistent<Value>* obj_persist (const Local<Value> &v) {
+  Persistent<Value> *o = new Persistent<Value>();
+  if ((*v)->IsFunction()) {
+    Local<Object> otmp = Object::New();
+    otmp->Set(String::New("cb"), v);
+    *o = Persistent<Value>::New(otmp);
+  } else {
+    *o = Persistent<Value>::New((v));
+  }
+  return o;
+}
+static inline Persistent<Value>* obj_unwrap (void *data) {
+  Persistent<Value> *o = reinterpret_cast<Persistent<Value>*>(data);
+  return o;
+}
+static inline void obj_destroy(Persistent<Value> *o) {
+  o->Dispose();
+  delete o;
+}
+
 static int After(eio_req *req) {
   HandleScope scope;
 
-  Persistent<Function> *callback = cb_unwrap(req->data);
+  Persistent<Value> *data = obj_unwrap(req->data);
+  Local<Object> data_obj((*data)->ToObject());
+  Local<Function> callback = Local<Function>::Cast(data_obj->Get(String::New("cb")));
 
   ev_unref(EV_DEFAULT_UC);
 
@@ -48,7 +70,7 @@ static int After(eio_req *req) {
 
   // Allocate space for two args. We may only use one depending on the case.
   // (Feel free to increase this if you need more)
-  Local<Value> argv[2];
+  Local<Value> argv[3];
 
   // NOTE: This may be needed to be changed if something returns a -1
   // for a success, which is possible.
@@ -140,7 +162,7 @@ static int After(eio_req *req) {
   }
 
   TryCatch try_catch;
-
+  argv[argc++] = data_obj;
   (*callback)->Call(v8::Context::GetCurrent()->Global(), argc, argv);
 
   if (try_catch.HasCaught()) {
@@ -148,14 +170,14 @@ static int After(eio_req *req) {
   }
 
   // Dispose of the persistent handle
-  cb_destroy(callback);
+  obj_destroy(data);
 
   return 0;
 }
 
-#define ASYNC_CALL(func, callback, ...)                           \
+#define ASYNC_CALL(func, obj, ...)                           \
   eio_req *req = eio_##func(__VA_ARGS__, EIO_PRI_DEFAULT, After,  \
-    cb_persist(callback));                                        \
+    obj_persist(obj));                                        \
   assert(req);                                                    \
   ev_ref(EV_DEFAULT_UC);                                          \
   return Undefined();
@@ -638,7 +660,10 @@ static Handle<Value> Write(const Arguments& args) {
   Local<Value> cb = args[5];
 
   if (cb->IsFunction()) {
-    ASYNC_CALL(write, cb, fd, buf, len, pos)
+    Local<Object> obj = Object::New();
+    obj->Set(String::New("cb"), cb);
+    obj->Set(String::New("buffer"), buffer_obj);
+    ASYNC_CALL(write, obj, fd, buf, len, pos)
   } else {
     ssize_t written = pos < 0 ? write(fd, buf, len) : pwrite(fd, buf, len, pos);
     if (written < 0) return ThrowException(ErrnoException(errno, "write"));
diff --git a/test/pummel/writealot-fast.js b/test/pummel/writealot-fast.js
new file mode 100644
index 0000000..18def72
--- /dev/null
+++ b/test/pummel/writealot-fast.js
@@ -0,0 +1,30 @@
+var fs = require("fs");
+var child_process = require("child_process");
+var assert = require("assert");
+var fd = fs.openSync("out.txt", "w");
+var i = 0;
+var data = "";
+while (i ++ < 80) data += "a";
+data += "\n";
+var buffer = new Buffer(data);
+for (var i = 0; i < 1024; i ++) {
+  fs.write(fd, buffer, 0, data.length, null, function (er, w, obj) {
+    var b = obj.buffer;
+    assert.equal(b, buffer)
+    testBuffer(b)
+  });
+}
+function testBuffer (b) {
+  for (var i = 0, l = b.length; i < l; i ++) {
+    if (b[i] !== 97 && b[i] !== 10) {
+      throw new Error("invalid char "+i+","+b[i]);
+    }
+  }
+}
+setTimeout(function () {
+  fs.createReadStream("out.txt")
+    .on("data", function (chunk) {
+      testBuffer(chunk)
+    })
+}, 2000);
+
diff --git a/test/pummel/writealot-wait.js b/test/pummel/writealot-wait.js
new file mode 100644
index 0000000..1865770
--- /dev/null
+++ b/test/pummel/writealot-wait.js
@@ -0,0 +1,44 @@
+var fs = require("fs");
+var child_process = require("child_process");
+var assert = require("assert");
+var fd = fs.openSync("out.txt", "w");
+var i = 0;
+var data = "";
+while (i ++ < 80) data += "a";
+data += "\n";
+var buffer = new Buffer(data);
+
+
+;(function LOOP (i, l) {
+  fs.write(fd, buffer, 0, data.length, null, function (er, w, obj) {
+    if (obj) {
+      var b = obj.buffer;
+      assert.equal(b, buffer);
+      testBuffer(b);
+    }
+    if (i < l) LOOP(i+1, l);
+    else done();
+  })
+})(0, 1024);
+//
+//for (var i = 0; i < 500; i ++) fs.write(fd, buffer, 0, data.length, null, function (er, w, obj) {
+//  var b = obj.buffer;
+//  assert.equal(b, buffer)
+//  testBuffer(b)
+//});
+function testBuffer (b) {
+  for (var i = 0, l = b.length; i < l; i ++) {
+    if (b[i] !== 97 && b[i] !== 10) {
+      throw new Error("invalid char "+i+","+b[i]);
+    }
+  }
+}
+function done () { 
+  setTimeout(function () {
+    fs.createReadStream("out.txt")
+      .on("data", function (chunk) {
+        testBuffer(chunk)
+      })
+  }, 2000);
+}
+
-- 
1.7.1.1

