• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 
18 #include <binder/unique_fd.h>
19 #include <poll.h>
20 
21 #include "FdTrigger.h"
22 #include "RpcState.h"
23 
24 namespace android {
25 
26 template <typename SendOrReceive>
interruptableReadOrWrite(const android::RpcTransportFd & socket,FdTrigger * fdTrigger,iovec * iovs,int niovs,SendOrReceive sendOrReceiveFun,const char * funName,int16_t event,const std::optional<binder::impl::SmallFunction<status_t ()>> & altPoll)27 status_t interruptableReadOrWrite(
28         const android::RpcTransportFd& socket, FdTrigger* fdTrigger, iovec* iovs, int niovs,
29         SendOrReceive sendOrReceiveFun, const char* funName, int16_t event,
30         const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll) {
31     MAYBE_WAIT_IN_FLAKE_MODE;
32 
33     if (niovs < 0) {
34         return BAD_VALUE;
35     }
36 
37     // Since we didn't poll, we need to manually check to see if it was triggered. Otherwise, we
38     // may never know we should be shutting down.
39     if (fdTrigger->isTriggered()) {
40         return DEAD_OBJECT;
41     }
42 
43     // If iovs has one or more empty vectors at the end and
44     // we somehow advance past all the preceding vectors and
45     // pass some or all of the empty ones to sendmsg/recvmsg,
46     // the call will return processSize == 0. In that case
47     // we should be returning OK but instead return DEAD_OBJECT.
48     // To avoid this problem, we make sure here that the last
49     // vector at iovs[niovs - 1] has a non-zero length.
50     while (niovs > 0 && iovs[niovs - 1].iov_len == 0) {
51         niovs--;
52     }
53     if (niovs == 0) {
54         // The vectors are all empty, so we have nothing to send.
55         return OK;
56     }
57 
58     bool havePolled = false;
59     while (true) {
60         ssize_t processSize = sendOrReceiveFun(iovs, niovs);
61         if (processSize < 0) {
62             int savedErrno = errno;
63 
64             // Still return the error on later passes, since it would expose
65             // a problem with polling
66             if (havePolled || (savedErrno != EAGAIN && savedErrno != EWOULDBLOCK)) {
67                 LOG_RPC_DETAIL("RpcTransport %s(): %s", funName, strerror(savedErrno));
68                 return -savedErrno;
69             }
70         } else if (processSize == 0) {
71             return DEAD_OBJECT;
72         } else {
73             while (processSize > 0 && niovs > 0) {
74                 auto& iov = iovs[0];
75                 if (static_cast<size_t>(processSize) < iov.iov_len) {
76                     // Advance the base of the current iovec
77                     iov.iov_base = reinterpret_cast<char*>(iov.iov_base) + processSize;
78                     iov.iov_len -= processSize;
79                     break;
80                 }
81 
82                 // The current iovec was fully written
83                 processSize -= iov.iov_len;
84                 iovs++;
85                 niovs--;
86             }
87             if (niovs == 0) {
88                 LOG_ALWAYS_FATAL_IF(processSize > 0,
89                                     "Reached the end of iovecs "
90                                     "with %zd bytes remaining",
91                                     processSize);
92                 return OK;
93             }
94         }
95 
96         if (altPoll) {
97             if (status_t status = (*altPoll)(); status != OK) return status;
98             if (fdTrigger->isTriggered()) {
99                 return DEAD_OBJECT;
100             }
101         } else {
102             if (status_t status = fdTrigger->triggerablePoll(socket, event); status != OK)
103                 return status;
104             if (!havePolled) havePolled = true;
105         }
106     }
107 }
108 
109 } // namespace android
110