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>
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 /////////////////////////////////////////////////////////////////////////////
27 std::chrono::nanoseconds timeout
29 assertx(timeout
.count() >= 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(),
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
));
47 FileAwait::~FileAwait() {
49 m_file
->unregisterHandler();
53 getSingleton
<AsioEventBase
>()
54 ->runInEventBaseThreadAndWait([to
{std::move(m_timeout
)}] {
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)) {
70 if (status
> m_result
) {
76 /////////////////////////////////////////////////////////////////////////////
78 Object
File::await(uint16_t events
, double timeout
) {
80 TypedValue closedResult
;
81 closedResult
.m_type
= KindOfInt64
;
82 closedResult
.m_data
.num
= FileAwait::CLOSED
;
83 return Object
{c_StaticWaitHandle::CreateSucceeded(closedResult
)};
86 SystemLib::throwExceptionObject(
87 "Unable to await on stream, invalid file descriptor");
89 events
= events
& FileEventHandler::READ_WRITE
;
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(
104 "File descriptor {} is not awaitable - real file?",
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(
118 std::chrono::milliseconds(static_cast<int64_t>(timeout
* 1000.0))
121 return Object
{ev
->getWaitHandle()};
129 /////////////////////////////////////////////////////////////////////////////