Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
process_win.cc
Go to the documentation of this file.
1// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "platform/globals.h"
6#if defined(DART_HOST_OS_WINDOWS)
7
8#include "bin/process.h"
9
10#include <process.h> // NOLINT
11#include <psapi.h> // NOLINT
12#include <vector>
13
14#include "bin/builtin.h"
15#include "bin/dartutils.h"
16#include "bin/eventhandler.h"
17#include "bin/lockers.h"
18#include "bin/socket.h"
19#include "bin/thread.h"
20#include "bin/utils.h"
21#include "bin/utils_win.h"
22#include "platform/syslog.h"
23
24namespace dart {
25namespace bin {
26
27static constexpr int kReadHandle = 0;
28static constexpr int kWriteHandle = 1;
29
30int Process::global_exit_code_ = 0;
31Mutex* Process::global_exit_code_mutex_ = nullptr;
32Process::ExitHook Process::exit_hook_ = nullptr;
33
34// ProcessInfo is used to map a process id to the process handle,
35// wait handle for registered exit code event and the pipe used to
36// communicate the exit code of the process to Dart.
37// ProcessInfo objects are kept in the static singly-linked
38// ProcessInfoList.
39class ProcessInfo {
40 public:
41 ProcessInfo(DWORD process_id,
42 HANDLE process_handle,
43 HANDLE wait_handle,
44 HANDLE exit_pipe)
45 : process_id_(process_id),
46 process_handle_(process_handle),
47 wait_handle_(wait_handle),
48 exit_pipe_(exit_pipe) {}
49
50 ~ProcessInfo() {
51 BOOL success = CloseHandle(process_handle_);
52 if (!success) {
53 FATAL("Failed to close process handle");
54 }
55 success = CloseHandle(exit_pipe_);
56 if (!success) {
57 FATAL("Failed to close process exit code pipe");
58 }
59 }
60
61 DWORD pid() { return process_id_; }
62 HANDLE process_handle() { return process_handle_; }
63 HANDLE wait_handle() { return wait_handle_; }
64 HANDLE exit_pipe() { return exit_pipe_; }
65 ProcessInfo* next() { return next_; }
66 void set_next(ProcessInfo* next) { next_ = next; }
67
68 private:
69 // Process id.
70 DWORD process_id_;
71 // Process handle.
72 HANDLE process_handle_;
73 // Wait handle identifying the exit-code wait operation registered
74 // with RegisterWaitForSingleObject.
75 HANDLE wait_handle_;
76 // File descriptor for pipe to report exit code.
77 HANDLE exit_pipe_;
78 // Link to next ProcessInfo object in the singly-linked list.
79 ProcessInfo* next_;
80
81 DISALLOW_COPY_AND_ASSIGN(ProcessInfo);
82};
83
84// Singly-linked list of ProcessInfo objects for all active processes
85// started from Dart.
86class ProcessInfoList {
87 public:
88 static void Init();
89 static void Cleanup();
90
91 static void AddProcess(DWORD pid, HANDLE handle, HANDLE pipe) {
92 // Register a callback to extract the exit code, when the process
93 // is signaled. The callback runs in a independent thread from the OS pool.
94 // Because the callback depends on the process list containing
95 // the process, lock the mutex until the process is added to the list.
96 MutexLocker locker(mutex_);
97 HANDLE wait_handle = INVALID_HANDLE_VALUE;
98 BOOL success = RegisterWaitForSingleObject(
99 &wait_handle, handle, &ExitCodeCallback, reinterpret_cast<PVOID>(pid),
100 INFINITE, WT_EXECUTEONLYONCE);
101 if (!success) {
102 FATAL("Failed to register exit code wait operation.");
103 }
104 ProcessInfo* info = new ProcessInfo(pid, handle, wait_handle, pipe);
105 // Mutate the process list under the mutex.
106 info->set_next(active_processes_);
107 active_processes_ = info;
108 }
109
110 static bool LookupProcess(DWORD pid,
111 HANDLE* handle,
112 HANDLE* wait_handle,
113 HANDLE* pipe) {
114 MutexLocker locker(mutex_);
115 ProcessInfo* current = active_processes_;
116 while (current != nullptr) {
117 if (current->pid() == pid) {
118 *handle = current->process_handle();
119 *wait_handle = current->wait_handle();
120 *pipe = current->exit_pipe();
121 return true;
122 }
123 current = current->next();
124 }
125 return false;
126 }
127
128 static void RemoveProcess(DWORD pid) {
129 MutexLocker locker(mutex_);
130 ProcessInfo* prev = nullptr;
131 ProcessInfo* current = active_processes_;
132 while (current != nullptr) {
133 if (current->pid() == pid) {
134 if (prev == nullptr) {
135 active_processes_ = current->next();
136 } else {
137 prev->set_next(current->next());
138 }
139 delete current;
140 return;
141 }
142 prev = current;
143 current = current->next();
144 }
145 }
146
147 private:
148 // Callback called when an exit code is available from one of the
149 // processes in the list.
150 static void CALLBACK ExitCodeCallback(PVOID data, BOOLEAN timed_out) {
151 if (timed_out) {
152 return;
153 }
154 DWORD pid = reinterpret_cast<UINT_PTR>(data);
155 HANDLE handle;
156 HANDLE wait_handle;
157 HANDLE exit_pipe;
158 bool success = LookupProcess(pid, &handle, &wait_handle, &exit_pipe);
159 if (!success) {
160 FATAL("Failed to lookup process in list of active processes");
161 }
162 // Unregister the event in a non-blocking way.
163 BOOL ok = UnregisterWait(wait_handle);
164 if (!ok && (GetLastError() != ERROR_IO_PENDING)) {
165 FATAL("Failed unregistering wait operation");
166 }
167 // Get and report the exit code to Dart.
168 int exit_code;
169 ok = GetExitCodeProcess(handle, reinterpret_cast<DWORD*>(&exit_code));
170 if (!ok) {
171 FATAL("GetExitCodeProcess failed %d\n", GetLastError());
172 }
173 int negative = 0;
174 if (exit_code < 0) {
175 exit_code = abs(exit_code);
176 negative = 1;
177 }
178 int message[2] = {exit_code, negative};
180 ok = WriteFile(exit_pipe, message, sizeof(message), &written, nullptr);
181 // If the process has been closed, the read end of the exit
182 // pipe has been closed. It is therefore not a problem that
183 // WriteFile fails with a closed pipe error
184 // (ERROR_NO_DATA). Other errors should not happen.
185 if (ok && (written != sizeof(message))) {
186 FATAL("Failed to write entire process exit message");
187 } else if (!ok && (GetLastError() != ERROR_NO_DATA)) {
188 FATAL("Failed to write exit code: %d", GetLastError());
189 }
190 // Remove the process from the list of active processes.
191 RemoveProcess(pid);
192 }
193
194 // Linked list of ProcessInfo objects for all active processes
195 // started from Dart code.
196 static ProcessInfo* active_processes_;
197 // Mutex protecting all accesses to the linked list of active
198 // processes.
199 static Mutex* mutex_;
200
202 DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessInfoList);
203};
204
205ProcessInfo* ProcessInfoList::active_processes_ = nullptr;
206Mutex* ProcessInfoList::mutex_ = nullptr;
207
208// Types of pipes to create.
209enum NamedPipeType { kInheritRead, kInheritWrite, kInheritNone };
210
211// Create a pipe for communicating with a new process. The handles array
212// will contain the read and write ends of the pipe. Based on the type
213// one of the handles will be inheritable.
214// NOTE: If this function returns false the handles might have been allocated
215// and the caller should make sure to close them in case of an error.
216static bool CreateProcessPipe(HANDLE handles[2],
217 wchar_t* pipe_name,
218 NamedPipeType type) {
219 // Security attributes describing an inheritable handle.
220 SECURITY_ATTRIBUTES inherit_handle;
221 inherit_handle.nLength = sizeof(SECURITY_ATTRIBUTES);
222 inherit_handle.bInheritHandle = TRUE;
223 inherit_handle.lpSecurityDescriptor = nullptr;
224
225 if (type == kInheritRead) {
226 handles[kWriteHandle] =
227 CreateNamedPipeW(pipe_name, PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
228 PIPE_TYPE_BYTE | PIPE_WAIT,
229 1, // Number of pipes
230 1024, // Out buffer size
231 1024, // In buffer size
232 0, // Timeout in ms
233 nullptr);
234
235 if (handles[kWriteHandle] == INVALID_HANDLE_VALUE) {
236 Syslog::PrintErr("CreateNamedPipe failed %d\n", GetLastError());
237 return false;
238 }
239
240 handles[kReadHandle] =
241 CreateFileW(pipe_name, GENERIC_READ, 0, &inherit_handle, OPEN_EXISTING,
242 FILE_READ_ATTRIBUTES | FILE_FLAG_OVERLAPPED, nullptr);
243 if (handles[kReadHandle] == INVALID_HANDLE_VALUE) {
244 Syslog::PrintErr("CreateFile failed %d\n", GetLastError());
245 return false;
246 }
247 } else {
248 ASSERT((type == kInheritWrite) || (type == kInheritNone));
249 handles[kReadHandle] =
250 CreateNamedPipeW(pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
251 PIPE_TYPE_BYTE | PIPE_WAIT,
252 1, // Number of pipes
253 1024, // Out buffer size
254 1024, // In buffer size
255 0, // Timeout in ms
256 nullptr);
257
258 if (handles[kReadHandle] == INVALID_HANDLE_VALUE) {
259 Syslog::PrintErr("CreateNamedPipe failed %d\n", GetLastError());
260 return false;
261 }
262
263 handles[kWriteHandle] = CreateFileW(
264 pipe_name, GENERIC_WRITE, 0,
265 (type == kInheritWrite) ? &inherit_handle : nullptr, OPEN_EXISTING,
266 FILE_WRITE_ATTRIBUTES | FILE_FLAG_OVERLAPPED, nullptr);
267 if (handles[kWriteHandle] == INVALID_HANDLE_VALUE) {
268 Syslog::PrintErr("CreateFile failed %d\n", GetLastError());
269 return false;
270 }
271 }
272 return true;
273}
274
275static void CloseProcessPipe(HANDLE handles[2]) {
276 for (int i = kReadHandle; i < kWriteHandle; i++) {
277 if (handles[i] != INVALID_HANDLE_VALUE) {
278 if (!CloseHandle(handles[i])) {
279 Syslog::PrintErr("CloseHandle failed %d\n", GetLastError());
280 }
281 handles[i] = INVALID_HANDLE_VALUE;
282 }
283 }
284}
285
286static void CloseProcessPipes(HANDLE handles1[2],
287 HANDLE handles2[2],
288 HANDLE handles3[2],
289 HANDLE handles4[2]) {
290 CloseProcessPipe(handles1);
291 CloseProcessPipe(handles2);
292 CloseProcessPipe(handles3);
293 CloseProcessPipe(handles4);
294}
295
296static int SetOsErrorMessage(char** os_error_message) {
297 int error_code = GetLastError();
298 const int kMaxMessageLength = 256;
299 wchar_t message[kMaxMessageLength];
300 FormatMessageIntoBuffer(error_code, message, kMaxMessageLength);
301 *os_error_message = StringUtilsWin::WideToUtf8(message);
302 return error_code;
303}
304
305// Open an inheritable handle to NUL.
306static HANDLE OpenNul() {
307 SECURITY_ATTRIBUTES inherit_handle;
308 inherit_handle.nLength = sizeof(SECURITY_ATTRIBUTES);
309 inherit_handle.bInheritHandle = TRUE;
310 inherit_handle.lpSecurityDescriptor = nullptr;
311 HANDLE nul = CreateFile(L"NUL", GENERIC_READ | GENERIC_WRITE, 0,
312 &inherit_handle, OPEN_EXISTING, 0, nullptr);
313 if (nul == INVALID_HANDLE_VALUE) {
314 Syslog::PrintErr("CloseHandle failed %d\n", GetLastError());
315 }
316 return nul;
317}
318
319const int kMaxPipeNameSize = 80;
320template <int Count>
321static int GenerateNames(wchar_t pipe_names[Count][kMaxPipeNameSize]) {
322 UUID uuid;
323 RPC_STATUS status = UuidCreateSequential(&uuid);
324 if ((status != RPC_S_OK) && (status != RPC_S_UUID_LOCAL_ONLY)) {
325 return status;
326 }
327 RPC_WSTR uuid_string;
328 status = UuidToStringW(&uuid, &uuid_string);
329 if (status != RPC_S_OK) {
330 return status;
331 }
332 for (int i = 0; i < Count; i++) {
333 static const wchar_t* prefix = L"\\\\.\\Pipe\\dart";
334 _snwprintf(pipe_names[i], kMaxPipeNameSize, L"%s_%s_%d", prefix,
335 uuid_string, i + 1);
336 }
337 status = RpcStringFreeW(&uuid_string);
338 if (status != RPC_S_OK) {
339 return status;
340 }
341 return 0;
342}
343
344class ProcessStarter {
345 public:
346 ProcessStarter(const char* path,
347 char* arguments[],
348 intptr_t arguments_length,
349 const char* working_directory,
350 char* environment[],
351 intptr_t environment_length,
352 ProcessStartMode mode,
353 intptr_t* in,
354 intptr_t* out,
355 intptr_t* err,
356 intptr_t* id,
357 intptr_t* exit_handler,
358 char** os_error_message)
359 : path_(path),
360 working_directory_(working_directory),
361 mode_(mode),
362 in_(in),
363 out_(out),
364 err_(err),
365 id_(id),
366 exit_handler_(exit_handler),
367 os_error_message_(os_error_message) {
368 stdin_handles_[kReadHandle] = INVALID_HANDLE_VALUE;
369 stdin_handles_[kWriteHandle] = INVALID_HANDLE_VALUE;
370 stdout_handles_[kReadHandle] = INVALID_HANDLE_VALUE;
371 stdout_handles_[kWriteHandle] = INVALID_HANDLE_VALUE;
372 stderr_handles_[kReadHandle] = INVALID_HANDLE_VALUE;
373 stderr_handles_[kWriteHandle] = INVALID_HANDLE_VALUE;
374 exit_handles_[kReadHandle] = INVALID_HANDLE_VALUE;
375 exit_handles_[kWriteHandle] = INVALID_HANDLE_VALUE;
376
377 // Transform input strings to system format.
378 const wchar_t* system_path = StringUtilsWin::Utf8ToWide(path_);
379 wchar_t** system_arguments;
380 system_arguments = reinterpret_cast<wchar_t**>(
381 Dart_ScopeAllocate(arguments_length * sizeof(*system_arguments)));
382 for (int i = 0; i < arguments_length; i++) {
383 system_arguments[i] = StringUtilsWin::Utf8ToWide(arguments[i]);
384 }
385
386 // Compute command-line length.
387 int command_line_length = wcslen(system_path);
388 for (int i = 0; i < arguments_length; i++) {
389 command_line_length += wcslen(system_arguments[i]);
390 }
391 // Account for null termination and one space per argument.
392 command_line_length += arguments_length + 1;
393
394 // Put together command-line string.
395 command_line_ = reinterpret_cast<wchar_t*>(
396 Dart_ScopeAllocate(command_line_length * sizeof(*command_line_)));
397 int len = 0;
398 int remaining = command_line_length;
399 int written =
400 _snwprintf(command_line_ + len, remaining, L"%s", system_path);
401 len += written;
402 remaining -= written;
403 ASSERT(remaining >= 0);
404 for (int i = 0; i < arguments_length; i++) {
405 written = _snwprintf(command_line_ + len, remaining, L" %s",
406 system_arguments[i]);
407 len += written;
408 remaining -= written;
409 ASSERT(remaining >= 0);
410 }
411
412 // Create environment block if an environment is supplied.
413 environment_block_ = nullptr;
414 if (environment != nullptr) {
415 wchar_t** system_environment;
416 system_environment = reinterpret_cast<wchar_t**>(
417 Dart_ScopeAllocate(environment_length * sizeof(*system_environment)));
418 // Convert environment strings to system strings.
419 for (intptr_t i = 0; i < environment_length; i++) {
420 system_environment[i] = StringUtilsWin::Utf8ToWide(environment[i]);
421 }
422
423 // An environment block is a sequence of zero-terminated strings
424 // followed by a block-terminating zero char.
425 intptr_t block_size = 1;
426 for (intptr_t i = 0; i < environment_length; i++) {
427 block_size += wcslen(system_environment[i]) + 1;
428 }
429 environment_block_ = reinterpret_cast<wchar_t*>(
430 Dart_ScopeAllocate(block_size * sizeof(*environment_block_)));
431 intptr_t block_index = 0;
432 for (intptr_t i = 0; i < environment_length; i++) {
433 intptr_t len = wcslen(system_environment[i]);
434 intptr_t result = _snwprintf(environment_block_ + block_index, len,
435 L"%s", system_environment[i]);
436 ASSERT(result == len);
437 block_index += len;
438 environment_block_[block_index++] = '\0';
439 }
440 // Block-terminating zero char.
441 environment_block_[block_index++] = '\0';
442 ASSERT(block_index == block_size);
443 }
444
445 system_working_directory_ = nullptr;
446 if (working_directory_ != nullptr) {
447 system_working_directory_ =
448 StringUtilsWin::Utf8ToWide(working_directory_);
449 }
450
451 attribute_list_ = nullptr;
452 }
453
454 ~ProcessStarter() {
455 if (attribute_list_ != nullptr) {
456 DeleteProcThreadAttributeList(attribute_list_);
457 }
458 }
459
460 int Start() {
461 // Create pipes required.
462 int err = CreatePipes();
463 if (err != 0) {
464 return err;
465 }
466
467 // Setup info structures.
468 STARTUPINFOEXW startup_info;
469 ZeroMemory(&startup_info, sizeof(startup_info));
470 startup_info.StartupInfo.cb = sizeof(startup_info);
471 if (mode_ != kInheritStdio) {
472 startup_info.StartupInfo.hStdInput = stdin_handles_[kReadHandle];
473 startup_info.StartupInfo.hStdOutput = stdout_handles_[kWriteHandle];
474 startup_info.StartupInfo.hStdError = stderr_handles_[kWriteHandle];
475 startup_info.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
476
477 // Setup the handles to inherit. We only want to inherit the three
478 // handles for stdin, stdout and stderr.
479 SIZE_T size = 0;
480 // The call to determine the size of an attribute list always fails with
481 // ERROR_INSUFFICIENT_BUFFER and that error should be ignored.
482 if (!InitializeProcThreadAttributeList(nullptr, 1, 0, &size) &&
483 (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
484 return CleanupAndReturnError();
485 }
486 attribute_list_ = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(
487 Dart_ScopeAllocate(size));
488 ZeroMemory(attribute_list_, size);
489 if (!InitializeProcThreadAttributeList(attribute_list_, 1, 0, &size)) {
490 return CleanupAndReturnError();
491 }
492 inherited_handles_ = {stdin_handles_[kReadHandle],
493 stdout_handles_[kWriteHandle],
494 stderr_handles_[kWriteHandle]};
495 if (!UpdateProcThreadAttribute(
496 attribute_list_, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
497 inherited_handles_.data(),
498 inherited_handles_.size() * sizeof(HANDLE), nullptr, nullptr)) {
499 return CleanupAndReturnError();
500 }
501 startup_info.lpAttributeList = attribute_list_;
502 }
503
504 PROCESS_INFORMATION process_info;
505 ZeroMemory(&process_info, sizeof(process_info));
506
507 // Create process.
508 DWORD creation_flags =
509 EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT;
510 if (!Process::ModeIsAttached(mode_)) {
511 creation_flags |= DETACHED_PROCESS;
512 } else {
513 // Unless we are inheriting stdio which means there is some console
514 // associated with the app, we want to ensure no console window pops
515 // up for the spawned child.
516 if (mode_ != kInheritStdio) {
517 // Normally stdout for console dart application is associated with a
518 // console that is launched from, but for gui applications(flutter on
519 // windows) console might be absent, will be created by CreateProcessW
520 // below. When that happens we ensure that console window doesn't
521 // pop up.
522 creation_flags |= CREATE_NO_WINDOW;
523 }
524 }
525 BOOL result = CreateProcessW(
526 nullptr, // ApplicationName
527 command_line_,
528 nullptr, // ProcessAttributes
529 nullptr, // ThreadAttributes
530 TRUE, // InheritHandles
531 creation_flags, environment_block_, system_working_directory_,
532 reinterpret_cast<STARTUPINFOW*>(&startup_info), &process_info);
533
534 if (result == 0) {
535 return CleanupAndReturnError();
536 }
537
538 if (mode_ != kInheritStdio) {
539 CloseHandle(stdin_handles_[kReadHandle]);
540 CloseHandle(stdout_handles_[kWriteHandle]);
541 CloseHandle(stderr_handles_[kWriteHandle]);
542 }
543 if (Process::ModeIsAttached(mode_)) {
544 ProcessInfoList::AddProcess(process_info.dwProcessId,
545 process_info.hProcess,
546 exit_handles_[kWriteHandle]);
547 }
548 if (mode_ != kDetached) {
549 // Connect the three stdio streams.
550 if (Process::ModeHasStdio(mode_)) {
551 FileHandle* stdin_handle = new FileHandle(stdin_handles_[kWriteHandle]);
552 FileHandle* stdout_handle =
553 new FileHandle(stdout_handles_[kReadHandle]);
554 FileHandle* stderr_handle =
555 new FileHandle(stderr_handles_[kReadHandle]);
556 *in_ = reinterpret_cast<intptr_t>(stdout_handle);
557 *out_ = reinterpret_cast<intptr_t>(stdin_handle);
558 *err_ = reinterpret_cast<intptr_t>(stderr_handle);
559 }
560 if (Process::ModeIsAttached(mode_)) {
561 FileHandle* exit_handle = new FileHandle(exit_handles_[kReadHandle]);
562 *exit_handler_ = reinterpret_cast<intptr_t>(exit_handle);
563 }
564 }
565
566 CloseHandle(process_info.hThread);
567
568 // Return process id.
569 *id_ = process_info.dwProcessId;
570 return 0;
571 }
572
573 int CreatePipes() {
574 // Generate unique pipe names for the four named pipes needed.
575 wchar_t pipe_names[4][kMaxPipeNameSize];
576 int status = GenerateNames<4>(pipe_names);
577 if (status != 0) {
578 SetOsErrorMessage(os_error_message_);
579 Syslog::PrintErr("UuidCreateSequential failed %d\n", status);
580 return status;
581 }
582
583 if (mode_ != kDetached) {
584 // Open pipes for stdin, stdout, stderr and for communicating the exit
585 // code.
586 if (Process::ModeHasStdio(mode_)) {
587 if (!CreateProcessPipe(stdin_handles_, pipe_names[0], kInheritRead) ||
588 !CreateProcessPipe(stdout_handles_, pipe_names[1], kInheritWrite) ||
589 !CreateProcessPipe(stderr_handles_, pipe_names[2], kInheritWrite)) {
590 return CleanupAndReturnError();
591 }
592 }
593 // Only open exit code pipe for non detached processes.
594 if (Process::ModeIsAttached(mode_)) {
595 if (!CreateProcessPipe(exit_handles_, pipe_names[3], kInheritNone)) {
596 return CleanupAndReturnError();
597 }
598 }
599 } else {
600 // Open NUL for stdin, stdout, and stderr.
601 stdin_handles_[kReadHandle] = OpenNul();
602 if (stdin_handles_[kReadHandle] == INVALID_HANDLE_VALUE) {
603 return CleanupAndReturnError();
604 }
605
606 stdout_handles_[kWriteHandle] = OpenNul();
607 if (stdout_handles_[kWriteHandle] == INVALID_HANDLE_VALUE) {
608 return CleanupAndReturnError();
609 }
610
611 stderr_handles_[kWriteHandle] = OpenNul();
612 if (stderr_handles_[kWriteHandle] == INVALID_HANDLE_VALUE) {
613 return CleanupAndReturnError();
614 }
615 }
616 return 0;
617 }
618
619 int CleanupAndReturnError() {
620 int error_code = SetOsErrorMessage(os_error_message_);
621 CloseProcessPipes(stdin_handles_, stdout_handles_, stderr_handles_,
622 exit_handles_);
623 return error_code;
624 }
625
626 HANDLE stdin_handles_[2];
627 HANDLE stdout_handles_[2];
628 HANDLE stderr_handles_[2];
629 HANDLE exit_handles_[2];
630
631 const wchar_t* system_working_directory_;
632 wchar_t* command_line_;
633 wchar_t* environment_block_;
634 std::vector<HANDLE> inherited_handles_;
635 LPPROC_THREAD_ATTRIBUTE_LIST attribute_list_;
636
637 const char* path_;
638 const char* working_directory_;
639 ProcessStartMode mode_;
640 intptr_t* in_;
641 intptr_t* out_;
642 intptr_t* err_;
643 intptr_t* id_;
644 intptr_t* exit_handler_;
645 char** os_error_message_;
646
647 private:
649 DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessStarter);
650};
651
652int Process::Start(Namespace* namespc,
653 const char* path,
654 char* arguments[],
655 intptr_t arguments_length,
656 const char* working_directory,
657 char* environment[],
658 intptr_t environment_length,
659 ProcessStartMode mode,
660 intptr_t* in,
661 intptr_t* out,
662 intptr_t* err,
663 intptr_t* id,
664 intptr_t* exit_handler,
665 char** os_error_message) {
666 ProcessStarter starter(path, arguments, arguments_length, working_directory,
667 environment, environment_length, mode, in, out, err,
668 id, exit_handler, os_error_message);
669 return starter.Start();
670}
671
672class BufferList : public BufferListBase {
673 public:
674 BufferList() : read_pending_(true) {}
675
676 // Indicate that data has been read into the buffer provided to
677 // overlapped read.
678 void DataIsRead(intptr_t size) {
679 ASSERT(read_pending_ == true);
680 set_data_size(data_size() + size);
681 set_free_size(free_size() - size);
682 ASSERT(free_size() >= 0);
683 read_pending_ = false;
684 }
685
686 // The access to the read buffer for overlapped read.
687 bool GetReadBuffer(uint8_t** buffer, intptr_t* size) {
688 ASSERT(!read_pending_);
689 if (free_size() == 0) {
690 if (!Allocate()) {
691 return false;
692 }
693 }
694 ASSERT(free_size() > 0);
695 ASSERT(free_size() <= kBufferSize);
696 *buffer = FreeSpaceAddress();
697 *size = free_size();
698 read_pending_ = true;
699 return true;
700 }
701
702 intptr_t GetDataSize() { return data_size(); }
703
704 uint8_t* GetFirstDataBuffer() {
705 ASSERT(head() != nullptr);
706 ASSERT(head() == tail());
707 ASSERT(data_size() <= kBufferSize);
708 return head()->data();
709 }
710
711 void FreeDataBuffer() { Free(); }
712
713 private:
714 bool read_pending_;
715
716 DISALLOW_COPY_AND_ASSIGN(BufferList);
717};
718
719class OverlappedHandle {
720 public:
721 OverlappedHandle() {}
722
723 void Init(HANDLE handle, HANDLE event) {
724 handle_ = handle;
725 event_ = event;
726 ClearOverlapped();
727 }
728
729 bool HasEvent(HANDLE event) { return (event_ == event); }
730
731 bool Read() {
732 // Get the data read as a result of a completed overlapped operation.
733 if (overlapped_.InternalHigh > 0) {
734 buffer_.DataIsRead(overlapped_.InternalHigh);
735 } else {
736 buffer_.DataIsRead(0);
737 }
738
739 // Keep reading until error or pending operation.
740 while (true) {
741 ClearOverlapped();
742 uint8_t* buffer;
743 intptr_t buffer_size;
744 if (!buffer_.GetReadBuffer(&buffer, &buffer_size)) {
745 return false;
746 }
747 BOOL ok = ReadFile(handle_, buffer, buffer_size, nullptr, &overlapped_);
748 if (!ok) {
749 return (GetLastError() == ERROR_IO_PENDING);
750 }
751 buffer_.DataIsRead(overlapped_.InternalHigh);
752 }
753 }
754
755 Dart_Handle GetData() { return buffer_.GetData(); }
756
757 intptr_t GetDataSize() { return buffer_.GetDataSize(); }
758
759 uint8_t* GetFirstDataBuffer() { return buffer_.GetFirstDataBuffer(); }
760
761 void FreeDataBuffer() { return buffer_.FreeDataBuffer(); }
762
763#if defined(DEBUG)
764 bool IsEmpty() const { return buffer_.IsEmpty(); }
765#endif
766
767 void Close() {
768 CloseHandle(handle_);
769 CloseHandle(event_);
771 overlapped_.hEvent = INVALID_HANDLE_VALUE;
772 }
773
774 private:
775 void ClearOverlapped() {
776 memset(&overlapped_, 0, sizeof(overlapped_));
777 overlapped_.hEvent = event_;
778 }
779
780 OVERLAPPED overlapped_;
782 HANDLE event_;
783 BufferList buffer_;
784
786 DISALLOW_COPY_AND_ASSIGN(OverlappedHandle);
787};
788
789bool Process::Wait(intptr_t pid,
790 intptr_t in,
791 intptr_t out,
792 intptr_t err,
793 intptr_t exit_event,
794 ProcessResult* result) {
795 // Close input to the process right away.
796 reinterpret_cast<FileHandle*>(in)->Close();
797
798 // All pipes created to the sub-process support overlapped IO.
799 FileHandle* stdout_handle = reinterpret_cast<FileHandle*>(out);
800 ASSERT(stdout_handle->SupportsOverlappedIO());
801 FileHandle* stderr_handle = reinterpret_cast<FileHandle*>(err);
802 ASSERT(stderr_handle->SupportsOverlappedIO());
803 FileHandle* exit_handle = reinterpret_cast<FileHandle*>(exit_event);
804 ASSERT(exit_handle->SupportsOverlappedIO());
805
806 // Create three events for overlapped IO. These are created as already
807 // signalled to ensure they have read called at least once.
808 const int kHandles = 3;
809 HANDLE events[kHandles];
810 for (int i = 0; i < kHandles; i++) {
811 events[i] = CreateEvent(nullptr, FALSE, TRUE, nullptr);
812 }
813
814 // Setup the structure for handling overlapped IO.
815 OverlappedHandle oh[kHandles];
816 oh[0].Init(stdout_handle->handle(), events[0]);
817 oh[1].Init(stderr_handle->handle(), events[1]);
818 oh[2].Init(exit_handle->handle(), events[2]);
819
820 // Continue until all handles are closed.
821 int alive = kHandles;
822 while (alive > 0) {
823 // Blocking call waiting for events from the child process.
824 DWORD wait_result = WaitForMultipleObjects(alive, events, FALSE, INFINITE);
825
826 // Find the handle signalled.
827 int index = wait_result - WAIT_OBJECT_0;
828 for (int i = 0; i < kHandles; i++) {
829 if (oh[i].HasEvent(events[index])) {
830 bool ok = oh[i].Read();
831 if (!ok) {
832 if (GetLastError() == ERROR_BROKEN_PIPE) {
833 oh[i].Close();
834 alive--;
835 if (index < alive) {
836 events[index] = events[alive];
837 }
838 } else if (err != ERROR_IO_PENDING) {
839 DWORD e = GetLastError();
840 oh[0].Close();
841 oh[1].Close();
842 oh[2].Close();
843 SetLastError(e);
844 return false;
845 }
846 }
847 break;
848 }
849 }
850 }
851
852 // All handles closed and all data read.
853 result->set_stdout_data(oh[0].GetData());
854 result->set_stderr_data(oh[1].GetData());
855 DEBUG_ASSERT(oh[0].IsEmpty());
856 DEBUG_ASSERT(oh[1].IsEmpty());
857
858 // Calculate the exit code.
859 ASSERT(oh[2].GetDataSize() == 8);
860 uint32_t exit_codes[2];
861 memmove(&exit_codes, oh[2].GetFirstDataBuffer(), sizeof(exit_codes));
862 oh[2].FreeDataBuffer();
863 intptr_t exit_code = exit_codes[0];
864 intptr_t negative = exit_codes[1];
865 if (negative != 0) {
866 exit_code = -exit_code;
867 }
868 result->set_exit_code(exit_code);
869 return true;
870}
871
872bool Process::Kill(intptr_t id, int signal) {
873 USE(signal); // signal is not used on Windows.
874 HANDLE process_handle;
875 HANDLE wait_handle;
876 HANDLE exit_pipe;
877 // First check the process info list for the process to get a handle to it.
878 bool success = ProcessInfoList::LookupProcess(id, &process_handle,
879 &wait_handle, &exit_pipe);
880 // For detached processes we don't have the process registered in the
881 // process info list. Try to look it up through the OS.
882 if (!success) {
883 process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, id);
884 // The process is already dead.
885 if (process_handle == INVALID_HANDLE_VALUE) {
886 return false;
887 }
888 }
889 BOOL result = TerminateProcess(process_handle, -1);
890 return result ? true : false;
891}
892
894 // Nothing needs to be done on Windows.
895}
896
897intptr_t Process::CurrentProcessId() {
898 return static_cast<intptr_t>(GetCurrentProcessId());
899}
900
901int64_t Process::CurrentRSS() {
902// Although the documentation at
903// https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getprocessmemoryinfo
904// claims that GetProcessMemoryInfo is UWP compatible, it is actually not
905// hence this function cannot work when compiled in UWP mode.
906#ifdef DART_TARGET_OS_WINDOWS_UWP
907 return -1;
908#else
909 PROCESS_MEMORY_COUNTERS pmc;
910 if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
911 return -1;
912 }
913 return pmc.WorkingSetSize;
914#endif
915}
916
917int64_t Process::MaxRSS() {
918#ifdef DART_TARGET_OS_WINDOWS_UWP
919 return -1;
920#else
921 PROCESS_MEMORY_COUNTERS pmc;
922 if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
923 return -1;
924 }
925 return pmc.PeakWorkingSetSize;
926#endif
927}
928
929static SignalInfo* signal_handlers = nullptr;
930static Mutex* signal_mutex = nullptr;
931
933 FileHandle* file_handle = reinterpret_cast<FileHandle*>(fd_);
934 file_handle->Close();
935 file_handle->Release();
936}
937
938BOOL WINAPI SignalHandler(DWORD signal) {
939 MutexLocker lock(signal_mutex);
940 const SignalInfo* handler = signal_handlers;
941 bool handled = false;
942 while (handler != nullptr) {
943 if (handler->signal() == signal) {
944 int value = 0;
945 SocketBase::Write(handler->fd(), &value, 1, SocketBase::kAsync);
946 handled = true;
947 }
948 handler = handler->next();
949 }
950 return handled;
951}
952
953intptr_t GetWinSignal(intptr_t signal) {
954 switch (signal) {
955 case kSighup:
956 return CTRL_CLOSE_EVENT;
957 case kSigint:
958 return CTRL_C_EVENT;
959 default:
960 return -1;
961 }
962}
963
964intptr_t Process::SetSignalHandler(intptr_t signal) {
965 signal = GetWinSignal(signal);
966 if (signal == -1) {
967 SetLastError(ERROR_NOT_SUPPORTED);
968 return -1;
969 }
970
971 // Generate a unique pipe name for the named pipe.
972 wchar_t pipe_name[kMaxPipeNameSize];
973 int status = GenerateNames<1>(&pipe_name);
974 if (status != 0) {
975 return status;
976 }
977
978 HANDLE fds[2];
979 if (!CreateProcessPipe(fds, pipe_name, kInheritNone)) {
980 int error_code = GetLastError();
981 CloseProcessPipe(fds);
982 SetLastError(error_code);
983 return -1;
984 }
985 MutexLocker lock(signal_mutex);
986 FileHandle* write_handle = new FileHandle(fds[kWriteHandle]);
987 write_handle->EnsureInitialized(EventHandler::delegate());
988 intptr_t write_fd = reinterpret_cast<intptr_t>(write_handle);
989 if (signal_handlers == nullptr) {
990 if (SetConsoleCtrlHandler(SignalHandler, true) == 0) {
991 int error_code = GetLastError();
992 // Since SetConsoleCtrlHandler failed, the IO completion port will
993 // never receive an event for this handle, and will therefore never
994 // release the reference Retained by EnsureInitialized(). So, we
995 // have to do a second Release() here.
996 write_handle->Release();
997 write_handle->Release();
998 CloseProcessPipe(fds);
999 SetLastError(error_code);
1000 return -1;
1001 }
1002 }
1003 signal_handlers =
1004 new SignalInfo(write_fd, signal, /*oldact=*/nullptr, signal_handlers);
1005 return reinterpret_cast<intptr_t>(new FileHandle(fds[kReadHandle]));
1006}
1007
1008void Process::ClearSignalHandler(intptr_t signal, Dart_Port port) {
1009 signal = GetWinSignal(signal);
1010 if (signal == -1) {
1011 return;
1012 }
1013 MutexLocker lock(signal_mutex);
1014 SignalInfo* handler = signal_handlers;
1015 while (handler != nullptr) {
1016 bool remove = false;
1017 if (handler->signal() == signal) {
1018 if ((port == ILLEGAL_PORT) || (handler->port() == port)) {
1019 if (signal_handlers == handler) {
1020 signal_handlers = handler->next();
1021 }
1022 handler->Unlink();
1023 FileHandle* file_handle = reinterpret_cast<FileHandle*>(handler->fd());
1024 file_handle->Release();
1025 remove = true;
1026 }
1027 }
1028 SignalInfo* next = handler->next();
1029 if (remove) {
1030 delete handler;
1031 }
1032 handler = next;
1033 }
1034 if (signal_handlers == nullptr) {
1035 USE(SetConsoleCtrlHandler(SignalHandler, false));
1036 }
1037}
1038
1039void Process::ClearSignalHandlerByFd(intptr_t fd, Dart_Port port) {
1040 MutexLocker lock(signal_mutex);
1041 SignalInfo* handler = signal_handlers;
1042 while (handler != nullptr) {
1043 bool remove = false;
1044 if (handler->fd() == fd) {
1045 if ((port == ILLEGAL_PORT) || (handler->port() == port)) {
1046 if (signal_handlers == handler) {
1047 signal_handlers = handler->next();
1048 }
1049 handler->Unlink();
1050 FileHandle* file_handle = reinterpret_cast<FileHandle*>(handler->fd());
1051 file_handle->Release();
1052 remove = true;
1053 }
1054 }
1055 SignalInfo* next = handler->next();
1056 if (remove) {
1057 delete handler;
1058 }
1059 handler = next;
1060 }
1061 if (signal_handlers == nullptr) {
1062 USE(SetConsoleCtrlHandler(SignalHandler, false));
1063 }
1064}
1065
1066void ProcessInfoList::Init() {
1067 active_processes_ = nullptr;
1068 ASSERT(ProcessInfoList::mutex_ == nullptr);
1069 ProcessInfoList::mutex_ = new Mutex();
1070}
1071
1072void ProcessInfoList::Cleanup() {
1073 ASSERT(ProcessInfoList::mutex_ != nullptr);
1074 delete ProcessInfoList::mutex_;
1075 ProcessInfoList::mutex_ = nullptr;
1076}
1077
1078void Process::Init() {
1079 ProcessInfoList::Init();
1080
1081 signal_handlers = nullptr;
1082 ASSERT(signal_mutex == nullptr);
1083 signal_mutex = new Mutex();
1084
1085 ASSERT(Process::global_exit_code_mutex_ == nullptr);
1086 Process::global_exit_code_mutex_ = new Mutex();
1087}
1088
1089void Process::Cleanup() {
1091
1092 ASSERT(signal_mutex != nullptr);
1093 delete signal_mutex;
1094 signal_mutex = nullptr;
1095
1096 ASSERT(Process::global_exit_code_mutex_ != nullptr);
1097 delete Process::global_exit_code_mutex_;
1098 Process::global_exit_code_mutex_ = nullptr;
1099
1100 ProcessInfoList::Cleanup();
1101}
1102
1103} // namespace bin
1104} // namespace dart
1105
1106#endif // defined(DART_HOST_OS_WINDOWS)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
static uint32_t buffer_size(uint32_t offset, uint32_t maxAlignment)
static float next(float f)
static float prev(float f)
static bool ok(int result)
static const size_t kBufferSize
Definition SkString.cpp:27
#define DEBUG_ASSERT(cond)
Definition assert.h:321
static void PrintErr(const char *format,...) PRINTF_ATTRIBUTE(1
static EventHandlerImplementation * delegate()
static void ClearSignalHandler(intptr_t signal, Dart_Port port)
static void ClearSignalHandlerByFd(intptr_t fd, Dart_Port port)
static void Cleanup()
static int64_t CurrentRSS()
static intptr_t CurrentProcessId()
static void TerminateExitCodeHandler()
static bool Wait(intptr_t id, intptr_t in, intptr_t out, intptr_t err, intptr_t exit_handler, ProcessResult *result)
static void Init()
static int64_t MaxRSS()
static bool Kill(intptr_t id, int signal)
static intptr_t SetSignalHandler(intptr_t signal)
void(* ExitHook)(int64_t exit_code)
Definition process.h:134
static void ClearAllSignalHandlers()
Definition process.cc:70
static int Start(Namespace *namespc, const char *path, char *arguments[], intptr_t arguments_length, const char *working_directory, char *environment[], intptr_t environment_length, ProcessStartMode mode, intptr_t *in, intptr_t *out, intptr_t *err, intptr_t *id, intptr_t *exit_handler, char **os_error_message)
static intptr_t Write(intptr_t fd, const void *buffer, intptr_t num_bytes, SocketOpKind sync)
static char * WideToUtf8(wchar_t *wide, intptr_t len=-1, intptr_t *result_len=nullptr)
#define ILLEGAL_PORT
Definition dart_api.h:1530
int64_t Dart_Port
Definition dart_api.h:1524
struct _Dart_Handle * Dart_Handle
Definition dart_api.h:258
DART_EXPORT uint8_t * Dart_ScopeAllocate(intptr_t size)
#define ASSERT(E)
#define FATAL(error)
FlKeyEvent * event
static const uint8_t buffer[]
uint8_t value
GAsyncResult * result
void Init()
Win32Message message
return FALSE
SK_API bool Read(SkStreamSeekable *src, SkDocumentPage *dstArray, int dstArrayCount, const SkDeserialProcs *=nullptr)
void ReadFile(uint8_t **data, intptr_t *file_len, void *stream)
ProcessStartMode
Definition process.h:81
void FormatMessageIntoBuffer(DWORD code, wchar_t *buffer, int buffer_length)
@ kSighup
Definition process.h:48
@ kSigint
Definition process.h:49
static dart::SimpleHashMap * environment
bool WriteFile(const std::string &path, const char *data, ssize_t size)
Definition files.cc:69
static void USE(T &&)
Definition globals.h:618
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition switches.h:228
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition switches.h:259
void Close(PathBuilder *builder)
SIN Vec< N, float > abs(const Vec< N, float > &x)
Definition SkVx.h:707
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName)
Definition globals.h:593
#define DISALLOW_ALLOCATION()
Definition globals.h:604
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition globals.h:581
fuchsia::ui::composition::ParentViewportWatcherHandle handle_
const uintptr_t id
int BOOL
__w64 unsigned int UINT_PTR
void * PVOID
BYTE BOOLEAN
#define INVALID_HANDLE_VALUE
#define CreateEvent
WINBASEAPI VOID WINAPI SetLastError(_In_ DWORD dwErrCode)
#define WINAPI
WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI GetLastError(VOID)
void * HANDLE
struct _OVERLAPPED OVERLAPPED
unsigned long DWORD
#define CALLBACK
#define CreateFile
ULONG_PTR SIZE_T