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
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
index 5bc5497..b42a049 100644
--- a/libappfuse/FuseBuffer.cc
+++ b/libappfuse/FuseBuffer.cc
@@ -34,6 +34,8 @@
 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 @@
     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 @@
 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>