1515#include < pthread.h>
1616#include < sstream>
1717#include < string>
18- #include < fcntl.h>
19- #include < sys/resource.h>
20- #include < sys/socket.h>
21- #include < sys/wait.h>
2218#include < system_error>
2319#include < thread> // NOLINT(misc-include-cleaner) // IWYU pragma: keep
2420#include < unistd.h>
2521#include < utility>
2622#include < vector>
2723
24+ #ifdef WIN32
25+ #include < atomic>
26+ #include < windows.h>
27+ #include < winsock2.h>
28+ #else
29+ #include < fcntl.h>
30+ #include < sys/resource.h>
31+ #include < sys/socket.h>
32+ #include < sys/types.h>
33+ #include < sys/wait.h>
34+ #endif
35+
2836#ifdef __linux__
2937#include < sys/syscall.h>
3038#endif
3341#include < pthread_np.h>
3442#endif // HAVE_PTHREAD_GETTHREADID_NP
3543
44+ #ifdef WIN32
45+ // Forward-declare internal capnp function.
46+ namespace kj { namespace _ { int win32Socketpair (SOCKET socks[2 ]); } }
47+ #endif
48+
3649namespace fs = std::filesystem;
3750
3851namespace mp {
@@ -49,6 +62,7 @@ std::vector<char*> MakeArgv(const std::vector<std::string>& args)
4962 return argv;
5063}
5164
65+ #ifndef WIN32
5266// ! Return highest possible file descriptor.
5367size_t MaxFd ()
5468{
@@ -59,6 +73,7 @@ size_t MaxFd()
5973 return 1023 ;
6074 }
6175}
76+ #endif
6277
6378} // namespace
6479
@@ -80,6 +95,8 @@ std::string ThreadName(const char* exe_name)
8095 // the former are shorter and are the same as what gdb prints "LWP ...".
8196#ifdef __linux__
8297 buffer << syscall (SYS_gettid);
98+ #elif defined(WIN32)
99+ buffer << GetCurrentThreadId ();
83100#elif defined(HAVE_PTHREAD_THREADID_NP)
84101 uint64_t tid = 0 ;
85102 pthread_threadid_np (nullptr , &tid);
@@ -117,10 +134,56 @@ std::string LogEscape(const kj::StringTree& string, size_t max_size)
117134 return result;
118135}
119136
137+ // ! Generate command line that the executable being invoked will split up using
138+ // ! the CommandLineToArgvW function, which expects arguments with spaces to be
139+ // ! quoted, quote characters to be backslash-escaped, and backslashes to also be
140+ // ! backslash-escaped, but only if they precede a quote character.
141+ std::string CommandLineFromArgv (const std::vector<std::string>& argv)
142+ {
143+ std::string out;
144+ for (const auto & arg : argv) {
145+ if (!out.empty ()) out += " " ;
146+ if (!arg.empty () && arg.find_first_of (" \t\" " ) == std::string::npos) {
147+ // Argument has no quotes or spaces so escaping not necessary.
148+ out += arg;
149+ } else {
150+ out += ' "' ; // Start with a quote
151+ for (size_t i = 0 ; i < arg.size (); ++i) {
152+ if (arg[i] == ' \\ ' ) {
153+ // Count consecutive backslashes
154+ size_t backslash_count = 0 ;
155+ while (i < arg.size () && arg[i] == ' \\ ' ) {
156+ ++backslash_count;
157+ ++i;
158+ }
159+ if (i < arg.size () && arg[i] == ' "' ) {
160+ // Backslashes before a quote need to be doubled
161+ out.append (backslash_count * 2 + 1 , ' \\ ' );
162+ out.push_back (' "' );
163+ } else {
164+ // Otherwise, backslashes remain as-is
165+ out.append (backslash_count, ' \\ ' );
166+ --i; // Compensate for the outer loop's increment
167+ }
168+ } else if (arg[i] == ' "' ) {
169+ // Escape double quotes with a backslash
170+ out.push_back (' \\ ' );
171+ out.push_back (' "' );
172+ } else {
173+ out.push_back (arg[i]);
174+ }
175+ }
176+ out += ' "' ; // End with a quote
177+ }
178+ }
179+ return out;
180+ }
181+
120182std::tuple<ProcessId, SocketId> SpawnProcess (ConnectInfoToArgsFn&& connect_info_to_args)
121183{
122184 auto fds{SocketPair ()};
123185
186+ #ifndef WIN32
124187 // Evaluate the callback and build the argv array before forking.
125188 //
126189 // The parent process may be multi-threaded and holding internal library
@@ -177,17 +240,66 @@ std::tuple<ProcessId, SocketId> SpawnProcess(ConnectInfoToArgsFn&& connect_info_
177240 _exit (127 );
178241 }
179242 return {pid, fds[1 ]};
243+ #else
244+ // Create windows pipe to send socket over to child process.
245+ static std::atomic<int > counter{1 };
246+ ConnectInfo pipe_path{" \\\\ .\\ pipe\\ mp-" + std::to_string (GetCurrentProcessId ()) + " -" + std::to_string (counter.fetch_add (1 ))};
247+ HANDLE pipe{CreateNamedPipeA (pipe_path.c_str (), PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE | PIPE_WAIT, 1 , 0 , 0 , 0 , nullptr )};
248+ KJ_WIN32 (pipe != INVALID_HANDLE_VALUE, " CreateNamedPipe failed" );
249+
250+ // Start child process
251+ std::string cmd{CommandLineFromArgv (connect_info_to_args (pipe_path))};
252+ STARTUPINFOA si{};
253+ si.cb = sizeof (si);
254+ PROCESS_INFORMATION pi{};
255+ KJ_WIN32 (CreateProcessA (nullptr , const_cast <char *>(cmd.c_str ()), nullptr , nullptr , TRUE , 0 , nullptr , nullptr , &si, &pi), " CreateProcess failed" );
256+ CloseHandle (pi.hThread ); // not needed
257+
258+ // Duplicate socket for the child (now that we know its PID)
259+ WSAPROTOCOL_INFO info{};
260+ KJ_WINSOCK (WSADuplicateSocket (fds[0 ], pi.dwProcessId , &info), " WSADuplicateSocket failed" );
261+
262+ // Send socket to the child via the pipe
263+ KJ_WIN32 (ConnectNamedPipe (pipe, nullptr ) || GetLastError () == ERROR_PIPE_CONNECTED, " ConnectNamedPipe failed" );
264+ DWORD wr;
265+ KJ_WIN32 (WriteFile (pipe, &info, sizeof (info), &wr, nullptr ) && wr == sizeof (info), " WriteFile(pipe) failed" );
266+ CloseHandle (pipe);
267+
268+ return {reinterpret_cast <ProcessId>(pi.hProcess ), fds[1 ]};
269+ #endif
180270}
181271
182272SocketId StartSpawned (const ConnectInfo& connect_info)
183273{
274+ #ifndef WIN32
184275 return std::stoi (connect_info);
276+ #else
277+ HANDLE pipe = CreateFileA (connect_info.c_str (), GENERIC_READ, 0 , nullptr , OPEN_EXISTING, 0 , nullptr );
278+ KJ_WIN32 (pipe != INVALID_HANDLE_VALUE, " CreateFile(pipe) failed" );
279+
280+ WSAPROTOCOL_INFO info{};
281+ DWORD rd;
282+ KJ_WIN32 (ReadFile (pipe, &info, sizeof (info), &rd, nullptr ) && rd == sizeof (info), " ReadFile(pipe) failed" );
283+ CloseHandle (pipe);
284+
285+ WSADATA dontcare;
286+ KJ_WIN32 (WSAStartup (MAKEWORD (2 , 2 ), &dontcare) != 0 , " WSAStartup() failed" );
287+
288+ SOCKET socket{WSASocketA (FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &info, 0 , WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT)};
289+ KJ_WINSOCK (socket, " WSASocket(FROM_PROTOCOL_INFO) failed" );
290+ return socket;
291+ #endif
185292}
186293
187294std::array<SocketId, 2 > SocketPair ()
188295{
296+ #ifdef WIN32
297+ SOCKET pair[2 ];
298+ KJ_WINSOCK (kj::_::win32Socketpair (pair));
299+ #else
189300 int pair[2 ];
190301 KJ_SYSCALL (socketpair (AF_UNIX, SOCK_STREAM, 0 , pair));
302+ #endif
191303 return {pair[0 ], pair[1 ]};
192304}
193305
@@ -205,11 +317,20 @@ void ExecProcess(const std::vector<std::string>& args)
205317
206318int WaitProcess (ProcessId pid)
207319{
320+ #ifndef WIN32
208321 int status;
209322 if (::waitpid (pid, &status, /* options=*/ 0 ) != pid) {
210323 throw std::system_error (errno, std::system_category (), " waitpid" );
211324 }
212325 return status;
326+ #else
327+ HANDLE handle{reinterpret_cast <HANDLE>(pid)};
328+ DWORD result{WaitForSingleObject (handle, INFINITE)};
329+ KJ_WIN32 (result != WAIT_OBJECT_0, " WaitForSingleObject(child) failed" );
330+ KJ_WIN32 (GetExitCodeProcess (handle, &result), " GetExitCodeProcess failed" );
331+ CloseHandle (handle);
332+ return result;
333+ #endif
213334}
214335
215336} // namespace mp
0 commit comments