summaryrefslogtreecommitdiffstats
path: root/libappfuse/FuseBuffer.cc
diff options
context:
space:
mode:
authorDaichi Hirono <hirono@google.com>2017-03-29 13:02:30 +0900
committerDaichi Hirono <hirono@google.com>2017-03-31 01:50:50 +0000
commit6f6210dd5c0aae99d67fd945e270a0948e6c4f42 (patch)
tree936618a2efc279f7fc2244ca847cba08ddd65798 /libappfuse/FuseBuffer.cc
parent281531bdcbfc99128d54fc5220488a5a6612079b (diff)
downloadcore-6f6210dd5c0aae99d67fd945e270a0948e6c4f42.tar.gz
core-6f6210dd5c0aae99d67fd945e270a0948e6c4f42.tar.bz2
core-6f6210dd5c0aae99d67fd945e270a0948e6c4f42.zip
Retry write operation when getting ENOBUFS.
Previously libappfuse set SO_SNDBUF to the maximum message size. However it does not prevent ENOBUF and it made AppFusePerfTest#testReadWriteFile flaky. The CL let FuseBuffer retry write operation when getting ENOBUFS. Bug: 34903085 Test: libappfuse Change-Id: I1602474d852e1599f6e69103bcf6f18277a5644b
Diffstat (limited to 'libappfuse/FuseBuffer.cc')
-rw-r--r--libappfuse/FuseBuffer.cc51
1 files changed, 30 insertions, 21 deletions
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
index 5bc549743..b42a04954 100644
--- a/libappfuse/FuseBuffer.cc
+++ b/libappfuse/FuseBuffer.cc
@@ -34,6 +34,8 @@ namespace android {
namespace fuse {
namespace {
+constexpr useconds_t kRetrySleepForWriting = 1000; // 1 ms
+
template <typename T>
bool CheckHeaderLength(const FuseMessage<T>* self, const char* name, size_t max_size) {
const auto& header = static_cast<const T*>(self)->header;
@@ -91,28 +93,35 @@ ResultOrAgain WriteInternal(const FuseMessage<T>* self, int fd, int sockflag, co
const char* const buf = reinterpret_cast<const char*>(self);
const auto& header = static_cast<const T*>(self)->header;
- int result;
- if (sockflag) {
- CHECK(data == nullptr);
- result = TEMP_FAILURE_RETRY(send(fd, buf, header.len, sockflag));
- } else if (data) {
- const struct iovec vec[] = {{const_cast<char*>(buf), sizeof(header)},
- {const_cast<void*>(data), header.len - sizeof(header)}};
- result = TEMP_FAILURE_RETRY(writev(fd, vec, arraysize(vec)));
- } else {
- result = TEMP_FAILURE_RETRY(write(fd, buf, header.len));
- }
-
- if (result == -1) {
- if (errno == EAGAIN) {
- return ResultOrAgain::kAgain;
+ while (true) {
+ int result;
+ if (sockflag) {
+ CHECK(data == nullptr);
+ result = TEMP_FAILURE_RETRY(send(fd, buf, header.len, sockflag));
+ } else if (data) {
+ const struct iovec vec[] = {{const_cast<char*>(buf), sizeof(header)},
+ {const_cast<void*>(data), header.len - sizeof(header)}};
+ result = TEMP_FAILURE_RETRY(writev(fd, vec, arraysize(vec)));
+ } else {
+ result = TEMP_FAILURE_RETRY(write(fd, buf, header.len));
}
- PLOG(ERROR) << "Failed to write a FUSE message";
- return ResultOrAgain::kFailure;
+ if (result == -1) {
+ switch (errno) {
+ case ENOBUFS:
+ // When returning ENOBUFS, epoll still reports the FD is writable. Just usleep
+ // and retry again.
+ usleep(kRetrySleepForWriting);
+ continue;
+ case EAGAIN:
+ return ResultOrAgain::kAgain;
+ default:
+ PLOG(ERROR) << "Failed to write a FUSE message";
+ return ResultOrAgain::kFailure;
+ }
+ }
+ CHECK(static_cast<uint32_t>(result) == header.len);
+ return ResultOrAgain::kSuccess;
}
-
- CHECK(static_cast<uint32_t>(result) == header.len);
- return ResultOrAgain::kSuccess;
}
}
@@ -161,7 +170,7 @@ bool FuseMessage<T>::Write(int fd) const {
template <typename T>
bool FuseMessage<T>::WriteWithBody(int fd, size_t max_size, const void* data) const {
CHECK(data != nullptr);
- return WriteInternal<T>(this, fd, 0, data, max_size) == ResultOrAgain::kSuccess;
+ return WriteInternal(this, fd, 0, data, max_size) == ResultOrAgain::kSuccess;
}
template <typename T>