Bug 444328 - Add PRFileDescAutoLock and LockedPRFileDesc to automate and enforce calls to Get|ReleaseFD_Locked r=mcmanus

This commit is contained in:
Steve Workman 2013-12-16 16:46:09 -08:00
Родитель df8b1437b6
Коммит 81ddca1c40
2 изменённых файлов: 118 добавлений и 86 удалений

Просмотреть файл

@ -748,7 +748,7 @@ nsSocketTransport::nsSocketTransport()
, mResolving(false) , mResolving(false)
, mNetAddrIsSet(false) , mNetAddrIsSet(false)
, mLock("nsSocketTransport.mLock") , mLock("nsSocketTransport.mLock")
, mFD(nullptr) , mFD(MOZ_THIS_IN_INITIALIZER_LIST())
, mFDref(0) , mFDref(0)
, mFDconnected(false) , mFDconnected(false)
, mInput(MOZ_THIS_IN_INITIALIZER_LIST()) , mInput(MOZ_THIS_IN_INITIALIZER_LIST())
@ -891,7 +891,7 @@ nsSocketTransport::InitWithFilename(const char *filename)
nsresult nsresult
nsSocketTransport::InitWithConnectedSocket(PRFileDesc *fd, const NetAddr *addr) nsSocketTransport::InitWithConnectedSocket(PRFileDesc *fd, const NetAddr *addr)
{ {
NS_ASSERTION(!mFD, "already initialized"); NS_ASSERTION(!mFD.IsInitialized(), "already initialized");
char buf[kNetAddrMaxCStrBufSize]; char buf[kNetAddrMaxCStrBufSize];
NetAddrToString(addr, buf, sizeof(buf)); NetAddrToString(addr, buf, sizeof(buf));
@ -913,15 +913,19 @@ nsSocketTransport::InitWithConnectedSocket(PRFileDesc *fd, const NetAddr *addr)
mState = STATE_TRANSFERRING; mState = STATE_TRANSFERRING;
mNetAddrIsSet = true; mNetAddrIsSet = true;
{
MutexAutoLock lock(mLock);
mFD = fd; mFD = fd;
mFDref = 1; mFDref = 1;
mFDconnected = 1; mFDconnected = 1;
}
// make sure new socket is non-blocking // make sure new socket is non-blocking
PRSocketOptionData opt; PRSocketOptionData opt;
opt.option = PR_SockOpt_Nonblocking; opt.option = PR_SockOpt_Nonblocking;
opt.value.non_blocking = true; opt.value.non_blocking = true;
PR_SetSocketOption(mFD, &opt); PR_SetSocketOption(fd, &opt);
SOCKET_LOG(("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n", SOCKET_LOG(("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",
this, mHost.get(), mPort)); this, mHost.get(), mPort));
@ -1209,7 +1213,7 @@ nsSocketTransport::InitiateSocket()
// //
// if we already have a connected socket, then just attach and return. // if we already have a connected socket, then just attach and return.
// //
if (mFD) { if (mFD.IsInitialized()) {
rv = gSocketTransportService->AttachSocket(mFD, this); rv = gSocketTransportService->AttachSocket(mFD, this);
if (NS_SUCCEEDED(rv)) if (NS_SUCCEEDED(rv))
mAttached = true; mAttached = true;
@ -1533,7 +1537,7 @@ nsSocketTransport::OnSocketConnected()
// to trample over mFDref if mFD is already set. // to trample over mFDref if mFD is already set.
{ {
MutexAutoLock lock(mLock); MutexAutoLock lock(mLock);
NS_ASSERTION(mFD, "no socket"); NS_ASSERTION(mFD.IsInitialized(), "no socket");
NS_ASSERTION(mFDref == 1, "wrong socket ref count"); NS_ASSERTION(mFDref == 1, "wrong socket ref count");
mFDconnected = true; mFDconnected = true;
} }
@ -1546,11 +1550,13 @@ nsSocketTransport::OnSocketConnected()
PRFileDesc * PRFileDesc *
nsSocketTransport::GetFD_Locked() nsSocketTransport::GetFD_Locked()
{ {
mLock.AssertCurrentThreadOwns();
// mFD is not available to the streams while disconnected. // mFD is not available to the streams while disconnected.
if (!mFDconnected) if (!mFDconnected)
return nullptr; return nullptr;
if (mFD) if (mFD.IsInitialized())
mFDref++; mFDref++;
return mFD; return mFD;
@ -1587,6 +1593,8 @@ STS_PRCloseOnSocketTransport(PRFileDesc *fd)
void void
nsSocketTransport::ReleaseFD_Locked(PRFileDesc *fd) nsSocketTransport::ReleaseFD_Locked(PRFileDesc *fd)
{ {
mLock.AssertCurrentThreadOwns();
NS_ASSERTION(mFD == fd, "wrong fd"); NS_ASSERTION(mFD == fd, "wrong fd");
SOCKET_LOG(("JIMB: ReleaseFD_Locked: mFDref = %d\n", mFDref)); SOCKET_LOG(("JIMB: ReleaseFD_Locked: mFDref = %d\n", mFDref));
@ -1855,7 +1863,7 @@ nsSocketTransport::OnSocketDetached(PRFileDesc *fd)
nsCOMPtr<nsITransportEventSink> ourEventSink; nsCOMPtr<nsITransportEventSink> ourEventSink;
{ {
MutexAutoLock lock(mLock); MutexAutoLock lock(mLock);
if (mFD) { if (mFD.IsInitialized()) {
ReleaseFD_Locked(mFD); ReleaseFD_Locked(mFD);
// flag mFD as unusable; this prevents other consumers from // flag mFD as unusable; this prevents other consumers from
// acquiring a reference to mFD. // acquiring a reference to mFD.
@ -2074,13 +2082,9 @@ nsSocketTransport::IsAlive(bool *result)
{ {
*result = false; *result = false;
PRFileDesc* fd = nullptr; nsresult conditionWhileLocked = NS_OK;
{ PRFileDescAutoLock fd(this, &conditionWhileLocked);
MutexAutoLock lock(mLock); if (NS_FAILED(conditionWhileLocked) || !fd.IsInitialized()) {
if (NS_FAILED(mCondition))
return NS_OK;
fd = GetFD_Locked();
if (!fd)
return NS_OK; return NS_OK;
} }
@ -2092,10 +2096,6 @@ nsSocketTransport::IsAlive(bool *result)
if ((rval > 0) || (rval < 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR)) if ((rval > 0) || (rval < 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR))
*result = true; *result = true;
{
MutexAutoLock lock(mLock);
ReleaseFD_Locked(fd);
}
return NS_OK; return NS_OK;
} }
@ -2138,13 +2138,8 @@ nsSocketTransport::GetSelfAddr(NetAddr *addr)
// while holding mLock since those methods might re-enter // while holding mLock since those methods might re-enter
// socket transport code. // socket transport code.
PRFileDesc *fd; PRFileDescAutoLock fd(this);
{ if (!fd.IsInitialized()) {
MutexAutoLock lock(mLock);
fd = GetFD_Locked();
}
if (!fd) {
return NS_ERROR_NOT_CONNECTED; return NS_ERROR_NOT_CONNECTED;
} }
@ -2163,11 +2158,6 @@ nsSocketTransport::GetSelfAddr(NetAddr *addr)
(PR_GetSockName(fd, &prAddr) == PR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; (PR_GetSockName(fd, &prAddr) == PR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
PRNetAddrToNetAddr(&prAddr, addr); PRNetAddrToNetAddr(&prAddr, addr);
{
MutexAutoLock lock(mLock);
ReleaseFD_Locked(fd);
}
return rv; return rv;
} }
@ -2244,106 +2234,70 @@ nsSocketTransport::GetQoSBits(uint8_t *aQoSBits)
NS_IMETHODIMP NS_IMETHODIMP
nsSocketTransport::GetRecvBufferSize(uint32_t *aSize) nsSocketTransport::GetRecvBufferSize(uint32_t *aSize)
{ {
PRFileDesc *fd; PRFileDescAutoLock fd(this);
{ if (!fd.IsInitialized())
MutexAutoLock lock(mLock);
fd = GetFD_Locked();
}
if (!fd)
return NS_ERROR_NOT_CONNECTED; return NS_ERROR_NOT_CONNECTED;
nsresult rv = NS_OK; nsresult rv = NS_OK;
PRSocketOptionData opt; PRSocketOptionData opt;
opt.option = PR_SockOpt_RecvBufferSize; opt.option = PR_SockOpt_RecvBufferSize;
if (PR_GetSocketOption(mFD, &opt) == PR_SUCCESS) if (PR_GetSocketOption(fd, &opt) == PR_SUCCESS)
*aSize = opt.value.recv_buffer_size; *aSize = opt.value.recv_buffer_size;
else else
rv = NS_ERROR_FAILURE; rv = NS_ERROR_FAILURE;
{
MutexAutoLock lock(mLock);
ReleaseFD_Locked(fd);
}
return rv; return rv;
} }
NS_IMETHODIMP NS_IMETHODIMP
nsSocketTransport::GetSendBufferSize(uint32_t *aSize) nsSocketTransport::GetSendBufferSize(uint32_t *aSize)
{ {
PRFileDesc *fd; PRFileDescAutoLock fd(this);
{ if (!fd.IsInitialized())
MutexAutoLock lock(mLock);
fd = GetFD_Locked();
}
if (!fd)
return NS_ERROR_NOT_CONNECTED; return NS_ERROR_NOT_CONNECTED;
nsresult rv = NS_OK; nsresult rv = NS_OK;
PRSocketOptionData opt; PRSocketOptionData opt;
opt.option = PR_SockOpt_SendBufferSize; opt.option = PR_SockOpt_SendBufferSize;
if (PR_GetSocketOption(mFD, &opt) == PR_SUCCESS) if (PR_GetSocketOption(fd, &opt) == PR_SUCCESS)
*aSize = opt.value.send_buffer_size; *aSize = opt.value.send_buffer_size;
else else
rv = NS_ERROR_FAILURE; rv = NS_ERROR_FAILURE;
{
MutexAutoLock lock(mLock);
ReleaseFD_Locked(fd);
}
return rv; return rv;
} }
NS_IMETHODIMP NS_IMETHODIMP
nsSocketTransport::SetRecvBufferSize(uint32_t aSize) nsSocketTransport::SetRecvBufferSize(uint32_t aSize)
{ {
PRFileDesc *fd; PRFileDescAutoLock fd(this);
{ if (!fd.IsInitialized())
MutexAutoLock lock(mLock);
fd = GetFD_Locked();
}
if (!fd)
return NS_ERROR_NOT_CONNECTED; return NS_ERROR_NOT_CONNECTED;
nsresult rv = NS_OK; nsresult rv = NS_OK;
PRSocketOptionData opt; PRSocketOptionData opt;
opt.option = PR_SockOpt_RecvBufferSize; opt.option = PR_SockOpt_RecvBufferSize;
opt.value.recv_buffer_size = aSize; opt.value.recv_buffer_size = aSize;
if (PR_SetSocketOption(mFD, &opt) != PR_SUCCESS) if (PR_SetSocketOption(fd, &opt) != PR_SUCCESS)
rv = NS_ERROR_FAILURE; rv = NS_ERROR_FAILURE;
{
MutexAutoLock lock(mLock);
ReleaseFD_Locked(fd);
}
return rv; return rv;
} }
NS_IMETHODIMP NS_IMETHODIMP
nsSocketTransport::SetSendBufferSize(uint32_t aSize) nsSocketTransport::SetSendBufferSize(uint32_t aSize)
{ {
PRFileDesc *fd; PRFileDescAutoLock fd(this);
{ if (!fd.IsInitialized())
MutexAutoLock lock(mLock);
fd = GetFD_Locked();
}
if (!fd)
return NS_ERROR_NOT_CONNECTED; return NS_ERROR_NOT_CONNECTED;
nsresult rv = NS_OK; nsresult rv = NS_OK;
PRSocketOptionData opt; PRSocketOptionData opt;
opt.option = PR_SockOpt_SendBufferSize; opt.option = PR_SockOpt_SendBufferSize;
opt.value.send_buffer_size = aSize; opt.value.send_buffer_size = aSize;
if (PR_SetSocketOption(mFD, &opt) != PR_SUCCESS) if (PR_SetSocketOption(fd, &opt) != PR_SUCCESS)
rv = NS_ERROR_FAILURE; rv = NS_ERROR_FAILURE;
{
MutexAutoLock lock(mLock);
ReleaseFD_Locked(fd);
}
return rv; return rv;
} }

Просмотреть файл

@ -172,6 +172,84 @@ private:
STATE_TRANSFERRING STATE_TRANSFERRING
}; };
// Safer way to get and automatically release PRFileDesc objects.
class MOZ_STACK_CLASS PRFileDescAutoLock
{
public:
typedef mozilla::MutexAutoLock MutexAutoLock;
PRFileDescAutoLock(nsSocketTransport *aSocketTransport,
nsresult *aConditionWhileLocked = nullptr)
: mSocketTransport(aSocketTransport)
, mFd(nullptr)
{
MOZ_ASSERT(aSocketTransport);
MutexAutoLock lock(mSocketTransport->mLock);
if (aConditionWhileLocked) {
*aConditionWhileLocked = mSocketTransport->mCondition;
if (NS_FAILED(mSocketTransport->mCondition)) {
return;
}
}
mFd = mSocketTransport->GetFD_Locked();
NS_WARN_IF_FALSE(mFd, "PRFileDescAutoLock cannot get fd!");
}
~PRFileDescAutoLock() {
MutexAutoLock lock(mSocketTransport->mLock);
if (mFd) {
mSocketTransport->ReleaseFD_Locked(mFd);
}
}
bool IsInitialized() {
return mFd;
}
operator PRFileDesc*() {
return mFd;
}
private:
operator PRFileDescAutoLock*() { return nullptr; }
// Weak ptr to nsSocketTransport since this is a stack class only.
nsSocketTransport *mSocketTransport;
PRFileDesc *mFd;
};
friend class PRFileDescAutoLock;
class LockedPRFileDesc
{
public:
LockedPRFileDesc(nsSocketTransport *aSocketTransport)
: mSocketTransport(aSocketTransport)
, mFd(nullptr)
{
MOZ_ASSERT(aSocketTransport);
}
~LockedPRFileDesc() {}
bool IsInitialized() {
return mFd;
}
LockedPRFileDesc& operator=(PRFileDesc *aFd) {
mSocketTransport->mLock.AssertCurrentThreadOwns();
mFd = aFd;
return *this;
}
operator PRFileDesc*() {
if (mSocketTransport->mAttached) {
mSocketTransport->mLock.AssertCurrentThreadOwns();
}
return mFd;
}
bool operator==(PRFileDesc *aFd) {
mSocketTransport->mLock.AssertCurrentThreadOwns();
return mFd == aFd;
}
private:
operator LockedPRFileDesc*() { return nullptr; }
// Weak ptr to nsSocketTransport since it owns this class.
nsSocketTransport *mSocketTransport;
PRFileDesc *mFd;
};
friend class LockedPRFileDesc;
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// these members are "set" at initialization time and are never modified // these members are "set" at initialization time and are never modified
// afterwards. this allows them to be safely accessed from any thread. // afterwards. this allows them to be safely accessed from any thread.
@ -242,8 +320,8 @@ private:
// socket input/output objects. these may be accessed on any thread with // socket input/output objects. these may be accessed on any thread with
// the exception of some specific methods (XXX). // the exception of some specific methods (XXX).
Mutex mLock; // protects members in this section Mutex mLock; // protects members in this section.
PRFileDesc *mFD; LockedPRFileDesc mFD;
nsrefcnt mFDref; // mFD is closed when mFDref goes to zero. nsrefcnt mFDref; // mFD is closed when mFDref goes to zero.
bool mFDconnected; // mFD is available to consumer when TRUE. bool mFDconnected; // mFD is available to consumer when TRUE.