Skip to content
This repository
Browse code

Merge branch 'http-memleak' into v0.6

  • Loading branch information...
commit d1effbb338fe5916e5a25fc3d3587016d0725761 2 parents ebd0f98 + 2fc528c
Isaac Z. Schlueter authored May 04, 2012
1  .gitignore
@@ -40,3 +40,4 @@ ipch/
40 40
 email.md
41 41
 blog.html
42 42
 deps/v8-*
  43
+node_modules
13  Makefile
@@ -32,7 +32,7 @@ install:
32 32
 uninstall:
33 33
 	@$(WAF) uninstall
34 34
 
35  
-test: all
  35
+test: all node_modules/weak
36 36
 	$(PYTHON) tools/test.py --mode=release simple message
37 37
 
38 38
 test-http1: all
@@ -41,7 +41,15 @@ test-http1: all
41 41
 test-valgrind: all
42 42
 	$(PYTHON) tools/test.py --mode=release --valgrind simple message
43 43
 
44  
-test-all: all
  44
+node_modules/weak:
  45
+	@if [ ! -f node ]; then make all; fi
  46
+	@if [ ! -d node_modules ]; then mkdir -p node_modules; fi
  47
+	./node deps/npm/bin/npm-cli.js install weak --prefix="$(shell pwd)"
  48
+
  49
+test-gc: all node_modules/weak
  50
+	$(PYTHON) tools/test.py --mode=release gc
  51
+
  52
+test-all: all node_modules/weak
45 53
 	$(PYTHON) tools/test.py --mode=debug,release
46 54
 	make test-npm
47 55
 
@@ -153,6 +161,7 @@ clean:
153 161
 	$(WAF) clean
154 162
 	-find tools -name "*.pyc" | xargs rm -f
155 163
 	-rm -rf blog.html email.md
  164
+	-rm -rf node_modules
156 165
 
157 166
 distclean: docclean
158 167
 	-find tools -name "*.pyc" | xargs rm -f
272  lib/http.js
@@ -35,113 +35,120 @@ if (process.env.NODE_DEBUG && /http/.test(process.env.NODE_DEBUG)) {
35 35
   debug = function() { };
36 36
 }
37 37
 
  38
+function parserOnHeaders(headers, url) {
  39
+  this._headers = this._headers.concat(headers);
  40
+  this._url += url;
  41
+}
38 42
 
39  
-var parsers = new FreeList('parsers', 1000, function() {
40  
-  var parser = new HTTPParser(HTTPParser.REQUEST);
  43
+// info.headers and info.url are set only if .onHeaders()
  44
+// has not been called for this request.
  45
+//
  46
+// info.url is not set for response parsers but that's not
  47
+// applicable here since all our parsers are request parsers.
  48
+function parserOnHeadersComplete(info) {
  49
+  var parser = this;
  50
+  var headers = info.headers;
  51
+  var url = info.url;
  52
+
  53
+  if (!headers) {
  54
+    headers = parser._headers;
  55
+    parser._headers = [];
  56
+  }
41 57
 
42  
-  parser._headers = [];
43  
-  parser._url = '';
  58
+  if (!url) {
  59
+    url = parser._url;
  60
+    parser._url = '';
  61
+  }
44 62
 
45  
-  // Only called in the slow case where slow means
46  
-  // that the request headers were either fragmented
47  
-  // across multiple TCP packets or too large to be
48  
-  // processed in a single run. This method is also
49  
-  // called to process trailing HTTP headers.
50  
-  parser.onHeaders = function(headers, url) {
51  
-    parser._headers = parser._headers.concat(headers);
52  
-    parser._url += url;
53  
-  };
  63
+  parser.incoming = new IncomingMessage(parser.socket);
  64
+  parser.incoming.httpVersionMajor = info.versionMajor;
  65
+  parser.incoming.httpVersionMinor = info.versionMinor;
  66
+  parser.incoming.httpVersion = info.versionMajor + '.' + info.versionMinor;
  67
+  parser.incoming.url = url;
54 68
 
55  
-  // info.headers and info.url are set only if .onHeaders()
56  
-  // has not been called for this request.
57  
-  //
58  
-  // info.url is not set for response parsers but that's not
59  
-  // applicable here since all our parsers are request parsers.
60  
-  parser.onHeadersComplete = function(info) {
61  
-    var headers = info.headers;
62  
-    var url = info.url;
63  
-
64  
-    if (!headers) {
65  
-      headers = parser._headers;
66  
-      parser._headers = [];
67  
-    }
  69
+  for (var i = 0, n = headers.length; i < n; i += 2) {
  70
+    var k = headers[i];
  71
+    var v = headers[i + 1];
  72
+    parser.incoming._addHeaderLine(k.toLowerCase(), v);
  73
+  }
68 74
 
69  
-    if (!url) {
70  
-      url = parser._url;
71  
-      parser._url = '';
72  
-    }
  75
+  if (info.method) {
  76
+    // server only
  77
+    parser.incoming.method = info.method;
  78
+  } else {
  79
+    // client only
  80
+    parser.incoming.statusCode = info.statusCode;
  81
+    // CHECKME dead code? we're always a request parser
  82
+  }
73 83
 
74  
-    parser.incoming = new IncomingMessage(parser.socket);
75  
-    parser.incoming.httpVersionMajor = info.versionMajor;
76  
-    parser.incoming.httpVersionMinor = info.versionMinor;
77  
-    parser.incoming.httpVersion = info.versionMajor + '.' + info.versionMinor;
78  
-    parser.incoming.url = url;
  84
+  parser.incoming.upgrade = info.upgrade;
79 85
 
80  
-    for (var i = 0, n = headers.length; i < n; i += 2) {
81  
-      var k = headers[i];
82  
-      var v = headers[i + 1];
83  
-      parser.incoming._addHeaderLine(k.toLowerCase(), v);
84  
-    }
  86
+  var isHeadResponse = false;
85 87
 
86  
-    if (info.method) {
87  
-      // server only
88  
-      parser.incoming.method = info.method;
89  
-    } else {
90  
-      // client only
91  
-      parser.incoming.statusCode = info.statusCode;
92  
-      // CHECKME dead code? we're always a request parser
93  
-    }
  88
+  if (!info.upgrade) {
  89
+    // For upgraded connections, we'll emit this after parser.execute
  90
+    // so that we can capture the first part of the new protocol
  91
+    isHeadResponse = parser.onIncoming(parser.incoming, info.shouldKeepAlive);
  92
+  }
94 93
 
95  
-    parser.incoming.upgrade = info.upgrade;
  94
+  return isHeadResponse;
  95
+}
96 96
 
97  
-    var isHeadResponse = false;
  97
+function parserOnBody(b, start, len) {
  98
+  var parser = this;
  99
+  // TODO body encoding?
  100
+  var slice = b.slice(start, start + len);
  101
+  if (parser.incoming._decoder) {
  102
+    var string = parser.incoming._decoder.write(slice);
  103
+    if (string.length) parser.incoming.emit('data', string);
  104
+  } else {
  105
+    parser.incoming.emit('data', slice);
  106
+  }
  107
+}
  108
+function parserOnMessageComplete() {
  109
+  var parser = this;
  110
+  parser.incoming.complete = true;
98 111
 
99  
-    if (!info.upgrade) {
100  
-      // For upgraded connections, we'll emit this after parser.execute
101  
-      // so that we can capture the first part of the new protocol
102  
-      isHeadResponse = parser.onIncoming(parser.incoming, info.shouldKeepAlive);
  112
+  // Emit any trailing headers.
  113
+  var headers = parser._headers;
  114
+  if (headers) {
  115
+    for (var i = 0, n = headers.length; i < n; i += 2) {
  116
+      var k = headers[i];
  117
+      var v = headers[i + 1];
  118
+      parser.incoming._addHeaderLine(k.toLowerCase(), v);
103 119
     }
  120
+    parser._headers = [];
  121
+    parser._url = '';
  122
+  }
104 123
 
105  
-    return isHeadResponse;
106  
-  };
  124
+  if (!parser.incoming.upgrade) {
  125
+    // For upgraded connections, also emit this after parser.execute
  126
+    parser.incoming.readable = false;
  127
+    parser.incoming.emit('end');
  128
+  }
107 129
 
108  
-  parser.onBody = function(b, start, len) {
109  
-    // TODO body encoding?
110  
-    var slice = b.slice(start, start + len);
111  
-    if (parser.incoming._decoder) {
112  
-      var string = parser.incoming._decoder.write(slice);
113  
-      if (string.length) parser.incoming.emit('data', string);
114  
-    } else {
115  
-      parser.incoming.emit('data', slice);
116  
-    }
117  
-  };
  130
+  if (parser.socket.readable) {
  131
+    // force to read the next incoming message
  132
+    parser.socket.resume();
  133
+  }
  134
+}
118 135
 
119  
-  parser.onMessageComplete = function() {
120  
-    parser.incoming.complete = true;
121 136
 
122  
-    // Emit any trailing headers.
123  
-    var headers = parser._headers;
124  
-    if (headers) {
125  
-      for (var i = 0, n = headers.length; i < n; i += 2) {
126  
-        var k = headers[i];
127  
-        var v = headers[i + 1];
128  
-        parser.incoming._addHeaderLine(k.toLowerCase(), v);
129  
-      }
130  
-      parser._headers = [];
131  
-      parser._url = '';
132  
-    }
  137
+var parsers = new FreeList('parsers', 1000, function() {
  138
+  var parser = new HTTPParser(HTTPParser.REQUEST);
133 139
 
134  
-    if (!parser.incoming.upgrade) {
135  
-      // For upgraded connections, also emit this after parser.execute
136  
-      parser.incoming.readable = false;
137  
-      parser.incoming.emit('end');
138  
-    }
  140
+  parser._headers = [];
  141
+  parser._url = '';
139 142
 
140  
-    if (parser.socket.readable) {
141  
-      // force to read the next incoming message
142  
-      parser.socket.resume();
143  
-    }
144  
-  };
  143
+  // Only called in the slow case where slow means
  144
+  // that the request headers were either fragmented
  145
+  // across multiple TCP packets or too large to be
  146
+  // processed in a single run. This method is also
  147
+  // called to process trailing HTTP headers.
  148
+  parser.onHeaders = parserOnHeaders;
  149
+  parser.onHeadersComplete = parserOnHeadersComplete;
  150
+  parser.onBody = parserOnBody;
  151
+  parser.onMessageComplete = parserOnMessageComplete;
145 152
 
146 153
   return parser;
147 154
 });
@@ -1014,7 +1021,7 @@ function ClientRequest(options, cb) {
1014 1021
   var method = self.method = (options.method || 'GET').toUpperCase();
1015 1022
   self.path = options.path || '/';
1016 1023
   if (cb) {
1017  
-    self.on('response', cb);
  1024
+    self.once('response', cb);
1018 1025
   }
1019 1026
 
1020 1027
   if (!Array.isArray(options.headers)) {
@@ -1079,6 +1086,7 @@ function ClientRequest(options, cb) {
1079 1086
 
1080 1087
   self._deferToConnect(null, null, function() {
1081 1088
     self._flush();
  1089
+    self = null;
1082 1090
   });
1083 1091
 
1084 1092
 }
@@ -1109,6 +1117,31 @@ function createHangUpError() {
1109 1117
   return error;
1110 1118
 }
1111 1119
 
  1120
+// Free the parser and also break any links that it
  1121
+// might have to any other things.
  1122
+// TODO: All parser data should be attached to a
  1123
+// single object, so that it can be easily cleaned
  1124
+// up by doing `parser.data = {}`, which should
  1125
+// be done in FreeList.free.  `parsers.free(parser)`
  1126
+// should be all that is needed.
  1127
+function freeParser(parser, req) {
  1128
+  if (parser) {
  1129
+    parser._headers = [];
  1130
+    parser.onIncoming = null;
  1131
+    if (parser.socket) {
  1132
+      parser.socket.onend = null;
  1133
+      parser.socket.ondata = null;
  1134
+    }
  1135
+    parser.socket = null;
  1136
+    parser.incoming = null;
  1137
+    parsers.free(parser);
  1138
+    parser = null;
  1139
+  }
  1140
+  if (req) {
  1141
+    req.parser = null;
  1142
+  }
  1143
+};
  1144
+
1112 1145
 
1113 1146
 ClientRequest.prototype.onSocket = function(socket) {
1114 1147
   var req = this;
@@ -1125,13 +1158,6 @@ ClientRequest.prototype.onSocket = function(socket) {
1125 1158
     // Setup "drain" propogation.
1126 1159
     httpSocketSetup(socket);
1127 1160
 
1128  
-    var freeParser = function() {
1129  
-      if (parser) {
1130  
-        parsers.free(parser);
1131  
-        parser = null;
1132  
-      }
1133  
-    };
1134  
-
1135 1161
     var errorListener = function(err) {
1136 1162
       debug('HTTP SOCKET ERROR: ' + err.message + '\n' + err.stack);
1137 1163
       req.emit('error', err);
@@ -1140,7 +1166,7 @@ ClientRequest.prototype.onSocket = function(socket) {
1140 1166
       req._hadError = true;
1141 1167
       if (parser) {
1142 1168
         parser.finish();
1143  
-        freeParser();
  1169
+        freeParser(parser, req);
1144 1170
       }
1145 1171
       socket.destroy();
1146 1172
     }
@@ -1150,7 +1176,7 @@ ClientRequest.prototype.onSocket = function(socket) {
1150 1176
       var ret = parser.execute(d, start, end - start);
1151 1177
       if (ret instanceof Error) {
1152 1178
         debug('parse error');
1153  
-        freeParser();
  1179
+        freeParser(parser, req);
1154 1180
         socket.destroy(ret);
1155 1181
       } else if (parser.incoming && parser.incoming.upgrade) {
1156 1182
         var bytesParsed = ret;
@@ -1171,13 +1197,13 @@ ClientRequest.prototype.onSocket = function(socket) {
1171 1197
           // Got upgrade header, but have no handler.
1172 1198
           socket.destroy();
1173 1199
         }
1174  
-        freeParser();
  1200
+        freeParser(parser, req);
1175 1201
       } else if (parser.incoming && parser.incoming.complete &&
1176 1202
                  // When the status code is 100 (Continue), the server will
1177 1203
                  // send a final response after this client sends a request
1178 1204
                  // body. So, we must not free the parser.
1179 1205
                  parser.incoming.statusCode !== 100) {
1180  
-        freeParser();
  1206
+        freeParser(parser, req);
1181 1207
       }
1182 1208
     };
1183 1209
 
@@ -1190,7 +1216,7 @@ ClientRequest.prototype.onSocket = function(socket) {
1190 1216
       }
1191 1217
       if (parser) {
1192 1218
         parser.finish();
1193  
-        freeParser();
  1219
+        freeParser(parser, req);
1194 1220
       }
1195 1221
       socket.destroy();
1196 1222
     };
@@ -1209,6 +1235,13 @@ ClientRequest.prototype.onSocket = function(socket) {
1209 1235
         // fire on the request.
1210 1236
         req.emit('error', createHangUpError());
1211 1237
       }
  1238
+
  1239
+      // Nothing more to be done with this req, since the socket
  1240
+      // is closed, and we've emitted the appropriate abort/end/close/error
  1241
+      // events.  Disavow all knowledge, and break the references to
  1242
+      // the variables trapped by closures and on the socket object.
  1243
+      req = null;
  1244
+      socket._httpMessage = null;
1212 1245
     }
1213 1246
     socket.on('close', closeListener);
1214 1247
 
@@ -1274,6 +1307,7 @@ ClientRequest.prototype.onSocket = function(socket) {
1274 1307
   });
1275 1308
 
1276 1309
 };
  1310
+
1277 1311
 ClientRequest.prototype._deferToConnect = function(method, arguments_, cb) {
1278 1312
   // This function is for calls that need to happen once the socket is
1279 1313
   // connected and writable. It's an important promisy thing for all the socket
@@ -1302,9 +1336,33 @@ ClientRequest.prototype._deferToConnect = function(method, arguments_, cb) {
1302 1336
     onSocket();
1303 1337
   }
1304 1338
 };
1305  
-ClientRequest.prototype.setTimeout = function() {
1306  
-  this._deferToConnect('setTimeout', arguments);
  1339
+
  1340
+ClientRequest.prototype.setTimeout = function(msecs, callback) {
  1341
+  if (callback) this.once('timeout', callback);
  1342
+
  1343
+  var self = this;
  1344
+  function emitTimeout() {
  1345
+    self.emit('timeout');
  1346
+    self.destroy(new Error('timeout'));
  1347
+  }
  1348
+
  1349
+  if (this.socket && this.socket.writable) {
  1350
+    this.socket.setTimeout(msecs, emitTimeout);
  1351
+    return;
  1352
+  }
  1353
+
  1354
+  if (this.socket) {
  1355
+    this.socket.once('connect', function() {
  1356
+      this.setTimeout(msecs, emitTimeout);
  1357
+    });
  1358
+    return;
  1359
+  }
  1360
+
  1361
+  this.once('socket', function(sock) {
  1362
+    this.setTimeout(msecs, emitTimeout);
  1363
+  });
1307 1364
 };
  1365
+
1308 1366
 ClientRequest.prototype.setNoDelay = function() {
1309 1367
   this._deferToConnect('setNoDelay', arguments);
1310 1368
 };
@@ -1393,7 +1451,7 @@ function connectionListener(socket) {
1393 1451
   httpSocketSetup(socket);
1394 1452
 
1395 1453
   socket.setTimeout(2 * 60 * 1000); // 2 minute timeout
1396  
-  socket.addListener('timeout', function() {
  1454
+  socket.once('timeout', function() {
1397 1455
     socket.destroy();
1398 1456
   });
1399 1457
 
@@ -1453,8 +1511,8 @@ function connectionListener(socket) {
1453 1511
 
1454 1512
   socket.addListener('close', function() {
1455 1513
     debug('server socket close');
1456  
-    // unref the parser for easy gc
1457  
-    parsers.free(parser);
  1514
+    // mark this parser as reusable
  1515
+    freeParser(parser);
1458 1516
 
1459 1517
     abortIncoming();
1460 1518
   });
64  test/gc/test-http-client-connaborted.js
... ...
@@ -0,0 +1,64 @@
  1
+// just like test/gc/http-client.js,
  2
+// but aborting every connection that comes in.
  3
+
  4
+function serverHandler(req, res) {
  5
+  res.connection.destroy();
  6
+}
  7
+
  8
+var http  = require('http'),
  9
+    weak    = require('weak'),
  10
+    done    = 0,
  11
+    count   = 0,
  12
+    countGC = 0,
  13
+    todo    = 500,
  14
+    common = require('../common.js'),
  15
+    assert = require('assert'),
  16
+    PORT = common.PORT;
  17
+
  18
+console.log('We should do '+ todo +' requests');
  19
+
  20
+var http = require('http');
  21
+var server = http.createServer(serverHandler);
  22
+server.listen(PORT, getall);
  23
+
  24
+function getall() {
  25
+  for (var i = 0; i < todo; i++) {
  26
+    (function(){
  27
+      function cb(res) {
  28
+        done+=1;
  29
+        statusLater();
  30
+      }
  31
+
  32
+      var req = http.get({
  33
+        hostname: 'localhost',
  34
+        pathname: '/',
  35
+        port: PORT
  36
+      }, cb).on('error', cb);
  37
+
  38
+      count++;
  39
+      weak(req, afterGC);
  40
+    })()
  41
+  }
  42
+}
  43
+
  44
+function afterGC(){
  45
+  countGC ++;
  46
+}
  47
+
  48
+var timer;
  49
+function statusLater() {
  50
+  gc();
  51
+  if (timer) clearTimeout(timer);
  52
+  timer = setTimeout(status, 1);
  53
+}
  54
+
  55
+function status() {
  56
+  gc();
  57
+  console.log('Done: %d/%d', done, todo);
  58
+  console.log('Collected: %d/%d', countGC, count);
  59
+  if (done === todo) {
  60
+    console.log('All should be collected now.');
  61
+    assert(count === countGC);
  62
+    process.exit(0);
  63
+  }
  64
+}
69  test/gc/test-http-client-onerror.js
... ...
@@ -0,0 +1,69 @@
  1
+// just like test/gc/http-client.js,
  2
+// but with an on('error') handler that does nothing.
  3
+
  4
+function serverHandler(req, res) {
  5
+  res.writeHead(200, {'Content-Type': 'text/plain'});
  6
+  res.end('Hello World\n');
  7
+}
  8
+
  9
+var http  = require('http'),
  10
+    weak    = require('weak'),
  11
+    done    = 0,
  12
+    count   = 0,
  13
+    countGC = 0,
  14
+    todo    = 500,
  15
+    common = require('../common.js'),
  16
+    assert = require('assert'),
  17
+    PORT = common.PORT;
  18
+
  19
+console.log('We should do '+ todo +' requests');
  20
+
  21
+var http = require('http');
  22
+var server = http.createServer(serverHandler);
  23
+server.listen(PORT, getall);
  24
+
  25
+function getall() {
  26
+  for (var i = 0; i < todo; i++) {
  27
+    (function(){
  28
+      function cb(res) {
  29
+        done+=1;
  30
+        statusLater();
  31
+      }
  32
+      function onerror(er) {
  33
+        throw er;
  34
+      }
  35
+
  36
+      var req = http.get({
  37
+        hostname: 'localhost',
  38
+        pathname: '/',
  39
+        port: PORT
  40
+      }, cb).on('error', onerror);
  41
+
  42
+      count++;
  43
+      weak(req, afterGC);
  44
+    })()
  45
+  }
  46
+}
  47
+
  48
+function afterGC(){
  49
+  countGC ++;
  50
+}
  51
+
  52
+var timer;
  53
+function statusLater() {
  54
+  gc();
  55
+  if (timer) clearTimeout(timer);
  56
+  timer = setTimeout(status, 1);
  57
+}
  58
+
  59
+function status() {
  60
+  gc();
  61
+  console.log('Done: %d/%d', done, todo);
  62
+  console.log('Collected: %d/%d', countGC, count);
  63
+  if (done === todo) {
  64
+    console.log('All should be collected now.');
  65
+    assert(count === countGC);
  66
+    process.exit(0);
  67
+  }
  68
+}
  69
+
72  test/gc/test-http-client-timeout.js
... ...
@@ -0,0 +1,72 @@
  1
+// just like test/gc/http-client.js,
  2
+// but with a timeout set
  3
+
  4
+function serverHandler(req, res) {
  5
+  setTimeout(function () {
  6
+    res.writeHead(200)
  7
+    res.end('hello\n');
  8
+  }, 100);
  9
+}
  10
+
  11
+var http  = require('http'),
  12
+    weak    = require('weak'),
  13
+    done    = 0,
  14
+    count   = 0,
  15
+    countGC = 0,
  16
+    todo    = 500,
  17
+    common = require('../common.js'),
  18
+    assert = require('assert'),
  19
+    PORT = common.PORT;
  20
+
  21
+console.log('We should do '+ todo +' requests');
  22
+
  23
+var http = require('http');
  24
+var server = http.createServer(serverHandler);
  25
+server.listen(PORT, getall);
  26
+
  27
+function getall() {
  28
+  for (var i = 0; i < todo; i++) {
  29
+    (function(){
  30
+      function cb() {
  31
+        done+=1;
  32
+        statusLater();
  33
+      }
  34
+
  35
+      var req = http.get({
  36
+        hostname: 'localhost',
  37
+        pathname: '/',
  38
+        port: PORT
  39
+      }, cb);
  40
+      req.on('error', cb);
  41
+      req.setTimeout(10, function(){
  42
+        console.log('timeout (expected)')
  43
+      });
  44
+
  45
+      count++;
  46
+      weak(req, afterGC);
  47
+    })()
  48
+  }
  49
+}
  50
+
  51
+function afterGC(){
  52
+  countGC ++;
  53
+}
  54
+
  55
+var timer;
  56
+function statusLater() {
  57
+  gc();
  58
+  if (timer) clearTimeout(timer);
  59
+  timer = setTimeout(status, 1);
  60
+}
  61
+
  62
+function status() {
  63
+  gc();
  64
+  console.log('Done: %d/%d', done, todo);
  65
+  console.log('Collected: %d/%d', countGC, count);
  66
+  if (done === todo) {
  67
+    console.log('All should be collected now.');
  68
+    assert(count === countGC);
  69
+    process.exit(0);
  70
+  }
  71
+}
  72
+
67  test/gc/test-http-client.js
... ...
@@ -0,0 +1,67 @@
  1
+// just a simple http server and client.
  2
+
  3
+function serverHandler(req, res) {
  4
+  res.writeHead(200, {'Content-Type': 'text/plain'});
  5
+  res.end('Hello World\n');
  6
+}
  7
+
  8
+var http  = require('http'),
  9
+    weak    = require('weak'),
  10
+    done    = 0,
  11
+    count   = 0,
  12
+    countGC = 0,
  13
+    todo    = 500,
  14
+    common = require('../common.js'),
  15
+    assert = require('assert'),
  16
+    PORT = common.PORT;
  17
+
  18
+console.log('We should do '+ todo +' requests');
  19
+
  20
+var http = require('http');
  21
+var server = http.createServer(serverHandler);
  22
+server.listen(PORT, getall);
  23
+
  24
+
  25
+function getall() {
  26
+  for (var i = 0; i < todo; i++) {
  27
+    (function(){
  28
+      function cb(res) {
  29
+        console.error('in cb')
  30
+        done+=1;
  31
+        res.on('end', statusLater);
  32
+      }
  33
+
  34
+      var req = http.get({
  35
+        hostname: 'localhost',
  36
+        pathname: '/',
  37
+        port: PORT
  38
+      }, cb)
  39
+
  40
+      count++;
  41
+      weak(req, afterGC);
  42
+    })()
  43
+  }
  44
+}
  45
+
  46
+function afterGC(){
  47
+  countGC ++;
  48
+}
  49
+
  50
+var timer;
  51
+function statusLater() {
  52
+  gc();
  53
+  if (timer) clearTimeout(timer);
  54
+  timer = setTimeout(status, 1);
  55
+}
  56
+
  57
+function status() {
  58
+  gc();
  59
+  console.log('Done: %d/%d', done, todo);
  60
+  console.log('Collected: %d/%d', countGC, count);
  61
+  if (done === todo) {
  62
+    console.log('All should be collected now.');
  63
+    assert(count === countGC);
  64
+    process.exit(0);
  65
+  }
  66
+}
  67
+
64  test/gc/test-net-timeout.js
... ...
@@ -0,0 +1,64 @@
  1
+// just like test/gc/http-client-timeout.js,
  2
+// but using a net server/client instead
  3
+
  4
+function serverHandler(sock) {
  5
+  sock.setTimeout(120000);
  6
+  setTimeout(function () {
  7
+    sock.end('hello\n');
  8
+  }, 100);
  9
+}
  10
+
  11
+var net  = require('net'),
  12
+    weak    = require('weak'),
  13
+    done    = 0,
  14
+    count   = 0,
  15
+    countGC = 0,
  16
+    todo    = 500,
  17
+    common = require('../common.js'),
  18
+    assert = require('assert'),
  19
+    PORT = common.PORT;
  20
+
  21
+console.log('We should do '+ todo +' requests');
  22
+
  23
+var server = net.createServer(serverHandler);
  24
+server.listen(PORT, getall);
  25
+
  26
+function getall() {
  27
+  for (var i = 0; i < todo; i++) {
  28
+    (function(){
  29
+      var req = net.connect(PORT, '127.0.0.1');
  30
+      req.setTimeout(10, function() {
  31
+        console.log('timeout (expected)')
  32
+        req.destroy();
  33
+        done++;
  34
+        statusLater();
  35
+      });
  36
+
  37
+      count++;
  38
+      weak(req, afterGC);
  39
+    })();
  40
+  }
  41
+}
  42
+
  43
+function afterGC(){
  44
+  countGC ++;
  45
+}
  46
+
  47
+var timer;
  48
+function statusLater() {
  49
+  gc();
  50
+  if (timer) clearTimeout(timer);
  51
+  timer = setTimeout(status, 1);
  52
+}
  53
+
  54
+function status() {
  55
+  gc();
  56
+  console.log('Done: %d/%d', done, todo);
  57
+  console.log('Collected: %d/%d', countGC, count);
  58
+  if (done === todo) {
  59
+    console.log('All should be collected now.');
  60
+    assert(count === countGC);
  61
+    process.exit(0);
  62
+  }
  63
+}
  64
+
133  test/gc/testcfg.py
... ...
@@ -0,0 +1,133 @@
  1
+# Copyright 2008 the V8 project authors. All rights reserved.
  2
+# Redistribution and use in source and binary forms, with or without
  3
+# modification, are permitted provided that the following conditions are
  4
+# met:
  5
+#
  6
+#     * Redistributions of source code must retain the above copyright
  7
+#       notice, this list of conditions and the following disclaimer.
  8
+#     * Redistributions in binary form must reproduce the above
  9
+#       copyright notice, this list of conditions and the following
  10
+#       disclaimer in the documentation and/or other materials provided
  11
+#       with the distribution.
  12
+#     * Neither the name of Google Inc. nor the names of its
  13
+#       contributors may be used to endorse or promote products derived
  14
+#       from this software without specific prior written permission.
  15
+#
  16
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  17
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  18
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  19
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  20
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  21
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  22
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  26
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27
+
  28
+import test
  29
+import os
  30
+import shutil
  31
+from shutil import rmtree
  32
+from os import mkdir
  33
+from glob import glob
  34
+from os.path import join, dirname, exists
  35
+import re
  36
+
  37
+
  38
+FLAGS_PATTERN = re.compile(r"//\s+Flags:(.*)")
  39
+FILES_PATTERN = re.compile(r"//\s+Files:(.*)")
  40
+
  41
+
  42
+class GCTestCase(test.TestCase):
  43
+
  44
+  def __init__(self, path, file, mode, context, config):
  45
+    super(GCTestCase, self).__init__(context, path, mode)
  46
+    self.file = file
  47
+    self.config = config
  48
+    self.mode = mode
  49
+    self.tmpdir = join(dirname(self.config.root), 'tmp')
  50
+  
  51
+  def AfterRun(self, result):
  52
+    # delete the whole tmp dir
  53
+    try:
  54
+      rmtree(self.tmpdir)
  55
+    except:
  56
+      pass
  57
+    # make it again.
  58
+    try:
  59
+      mkdir(self.tmpdir)
  60
+    except:
  61
+      pass
  62
+
  63
+  def BeforeRun(self):
  64
+    # delete the whole tmp dir
  65
+    try:
  66
+      rmtree(self.tmpdir)
  67
+    except:
  68
+      pass
  69
+    # make it again.
  70
+    # intermittently fails on win32, so keep trying
  71
+    while not os.path.exists(self.tmpdir):
  72
+      try:
  73
+        mkdir(self.tmpdir)
  74
+      except:
  75
+        pass
  76
+  
  77
+  def GetLabel(self):
  78
+    return "%s %s" % (self.mode, self.GetName())
  79
+
  80
+  def GetName(self):
  81
+    return self.path[-1]
  82
+
  83
+  def GetCommand(self):
  84
+    result = [self.config.context.GetVm(self.mode)]
  85
+    source = open(self.file).read()
  86
+    flags_match = FLAGS_PATTERN.search(source)
  87
+    if flags_match:
  88
+      result += flags_match.group(1).strip().split()
  89
+    files_match = FILES_PATTERN.search(source);
  90
+    additional_files = []
  91
+    if files_match:
  92
+      additional_files += files_match.group(1).strip().split()
  93
+    for a_file in additional_files:
  94
+      result.append(join(dirname(self.config.root), '..', a_file))
  95
+    result += ["--expose-gc"]
  96
+    result += [self.file]
  97
+    return result
  98
+
  99
+  def GetSource(self):
  100
+    return open(self.file).read()
  101
+
  102
+
  103
+class GCTestConfiguration(test.TestConfiguration):
  104
+
  105
+  def __init__(self, context, root):
  106
+    super(GCTestConfiguration, self).__init__(context, root)
  107
+
  108
+  def Ls(self, path):
  109
+    def SelectTest(name):
  110
+      return name.startswith('test-') and name.endswith('.js')
  111
+    return [f[:-3] for f in os.listdir(path) if SelectTest(f)]
  112
+
  113
+  def ListTests(self, current_path, path, mode):
  114
+    all_tests = [current_path + [t] for t in self.Ls(join(self.root))]
  115
+    result = []
  116
+    for test in all_tests:
  117
+      if self.Contains(path, test):
  118
+        file_path = join(self.root, reduce(join, test[1:], "") + ".js")
  119
+        result.append(GCTestCase(test, file_path, mode, self.context, self))
  120
+    return result
  121
+
  122
+  def GetBuildRequirements(self):
  123
+    return ['sample', 'sample=shell']
  124
+
  125
+  def GetTestStatus(self, sections, defs):
  126
+    status_file = join(self.root, 'gc.status')
  127
+    if exists(status_file):
  128
+      test.ReadConfigurationInto(status_file, sections, defs)
  129
+
  130
+
  131
+
  132
+def GetConfiguration(context, root):
  133
+  return GCTestConfiguration(context, root)
2  tools/test.py
@@ -1275,7 +1275,7 @@ def ExpandCommand(args):
1275 1275
     return ExpandCommand
1276 1276
 
1277 1277
 
1278  
-BUILT_IN_TESTS = ['simple', 'pummel', 'message', 'internet']
  1278
+BUILT_IN_TESTS = ['simple', 'pummel', 'message', 'internet', 'gc']
1279 1279
 
1280 1280
 
1281 1281
 def GetSuites(test_root):

0 notes on commit d1effbb

Please sign in to comment.
Something went wrong with that request. Please try again.