From 455765470b204b16659122bcaf0043a8f6d34209 Mon Sep 17 00:00:00 2001
From: Jorge Chamorro Bieling <jorge@jorgechamorro.com>
Date: Mon, 8 Nov 2010 14:14:30 +0100
Subject: [PATCH] my fs.write buffers bug patch

---
 lib/fs.js                          |    2 +-
 src/node_file.cc                   |   40 ++++++++++++++++++++++++++----
 test/pummel/test-writealot-fast.js |   46 ++++++++++++++++++++++++++++++++++++
 3 files changed, 81 insertions(+), 7 deletions(-)
 create mode 100644 test/pummel/test-writealot-fast.js

diff --git a/lib/fs.js b/lib/fs.js
index 24cc07a..4a7cb3a 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -235,7 +235,7 @@ fs.write = function (fd, buffer, offset, length, position, callback) {
     return;
   }
 
-  binding.write(fd, buffer, offset, length, position, callback || noop);
+  binding.write(fd, buffer, offset, length, position, {cb: callback || noop, buf: buffer});
 };
 
 fs.writeSync = function (fd, buffer, offset, length, position) {
diff --git a/src/node_file.cc b/src/node_file.cc
index efdc64c..631d007 100644
--- a/src/node_file.cc
+++ b/src/node_file.cc
@@ -28,7 +28,8 @@ using namespace v8;
 
 #define MIN(a,b) ((a) < (b) ? (a) : (b))
 #define THROW_BAD_ARGS \
-  ThrowException(Exception::TypeError(String::New("Bad argument")))
+  ThrowException(Exception::TypeError(String::New("Bad argument")));
+  
 static Persistent<String> encoding_symbol;
 static Persistent<String> errno_symbol;
 
@@ -36,10 +37,37 @@ 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<Function>* callback;
+  
+  if ((*data)->IsFunction()) {
+    callback = reinterpret_cast<v8::Local<v8::Function>*>(data);
+  } else {
+    callback = reinterpret_cast<v8::Local<v8::Function>*>(&(*data)->ToObject()->Get(String::New("cb")));
+  }
 
   ev_unref(EV_DEFAULT_UC);
 
@@ -159,14 +187,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();
@@ -648,7 +676,7 @@ static Handle<Value> Write(const Arguments& args) {
   char * buf = (char*)buffer_data + off;
   Local<Value> cb = args[5];
 
-  if (cb->IsFunction()) {
+  if (cb->IsFunction() || cb->IsObject()) {
     ASYNC_CALL(write, cb, fd, buf, len, pos)
   } else {
     ssize_t written = pos < 0 ? write(fd, buf, len) : pwrite(fd, buf, len, pos);
diff --git a/test/pummel/test-writealot-fast.js b/test/pummel/test-writealot-fast.js
new file mode 100644
index 0000000..a41a22c
--- /dev/null
+++ b/test/pummel/test-writealot-fast.js
@@ -0,0 +1,46 @@
+var fs = require("fs");
+var exec = require("child_process").exec;
+var assert = require("assert");
+exec('rm out.txt', function (err,o,e) {
+  var fd = fs.openSync("out.txt", "a");
+
+  var kLoops= 5e5;
+  var i = 0;
+  var notDoneYet= kLoops;
+  var data = ["{ write()Nr : ",0,' }\n'];
+  var total_A= 0;
+  var total_B= 0;
+  var total_C= 0;
+
+  while (++i <= kLoops) {
+    data[1]= i;
+    var str= data.join('');
+    total_A+= str.length;
+    fs.write(fd, str, -1, 'utf8', function (err, written) {
+      if (err) throw err;
+      total_B+= written;
+      notDoneYet--;
+    });
+  }
+
+  var t= Date.now();
+  var res= [];
+  (function loop () {
+    if ((Date.now()- t) > 15e3) assert.equal('Error','TIMEOUT');
+    if (notDoneYet) return setTimeout(loop, 500);
+    assert.equal(total_A, total_B);
+    fs.close(fd, function (err) {
+      exec("cat -v out.txt | grep -ace '^[^{]'", function (err,o,e) {
+        assert.equal(+o, 0);
+        res.push[+o];
+        exec("cat -v out.txt | grep -ace '^{'", function (err,o,e) {
+          assert.equal(+o, kLoops);
+          res.push[+o];
+          console.log(res);
+          exec('rm out.txt', function (err,o,e) {
+          });
+        });
+      });
+    });
+  })();
+});
\ No newline at end of file
-- 
1.7.1

