Bug 551049 (was bug 532208) part 1 - Make PBrowserStream destructor uni-directional by delivering all data asynchronously and doing two-phase (acknowledged) destruction, r=bent

This commit is contained in:
Benjamin Smedberg 2010-02-23 16:45:00 -05:00
parent 1fc1bc4a5a
commit 39df629db6
7 changed files with 245 additions and 92 deletions

View File

@ -54,6 +54,7 @@ BrowserStreamChild::BrowserStreamChild(PluginInstanceChild* instance,
uint16_t* stype)
: mInstance(instance)
, mClosed(false)
, mState(CONSTRUCTING)
, mURL(url)
, mHeaders(headers)
{
@ -91,49 +92,43 @@ BrowserStreamChild::StreamConstructed(
rv = mInstance->mPluginIface->newstream(
&mInstance->mData, const_cast<char*>(NullableStringGet(mimeType)),
&mStream, seekable, stype);
if (rv != NPERR_NO_ERROR)
if (rv != NPERR_NO_ERROR) {
mClosed = true;
mState = DELETING;
}
else {
mState = ALIVE;
}
return rv;
}
bool
BrowserStreamChild::AnswerNPP_WriteReady(const int32_t& newlength,
int32_t *size)
BrowserStreamChild::RecvWrite(const int32_t& offset,
const Buffer& data,
const uint32_t& newlength)
{
PLUGIN_LOG_DEBUG_FUNCTION;
AssertPluginThread();
if (mClosed) {
*size = 0;
if (ALIVE != mState)
NS_RUNTIMEABORT("Unexpected state: received data after NPP_DestroyStream?");
if (mClosed)
return true;
}
mStream.end = newlength;
*size = mInstance->mPluginIface->writeready(&mInstance->mData, &mStream);
return true;
}
NS_ASSERTION(data.Length() > 0, "Empty data");
bool
BrowserStreamChild::AnswerNPP_Write(const int32_t& offset,
const Buffer& data,
int32_t* consumed)
{
PLUGIN_LOG_DEBUG(("%s (offset=%i, data.length=%i)", FULLFUNCTION,
offset, data.Length()));
PendingData* newdata = mPendingData.AppendElement();
newdata->offset = offset;
newdata->data = data;
newdata->curpos = 0;
AssertPluginThread();
DeliverData();
if (mClosed) {
*consumed = -1;
return true;
}
*consumed = mInstance->mPluginIface->write(&mInstance->mData, &mStream,
offset, data.Length(),
const_cast<char*>(data.get()));
return true;
}
@ -144,6 +139,9 @@ BrowserStreamChild::AnswerNPP_StreamAsFile(const nsCString& fname)
AssertPluginThread();
if (ALIVE != mState)
NS_RUNTIMEABORT("Unexpected state: received file after NPP_DestroyStream?");
if (mClosed)
return true;
@ -153,12 +151,32 @@ BrowserStreamChild::AnswerNPP_StreamAsFile(const nsCString& fname)
}
bool
BrowserStreamChild::Answer__delete__(const NPError& reason,
const bool& artificial)
BrowserStreamChild::RecvNPP_DestroyStream(const NPReason& reason)
{
PLUGIN_LOG_DEBUG_METHOD;
if (ALIVE != mState)
NS_RUNTIMEABORT("Unexpected state: recevied NPP_DestroyStream twice?");
mState = DYING;
mClosed = true;
mInstance->mPluginIface->destroystream(&mInstance->mData, &mStream, reason);
SendStreamDestroyed();
mState = DELETING;
return true;
}
bool
BrowserStreamChild::Recv__delete__()
{
AssertPluginThread();
if (!artificial)
NPP_DestroyStream(reason);
if (DELETING != mState)
NS_RUNTIMEABORT("Bad state, not DELETING");
return true;
}
@ -169,6 +187,9 @@ BrowserStreamChild::NPN_RequestRead(NPByteRange* aRangeList)
AssertPluginThread();
if (ALIVE != mState || mClosed)
return NPERR_GENERIC_ERROR;
IPCByteRanges ranges;
for (; aRangeList; aRangeList = aRangeList->next) {
IPCByteRange br = {aRangeList->offset, aRangeList->length};
@ -181,17 +202,60 @@ BrowserStreamChild::NPN_RequestRead(NPByteRange* aRangeList)
}
void
BrowserStreamChild::NPP_DestroyStream(NPError reason)
BrowserStreamChild::DeliverData()
{
PLUGIN_LOG_DEBUG(("%s (reason=%i)", FULLFUNCTION, reason));
AssertPluginThread();
if (mClosed)
if (ALIVE != mState || mClosed) {
ClearSuspendedTimer();
return;
}
mInstance->mPluginIface->destroystream(&mInstance->mData, &mStream, reason);
mClosed = true;
while (mPendingData.Length()) {
PendingData& cur = mPendingData[0];
while (cur.curpos < cur.data.Length()) {
int32_t r = mInstance->mPluginIface->writeready(&mInstance->mData, &mStream);
if (r == 0) {
SetSuspendedTimer();
return;
}
r = mInstance->mPluginIface->write(
&mInstance->mData, &mStream,
cur.offset + cur.curpos, // offset
cur.data.Length() - cur.curpos, // length
const_cast<char*>(cur.data.BeginReading() + cur.curpos));
if (r == 0) {
SetSuspendedTimer();
return;
}
if (r < 0) { // error condition
if (ALIVE == mState && !mClosed) { // re-check
mClosed = true;
SendNPN_DestroyStream(NPRES_NETWORK_ERR);
}
ClearSuspendedTimer();
return;
}
cur.curpos += r;
}
mPendingData.RemoveElementAt(0);
}
ClearSuspendedTimer();
}
void
BrowserStreamChild::SetSuspendedTimer()
{
if (mSuspendedTimer.IsRunning())
return;
mSuspendedTimer.Start(
base::TimeDelta::FromMilliseconds(100),
this, &BrowserStreamChild::DeliverData);
}
void
BrowserStreamChild::ClearSuspendedTimer()
{
mSuspendedTimer.Stop();
}
} /* namespace plugins */

View File

@ -74,16 +74,12 @@ public:
const bool& seekable,
uint16_t* stype);
virtual bool AnswerNPP_WriteReady(const int32_t& newlength,
int32_t *size);
virtual bool AnswerNPP_Write(const int32_t& offset,
const Buffer& data,
int32_t* consumed);
virtual bool RecvWrite(const int32_t& offset,
const Buffer& data,
const uint32_t& newsize);
virtual bool AnswerNPP_StreamAsFile(const nsCString& fname);
virtual bool Answer__delete__(const NPError& reason,
const bool& artificial);
virtual bool RecvNPP_DestroyStream(const NPReason& reason);
virtual bool Recv__delete__();
void EnsureCorrectInstance(PluginInstanceChild* i)
{
@ -97,14 +93,37 @@ public:
}
NPError NPN_RequestRead(NPByteRange* aRangeList);
void NPP_DestroyStream(NPError reason);
private:
/**
* Deliver the data currently in mPending, scheduling
* or cancelling the suspended timer as needed.
*/
void DeliverData();
void SetSuspendedTimer();
void ClearSuspendedTimer();
PluginInstanceChild* mInstance;
NPStream mStream;
bool mClosed;
enum {
CONSTRUCTING,
ALIVE,
DYING,
DELETING
} mState;
nsCString mURL;
nsCString mHeaders;
struct PendingData
{
int32_t offset;
Buffer data;
int32_t curpos;
};
nsTArray<PendingData> mPendingData;
base::RepeatingTimer<BrowserStreamChild> mSuspendedTimer;
};
} // namespace plugins

View File

@ -3,6 +3,10 @@
#include "BrowserStreamParent.h"
#include "PluginInstanceParent.h"
// How much data are we willing to send across the wire
// in one chunk?
static const int32_t kSendDataChunk = 0x1000;
namespace mozilla {
namespace plugins {
@ -10,6 +14,7 @@ BrowserStreamParent::BrowserStreamParent(PluginInstanceParent* npp,
NPStream* stream)
: mNPP(npp)
, mStream(stream)
, mState(ALIVE)
{
mStream->pdata = static_cast<AStream*>(this);
}
@ -24,6 +29,19 @@ BrowserStreamParent::AnswerNPN_RequestRead(const IPCByteRanges& ranges,
{
PLUGIN_LOG_DEBUG_FUNCTION;
switch (mState) {
case ALIVE:
break;
case DYING:
*result = NPERR_GENERIC_ERROR;
return true;
default:
NS_ERROR("Unexpected state");
return false;
}
if (!mStream)
return false;
@ -43,24 +61,49 @@ BrowserStreamParent::AnswerNPN_RequestRead(const IPCByteRanges& ranges,
}
bool
BrowserStreamParent::Answer__delete__(const NPError& reason,
const bool& artificial)
BrowserStreamParent::RecvNPN_DestroyStream(const NPReason& reason)
{
if (!artificial)
NPN_DestroyStream(reason);
switch (mState) {
case ALIVE:
break;
case DYING:
return true;
default:
NS_ERROR("Unexpected state");
return false;
};
mNPP->mNPNIface->destroystream(mNPP->mNPP, mStream, reason);
return true;
}
void
BrowserStreamParent::NPP_DestroyStream(NPReason reason)
{
NS_ASSERTION(ALIVE == mState, "NPP_DestroyStream called twice?");
mState = DYING;
SendNPP_DestroyStream(reason);
}
bool
BrowserStreamParent::RecvStreamDestroyed()
{
if (DYING != mState) {
NS_ERROR("Unexpected state");
return false;
}
mState = DELETING;
Send__delete__(this);
return true;
}
int32_t
BrowserStreamParent::WriteReady()
{
PLUGIN_LOG_DEBUG_FUNCTION;
int32_t result;
if (!CallNPP_WriteReady(mStream->end, &result))
return -1;
return result;
return kSendDataChunk;
}
int32_t
@ -70,13 +113,17 @@ BrowserStreamParent::Write(int32_t offset,
{
PLUGIN_LOG_DEBUG_FUNCTION;
int32_t result;
if (!CallNPP_Write(offset,
nsCString(static_cast<char*>(buffer), len),
&result))
return -1;
NS_ASSERTION(ALIVE == mState, "Sending data after NPP_DestroyStream?");
NS_ASSERTION(len > 0, "Non-positive length to NPP_Write");
return result;
if (len > kSendDataChunk)
len = kSendDataChunk;
SendWrite(offset,
nsCString(static_cast<char*>(buffer), len),
mStream->end);
return len;
}
void
@ -84,15 +131,11 @@ BrowserStreamParent::StreamAsFile(const char* fname)
{
PLUGIN_LOG_DEBUG_FUNCTION;
NS_ASSERTION(ALIVE == mState,
"Calling streamasfile after NPP_DestroyStream?");
CallNPP_StreamAsFile(nsCString(fname));
}
NPError
BrowserStreamParent::NPN_DestroyStream(NPReason reason)
{
PLUGIN_LOG_DEBUG_FUNCTION;
return mNPP->mNPNIface->destroystream(mNPP->mNPP, mStream, reason);
return;
}
} // namespace plugins

View File

@ -61,18 +61,27 @@ public:
virtual bool AnswerNPN_RequestRead(const IPCByteRanges& ranges,
NPError* result);
virtual bool
Answer__delete__(const NPError& reason, const bool& artificial);
virtual bool RecvNPN_DestroyStream(const NPReason& reason);
virtual bool RecvStreamDestroyed();
int32_t WriteReady();
int32_t Write(int32_t offset, int32_t len, void* buffer);
void StreamAsFile(const char* fname);
void NPP_DestroyStream(NPReason reason);
private:
NPError NPN_DestroyStream(NPError reason);
using PBrowserStreamParent::SendNPP_DestroyStream;
PluginInstanceParent* mNPP;
NPStream* mStream;
enum {
ALIVE,
DYING,
DELETING
} mState;
};
} // namespace plugins

View File

@ -57,26 +57,44 @@ rpc protocol PBrowserStream
manager PPluginInstance;
child:
rpc NPP_WriteReady(int32_t newlength)
returns (int32_t size);
rpc NPP_Write(int32_t offset,
Buffer data)
returns (int32_t consumed);
async Write(int32_t offset, Buffer data,
uint32_t newlength);
rpc NPP_StreamAsFile(nsCString fname);
/**
* NPP_DestroyStream may race with other messages: the child acknowledges
* the message with StreamDestroyed before this actor is deleted.
*/
async NPP_DestroyStream(NPReason reason);
async __delete__();
parent:
rpc NPN_RequestRead(IPCByteRanges ranges)
returns (NPError result);
async NPN_DestroyStream(NPReason reason);
async StreamDestroyed();
both:
/**
* ~PBrowserStream is for both NPN_DestroyStream and NPP_DestroyStream.
* @param artificial True when the stream is closed as a by-product of
* some other call (such as a failure in NPP_Write).
*/
rpc __delete__(NPReason reason, bool artificial);
/*
TODO: turn on state machine.
// need configurable start state: if the constructor
// returns an error in result, start state should
// be DELETING.
start state ALIVE:
send Write goto ALIVE;
call NPP_StreamAsFile goto ALIVE;
send NPP_DestroyStream goto ALIVE;
answer NPN_RequestRead goto ALIVE;
recv NPN_DestroyStream goto DYING;
state DYING:
answer NPN_RequestRead goto DYING;
recv NPN_DestroyStream goto DYING;
recv StreamDestroyed goto DELETING;
state DELETING:
send __delete__;
*/
};
} // namespace plugins

View File

@ -612,7 +612,7 @@ PluginInstanceParent::NPP_NewStream(NPMIMEType type, NPStream* stream,
return NPERR_GENERIC_ERROR;
if (NPERR_NO_ERROR != err)
PBrowserStreamParent::Call__delete__(bs, NPERR_GENERIC_ERROR, true);
PBrowserStreamParent::Send__delete__(bs);
return err;
}
@ -630,7 +630,7 @@ PluginInstanceParent::NPP_DestroyStream(NPStream* stream, NPReason reason)
if (sp->mNPP != this)
NS_RUNTIMEABORT("Mismatched plugin data");
PBrowserStreamParent::Call__delete__(sp, reason, false);
sp->NPP_DestroyStream(reason);
return NPERR_NO_ERROR;
}
else {

View File

@ -898,7 +898,7 @@ _destroystream(NPP aNPP,
if (s->IsBrowserStream()) {
BrowserStreamChild* bs = static_cast<BrowserStreamChild*>(s);
bs->EnsureCorrectInstance(p);
PBrowserStreamChild::Call__delete__(bs, aReason, false);
bs->SendNPN_DestroyStream(aReason);
}
else {
PluginStreamChild* ps = static_cast<PluginStreamChild*>(s);