|
44 | 44 | /* 0 */
|
45 | 45 | /***/ function(module, exports, __webpack_require__) {
|
46 | 46 |
|
47 |
| - module.exports = __webpack_require__(6); |
| 47 | + module.exports = __webpack_require__(7); |
48 | 48 |
|
49 | 49 |
|
50 | 50 | /***/ },
|
|
124 | 124 |
|
125 | 125 | /***/ },
|
126 | 126 | /* 6 */
|
| 127 | +/***/ function(module, exports) { |
| 128 | + |
| 129 | + /* |
| 130 | + In general, we want the Node child processes to be terminated as soon as the parent .NET processes exit, |
| 131 | + because we have no further use for them. If the .NET process shuts down gracefully, it will run its |
| 132 | + finalizers, one of which (in OutOfProcessNodeInstance.cs) will kill its associated Node process immediately. |
| 133 | +
|
| 134 | + But if the .NET process is terminated forcefully (e.g., on Linux/OSX with 'kill -9'), then it won't have |
| 135 | + any opportunity to shut down its child processes, and by default they will keep running. In this case, it's |
| 136 | + up to the child process to detect this has happened and terminate itself. |
| 137 | +
|
| 138 | + There are many possible approaches to detecting when a parent process has exited, most of which behave |
| 139 | + differently between Windows and Linux/OS X: |
| 140 | +
|
| 141 | + - On Windows, the parent process can mark its child as being a 'job' that should auto-terminate when |
| 142 | + the parent does (http://stackoverflow.com/a/4657392). Not cross-platform. |
| 143 | + - The child Node process can get a callback when the parent disconnects (process.on('disconnect', ...)). |
| 144 | + But despite http://stackoverflow.com/a/16487966, no callback fires in any case I've tested (Windows / OS X). |
| 145 | + - The child Node process can get a callback when its stdin/stdout are disconnected, as described at |
| 146 | + http://stackoverflow.com/a/15693934. This works well on OS X, but calling stdout.resume() on Windows |
| 147 | + causes the process to terminate prematurely. |
| 148 | + - I don't know why, but on Windows, it's enough to invoke process.stdin.resume(). For some reason this causes |
| 149 | + the child Node process to exit as soon as the parent one does, but I don't see this documented anywhere. |
| 150 | + - You can poll to see if the parent process, or your stdin/stdout connection to it, is gone |
| 151 | + - You can directly pass a parent process PID to the child, and then have the child poll to see if it's |
| 152 | + still running (e.g., using process.kill(pid, 0), which doesn't kill it but just tests whether it exists, |
| 153 | + as per https://nodejs.org/api/process.html#process_process_kill_pid_signal) |
| 154 | + - Or, on each poll, you can try writing to process.stdout. If the parent has died, then this will throw. |
| 155 | + However I don't see this documented anywhere. It would be nice if you could just poll for whether or not |
| 156 | + process.stdout is still connected (without actually writing to it) but I haven't found any property whose |
| 157 | + value changes until you actually try to write to it. |
| 158 | +
|
| 159 | + Of these, the only cross-platform approach that is actually documented as a valid strategy is simply polling |
| 160 | + to check whether the parent PID is still running. So that's what we do here. |
| 161 | + */ |
| 162 | + "use strict"; |
| 163 | + var pollIntervalMs = 1000; |
| 164 | + function exitWhenParentExits(parentPid) { |
| 165 | + setInterval(function () { |
| 166 | + if (!processExists(parentPid)) { |
| 167 | + // Can't log anything at this point, because out stdout was connected to the parent, |
| 168 | + // but the parent is gone. |
| 169 | + process.exit(); |
| 170 | + } |
| 171 | + }, pollIntervalMs); |
| 172 | + } |
| 173 | + exports.exitWhenParentExits = exitWhenParentExits; |
| 174 | + function processExists(pid) { |
| 175 | + try { |
| 176 | + // Sending signal 0 - on all platforms - tests whether the process exists. As long as it doesn't |
| 177 | + // throw, that means it does exist. |
| 178 | + process.kill(pid, 0); |
| 179 | + return true; |
| 180 | + } |
| 181 | + catch (ex) { |
| 182 | + // If the reason for the error is that we don't have permission to ask about this process, |
| 183 | + // report that as a separate problem. |
| 184 | + if (ex.code === 'EPERM') { |
| 185 | + throw new Error("Attempted to check whether process " + pid + " was running, but got a permissions error."); |
| 186 | + } |
| 187 | + return false; |
| 188 | + } |
| 189 | + } |
| 190 | + |
| 191 | + |
| 192 | +/***/ }, |
| 193 | +/* 7 */ |
127 | 194 | /***/ function(module, exports, __webpack_require__) {
|
128 | 195 |
|
129 | 196 | "use strict";
|
130 | 197 | // Limit dependencies to core Node modules. This means the code in this file has to be very low-level and unattractive,
|
131 | 198 | // but simplifies things for the consumer of this module.
|
132 | 199 | __webpack_require__(2);
|
133 |
| - var net = __webpack_require__(7); |
| 200 | + var net = __webpack_require__(8); |
134 | 201 | var path = __webpack_require__(4);
|
135 |
| - var readline = __webpack_require__(8); |
| 202 | + var readline = __webpack_require__(9); |
136 | 203 | var ArgsUtil_1 = __webpack_require__(5);
|
137 |
| - var virtualConnectionServer = __webpack_require__(9); |
| 204 | + var ExitWhenParentExits_1 = __webpack_require__(6); |
| 205 | + var virtualConnectionServer = __webpack_require__(10); |
138 | 206 | // Webpack doesn't support dynamic requires for files not present at compile time, so grab a direct
|
139 | 207 | // reference to Node's runtime 'require' function.
|
140 | 208 | var dynamicRequire = eval('require');
|
|
189 | 257 | var parsedArgs = ArgsUtil_1.parseArgs(process.argv);
|
190 | 258 | var listenAddress = (useWindowsNamedPipes ? '\\\\.\\pipe\\' : '/tmp/') + parsedArgs.listenAddress;
|
191 | 259 | server.listen(listenAddress);
|
| 260 | + ExitWhenParentExits_1.exitWhenParentExits(parseInt(parsedArgs.parentPid)); |
192 | 261 |
|
193 | 262 |
|
194 | 263 | /***/ },
|
195 |
| -/* 7 */ |
| 264 | +/* 8 */ |
196 | 265 | /***/ function(module, exports) {
|
197 | 266 |
|
198 | 267 | module.exports = require("net");
|
199 | 268 |
|
200 | 269 | /***/ },
|
201 |
| -/* 8 */ |
| 270 | +/* 9 */ |
202 | 271 | /***/ function(module, exports) {
|
203 | 272 |
|
204 | 273 | module.exports = require("readline");
|
205 | 274 |
|
206 | 275 | /***/ },
|
207 |
| -/* 9 */ |
| 276 | +/* 10 */ |
208 | 277 | /***/ function(module, exports, __webpack_require__) {
|
209 | 278 |
|
210 | 279 | "use strict";
|
211 |
| - var events_1 = __webpack_require__(10); |
212 |
| - var VirtualConnection_1 = __webpack_require__(11); |
| 280 | + var events_1 = __webpack_require__(11); |
| 281 | + var VirtualConnection_1 = __webpack_require__(12); |
213 | 282 | // Keep this in sync with the equivalent constant in the .NET code. Both sides split up their transmissions into frames with this max length,
|
214 | 283 | // and both will reject longer frames.
|
215 | 284 | var MaxFrameBodyLength = 16 * 1024;
|
|
390 | 459 |
|
391 | 460 |
|
392 | 461 | /***/ },
|
393 |
| -/* 10 */ |
| 462 | +/* 11 */ |
394 | 463 | /***/ function(module, exports) {
|
395 | 464 |
|
396 | 465 | module.exports = require("events");
|
397 | 466 |
|
398 | 467 | /***/ },
|
399 |
| -/* 11 */ |
| 468 | +/* 12 */ |
400 | 469 | /***/ function(module, exports, __webpack_require__) {
|
401 | 470 |
|
402 | 471 | "use strict";
|
|
405 | 474 | function __() { this.constructor = d; }
|
406 | 475 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
407 | 476 | };
|
408 |
| - var stream_1 = __webpack_require__(12); |
| 477 | + var stream_1 = __webpack_require__(13); |
409 | 478 | /**
|
410 | 479 | * Represents a virtual connection. Multiple virtual connections may be multiplexed over a single physical socket connection.
|
411 | 480 | */
|
|
446 | 515 |
|
447 | 516 |
|
448 | 517 | /***/ },
|
449 |
| -/* 12 */ |
| 518 | +/* 13 */ |
450 | 519 | /***/ function(module, exports) {
|
451 | 520 |
|
452 | 521 | module.exports = require("stream");
|
|
0 commit comments