| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent fe9e0db commit fffd8a7
11 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3568,14 +3568,6 @@ An attempt was made to transfer a `net.Socket` or `net.Server` to another thread | |||
| 3568 | 3568 | via a `worker_threads` `postMessage()` call while it was not in a transferable | |
| 3569 | 3569 | state, for example because it had already started reading or had buffered data. | |
| 3570 | 3570 | ||
| 3571 | - <a id="ERR_WORKER_HANDLE_TRANSFER_UNSUPPORTED"></a> | ||
| 3572 | - | ||
| 3573 | - ### `ERR_WORKER_HANDLE_TRANSFER_UNSUPPORTED` | ||
| 3574 | - | ||
| 3575 | - An attempt was made to transfer a `net.Socket` or `net.Server` to another thread | ||
| 3576 | - on a platform where moving the underlying handle between event loops is not | ||
| 3577 | - supported (currently Windows). | ||
| 3578 | - | ||
| 3579 | 3571 | <a id="ERR_WORKER_INIT_FAILED"></a> | |
| 3580 | 3572 | ||
| 3581 | 3573 | ### `ERR_WORKER_INIT_FAILED` | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -770,9 +770,7 @@ threads. | |||
| 770 | 770 | The socket must be a freshly accepted or created TCP connection: it must still | |
| 771 | 771 | be attached to a live handle, must not be connecting or destroyed, and must not | |
| 772 | 772 | have started reading or have buffered data. Otherwise `postMessage()` throws | |
| 773 | - `ERR_WORKER_HANDLE_NOT_TRANSFERABLE`. Only TCP sockets are supported, and only | ||
| 774 | - on Unix-like platforms; on Windows `postMessage()` throws | ||
| 775 | - `ERR_WORKER_HANDLE_TRANSFER_UNSUPPORTED`. | ||
| 773 | + `ERR_WORKER_HANDLE_NOT_TRANSFERABLE`. Only TCP sockets are supported. | ||
| 776 | 774 | ||
| 777 | 775 | ```cjs | |
| 778 | 776 | const net = require('node:net'); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1249,8 +1249,7 @@ freshly accepted or created TCP connection that has not yet started reading and | |||
| 1249 | 1249 | has no buffered data, otherwise `postMessage()` throws | |
| 1250 | 1250 | `ERR_WORKER_HANDLE_NOT_TRANSFERABLE`. This makes it possible to accept | |
| 1251 | 1251 | connections on one thread and distribute them across a pool of worker threads. | |
| 1252 | - Only TCP handles are supported, and only on Unix-like platforms; on Windows | ||
| 1253 | - `postMessage()` throws `ERR_WORKER_HANDLE_TRANSFER_UNSUPPORTED`. | ||
| 1252 | + Only TCP handles are supported. | ||
| 1254 | 1253 | ||
| 1255 | 1254 | If `value` contains {SharedArrayBuffer} instances, those are accessible | |
| 1256 | 1255 | from either thread. They cannot be listed in `transferList`. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1975,8 +1975,6 @@ E('ERR_WORKER_HANDLE_NOT_TRANSFERABLE', | |||
| 1975 | 1975 | '%s cannot be transferred in its current state; it must be a freshly ' + | |
| 1976 | 1976 | 'created or accepted handle that has not started reading and has no ' + | |
| 1977 | 1977 | 'pending writes', Error); | |
| 1978 | - E('ERR_WORKER_HANDLE_TRANSFER_UNSUPPORTED', | ||
| 1979 | - 'Transferring a %s to another thread is not supported on this platform', Error); | ||
| 1980 | 1978 | E('ERR_WORKER_INIT_FAILED', 'Worker initialization failure: %s', Error); | |
| 1981 | 1979 | E('ERR_WORKER_INVALID_EXEC_ARGV', (errors, msg = 'invalid execArgv flags') => | |
| 1982 | 1980 | `Initiated Worker with ${msg}: ${ArrayPrototypeJoin(errors, ', ')}`, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -120,7 +120,6 @@ const { | |||
| 120 | 120 | ERR_SOCKET_CONNECTION_TIMEOUT, | |
| 121 | 121 | ERR_SOCKET_HANDLE_ADOPTED, | |
| 122 | 122 | ERR_WORKER_HANDLE_NOT_TRANSFERABLE, | |
| 123 | - ERR_WORKER_HANDLE_TRANSFER_UNSUPPORTED, | ||
| 124 | 123 | }, | |
| 125 | 124 | genericNodeError, | |
| 126 | 125 | } = require('internal/errors'); | |
@@ -1600,9 +1599,6 @@ Socket.prototype[kReinitializeHandle] = function reinitializeHandle(handle) { | |||
| 1600 | 1599 | // connecting or destroyed, and with no data already buffered in either | |
| 1601 | 1600 | // direction (which would otherwise be lost on the sending side). | |
| 1602 | 1601 | function assertTransferableSocket(socket) { | |
| 1603 | - if (isWindows) { | ||
| 1604 | - throw new ERR_WORKER_HANDLE_TRANSFER_UNSUPPORTED('net.Socket'); | ||
| 1605 | - } | ||
| 1606 | 1602 | const handle = socket._handle; | |
| 1607 | 1603 | if (handle == null || !(handle instanceof TCP) || | |
| 1608 | 1604 | socket.destroyed || socket.connecting || | |
@@ -2364,9 +2360,6 @@ Server.prototype._listen2 = setupListenHandle; // legacy alias | |||
| 2364 | 2360 | // underlying listening socket (and its pending accept queue) to that thread's | |
| 2365 | 2361 | // event loop. Only a server bound to a live TCP handle can be transferred. | |
| 2366 | 2362 | function assertTransferableServer(server) { | |
| 2367 | - if (isWindows) { | ||
| 2368 | - throw new ERR_WORKER_HANDLE_TRANSFER_UNSUPPORTED('net.Server'); | ||
| 2369 | - } | ||
| 2370 | 2363 | if (server._handle == null || !(server._handle instanceof TCP)) { | |
| 2371 | 2364 | throw new ERR_WORKER_HANDLE_NOT_TRANSFERABLE('net.Server'); | |
| 2372 | 2365 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -381,57 +381,66 @@ void TCPWrap::Open(const FunctionCallbackInfo<Value>& args) { | |||
| 381 | 381 | } | |
| 382 | 382 | ||
| 383 | 383 | BaseObject::TransferMode TCPWrap::GetTransferMode() const { | |
| 384 | - #ifdef _WIN32 | ||
| 385 | - // Re-adopting a socket into another thread's event loop requires | ||
| 386 | - // re-associating it with that loop's IOCP, which needs same-process | ||
| 387 | - // WSADuplicateSocket support that is not wired up yet. The JS net layer | ||
| 388 | - // throws a clearer error before reaching here; this is the backstop for the | ||
| 389 | - // low-level `socket._handle` transfer path. | ||
| 390 | - return TransferMode::kDisallowCloneAndTransfer; | ||
| 391 | - #else | ||
| 392 | 384 | // Only a live handle that is not already being torn down can be transferred. | |
| 393 | 385 | // Higher-level guards (no buffered reads, no pending writes) are enforced by | |
| 394 | 386 | // the JS net.Socket/net.Server layer before a handle reaches here. | |
| 395 | 387 | if (!HandleWrap::IsAlive(this) || IsHandleClosing()) | |
| 396 | 388 | return TransferMode::kDisallowCloneAndTransfer; | |
| 397 | 389 | return TransferMode::kTransferable; | |
| 398 | - #endif | ||
| 399 | 390 | } | |
| 400 | 391 | ||
| 401 | 392 | std::unique_ptr<worker::TransferData> TCPWrap::TransferForMessaging() { | |
| 402 | - #ifdef _WIN32 | ||
| 403 | - return {}; | ||
| 404 | - #else | ||
| 405 | 393 | CHECK_NE(GetTransferMode(), TransferMode::kDisallowCloneAndTransfer); | |
| 406 | 394 | ||
| 407 | 395 | uv_os_fd_t fd; | |
| 408 | 396 | if (uv_fileno(reinterpret_cast<uv_handle_t*>(&handle_), &fd) != 0) return {}; | |
| 409 | 397 | ||
| 410 | - // dup() the descriptor so the receiving event loop owns an independent | ||
| 411 | - // reference to the same socket. We then close the source handle, which | ||
| 412 | - // renders it unusable on this side (true transfer semantics) while the dup | ||
| 413 | - // keeps the underlying socket alive for the destination thread. | ||
| 414 | - int dup_fd = dup(fd); | ||
| 415 | - if (dup_fd < 0) return {}; | ||
| 398 | + #ifdef _WIN32 | ||
| 399 | + // A socket that is already associated with an IOCP cannot be associated with | ||
| 400 | + // another one. Create a same-process duplicate that is not associated with | ||
| 401 | + // any IOCP yet; uv_tcp_open() will associate it with the receiving loop. | ||
| 402 | + WSAPROTOCOL_INFOW protocol_info; | ||
| 403 | + uv_os_sock_t source_socket = reinterpret_cast<uv_os_sock_t>(fd); | ||
| 404 | + if (WSADuplicateSocketW( | ||
| 405 | + source_socket, GetCurrentProcessId(), &protocol_info) != 0) { | ||
| 406 | + return {}; | ||
| 407 | + } | ||
| 408 | + uv_os_sock_t duplicate = WSASocketW(FROM_PROTOCOL_INFO, | ||
| 409 | + FROM_PROTOCOL_INFO, | ||
| 410 | + FROM_PROTOCOL_INFO, | ||
| 411 | + &protocol_info, | ||
| 412 | + 0, | ||
| 413 | + WSA_FLAG_OVERLAPPED); | ||
| 414 | + if (duplicate == static_cast<uv_os_sock_t>(-1)) return {}; | ||
| 415 | + #else | ||
| 416 | + // Unix threads share the descriptor table, so dup() creates an independent | ||
| 417 | + // reference to the same socket for the receiving event loop. | ||
| 418 | + uv_os_sock_t duplicate = dup(fd); | ||
| 419 | + if (duplicate < 0) return {}; | ||
| 420 | + #endif | ||
| 416 | 421 | ||
| 417 | 422 | SocketType type = | |
| 418 | 423 | provider_type() == ProviderType::PROVIDER_TCPSERVERWRAP ? SERVER : SOCKET; | |
| 419 | 424 | ||
| 420 | - // Stop watching the fd and tear down the source handle. | ||
| 425 | + // Stop watching the original socket and tear down the source handle. The | ||
| 426 | + // duplicate keeps the underlying socket alive until the destination adopts | ||
| 427 | + // it, or until TransferData is destroyed if the message is not delivered. | ||
| 421 | 428 | Close(); | |
| 422 | 429 | ||
| 423 | - return std::make_unique<TransferData>(dup_fd, type); | ||
| 424 | - #endif | ||
| 430 | + return std::make_unique<TransferData>(duplicate, type); | ||
| 425 | 431 | } | |
| 426 | 432 | ||
| 427 | 433 | TCPWrap::TransferData::~TransferData() { | |
| 428 | - // Only reached if the message was never delivered (e.g. the destination port | ||
| 429 | - // closed in flight); close the dup'd fd so it is not leaked. | ||
| 430 | - if (fd_ >= 0) { | ||
| 434 | + #ifdef _WIN32 | ||
| 435 | + if (socket_ != static_cast<uv_os_sock_t>(-1)) | ||
| 436 | + CHECK_EQ(0, closesocket(socket_)); | ||
| 437 | + #else | ||
| 438 | + if (socket_ >= 0) { | ||
| 431 | 439 | uv_fs_t req; | |
| 432 | - CHECK_EQ(0, uv_fs_close(nullptr, &req, fd_, nullptr)); | ||
| 440 | + CHECK_EQ(0, uv_fs_close(nullptr, &req, socket_, nullptr)); | ||
| 433 | 441 | uv_fs_req_cleanup(&req); | |
| 434 | 442 | } | |
| 443 | + #endif | ||
| 435 | 444 | } | |
| 436 | 445 | ||
| 437 | 446 | BaseObjectPtr<BaseObject> TCPWrap::TransferData::Deserialize( | |
@@ -454,10 +463,14 @@ BaseObjectPtr<BaseObject> TCPWrap::TransferData::Deserialize( | |||
| 454 | 463 | TCPWrap* wrap = BaseObject::Unwrap<TCPWrap>(obj); | |
| 455 | 464 | if (wrap == nullptr) return {}; | |
| 456 | 465 | ||
| 457 | - if (uv_tcp_open(&wrap->handle_, fd_) != 0) return {}; | ||
| 466 | + if (uv_tcp_open(&wrap->handle_, socket_) != 0) return {}; | ||
| 458 | 467 | ||
| 459 | - wrap->set_fd(fd_); | ||
| 460 | - fd_ = -1; // Ownership has been handed to the new handle. | ||
| 468 | + #ifdef _WIN32 | ||
| 469 | + socket_ = static_cast<uv_os_sock_t>(-1); | ||
| 470 | + #else | ||
| 471 | + wrap->set_fd(socket_); | ||
| 472 | + socket_ = -1; | ||
| 473 | + #endif | ||
| 461 | 474 | return BaseObjectPtr<BaseObject>(wrap); | |
| 462 | 475 | } | |
| 463 | 476 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -62,11 +62,10 @@ class TCPWrap : public ConnectionWrap<TCPWrap, uv_tcp_t> { | |||
| 62 | 62 | } | |
| 63 | 63 | } | |
| 64 | 64 | ||
| 65 | - // Transfer the underlying socket to another thread via .postMessage(). Within | ||
| 66 | - // a single process all threads share the same file descriptor table, so the | ||
| 67 | - // transfer dup()s the fd and re-adopts it (uv_tcp_open) in the receiving | ||
| 68 | - // event loop. This is the building block for distributing listening sockets | ||
| 69 | - // and accepted connections across worker_threads. | ||
| 65 | + // Transfer the underlying socket to another thread via .postMessage(). The | ||
| 66 | + // transfer duplicates the socket and re-adopts it (uv_tcp_open) in the | ||
| 67 | + // receiving event loop. This is the building block for distributing | ||
| 68 | + // listening sockets and accepted connections across worker_threads. | ||
| 70 | 69 | BaseObject::TransferMode GetTransferMode() const override; | |
| 71 | 70 | std::unique_ptr<worker::TransferData> TransferForMessaging() override; | |
| 72 | 71 | ||
@@ -75,7 +74,8 @@ class TCPWrap : public ConnectionWrap<TCPWrap, uv_tcp_t> { | |||
| 75 | 74 | ||
| 76 | 75 | class TransferData : public worker::TransferData { | |
| 77 | 76 | public: | |
| 78 | - explicit TransferData(int fd, SocketType type) : fd_(fd), type_(type) {} | ||
| 77 | + explicit TransferData(uv_os_sock_t socket, SocketType type) | ||
| 78 | + : socket_(socket), type_(type) {} | ||
| 79 | 79 | ~TransferData() override; | |
| 80 | 80 | ||
| 81 | 81 | BaseObjectPtr<BaseObject> Deserialize( | |
@@ -88,7 +88,7 @@ class TCPWrap : public ConnectionWrap<TCPWrap, uv_tcp_t> { | |||
| 88 | 88 | SET_SELF_SIZE(TransferData) | |
| 89 | 89 | ||
| 90 | 90 | private: | |
| 91 | - int fd_; | ||
| 91 | + uv_os_sock_t socket_; | ||
| 92 | 92 | SocketType type_; | |
| 93 | 93 | }; | |
| 94 | 94 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,11 +7,6 @@ | |||
| 7 | 7 | ||
| 8 | 8 | const common = require('../common'); | |
| 9 | 9 | ||
| 10 | - if (common.isWindows) { | ||
| 11 | - common.skip('transferring TCP handles between threads is not supported on ' + | ||
| 12 | - 'Windows yet'); | ||
| 13 | - } | ||
| 14 | - | ||
| 15 | 10 | const assert = require('assert'); | |
| 16 | 11 | const net = require('net'); | |
| 17 | 12 | const { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,11 +7,6 @@ | |||
| 7 | 7 | ||
| 8 | 8 | const common = require('../common'); | |
| 9 | 9 | ||
| 10 | - if (common.isWindows) { | ||
| 11 | - common.skip('transferring TCP handles between threads is not supported on ' + | ||
| 12 | - 'Windows yet'); | ||
| 13 | - } | ||
| 14 | - | ||
| 15 | 10 | const assert = require('assert'); | |
| 16 | 11 | const net = require('net'); | |
| 17 | 12 | const http = require('http'); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,11 +7,6 @@ | |||
| 7 | 7 | ||
| 8 | 8 | const common = require('../common'); | |
| 9 | 9 | ||
| 10 | - if (common.isWindows) { | ||
| 11 | - common.skip('transferring TCP handles between threads is not supported on ' + | ||
| 12 | - 'Windows yet'); | ||
| 13 | - } | ||
| 14 | - | ||
| 15 | 10 | const assert = require('assert'); | |
| 16 | 11 | const net = require('net'); | |
| 17 | 12 | const { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments