nfs4: Correctly handle timeouts and other rpc errors

This commit is contained in:
Pawel Dziepak 2012-06-11 21:41:00 +02:00
parent 3ce57b347f
commit 5945c55ae4
4 changed files with 70 additions and 11 deletions

View File

@ -154,15 +154,28 @@ Server::SendCallAsync(Call* call, Reply** reply, Request** request)
fRequests.AddRequest(req);
*request = req;
return ResendCallAsync(call, req);
}
status_t
Server::ResendCallAsync(Call* call, Request* req)
{
if (fThreadError != B_OK) {
fRequests.FindRequest(req->fXID);
delete req;
return fThreadError;
}
XDR::WriteStream& stream = call->Stream();
status_t result = fConnection->Send(stream.Buffer(), stream.Size());
if (result != B_OK) {
fRequests.FindRequest(xid);
fRequests.FindRequest(req->fXID);
delete req;
return result;
}
*request = req;
return B_OK;
}
@ -198,7 +211,9 @@ Server::_Listener()
while (!fThreadCancel) {
result = fConnection->Receive(&buffer, &size);
if (result != B_OK) {
if (result == B_NO_MEMORY)
continue;
else if (result != B_OK) {
fThreadError = result;
return result;
}
@ -206,8 +221,7 @@ Server::_Listener()
Reply* reply = new(std::nothrow) Reply(buffer, size);
if (reply == NULL) {
free(buffer);
fThreadError = result;
return B_NO_MEMORY;
continue;
}
Request* req = fRequests.FindRequest(reply->GetXID());

View File

@ -52,6 +52,7 @@ public:
status_t SendCallAsync(Call* call, Reply** reply,
Request** request);
status_t ResendCallAsync(Call* call, Request* req);
inline status_t WaitCall(Request* request,
bigtime_t time = kWaitTime);
inline status_t CancelCall(Request* request);

View File

@ -13,12 +13,17 @@
status_t
Request::Send()
{
return _TrySend();
switch (fServer->ID().fProtocol) {
case ProtocolUDP: return _SendUDP();
case ProtocolTCP: return _SendTCP();
}
return B_BAD_VALUE;
}
status_t
Request::_TrySend()
Request::_SendUDP()
{
RPC::Reply *rpl;
RPC::Request *rpc;
@ -29,15 +34,51 @@ Request::_TrySend()
result = fServer->WaitCall(rpc);
if (result != B_OK) {
fServer->CancelCall(rpc);
delete rpc;
return result;
int attempts = 1;
while (result != B_OK && attempts++ < kRetryLimit)
result = fServer->ResendCallAsync(fBuilder.Request(), rpc);
if (attempts == kRetryLimit) {
fServer->CancelCall(rpc);
delete rpc;
return result;
}
}
return fReply.SetTo(rpl);
}
status_t
Request::_SendTCP()
{
RPC::Reply *rpl;
RPC::Request *rpc;
status_t result;
int attempts = 0;
do {
result = fServer->SendCallAsync(fBuilder.Request(), &rpl, &rpc);
if (result == B_NO_MEMORY)
return result;
else if (result != B_OK) {
fServer->Repair();
continue;
}
result = fServer->WaitCall(rpc);
if (result != B_OK) {
fServer->CancelCall(rpc);
delete rpc;
fServer->Repair();
}
} while (result != B_OK && attempts++ < kRetryLimit);
return fReply.SetTo(rpl);
}
void
Request::Reset()
{

View File

@ -25,12 +25,15 @@ public:
void Reset();
private:
status_t _TrySend();
status_t _SendUDP();
status_t _SendTCP();
RPC::Server* fServer;
RequestBuilder fBuilder;
ReplyInterpreter fReply;
static const int kRetryLimit = 5;
};