bug 517923: support serializing ns*Strings that represent NULL, use this mechanism in PluginInstanceParent/PluginModuleChild. also add basic crash-handling to *Channel code and some NS_OVERRIDE annotations.
This commit is contained in:
@@ -157,7 +157,9 @@ PluginInstanceParent::AnswerNPN_GetURL(const nsCString& url,
|
|||||||
const nsCString& target,
|
const nsCString& target,
|
||||||
NPError* result)
|
NPError* result)
|
||||||
{
|
{
|
||||||
*result = mNPNIface->geturl(mNPP, url.get(), target.get());
|
*result = mNPNIface->geturl(mNPP,
|
||||||
|
NullableStringGet(url),
|
||||||
|
NullableStringGet(target));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,11 +186,15 @@ PluginInstanceParent::PStreamNotifyConstructor(const nsCString& url,
|
|||||||
StreamNotifyParent* notifyData = new StreamNotifyParent();
|
StreamNotifyParent* notifyData = new StreamNotifyParent();
|
||||||
|
|
||||||
if (!post) {
|
if (!post) {
|
||||||
*result = mNPNIface->geturlnotify(mNPP, url.get(), target.get(),
|
*result = mNPNIface->geturlnotify(mNPP,
|
||||||
|
NullableStringGet(url),
|
||||||
|
NullableStringGet(target),
|
||||||
notifyData);
|
notifyData);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
*result = mNPNIface->posturlnotify(mNPP, url.get(), target.get(),
|
*result = mNPNIface->posturlnotify(mNPP,
|
||||||
|
NullableStringGet(url),
|
||||||
|
NullableStringGet(target),
|
||||||
buffer.Length(), buffer.get(),
|
buffer.Length(), buffer.get(),
|
||||||
file, notifyData);
|
file, notifyData);
|
||||||
}
|
}
|
||||||
@@ -266,6 +272,7 @@ PluginInstanceParent::NPP_NewStream(NPMIMEType type, NPStream* stream,
|
|||||||
BrowserStreamParent* bs = new BrowserStreamParent(this, stream);
|
BrowserStreamParent* bs = new BrowserStreamParent(this, stream);
|
||||||
|
|
||||||
NPError err;
|
NPError err;
|
||||||
|
// TODO are any of these strings nullable?
|
||||||
if (!CallPBrowserStreamConstructor(bs,
|
if (!CallPBrowserStreamConstructor(bs,
|
||||||
nsCString(stream->url),
|
nsCString(stream->url),
|
||||||
stream->end,
|
stream->end,
|
||||||
|
|||||||
@@ -84,6 +84,29 @@ typedef nsCString Buffer;
|
|||||||
|
|
||||||
} /* namespace mozilla */
|
} /* namespace mozilla */
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// in NPAPI, char* == NULL is sometimes meaningful. the following is
|
||||||
|
// helper code for dealing with nullable nsCString's
|
||||||
|
nsCString
|
||||||
|
NullableString(const char* aString)
|
||||||
|
{
|
||||||
|
if (!aString) {
|
||||||
|
nsCString str;
|
||||||
|
str.SetIsVoid(PR_TRUE);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
return nsCString(aString);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace <anon>
|
||||||
|
|
||||||
|
// TODO is there any safe way for this to be a function?
|
||||||
|
#define NullableStringGet(__string) \
|
||||||
|
( __string.IsVoid() ? NULL : __string.get())
|
||||||
|
|
||||||
|
|
||||||
namespace IPC {
|
namespace IPC {
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ namespace {
|
|||||||
PluginModuleChild* gInstance = nsnull;
|
PluginModuleChild* gInstance = nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PluginModuleChild::PluginModuleChild() :
|
PluginModuleChild::PluginModuleChild() :
|
||||||
mLibrary(0),
|
mLibrary(0),
|
||||||
mInitializeFunc(0),
|
mInitializeFunc(0),
|
||||||
@@ -451,11 +452,11 @@ _geturlnotify(NPP aNPP,
|
|||||||
{
|
{
|
||||||
_MOZ_LOG(__FUNCTION__);
|
_MOZ_LOG(__FUNCTION__);
|
||||||
|
|
||||||
const nsDependentCString url(aRelativeURL);
|
nsCString url = NullableString(aRelativeURL);
|
||||||
NPError err;
|
NPError err;
|
||||||
InstCast(aNPP)->CallPStreamNotifyConstructor(
|
InstCast(aNPP)->CallPStreamNotifyConstructor(
|
||||||
new StreamNotifyChild(url, aNotifyData),
|
new StreamNotifyChild(url, aNotifyData),
|
||||||
url, nsDependentCString(aTarget), false, nsCString(), false, &err);
|
url, NullableString(aTarget), false, nsCString(), false, &err);
|
||||||
// TODO: what if this fails?
|
// TODO: what if this fails?
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -486,8 +487,8 @@ _geturl(NPP aNPP,
|
|||||||
{
|
{
|
||||||
_MOZ_LOG(__FUNCTION__);
|
_MOZ_LOG(__FUNCTION__);
|
||||||
NPError err;
|
NPError err;
|
||||||
InstCast(aNPP)->CallNPN_GetURL(nsDependentCString(aRelativeURL),
|
InstCast(aNPP)->CallNPN_GetURL(NullableString(aRelativeURL),
|
||||||
nsDependentCString(aTarget), &err);
|
NullableString(aTarget), &err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,11 +503,12 @@ _posturlnotify(NPP aNPP,
|
|||||||
{
|
{
|
||||||
_MOZ_LOG(__FUNCTION__);
|
_MOZ_LOG(__FUNCTION__);
|
||||||
|
|
||||||
const nsDependentCString url(aRelativeURL);
|
nsCString url = NullableString(aRelativeURL);
|
||||||
NPError err;
|
NPError err;
|
||||||
|
// FIXME what should happen when |aBuffer| is null?
|
||||||
InstCast(aNPP)->CallPStreamNotifyConstructor(
|
InstCast(aNPP)->CallPStreamNotifyConstructor(
|
||||||
new StreamNotifyChild(url, aNotifyData),
|
new StreamNotifyChild(url, aNotifyData),
|
||||||
url, nsDependentCString(aTarget), true,
|
url, NullableString(aTarget), true,
|
||||||
nsDependentCString(aBuffer, aLength), aIsFile, &err);
|
nsDependentCString(aBuffer, aLength), aIsFile, &err);
|
||||||
// TODO: what if this fails?
|
// TODO: what if this fails?
|
||||||
return err;
|
return err;
|
||||||
@@ -522,8 +524,9 @@ _posturl(NPP aNPP,
|
|||||||
{
|
{
|
||||||
_MOZ_LOG(__FUNCTION__);
|
_MOZ_LOG(__FUNCTION__);
|
||||||
NPError err;
|
NPError err;
|
||||||
InstCast(aNPP)->CallNPN_PostURL(nsDependentCString(aRelativeURL),
|
// FIXME what should happen when |aBuffer| is null?
|
||||||
nsDependentCString(aTarget),
|
InstCast(aNPP)->CallNPN_PostURL(NullableString(aRelativeURL),
|
||||||
|
NullableString(aTarget),
|
||||||
nsDependentCString(aBuffer, aLength),
|
nsDependentCString(aBuffer, aLength),
|
||||||
aIsFile, &err);
|
aIsFile, &err);
|
||||||
return err;
|
return err;
|
||||||
@@ -1026,8 +1029,8 @@ PluginModuleChild::AnswerPPluginInstanceConstructor(PPluginInstanceChild* aActor
|
|||||||
|
|
||||||
printf ("(plugin args: ");
|
printf ("(plugin args: ");
|
||||||
for (int i = 0; i < argc; ++i) {
|
for (int i = 0; i < argc; ++i) {
|
||||||
argn[i] = const_cast<char*>(aNames[i].get());
|
argn[i] = const_cast<char*>(NullableStringGet(aNames[i]));
|
||||||
argv[i] = const_cast<char*>(aValues[i].get());
|
argv[i] = const_cast<char*>(NullableStringGet(aValues[i]));
|
||||||
printf("%s=%s, ", argn[i], argv[i]);
|
printf("%s=%s, ", argn[i], argv[i]);
|
||||||
}
|
}
|
||||||
printf(")\n");
|
printf(")\n");
|
||||||
@@ -1035,7 +1038,7 @@ PluginModuleChild::AnswerPPluginInstanceConstructor(PPluginInstanceChild* aActor
|
|||||||
NPP npp = childInstance->GetNPP();
|
NPP npp = childInstance->GetNPP();
|
||||||
|
|
||||||
// FIXME/cjones: use SAFE_CALL stuff
|
// FIXME/cjones: use SAFE_CALL stuff
|
||||||
*rv = mFunctions.newp((char*)aMimeType.get(),
|
*rv = mFunctions.newp((char*)NullableStringGet(aMimeType),
|
||||||
npp,
|
npp,
|
||||||
aMode,
|
aMode,
|
||||||
argc,
|
argc,
|
||||||
|
|||||||
@@ -112,9 +112,11 @@ AsyncChannel::Close()
|
|||||||
bool
|
bool
|
||||||
AsyncChannel::Send(Message* msg)
|
AsyncChannel::Send(Message* msg)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(ChannelConnected == mChannelState,
|
NS_ABORT_IF_FALSE(MSG_ROUTING_NONE != msg->routing_id(), "need a route");
|
||||||
"trying to Send() to a channel not yet open");
|
|
||||||
NS_PRECONDITION(MSG_ROUTING_NONE != msg->routing_id(), "need a route");
|
if (!Connected())
|
||||||
|
// trying to Send() to a closed or error'd channel
|
||||||
|
return false;
|
||||||
|
|
||||||
mIOLoop->PostTask(FROM_HERE,
|
mIOLoop->PostTask(FROM_HERE,
|
||||||
NewRunnableMethod(this, &AsyncChannel::OnSend, msg));
|
NewRunnableMethod(this, &AsyncChannel::OnSend, msg));
|
||||||
|
|||||||
@@ -120,11 +120,15 @@ public:
|
|||||||
bool Send(Message* msg);
|
bool Send(Message* msg);
|
||||||
|
|
||||||
// Implement the IPC::Channel::Listener interface
|
// Implement the IPC::Channel::Listener interface
|
||||||
virtual void OnMessageReceived(const Message& msg);
|
NS_OVERRIDE virtual void OnMessageReceived(const Message& msg);
|
||||||
virtual void OnChannelConnected(int32 peer_pid);
|
NS_OVERRIDE virtual void OnChannelConnected(int32 peer_pid);
|
||||||
virtual void OnChannelError();
|
NS_OVERRIDE virtual void OnChannelError();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
bool Connected() {
|
||||||
|
return ChannelConnected == mChannelState;
|
||||||
|
}
|
||||||
|
|
||||||
// Additional methods that execute on the worker thread
|
// Additional methods that execute on the worker thread
|
||||||
void OnDispatchMessage(const Message& aMsg);
|
void OnDispatchMessage(const Message& aMsg);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||||
/* ***** BEGIN LICENSE BLOCK *****
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
*
|
*
|
||||||
@@ -56,6 +58,13 @@ struct ParamTraits<nsACString>
|
|||||||
|
|
||||||
static void Write(Message* aMsg, const paramType& aParam)
|
static void Write(Message* aMsg, const paramType& aParam)
|
||||||
{
|
{
|
||||||
|
bool isVoid = aParam.IsVoid();
|
||||||
|
aMsg->WriteBool(isVoid);
|
||||||
|
|
||||||
|
if (isVoid)
|
||||||
|
// represents a NULL pointer
|
||||||
|
return;
|
||||||
|
|
||||||
PRUint32 length = aParam.Length();
|
PRUint32 length = aParam.Length();
|
||||||
WriteParam(aMsg, length);
|
WriteParam(aMsg, length);
|
||||||
aMsg->WriteBytes(aParam.BeginReading(), length);
|
aMsg->WriteBytes(aParam.BeginReading(), length);
|
||||||
@@ -63,6 +72,15 @@ struct ParamTraits<nsACString>
|
|||||||
|
|
||||||
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
|
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
|
||||||
{
|
{
|
||||||
|
bool isVoid;
|
||||||
|
if (!aMsg->ReadBool(aIter, &isVoid))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (isVoid) {
|
||||||
|
aResult->SetIsVoid(PR_TRUE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
PRUint32 length;
|
PRUint32 length;
|
||||||
if (ReadParam(aMsg, aIter, &length)) {
|
if (ReadParam(aMsg, aIter, &length)) {
|
||||||
const char* buf;
|
const char* buf;
|
||||||
@@ -76,6 +94,9 @@ struct ParamTraits<nsACString>
|
|||||||
|
|
||||||
static void Log(const paramType& aParam, std::wstring* aLog)
|
static void Log(const paramType& aParam, std::wstring* aLog)
|
||||||
{
|
{
|
||||||
|
if (aParam.IsVoid())
|
||||||
|
aLog->append(L"(NULL)");
|
||||||
|
else
|
||||||
aLog->append(UTF8ToWide(aParam.BeginReading()));
|
aLog->append(UTF8ToWide(aParam.BeginReading()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -87,6 +108,13 @@ struct ParamTraits<nsAString>
|
|||||||
|
|
||||||
static void Write(Message* aMsg, const paramType& aParam)
|
static void Write(Message* aMsg, const paramType& aParam)
|
||||||
{
|
{
|
||||||
|
bool isVoid = aParam.IsVoid();
|
||||||
|
aMsg->WriteBool(isVoid);
|
||||||
|
|
||||||
|
if (isVoid)
|
||||||
|
// represents a NULL pointer
|
||||||
|
return;
|
||||||
|
|
||||||
PRUint32 length = aParam.Length();
|
PRUint32 length = aParam.Length();
|
||||||
WriteParam(aMsg, length);
|
WriteParam(aMsg, length);
|
||||||
aMsg->WriteBytes(aParam.BeginReading(), length * sizeof(PRUnichar));
|
aMsg->WriteBytes(aParam.BeginReading(), length * sizeof(PRUnichar));
|
||||||
@@ -94,6 +122,15 @@ struct ParamTraits<nsAString>
|
|||||||
|
|
||||||
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
|
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
|
||||||
{
|
{
|
||||||
|
bool isVoid;
|
||||||
|
if (!aMsg->ReadBool(aIter, &isVoid))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (isVoid) {
|
||||||
|
aResult->SetIsVoid(PR_TRUE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
PRUint32 length;
|
PRUint32 length;
|
||||||
if (ReadParam(aMsg, aIter, &length)) {
|
if (ReadParam(aMsg, aIter, &length)) {
|
||||||
const PRUnichar* buf;
|
const PRUnichar* buf;
|
||||||
@@ -108,6 +145,9 @@ struct ParamTraits<nsAString>
|
|||||||
|
|
||||||
static void Log(const paramType& aParam, std::wstring* aLog)
|
static void Log(const paramType& aParam, std::wstring* aLog)
|
||||||
{
|
{
|
||||||
|
if (aParam.IsVoid())
|
||||||
|
aLog->append(L"(NULL)");
|
||||||
|
else {
|
||||||
#ifdef WCHAR_T_IS_UTF16
|
#ifdef WCHAR_T_IS_UTF16
|
||||||
aLog->append(reinterpret_cast<const wchar_t*>(aParam.BeginReading()));
|
aLog->append(reinterpret_cast<const wchar_t*>(aParam.BeginReading()));
|
||||||
#else
|
#else
|
||||||
@@ -117,6 +157,7 @@ struct ParamTraits<nsAString>
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|||||||
@@ -60,11 +60,13 @@ RPCChannel::Call(Message* msg, Message* reply)
|
|||||||
{
|
{
|
||||||
NS_ABORT_IF_FALSE(!ProcessingSyncMessage(),
|
NS_ABORT_IF_FALSE(!ProcessingSyncMessage(),
|
||||||
"violation of sync handler invariant");
|
"violation of sync handler invariant");
|
||||||
NS_ASSERTION(ChannelConnected == mChannelState,
|
NS_ABORT_IF_FALSE(msg->is_rpc(),
|
||||||
"trying to Send() to a channel not yet open");
|
|
||||||
NS_PRECONDITION(msg->is_rpc(),
|
|
||||||
"can only Call() RPC messages here");
|
"can only Call() RPC messages here");
|
||||||
|
|
||||||
|
if (!Connected())
|
||||||
|
// trying to Send() to a closed or error'd channel
|
||||||
|
return false;
|
||||||
|
|
||||||
MutexAutoLock lock(mMutex);
|
MutexAutoLock lock(mMutex);
|
||||||
|
|
||||||
msg->set_rpc_remote_stack_depth(mRemoteStackDepth);
|
msg->set_rpc_remote_stack_depth(mRemoteStackDepth);
|
||||||
@@ -77,10 +79,14 @@ RPCChannel::Call(Message* msg, Message* reply)
|
|||||||
while (1) {
|
while (1) {
|
||||||
// here we're waiting for something to happen. see long
|
// here we're waiting for something to happen. see long
|
||||||
// comment about the queue in RPCChannel.h
|
// comment about the queue in RPCChannel.h
|
||||||
while (mPending.empty()) {
|
while (Connected() && mPending.empty()) {
|
||||||
mCvar.Wait();
|
mCvar.Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Connected())
|
||||||
|
// FIXME more sophisticated error handling
|
||||||
|
return false;
|
||||||
|
|
||||||
Message recvd = mPending.front();
|
Message recvd = mPending.front();
|
||||||
mPending.pop();
|
mPending.pop();
|
||||||
|
|
||||||
@@ -363,5 +369,25 @@ RPCChannel::OnMessageReceived(const Message& msg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RPCChannel::OnChannelError()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
|
||||||
|
mChannelState = ChannelError;
|
||||||
|
|
||||||
|
if (AwaitingSyncReply()
|
||||||
|
|| 0 < StackDepth()) {
|
||||||
|
mCvar.Notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip SyncChannel::OnError(); we subsume its duties
|
||||||
|
|
||||||
|
return AsyncChannel::OnChannelError();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|||||||
@@ -80,7 +80,8 @@ public:
|
|||||||
bool Call(Message* msg, Message* reply);
|
bool Call(Message* msg, Message* reply);
|
||||||
|
|
||||||
// Override the SyncChannel handler so we can dispatch RPC messages
|
// Override the SyncChannel handler so we can dispatch RPC messages
|
||||||
virtual void OnMessageReceived(const Message& msg);
|
NS_OVERRIDE virtual void OnMessageReceived(const Message& msg);
|
||||||
|
NS_OVERRIDE virtual void OnChannelError();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Only exists because we can't schedule SyncChannel::OnDispatchMessage
|
// Only exists because we can't schedule SyncChannel::OnDispatchMessage
|
||||||
|
|||||||
@@ -59,18 +59,26 @@ SyncChannel::Send(Message* msg, Message* reply)
|
|||||||
{
|
{
|
||||||
NS_ABORT_IF_FALSE(!ProcessingSyncMessage(),
|
NS_ABORT_IF_FALSE(!ProcessingSyncMessage(),
|
||||||
"violation of sync handler invariant");
|
"violation of sync handler invariant");
|
||||||
NS_ASSERTION(ChannelConnected == mChannelState,
|
NS_ABORT_IF_FALSE(msg->is_sync(), "can only Send() sync messages here");
|
||||||
"trying to Send() to a channel not yet open");
|
|
||||||
NS_PRECONDITION(msg->is_sync(), "can only Send() sync messages here");
|
if (!Connected())
|
||||||
|
// trying to Send() to a closed or error'd channel
|
||||||
|
return false;
|
||||||
|
|
||||||
MutexAutoLock lock(mMutex);
|
MutexAutoLock lock(mMutex);
|
||||||
|
|
||||||
mPendingReply = msg->type() + 1;
|
mPendingReply = msg->type() + 1;
|
||||||
/*assert*/AsyncChannel::Send(msg);
|
if (!AsyncChannel::Send(msg))
|
||||||
|
// FIXME more sophisticated error handling
|
||||||
|
return false;
|
||||||
|
|
||||||
// wait for the next sync message to arrive
|
// wait for the next sync message to arrive
|
||||||
mCvar.Wait();
|
mCvar.Wait();
|
||||||
|
|
||||||
|
if (!Connected())
|
||||||
|
// FIXME more sophisticated error handling
|
||||||
|
return false;
|
||||||
|
|
||||||
// we just received a synchronous message from the other side.
|
// we just received a synchronous message from the other side.
|
||||||
// If it's not the reply we were awaiting, there's a serious
|
// If it's not the reply we were awaiting, there's a serious
|
||||||
// error: either a mistimed/malformed message or a sync in-message
|
// error: either a mistimed/malformed message or a sync in-message
|
||||||
@@ -156,6 +164,22 @@ SyncChannel::OnMessageReceived(const Message& msg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SyncChannel::OnChannelError()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
|
||||||
|
mChannelState = ChannelError;
|
||||||
|
|
||||||
|
if (AwaitingSyncReply()) {
|
||||||
|
mCvar.Notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AsyncChannel::OnChannelError();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SyncChannel::OnSendReply(Message* aReply)
|
SyncChannel::OnSendReply(Message* aReply)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ public:
|
|||||||
bool Send(Message* msg, Message* reply);
|
bool Send(Message* msg, Message* reply);
|
||||||
|
|
||||||
// Override the AsyncChannel handler so we can dispatch sync messages
|
// Override the AsyncChannel handler so we can dispatch sync messages
|
||||||
virtual void OnMessageReceived(const Message& msg);
|
NS_OVERRIDE virtual void OnMessageReceived(const Message& msg);
|
||||||
|
NS_OVERRIDE virtual void OnChannelError();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Executed on the worker thread
|
// Executed on the worker thread
|
||||||
|
|||||||
Reference in New Issue
Block a user