EvalEmitDVArray: varray
[hiphop-php.git] / hphp / runtime / base / file-await.cpp
blob6414eac0859f6f5bff770e0817af8cfafd1ba60a
1 #include "hphp/runtime/base/file-await.h"
2 #include "hphp/runtime/base/file.h"
3 #include "hphp/runtime/ext/asio/ext_static-wait-handle.h"
5 #include "hphp/util/compatibility.h"
7 #include <folly/Chrono.h>
9 #include <fcntl.h>
11 namespace HPHP {
12 /////////////////////////////////////////////////////////////////////////////
14 void FileTimeoutHandler::timeoutExpired() noexcept {
15 m_fileAwait.setFinished(FileAwait::TIMEOUT);
18 void FileEventHandler::handlerReady(uint16_t events) noexcept {
19 m_fileAwait.setFinished(events ? FileAwait::READY : FileAwait::CLOSED);
22 /////////////////////////////////////////////////////////////////////////////
24 FileAwait::FileAwait(
25 int fd,
26 uint16_t events,
27 std::chrono::nanoseconds timeout
28 ) {
29 assertx(timeout.count() >= 0);
30 assertx(fd >= 0);
31 assertx(events & FileEventHandler::READ_WRITE);
33 auto asio_event_base = getSingleton<AsioEventBase>();
34 m_file = std::make_unique<FileEventHandler>(asio_event_base.get(), fd, *this);
35 m_file->registerHandler(events);
37 if (timeout != std::chrono::nanoseconds::zero()) {
38 m_timeout = std::make_unique<FileTimeoutHandler>(asio_event_base.get(),
39 *this);
40 asio_event_base->runInEventBaseThreadAndWait([this,timeout] {
41 // Folly internally converts everything to milliseconds, so might as well do the same
42 m_timeout->scheduleTimeout(folly::chrono::ceil<std::chrono::milliseconds>(timeout));
43 });
47 FileAwait::~FileAwait() {
48 if (m_file) {
49 m_file->unregisterHandler();
50 m_file.reset();
52 if (m_timeout) {
53 getSingleton<AsioEventBase>()
54 ->runInEventBaseThreadAndWait([to{std::move(m_timeout)}] {
55 to->cancelTimeout();
56 });
60 void FileAwait::unserialize(TypedValue& c) {
61 c.m_type = KindOfInt64;
62 c.m_data.num = m_result;
65 void FileAwait::setFinished(int64_t status) {
66 if (m_finished.exchange(true)) {
67 return;
70 if (status > m_result) {
71 m_result = status;
73 markAsFinished();
76 /////////////////////////////////////////////////////////////////////////////
78 Object File::await(uint16_t events, double timeout) {
79 if (isClosed()) {
80 TypedValue closedResult;
81 closedResult.m_type = KindOfInt64;
82 closedResult.m_data.num = FileAwait::CLOSED;
83 return Object{c_StaticWaitHandle::CreateSucceeded(closedResult)};
85 if (fd() < 0) {
86 SystemLib::throwExceptionObject(
87 "Unable to await on stream, invalid file descriptor");
89 events = events & FileEventHandler::READ_WRITE;
90 if (!events) {
91 SystemLib::throwExceptionObject(
92 "Must await for reading, writing, or both.");
94 const auto originalFlags = ::fcntl(fd(), F_GETFL);
95 // This always succeeds...
96 ::fcntl(fd(), F_SETFL, originalFlags | O_ASYNC);
97 // ... but sometimes doesn't actually do anything
98 const bool isAsyncableFd = ::fcntl(fd(), F_GETFL) & O_ASYNC;
99 ::fcntl(fd(), F_SETFL, originalFlags);
101 if (!isAsyncableFd) {
102 SystemLib::throwInvalidOperationExceptionObject(
103 folly::sformat(
104 "File descriptor {} is not awaitable - real file?",
105 fd()
110 // we have a double timeout in seconds, need ms
111 // keeping with ms rather than more accuracy to avoid introducing subtle behavior
112 // changes to existing callsites that expect this behavior, e.g. `stream_await`;
113 // they already need to consider <1ms to be 0 to avoid blocking forever due to
114 // past bugs, so we don't get anything new here.
115 auto ev = new FileAwait(
116 fd(),
117 events,
118 std::chrono::milliseconds(static_cast<int64_t>(timeout * 1000.0))
120 try {
121 return Object{ev->getWaitHandle()};
122 } catch (...) {
123 assertx(false);
124 ev->abandon();
125 throw;
129 /////////////////////////////////////////////////////////////////////////////
130 } // namespace HPHP