@@ -381,57 +381,66 @@ void TCPWrap::Open(const FunctionCallbackInfo<Value>& args) {
381381}
382382
383383BaseObject::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
392384 // Only a live handle that is not already being torn down can be transferred.
393385 // Higher-level guards (no buffered reads, no pending writes) are enforced by
394386 // the JS net.Socket/net.Server layer before a handle reaches here.
395387 if (!HandleWrap::IsAlive (this ) || IsHandleClosing ())
396388 return TransferMode::kDisallowCloneAndTransfer ;
397389 return TransferMode::kTransferable ;
398- #endif
399390}
400391
401392std::unique_ptr<worker::TransferData> TCPWrap::TransferForMessaging () {
402- #ifdef _WIN32
403- return {};
404- #else
405393 CHECK_NE (GetTransferMode (), TransferMode::kDisallowCloneAndTransfer );
406394
407395 uv_os_fd_t fd;
408396 if (uv_fileno (reinterpret_cast <uv_handle_t *>(&handle_), &fd) != 0 ) return {};
409397
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
416421
417422 SocketType type =
418423 provider_type () == ProviderType::PROVIDER_TCPSERVERWRAP ? SERVER : SOCKET ;
419424
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.
421428 Close ();
422429
423- return std::make_unique<TransferData>(dup_fd, type);
424- #endif
430+ return std::make_unique<TransferData>(duplicate, type);
425431}
426432
427433TCPWrap::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 ) {
431439 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 ));
433441 uv_fs_req_cleanup (&req);
434442 }
443+ #endif
435444}
436445
437446BaseObjectPtr<BaseObject> TCPWrap::TransferData::Deserialize (
@@ -454,10 +463,14 @@ BaseObjectPtr<BaseObject> TCPWrap::TransferData::Deserialize(
454463 TCPWrap* wrap = BaseObject::Unwrap<TCPWrap>(obj);
455464 if (wrap == nullptr ) return {};
456465
457- if (uv_tcp_open (&wrap->handle_ , fd_ ) != 0 ) return {};
466+ if (uv_tcp_open (&wrap->handle_ , socket_ ) != 0 ) return {};
458467
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
461474 return BaseObjectPtr<BaseObject>(wrap);
462475}
463476
0 commit comments