Skip to content

Commit d32e55e

Browse files
committed
[fuchsia] Replace use of epoll with mx_ primitives in process_fuchsia
process_fuchsia.cc has the ability to drain stdout, stderr, and exit status from a child process. It was using epoll to multiplex these streams which worked but required complicated emulation in libc which we'd like to unwind as well as some tricky locking. This replaces the epoll with a direct mx_object_wait_many() call that waits on the Magenta handles backing the streams. [email protected] Review-Url: https://codereview.chromium.org/2903373003 .
1 parent f3a79c4 commit d32e55e

File tree

1 file changed

+84
-75
lines changed

1 file changed

+84
-75
lines changed

runtime/bin/process_fuchsia.cc

Lines changed: 84 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <magenta/status.h>
1818
#include <magenta/syscalls.h>
1919
#include <magenta/syscalls/object.h>
20+
#include <magenta/types.h>
21+
#include <mxio/private.h>
2022
#include <mxio/util.h>
2123
#include <pthread.h>
2224
#include <stdbool.h>
@@ -454,18 +456,56 @@ int64_t Process::MaxRSS() {
454456

455457
static bool ProcessWaitCleanup(intptr_t out,
456458
intptr_t err,
457-
intptr_t exit_event,
458-
intptr_t epoll_fd) {
459+
intptr_t exit_event) {
459460
int e = errno;
460461
VOID_NO_RETRY_EXPECTED(close(out));
461462
VOID_NO_RETRY_EXPECTED(close(err));
462463
VOID_NO_RETRY_EXPECTED(close(exit_event));
463-
VOID_NO_RETRY_EXPECTED(close(epoll_fd));
464464
errno = e;
465465
return false;
466466
}
467467

468468

469+
class MxioWaitEntry {
470+
public:
471+
MxioWaitEntry() {}
472+
~MxioWaitEntry() { Cancel(); }
473+
474+
void Init(int fd) { mxio_ = __mxio_fd_to_io(fd); }
475+
476+
void WaitBegin(mx_wait_item_t* wait_item) {
477+
if (mxio_ == NULL) {
478+
*wait_item = {};
479+
return;
480+
}
481+
482+
__mxio_wait_begin(mxio_, EPOLLRDHUP | EPOLLIN, &wait_item->handle,
483+
&wait_item->waitfor);
484+
wait_item->pending = 0;
485+
}
486+
487+
void WaitEnd(mx_wait_item_t* wait_item, uint32_t* event) {
488+
if (mxio_ == NULL) {
489+
*event = 0;
490+
return;
491+
}
492+
__mxio_wait_end(mxio_, wait_item->pending, event);
493+
}
494+
495+
void Cancel() {
496+
if (mxio_ != NULL) {
497+
__mxio_release(mxio_);
498+
}
499+
mxio_ = NULL;
500+
}
501+
502+
private:
503+
mxio_t* mxio_ = NULL;
504+
505+
DISALLOW_COPY_AND_ASSIGN(MxioWaitEntry);
506+
};
507+
508+
469509
bool Process::Wait(intptr_t pid,
470510
intptr_t in,
471511
intptr_t out,
@@ -484,83 +524,55 @@ bool Process::Wait(intptr_t pid,
484524
int32_t ints[2];
485525
} exit_code_data;
486526

487-
// The initial size passed to epoll_create is ignore on newer (>=
488-
// 2.6.8) Linux versions
489-
static const int kEpollInitialSize = 64;
490-
int epoll_fd = NO_RETRY_EXPECTED(epoll_create(kEpollInitialSize));
491-
if (epoll_fd == -1) {
492-
return ProcessWaitCleanup(out, err, exit_event, epoll_fd);
493-
}
494-
if (!FDUtils::SetCloseOnExec(epoll_fd)) {
495-
return ProcessWaitCleanup(out, err, exit_event, epoll_fd);
496-
}
527+
constexpr size_t kWaitItemsCount = 3;
528+
uint32_t events[kWaitItemsCount];
529+
mx_wait_item_t wait_items[kWaitItemsCount];
530+
size_t active = kWaitItemsCount;
497531

498-
struct epoll_event event;
499-
event.events = EPOLLRDHUP | EPOLLIN;
500-
event.data.fd = out;
501-
int status = NO_RETRY_EXPECTED(
502-
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, out, &event));
503-
if (status == -1) {
504-
return ProcessWaitCleanup(out, err, exit_event, epoll_fd);
505-
}
506-
event.data.fd = err;
507-
status = NO_RETRY_EXPECTED(
508-
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, err, &event));
509-
if (status == -1) {
510-
return ProcessWaitCleanup(out, err, exit_event, epoll_fd);
511-
}
512-
event.data.fd = exit_event;
513-
status = NO_RETRY_EXPECTED(
514-
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, exit_event, &event));
515-
if (status == -1) {
516-
return ProcessWaitCleanup(out, err, exit_event, epoll_fd);
517-
}
518-
intptr_t active = 3;
532+
MxioWaitEntry entries[kWaitItemsCount];
533+
entries[0].Init(out);
534+
entries[1].Init(err);
535+
entries[2].Init(exit_event);
519536

520-
static const intptr_t kMaxEvents = 16;
521-
struct epoll_event events[kMaxEvents];
522537
while (active > 0) {
523-
// TODO(US-109): When the epoll implementation is properly edge-triggered,
524-
// remove this sleep, which prevents the message queue from being
525-
// overwhelmed and leading to memory exhaustion.
526-
usleep(5000);
527-
intptr_t result = NO_RETRY_EXPECTED(
528-
epoll_wait(epoll_fd, events, kMaxEvents, -1));
529-
if ((result < 0) && (errno != EWOULDBLOCK)) {
530-
return ProcessWaitCleanup(out, err, exit_event, epoll_fd);
531-
}
532-
for (intptr_t i = 0; i < result; i++) {
533-
if ((events[i].events & EPOLLIN) != 0) {
534-
const intptr_t avail = FDUtils::AvailableBytes(events[i].data.fd);
535-
if (events[i].data.fd == out) {
536-
if (!out_data.Read(out, avail)) {
537-
return ProcessWaitCleanup(out, err, exit_event, epoll_fd);
538-
}
539-
} else if (events[i].data.fd == err) {
540-
if (!err_data.Read(err, avail)) {
541-
return ProcessWaitCleanup(out, err, exit_event, epoll_fd);
542-
}
543-
} else if (events[i].data.fd == exit_event) {
544-
if (avail == 8) {
545-
intptr_t b =
546-
NO_RETRY_EXPECTED(read(exit_event, exit_code_data.bytes, 8));
547-
if (b != 8) {
548-
return ProcessWaitCleanup(out, err, exit_event, epoll_fd);
549-
}
550-
}
551-
} else {
552-
UNREACHABLE();
538+
for (size_t i = 0; i < kWaitItemsCount; ++i) {
539+
entries[i].WaitBegin(&wait_items[i]);
540+
}
541+
mx_object_wait_many(wait_items, kWaitItemsCount, MX_TIME_INFINITE);
542+
543+
for (size_t i = 0; i < kWaitItemsCount; ++i) {
544+
entries[i].WaitEnd(&wait_items[i], &events[i]);
545+
}
546+
547+
if ((events[0] & EPOLLIN) != 0) {
548+
const intptr_t avail = FDUtils::AvailableBytes(out);
549+
if (!out_data.Read(out, avail)) {
550+
return ProcessWaitCleanup(out, err, exit_event);
551+
}
552+
}
553+
if ((events[1] & EPOLLIN) != 0) {
554+
const intptr_t avail = FDUtils::AvailableBytes(err);
555+
if (!err_data.Read(err, avail)) {
556+
return ProcessWaitCleanup(out, err, exit_event);
557+
}
558+
}
559+
if ((events[2] & EPOLLIN) != 0) {
560+
const intptr_t avail = FDUtils::AvailableBytes(exit_event);
561+
if (avail == 8) {
562+
intptr_t b =
563+
NO_RETRY_EXPECTED(read(exit_event, exit_code_data.bytes, 8));
564+
if (b != 8) {
565+
return ProcessWaitCleanup(out, err, exit_event);
553566
}
554567
}
555-
if ((events[i].events & (EPOLLHUP | EPOLLRDHUP)) != 0) {
556-
NO_RETRY_EXPECTED(close(events[i].data.fd));
568+
}
569+
for (size_t i = 0; i < kWaitItemsCount; ++i) {
570+
if ((events[i] & EPOLLRDHUP) != 0) {
557571
active--;
558-
VOID_NO_RETRY_EXPECTED(
559-
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL));
572+
entries[i].Cancel();
560573
}
561574
}
562575
}
563-
VOID_NO_RETRY_EXPECTED(close(epoll_fd));
564576

565577
// All handles closed and all data read.
566578
result->set_stdout_data(out_data.GetData());
@@ -827,15 +839,12 @@ int Process::Start(const char* path,
827839
return starter.Start();
828840
}
829841

830-
831842
intptr_t Process::SetSignalHandler(intptr_t signal) {
832843
errno = ENOSYS;
833844
return -1;
834845
}
835846

836-
837-
void Process::ClearSignalHandler(intptr_t signal) {
838-
}
847+
void Process::ClearSignalHandler(intptr_t signal) {}
839848

840849
} // namespace bin
841850
} // namespace dart

0 commit comments

Comments
 (0)