зеркало из https://github.com/mozilla/gecko-dev.git
landing patch for bug 243107 "make ipcIService and ipcILockService threadsafe" (this code is not currently used for anything)
This commit is contained in:
Родитель
51e61b254e
Коммит
89fdea0832
|
@ -74,17 +74,24 @@
|
||||||
* Initialization and Shutdown
|
* Initialization and Shutdown
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// XXX limit these to the main thread, and call them from our module's ctor/dtor?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures that this process is connected to the IPC daemon. If it is already
|
* Connects this process to the IPC daemon and initializes it for use as a
|
||||||
* connected, then this function call has no effect. Each call to IPC_Init
|
* client of the IPC daemon. This function must be called once before any
|
||||||
* should be balanced by a call to IPC_Shutdown. A reference counter is used
|
* other methods defined in this file can be used.
|
||||||
* to determine when to disconnect from the IPC daemon.
|
*
|
||||||
|
* @returns NS_ERROR_ALREADY_INITIALIZED if IPC_Shutdown was not called since
|
||||||
|
* the last time IPC_Init was called.
|
||||||
*/
|
*/
|
||||||
IPC_METHOD IPC_Init();
|
IPC_METHOD IPC_Init();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnects this process from the IPC daemon. Must be called once for
|
* Disconnects this process from the IPC daemon. After this function is
|
||||||
* every call to IPC_Init when the IPC connection is no longer needed.
|
* called, no other methods in this file except for IPC_Init may be called.
|
||||||
|
*
|
||||||
|
* @returns NS_ERROR_NOT_INITIALIZED if IPC_Init has not been called or if
|
||||||
|
* IPC_Init did not return a success code.
|
||||||
*/
|
*/
|
||||||
IPC_METHOD IPC_Shutdown();
|
IPC_METHOD IPC_Shutdown();
|
||||||
|
|
||||||
|
@ -99,16 +106,40 @@ IPC_METHOD IPC_Shutdown();
|
||||||
* whenever a message is sent to this target in this process.
|
* whenever a message is sent to this target in this process.
|
||||||
*
|
*
|
||||||
* This function has three main effects:
|
* This function has three main effects:
|
||||||
* o If the message target is already defined, then this function simply resets
|
* o If the message target is already defined, then this function simply
|
||||||
* its message observer.
|
* resets its message observer.
|
||||||
* o If the message target is not already defined, then the IPC daemon will be
|
* o If the message target is not already defined, then the message target
|
||||||
* notified of the existance of this message target.
|
* is defined and the IPC daemon is notified of the existance of this
|
||||||
|
* message target.
|
||||||
* o If null is passed for the message observer, then the message target is
|
* o If null is passed for the message observer, then the message target is
|
||||||
* removed, and the daemon is notified of the removal of this message target.
|
* removed, and the daemon is notified of the removal of this message target.
|
||||||
|
*
|
||||||
|
* If aOnCurrentThread is true, then notifications to the observer will occur
|
||||||
|
* on the current thread. This means that there must be a nsIEventTarget
|
||||||
|
* associated with the calling thread. If aOnCurrentThread is false, then
|
||||||
|
* notifications to the observer will occur on a background thread. In which
|
||||||
|
* case, the observer must be threadsafe.
|
||||||
*/
|
*/
|
||||||
IPC_METHOD IPC_DefineTarget(
|
IPC_METHOD IPC_DefineTarget(
|
||||||
const nsID &aTarget,
|
const nsID &aTarget,
|
||||||
ipcIMessageObserver *aObserver
|
ipcIMessageObserver *aObserver,
|
||||||
|
PRBool aOnCurrentThread = PR_TRUE
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this method to temporarily disable the message observer configured
|
||||||
|
* for a message target.
|
||||||
|
*/
|
||||||
|
IPC_METHOD IPC_DisableMessageObserver(
|
||||||
|
const nsID &aTarget
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this method to re-enable the message observer configured for a
|
||||||
|
* message target that was disabled by a call to IPC_DisableMessageObserver.
|
||||||
|
*/
|
||||||
|
IPC_METHOD IPC_EnableMessageObserver(
|
||||||
|
const nsID &aTarget
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -135,9 +166,9 @@ IPC_METHOD IPC_SendMessage(
|
||||||
* the IPC daemon.
|
* the IPC daemon.
|
||||||
* o If aSenderID is IPC_SENDER_ANY, then this function waits for a message
|
* o If aSenderID is IPC_SENDER_ANY, then this function waits for a message
|
||||||
* to be sent from any source.
|
* to be sent from any source.
|
||||||
* o Otherwise, this function waits for a message to be sent by client with
|
* o Otherwise, this function waits for a message to be sent by the client
|
||||||
* ID given by aSenderID. If aSenderID does not identify a valid client,
|
* with ID given by aSenderID. If aSenderID does not identify a valid
|
||||||
* then this function will return an error.
|
* client, then this function will return an error.
|
||||||
*
|
*
|
||||||
* The aObserver parameter is interpreted as follows:
|
* The aObserver parameter is interpreted as follows:
|
||||||
* o If aObserver is null, then the default message observer for the target
|
* o If aObserver is null, then the default message observer for the target
|
||||||
|
@ -157,7 +188,8 @@ IPC_METHOD IPC_SendMessage(
|
||||||
* If aObserver's OnMessageAvailable function returns IPC_WAIT_NEXT_MESSAGE,
|
* If aObserver's OnMessageAvailable function returns IPC_WAIT_NEXT_MESSAGE,
|
||||||
* then the function will continue blocking until the next matching message
|
* then the function will continue blocking until the next matching message
|
||||||
* is received. Bypassed messages will be dispatched to the default message
|
* is received. Bypassed messages will be dispatched to the default message
|
||||||
* observer when the thread's event queue is processed.
|
* observer when the event queue, associated with the thread that called
|
||||||
|
* IPC_DefineTarget, is processed.
|
||||||
*
|
*
|
||||||
* This function runs the risk of hanging the calling thread indefinitely if
|
* This function runs the risk of hanging the calling thread indefinitely if
|
||||||
* no matching message is ever received.
|
* no matching message is ever received.
|
||||||
|
@ -195,7 +227,7 @@ IPC_METHOD IPC_RemoveName(
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds client observer.
|
* Adds client observer. Will be called on the main thread.
|
||||||
*/
|
*/
|
||||||
IPC_METHOD IPC_AddClientObserver(
|
IPC_METHOD IPC_AddClientObserver(
|
||||||
ipcIClientObserver *aObserver
|
ipcIClientObserver *aObserver
|
||||||
|
@ -225,4 +257,31 @@ IPC_METHOD IPC_ClientExists(
|
||||||
PRBool *aResult
|
PRBool *aResult
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class can be used to temporarily disable the default message observer
|
||||||
|
* defined for a particular message target.
|
||||||
|
*/
|
||||||
|
class ipcDisableMessageObserverForScope
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ipcDisableMessageObserverForScope(const nsID &aTarget)
|
||||||
|
: mTarget(aTarget)
|
||||||
|
{
|
||||||
|
IPC_DisableMessageObserver(mTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
~ipcDisableMessageObserverForScope()
|
||||||
|
{
|
||||||
|
IPC_EnableMessageObserver(mTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const nsID &mTarget;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IPC_DISABLE_MESSAGE_OBSERVER_FOR_SCOPE(_t) \
|
||||||
|
ipcDisableMessageObserverForScope ipc_dmo_for_scope##_t(_t)
|
||||||
|
|
||||||
#endif /* ipcdclient_h__ */
|
#endif /* ipcdclient_h__ */
|
||||||
|
|
|
@ -49,6 +49,8 @@ class ipcMessage;
|
||||||
/* Platform specific IPC connection API.
|
/* Platform specific IPC connection API.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
typedef void (* ipcCallbackFunc)(void *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IPC_Connect
|
* IPC_Connect
|
||||||
*
|
*
|
||||||
|
@ -87,6 +89,20 @@ IPC_METHOD_PRIVATE IPC_Disconnect();
|
||||||
*/
|
*/
|
||||||
IPC_METHOD_PRIVATE IPC_SendMsg(ipcMessage *msg);
|
IPC_METHOD_PRIVATE IPC_SendMsg(ipcMessage *msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IPC_DoCallback
|
||||||
|
*
|
||||||
|
* This function executes a callback function on the same background thread
|
||||||
|
* that calls IPC_OnConnectionEnd and IPC_OnMessageAvailable.
|
||||||
|
*
|
||||||
|
* If this function succeeds, then the caller is guaranteed that |func| will
|
||||||
|
* be called. This guarantee is important because it allows the caller to
|
||||||
|
* free any memory associated with |arg| once |func| has been called.
|
||||||
|
*
|
||||||
|
* NOTE: This function may be called on any thread.
|
||||||
|
*/
|
||||||
|
IPC_METHOD_PRIVATE IPC_DoCallback(ipcCallbackFunc func, void *arg);
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
/* Cross-platform IPC connection methods.
|
/* Cross-platform IPC connection methods.
|
||||||
*/
|
*/
|
||||||
|
@ -95,9 +111,8 @@ IPC_METHOD_PRIVATE IPC_SendMsg(ipcMessage *msg);
|
||||||
* IPC_SpawnDaemon
|
* IPC_SpawnDaemon
|
||||||
*
|
*
|
||||||
* This function launches the IPC daemon process. It is called by the platform
|
* This function launches the IPC daemon process. It is called by the platform
|
||||||
* specific IPC_Connect implementation. This function may be called on any
|
* specific IPC_Connect implementation. It should not return until the daemon
|
||||||
* thread. It should not return until the daemon process is ready to receive
|
* process is ready to receive a client connection or an error occurs.
|
||||||
* a client connection or an error occurs.
|
|
||||||
*
|
*
|
||||||
* @param daemonPath
|
* @param daemonPath
|
||||||
* Specifies the path to the IPC daemon executable.
|
* Specifies the path to the IPC daemon executable.
|
||||||
|
|
|
@ -115,10 +115,21 @@ DoSecurityCheck(PRFileDesc *fd, const char *path)
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct ipcCallback : public ipcListNode<ipcCallback>
|
||||||
|
{
|
||||||
|
ipcCallbackFunc func;
|
||||||
|
void *arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef ipcList<ipcCallback> ipcCallbackQ;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
struct ipcConnectionState
|
struct ipcConnectionState
|
||||||
{
|
{
|
||||||
PRLock *lock;
|
PRLock *lock;
|
||||||
PRPollDesc fds[2];
|
PRPollDesc fds[2];
|
||||||
|
ipcCallbackQ callback_queue;
|
||||||
ipcMessageQ send_queue;
|
ipcMessageQ send_queue;
|
||||||
PRUint32 send_offset; // amount of send_queue.First() already written.
|
PRUint32 send_offset; // amount of send_queue.First() already written.
|
||||||
ipcMessage *in_msg;
|
ipcMessage *in_msg;
|
||||||
|
@ -315,17 +326,38 @@ ConnThread(void *arg)
|
||||||
num = PR_Poll(s->fds, 2, PR_INTERVAL_NO_TIMEOUT);
|
num = PR_Poll(s->fds, 2, PR_INTERVAL_NO_TIMEOUT);
|
||||||
if (num > 0)
|
if (num > 0)
|
||||||
{
|
{
|
||||||
|
ipcCallbackQ cbs_to_run;
|
||||||
|
|
||||||
// check if something has been added to the send queue. if so, then
|
// check if something has been added to the send queue. if so, then
|
||||||
// acknowledge pollable event (wait should not block), and configure
|
// acknowledge pollable event (wait should not block), and configure
|
||||||
// poll flags to find out when we can write.
|
// poll flags to find out when we can write.
|
||||||
|
//
|
||||||
|
// delay processing a shutdown request until after all queued up
|
||||||
|
// messages have been sent and until after all queued up callbacks
|
||||||
|
// have been run.
|
||||||
|
|
||||||
if (s->fds[POLL].out_flags & PR_POLL_READ)
|
if (s->fds[POLL].out_flags & PR_POLL_READ)
|
||||||
{
|
{
|
||||||
PR_WaitForPollableEvent(s->fds[POLL].fd);
|
PR_WaitForPollableEvent(s->fds[POLL].fd);
|
||||||
PR_Lock(s->lock);
|
PR_Lock(s->lock);
|
||||||
|
|
||||||
|
PRBool delayShutdown = PR_FALSE;
|
||||||
|
|
||||||
if (!s->send_queue.IsEmpty())
|
if (!s->send_queue.IsEmpty())
|
||||||
|
{
|
||||||
|
delayShutdown = PR_TRUE;
|
||||||
s->fds[SOCK].in_flags |= PR_POLL_WRITE;
|
s->fds[SOCK].in_flags |= PR_POLL_WRITE;
|
||||||
else if (s->shutdown)
|
}
|
||||||
|
|
||||||
|
if (!s->callback_queue.IsEmpty())
|
||||||
|
{
|
||||||
|
delayShutdown = PR_TRUE;
|
||||||
|
s->callback_queue.MoveTo(cbs_to_run);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!delayShutdown && s->shutdown)
|
||||||
rv = NS_ERROR_ABORT;
|
rv = NS_ERROR_ABORT;
|
||||||
|
|
||||||
PR_Unlock(s->lock);
|
PR_Unlock(s->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,6 +368,14 @@ ConnThread(void *arg)
|
||||||
// check if we can write...
|
// check if we can write...
|
||||||
if (s->fds[SOCK].out_flags & PR_POLL_WRITE)
|
if (s->fds[SOCK].out_flags & PR_POLL_WRITE)
|
||||||
rv = ConnWrite(s);
|
rv = ConnWrite(s);
|
||||||
|
|
||||||
|
// check if we have callbacks to run
|
||||||
|
while (!cbs_to_run.IsEmpty())
|
||||||
|
{
|
||||||
|
ipcCallback *cb = cbs_to_run.First();
|
||||||
|
(cb->func)(cb->arg);
|
||||||
|
cbs_to_run.DeleteFirst();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -510,6 +550,25 @@ IPC_SendMsg(ipcMessage *msg)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
IPC_DoCallback(ipcCallbackFunc func, void *arg)
|
||||||
|
{
|
||||||
|
if (!gConnState || !gConnThread)
|
||||||
|
return NS_ERROR_NOT_INITIALIZED;
|
||||||
|
|
||||||
|
ipcCallback *callback = new ipcCallback;
|
||||||
|
if (!callback)
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
callback->func = func;
|
||||||
|
callback->arg = arg;
|
||||||
|
|
||||||
|
PR_Lock(gConnState->lock);
|
||||||
|
gConnState->callback_queue.Append(callback);
|
||||||
|
PR_SetPollableEvent(gConnState->fds[POLL].fd);
|
||||||
|
PR_Unlock(gConnState->lock);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
#ifdef TEST_STANDALONE
|
#ifdef TEST_STANDALONE
|
||||||
|
|
|
@ -67,7 +67,8 @@
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
#define IPC_WM_SENDMSG (WM_USER + 0x1)
|
#define IPC_WM_SENDMSG (WM_USER + 0x1)
|
||||||
#define IPC_WM_SHUTDOWN (WM_USER + 0x2)
|
#define IPC_WM_CALLBACK (WM_USER + 0x2)
|
||||||
|
#define IPC_WM_SHUTDOWN (WM_USER + 0x3)
|
||||||
|
|
||||||
static nsresult ipcThreadStatus = NS_OK;
|
static nsresult ipcThreadStatus = NS_OK;
|
||||||
static PRThread *ipcThread = NULL;
|
static PRThread *ipcThread = NULL;
|
||||||
|
@ -118,6 +119,13 @@ ipcThreadWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (uMsg == IPC_WM_CALLBACK) {
|
||||||
|
ipcCallbackFunc func = (ipcCallbackFunc) wParam;
|
||||||
|
void *arg = (void *) lParam;
|
||||||
|
(func)(arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (uMsg == IPC_WM_SHUTDOWN) {
|
if (uMsg == IPC_WM_SHUTDOWN) {
|
||||||
IPC_OnConnectionEnd(NS_OK);
|
IPC_OnConnectionEnd(NS_OK);
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
|
@ -306,3 +314,19 @@ loser:
|
||||||
delete msg;
|
delete msg;
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
IPC_DoCallback(ipcCallbackFunc func, void *arg)
|
||||||
|
{
|
||||||
|
LOG(("IPC_DoCallback\n"));
|
||||||
|
|
||||||
|
if (ipcShutdown) {
|
||||||
|
LOG(("unable to send message b/c message thread is shutdown\n"));
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
if (!PostMessage(ipcLocalHwnd, IPC_WM_CALLBACK, (WPARAM) func, (LPARAM) arg)) {
|
||||||
|
LOG((" PostMessage failed w/ error = %u\n", GetLastError()));
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -38,17 +38,17 @@
|
||||||
#include "nsIServiceManager.h"
|
#include "nsIServiceManager.h"
|
||||||
#include "nsIGenericFactory.h"
|
#include "nsIGenericFactory.h"
|
||||||
#include "nsICategoryManager.h"
|
#include "nsICategoryManager.h"
|
||||||
|
#include "ipcdclient.h"
|
||||||
#include "ipcService.h"
|
#include "ipcService.h"
|
||||||
#include "ipcConfig.h"
|
#include "ipcConfig.h"
|
||||||
#include "ipcCID.h"
|
#include "ipcCID.h"
|
||||||
#include "ipcLockCID.h"
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Define the contructor function for the objects
|
// Define the contructor function for the objects
|
||||||
//
|
//
|
||||||
// NOTE: This creates an instance of objects by using the default constructor
|
// NOTE: This creates an instance of objects by using the default constructor
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(ipcService, Init)
|
NS_GENERIC_FACTORY_CONSTRUCTOR(ipcService)
|
||||||
|
|
||||||
// enable this code to make the IPC service auto-start.
|
// enable this code to make the IPC service auto-start.
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -90,6 +90,7 @@ ipcServiceUnregisterProc(nsIComponentManager *aCompMgr,
|
||||||
// extensions
|
// extensions
|
||||||
|
|
||||||
#include "ipcLockService.h"
|
#include "ipcLockService.h"
|
||||||
|
#include "ipcLockCID.h"
|
||||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(ipcLockService, Init)
|
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(ipcLockService, Init)
|
||||||
|
|
||||||
#include "tmTransactionService.h"
|
#include "tmTransactionService.h"
|
||||||
|
@ -167,8 +168,24 @@ static const nsModuleComponentInfo components[] = {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
PR_STATIC_CALLBACK(nsresult)
|
||||||
|
ipcdclient_init(nsIModule *module)
|
||||||
|
{
|
||||||
|
return IPC_Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
PR_STATIC_CALLBACK(void)
|
||||||
|
ipcdclient_shutdown(nsIModule *module)
|
||||||
|
{
|
||||||
|
IPC_Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Implement the NSGetModule() exported function for your module
|
// Implement the NSGetModule() exported function for your module
|
||||||
// and the entire implementation of the module object.
|
// and the entire implementation of the module object.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
NS_IMPL_NSGETMODULE(ipcd, components)
|
NS_IMPL_NSGETMODULE_WITH_CTOR_DTOR(ipcdclient, components,
|
||||||
|
ipcdclient_init,
|
||||||
|
ipcdclient_shutdown)
|
||||||
|
|
|
@ -37,7 +37,10 @@
|
||||||
|
|
||||||
#include "ipcService.h"
|
#include "ipcService.h"
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS1(ipcService, ipcIService)
|
// The ipcService implementation is nothing more than a thin XPCOM wrapper
|
||||||
|
// around the ipcdclient.h API.
|
||||||
|
|
||||||
|
NS_IMPL_THREADSAFE_ISUPPORTS1(ipcService, ipcIService)
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
ipcService::GetID(PRUint32 *aID)
|
ipcService::GetID(PRUint32 *aID)
|
||||||
|
|
|
@ -46,11 +46,6 @@ class ipcService : public ipcIService
|
||||||
public:
|
public:
|
||||||
NS_DECL_ISUPPORTS
|
NS_DECL_ISUPPORTS
|
||||||
NS_DECL_IPCISERVICE
|
NS_DECL_IPCISERVICE
|
||||||
|
|
||||||
NS_HIDDEN_(nsresult) Init() { return IPC_Init(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
~ipcService() { IPC_Shutdown(); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // !defined( ipcService_h__ )
|
#endif // !defined( ipcService_h__ )
|
||||||
|
|
|
@ -56,7 +56,6 @@
|
||||||
|
|
||||||
#include "prio.h"
|
#include "prio.h"
|
||||||
#include "prproces.h"
|
#include "prproces.h"
|
||||||
#include "prlock.h"
|
|
||||||
#include "pratom.h"
|
#include "pratom.h"
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
@ -68,25 +67,36 @@
|
||||||
class ipcTargetData
|
class ipcTargetData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static NS_HIDDEN_(ipcTargetData*) Create(ipcIMessageObserver *aObserver);
|
static NS_HIDDEN_(ipcTargetData*) Create();
|
||||||
|
|
||||||
// threadsafe addref/release
|
// threadsafe addref/release
|
||||||
NS_HIDDEN_(nsrefcnt) AddRef() { return PR_AtomicIncrement(&refcnt); }
|
NS_HIDDEN_(nsrefcnt) AddRef() { return PR_AtomicIncrement(&refcnt); }
|
||||||
NS_HIDDEN_(nsrefcnt) Release() { PRInt32 r = PR_AtomicDecrement(&refcnt); if (r == 0) delete this; return r; }
|
NS_HIDDEN_(nsrefcnt) Release() { PRInt32 r = PR_AtomicDecrement(&refcnt); if (r == 0) delete this; return r; }
|
||||||
|
|
||||||
|
NS_HIDDEN_(void) SetObserver(ipcIMessageObserver *aObserver, PRBool aOnCurrentThread);
|
||||||
|
|
||||||
// protects access to the members of this class
|
// protects access to the members of this class
|
||||||
PRMonitor *monitor;
|
PRMonitor *monitor;
|
||||||
|
|
||||||
// this may be null
|
// this may be null
|
||||||
nsCOMPtr<ipcIMessageObserver> observer;
|
nsCOMPtr<ipcIMessageObserver> observer;
|
||||||
|
|
||||||
|
// the message observer is called via this event queue
|
||||||
|
nsCOMPtr<nsIEventQueue> eventQ;
|
||||||
|
|
||||||
// incoming messages are added to this list
|
// incoming messages are added to this list
|
||||||
ipcMessageQ pendingQ;
|
ipcMessageQ pendingQ;
|
||||||
|
|
||||||
|
// non-zero if the observer has been disabled (this means that new messages
|
||||||
|
// should not be dispatched to the observer until the observer is re-enabled
|
||||||
|
// via IPC_EnableMessageObserver).
|
||||||
|
PRInt32 observerDisabled;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
ipcTargetData()
|
ipcTargetData()
|
||||||
: monitor(PR_NewMonitor())
|
: monitor(PR_NewMonitor())
|
||||||
|
, observerDisabled(0)
|
||||||
, refcnt(0)
|
, refcnt(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -100,7 +110,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
ipcTargetData *
|
ipcTargetData *
|
||||||
ipcTargetData::Create(ipcIMessageObserver *aObserver)
|
ipcTargetData::Create()
|
||||||
{
|
{
|
||||||
ipcTargetData *td = new ipcTargetData;
|
ipcTargetData *td = new ipcTargetData;
|
||||||
if (!td)
|
if (!td)
|
||||||
|
@ -111,11 +121,20 @@ ipcTargetData::Create(ipcIMessageObserver *aObserver)
|
||||||
delete td;
|
delete td;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
td->observer = aObserver;
|
|
||||||
return td;
|
return td;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ipcTargetData::SetObserver(ipcIMessageObserver *aObserver, PRBool aOnCurrentThread)
|
||||||
|
{
|
||||||
|
observer = aObserver;
|
||||||
|
|
||||||
|
if (aOnCurrentThread)
|
||||||
|
NS_GetCurrentEventQ(getter_AddRefs(eventQ));
|
||||||
|
else
|
||||||
|
eventQ = nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
typedef nsRefPtrHashtable<nsIDHashKey, ipcTargetData> ipcTargetMap;
|
typedef nsRefPtrHashtable<nsIDHashKey, ipcTargetData> ipcTargetMap;
|
||||||
|
@ -127,12 +146,19 @@ public:
|
||||||
|
|
||||||
~ipcClientState()
|
~ipcClientState()
|
||||||
{
|
{
|
||||||
if (lock)
|
if (monitor)
|
||||||
PR_DestroyLock(lock);
|
PR_DestroyMonitor(monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this lock protects the targetMap and the connected flag.
|
//
|
||||||
PRLock *lock;
|
// the monitor protects the targetMap and the connected flag.
|
||||||
|
//
|
||||||
|
// NOTE: we use a PRMonitor for this instead of a PRLock because we need
|
||||||
|
// the lock to be re-entrant. since we don't ever need to wait on
|
||||||
|
// this monitor, it might be worth it to implement a re-entrant
|
||||||
|
// wrapper for PRLock.
|
||||||
|
//
|
||||||
|
PRMonitor *monitor;
|
||||||
ipcTargetMap targetMap;
|
ipcTargetMap targetMap;
|
||||||
PRBool connected;
|
PRBool connected;
|
||||||
|
|
||||||
|
@ -144,7 +170,7 @@ public:
|
||||||
private:
|
private:
|
||||||
|
|
||||||
ipcClientState()
|
ipcClientState()
|
||||||
: lock(PR_NewLock())
|
: monitor(PR_NewMonitor())
|
||||||
, connected(PR_FALSE)
|
, connected(PR_FALSE)
|
||||||
, selfID(0)
|
, selfID(0)
|
||||||
{}
|
{}
|
||||||
|
@ -157,7 +183,7 @@ ipcClientState::Create()
|
||||||
if (!cs)
|
if (!cs)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (!cs->lock || !cs->targetMap.Init())
|
if (!cs->monitor || !cs->targetMap.Init())
|
||||||
{
|
{
|
||||||
delete cs;
|
delete cs;
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -168,27 +194,26 @@ ipcClientState::Create()
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
static PRInt32 gInitCount;
|
|
||||||
static ipcClientState *gClientState;
|
static ipcClientState *gClientState;
|
||||||
|
|
||||||
static PRBool
|
static PRBool
|
||||||
GetTarget(const nsID &aTarget, ipcTargetData **td)
|
GetTarget(const nsID &aTarget, ipcTargetData **td)
|
||||||
{
|
{
|
||||||
nsAutoLock lock(gClientState->lock);
|
nsAutoMonitor mon(gClientState->monitor);
|
||||||
return gClientState->targetMap.Get(nsIDHashKey(&aTarget).GetKey(), td);
|
return gClientState->targetMap.Get(nsIDHashKey(&aTarget).GetKey(), td);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PRBool
|
static PRBool
|
||||||
PutTarget(const nsID &aTarget, ipcTargetData *td)
|
PutTarget(const nsID &aTarget, ipcTargetData *td)
|
||||||
{
|
{
|
||||||
nsAutoLock lock(gClientState->lock);
|
nsAutoMonitor mon(gClientState->monitor);
|
||||||
return gClientState->targetMap.Put(nsIDHashKey(&aTarget).GetKey(), td);
|
return gClientState->targetMap.Put(nsIDHashKey(&aTarget).GetKey(), td);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
DelTarget(const nsID &aTarget)
|
DelTarget(const nsID &aTarget)
|
||||||
{
|
{
|
||||||
nsAutoLock lock(gClientState->lock);
|
nsAutoMonitor mon(gClientState->monitor);
|
||||||
gClientState->targetMap.Remove(nsIDHashKey(&aTarget).GetKey());
|
gClientState->targetMap.Remove(nsIDHashKey(&aTarget).GetKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +247,12 @@ ProcessPendingQ(const nsID &aTarget)
|
||||||
if (GetTarget(aTarget, getter_AddRefs(td)))
|
if (GetTarget(aTarget, getter_AddRefs(td)))
|
||||||
{
|
{
|
||||||
nsAutoMonitor mon(td->monitor);
|
nsAutoMonitor mon(td->monitor);
|
||||||
td->pendingQ.MoveTo(tempQ);
|
|
||||||
|
// if the observer for this target has been temporarily disabled, then
|
||||||
|
// we must not processing any pending messages at this time.
|
||||||
|
|
||||||
|
if (!td->observerDisabled)
|
||||||
|
td->pendingQ.MoveTo(tempQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
// process pending queue outside monitor
|
// process pending queue outside monitor
|
||||||
|
@ -249,14 +279,17 @@ ProcessPendingQ(const nsID &aTarget)
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
// WaitTarget enables support for multiple threads blocking on the same
|
// WaitTarget enables support for multiple threads blocking on the same
|
||||||
// message target. This functionality does not need to be exposed in our
|
// message target. the selector is called while inside the target's monitor.
|
||||||
// public API because targets are meant to be single threaded. This
|
|
||||||
// functionality only exists to support the IPCM protocol.
|
|
||||||
|
|
||||||
typedef PRBool (* ipcMessageSelector)(void *aArg, ipcTargetData *aTD, const ipcMessage *aMsg);
|
typedef PRBool (* ipcMessageSelector)(
|
||||||
|
void *arg,
|
||||||
|
ipcTargetData *td,
|
||||||
|
const ipcMessage *msg
|
||||||
|
);
|
||||||
|
|
||||||
|
// selects any
|
||||||
static PRBool
|
static PRBool
|
||||||
DefaultSelector(void *aArg, ipcTargetData *aTD, const ipcMessage *aMsg)
|
DefaultSelector(void *arg, ipcTargetData *td, const ipcMessage *msg)
|
||||||
{
|
{
|
||||||
return PR_TRUE;
|
return PR_TRUE;
|
||||||
}
|
}
|
||||||
|
@ -299,66 +332,50 @@ WaitTarget(const nsID &aTarget,
|
||||||
|
|
||||||
while (gClientState->connected)
|
while (gClientState->connected)
|
||||||
{
|
{
|
||||||
if (!lastChecked)
|
NS_ASSERTION(!lastChecked, "oops");
|
||||||
|
|
||||||
|
if (beforeLastChecked)
|
||||||
{
|
{
|
||||||
if (beforeLastChecked)
|
// verify that beforeLastChecked is still in the queue since it might
|
||||||
|
// have been removed while we were asleep on the monitor. we must not
|
||||||
|
// dereference it until we have verified this.
|
||||||
|
PRBool isValid = PR_FALSE;
|
||||||
|
for (ipcMessage *iter = td->pendingQ.First(); iter; iter=iter->mNext)
|
||||||
|
{
|
||||||
|
if (iter == beforeLastChecked)
|
||||||
|
{
|
||||||
|
isValid = PR_TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValid)
|
||||||
lastChecked = beforeLastChecked->mNext;
|
lastChecked = beforeLastChecked->mNext;
|
||||||
else
|
else
|
||||||
|
{
|
||||||
lastChecked = td->pendingQ.First();
|
lastChecked = td->pendingQ.First();
|
||||||
|
beforeLastChecked = nsnull;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (lastChecked->mNext)
|
else
|
||||||
lastChecked = lastChecked->mNext;
|
lastChecked = td->pendingQ.First();
|
||||||
|
|
||||||
|
// loop over pending queue until we find a message that our selector likes.
|
||||||
while (lastChecked)
|
while (lastChecked)
|
||||||
{
|
{
|
||||||
// remove this message from the pending queue. we'll put it back if
|
if ((aSelector)(aArg, td, lastChecked))
|
||||||
// it is not selected. we need to do this to allow the selector
|
|
||||||
// function to make calls back into our code. for example, it might
|
|
||||||
// try to undefine this message target!
|
|
||||||
|
|
||||||
if (beforeLastChecked)
|
|
||||||
td->pendingQ.RemoveAfter(beforeLastChecked);
|
|
||||||
else
|
|
||||||
td->pendingQ.RemoveFirst();
|
|
||||||
lastChecked->mNext = nsnull;
|
|
||||||
|
|
||||||
mon.Exit();
|
|
||||||
PRBool selected = (aSelector)(aArg, td, lastChecked);
|
|
||||||
mon.Enter();
|
|
||||||
|
|
||||||
if (selected)
|
|
||||||
{
|
{
|
||||||
|
// remove from pending queue
|
||||||
|
if (beforeLastChecked)
|
||||||
|
td->pendingQ.RemoveAfter(beforeLastChecked);
|
||||||
|
else
|
||||||
|
td->pendingQ.RemoveFirst();
|
||||||
|
lastChecked->mNext = nsnull;
|
||||||
|
|
||||||
*aMsg = lastChecked;
|
*aMsg = lastChecked;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// re-insert message into the pending queue. the only possible change
|
|
||||||
// that could have happened to the pending queue while we were in the
|
|
||||||
// callback is the addition of more messages (added to the end of the
|
|
||||||
// queue). our beforeLastChecked "iterator" must still be valid.
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
// scan td->pendingQ to ensure that beforeLastChecked is still valid.
|
|
||||||
if (beforeLastChecked)
|
|
||||||
{
|
|
||||||
PRBool found = PR_FALSE;
|
|
||||||
for (ipcMessage *iter = td->pendingQ.First(); iter; iter = iter->mNext)
|
|
||||||
{
|
|
||||||
if (iter == beforeLastChecked)
|
|
||||||
{
|
|
||||||
found = PR_TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NS_ASSERTION(found, "iterator is invalid");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (beforeLastChecked)
|
|
||||||
td->pendingQ.InsertAfter(beforeLastChecked, lastChecked);
|
|
||||||
else
|
|
||||||
td->pendingQ.Prepend(lastChecked);
|
|
||||||
|
|
||||||
beforeLastChecked = lastChecked;
|
beforeLastChecked = lastChecked;
|
||||||
lastChecked = lastChecked->mNext;
|
lastChecked = lastChecked->mNext;
|
||||||
}
|
}
|
||||||
|
@ -390,6 +407,144 @@ WaitTarget(const nsID &aTarget,
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
static void
|
||||||
|
PostEvent(nsIEventTarget *eventTarget, PLEvent *ev)
|
||||||
|
{
|
||||||
|
if (!ev)
|
||||||
|
return;
|
||||||
|
|
||||||
|
nsresult rv = eventTarget->PostEvent(ev);
|
||||||
|
if (NS_FAILED(rv))
|
||||||
|
{
|
||||||
|
NS_WARNING("PostEvent failed");
|
||||||
|
PL_DestroyEvent(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
PostEventToMainThread(PLEvent *ev)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIEventQueue> eventQ;
|
||||||
|
NS_GetMainEventQ(getter_AddRefs(eventQ));
|
||||||
|
if (!eventQ)
|
||||||
|
{
|
||||||
|
NS_WARNING("unable to get reference to main event queue");
|
||||||
|
PL_DestroyEvent(ev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PostEvent(eventQ, ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
class ipcEvent_ClientState : public PLEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ipcEvent_ClientState(PRUint32 aClientID, PRUint32 aClientState)
|
||||||
|
: mClientID(aClientID)
|
||||||
|
, mClientState(aClientState)
|
||||||
|
{
|
||||||
|
PL_InitEvent(this, nsnull, HandleEvent, DestroyEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
PR_STATIC_CALLBACK(void *) HandleEvent(PLEvent *ev)
|
||||||
|
{
|
||||||
|
// maybe we've been shutdown!
|
||||||
|
if (!gClientState)
|
||||||
|
return nsnull;
|
||||||
|
|
||||||
|
ipcEvent_ClientState *self = (ipcEvent_ClientState *) ev;
|
||||||
|
|
||||||
|
for (PRInt32 i=0; i<gClientState->clientObservers.Count(); ++i)
|
||||||
|
gClientState->clientObservers[i]->OnClientStateChange(self->mClientID,
|
||||||
|
self->mClientState);
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
PR_STATIC_CALLBACK(void) DestroyEvent(PLEvent *ev)
|
||||||
|
{
|
||||||
|
delete (ipcEvent_ClientState *) ev;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PRUint32 mClientID;
|
||||||
|
PRUint32 mClientState;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
class ipcEvent_ProcessPendingQ : public PLEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ipcEvent_ProcessPendingQ(const nsID &aTarget)
|
||||||
|
: mTarget(aTarget)
|
||||||
|
{
|
||||||
|
PL_InitEvent(this, nsnull, HandleEvent, DestroyEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
PR_STATIC_CALLBACK(void *) HandleEvent(PLEvent *ev)
|
||||||
|
{
|
||||||
|
ProcessPendingQ(((ipcEvent_ProcessPendingQ *) ev)->mTarget);
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
PR_STATIC_CALLBACK(void) DestroyEvent(PLEvent *ev)
|
||||||
|
{
|
||||||
|
delete (ipcEvent_ProcessPendingQ *) ev;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const nsID mTarget;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
CallProcessPendingQ(const nsID &target, ipcTargetData *td)
|
||||||
|
{
|
||||||
|
// we assume that we are inside td's monitor
|
||||||
|
|
||||||
|
PLEvent *ev = new ipcEvent_ProcessPendingQ(target);
|
||||||
|
if (!ev)
|
||||||
|
return;
|
||||||
|
|
||||||
|
nsresult rv;
|
||||||
|
|
||||||
|
if (td->eventQ)
|
||||||
|
rv = td->eventQ->PostEvent(ev);
|
||||||
|
else
|
||||||
|
rv = IPC_DoCallback((ipcCallbackFunc) PL_HandleEvent, ev);
|
||||||
|
|
||||||
|
if (NS_FAILED(rv))
|
||||||
|
PL_DestroyEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
static void
|
||||||
|
DisableMessageObserver(const nsID &aTarget)
|
||||||
|
{
|
||||||
|
nsRefPtr<ipcTargetData> td;
|
||||||
|
if (GetTarget(aTarget, getter_AddRefs(td)))
|
||||||
|
{
|
||||||
|
nsAutoMonitor mon(td->monitor);
|
||||||
|
++td->observerDisabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
EnableMessageObserver(const nsID &aTarget)
|
||||||
|
{
|
||||||
|
nsRefPtr<ipcTargetData> td;
|
||||||
|
if (GetTarget(aTarget, getter_AddRefs(td)))
|
||||||
|
{
|
||||||
|
nsAutoMonitor mon(td->monitor);
|
||||||
|
if (td->observerDisabled > 0 && --td->observerDisabled == 0)
|
||||||
|
if (!td->pendingQ.IsEmpty())
|
||||||
|
CallProcessPendingQ(aTarget, td);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
// selects the next IPCM message with matching request index
|
// selects the next IPCM message with matching request index
|
||||||
static PRBool
|
static PRBool
|
||||||
WaitIPCMResponseSelector(void *arg, ipcTargetData *td, const ipcMessage *msg)
|
WaitIPCMResponseSelector(void *arg, ipcTargetData *td, const ipcMessage *msg)
|
||||||
|
@ -438,9 +593,18 @@ MakeIPCMRequest(ipcMessage *msg, ipcMessage **responseMsg = nsnull)
|
||||||
|
|
||||||
PRUint32 requestIndex = IPCM_GetRequestIndex(msg);
|
PRUint32 requestIndex = IPCM_GetRequestIndex(msg);
|
||||||
|
|
||||||
|
// suppress 'ProcessPendingQ' for IPCM messages until we receive the
|
||||||
|
// response to this IPCM request. if we did not do this then there
|
||||||
|
// would be a race condition leading to the possible removal of our
|
||||||
|
// response from the pendingQ between sending the request and waiting
|
||||||
|
// for the response.
|
||||||
|
DisableMessageObserver(IPCM_TARGET);
|
||||||
|
|
||||||
nsresult rv = IPC_SendMsg(msg);
|
nsresult rv = IPC_SendMsg(msg);
|
||||||
if (NS_SUCCEEDED(rv))
|
if (NS_SUCCEEDED(rv))
|
||||||
rv = WaitIPCMResponse(requestIndex, responseMsg);
|
rv = WaitIPCMResponse(requestIndex, responseMsg);
|
||||||
|
|
||||||
|
EnableMessageObserver(IPCM_TARGET);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,14 +626,16 @@ RemoveTarget(const nsID &aTarget, PRBool aNotifyDaemon)
|
||||||
static nsresult
|
static nsresult
|
||||||
DefineTarget(const nsID &aTarget,
|
DefineTarget(const nsID &aTarget,
|
||||||
ipcIMessageObserver *aObserver,
|
ipcIMessageObserver *aObserver,
|
||||||
|
PRBool aOnCurrentThread,
|
||||||
PRBool aNotifyDaemon,
|
PRBool aNotifyDaemon,
|
||||||
ipcTargetData **aResult)
|
ipcTargetData **aResult)
|
||||||
{
|
{
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
nsRefPtr<ipcTargetData> td( ipcTargetData::Create(aObserver) );
|
nsRefPtr<ipcTargetData> td( ipcTargetData::Create() );
|
||||||
if (!td)
|
if (!td)
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
td->SetObserver(aObserver, aOnCurrentThread);
|
||||||
|
|
||||||
if (!PutTarget(aTarget, td))
|
if (!PutTarget(aTarget, td))
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
@ -506,7 +672,7 @@ TryConnect()
|
||||||
|
|
||||||
gClientState->connected = PR_TRUE;
|
gClientState->connected = PR_TRUE;
|
||||||
|
|
||||||
rv = DefineTarget(IPCM_TARGET, nsnull, PR_FALSE, nsnull);
|
rv = DefineTarget(IPCM_TARGET, nsnull, PR_FALSE, PR_FALSE, nsnull);
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return rv;
|
return rv;
|
||||||
|
|
||||||
|
@ -533,8 +699,7 @@ TryConnect()
|
||||||
nsresult
|
nsresult
|
||||||
IPC_Init()
|
IPC_Init()
|
||||||
{
|
{
|
||||||
if (gInitCount > 0)
|
NS_ENSURE_TRUE(!gClientState, NS_ERROR_ALREADY_INITIALIZED);
|
||||||
return NS_OK;
|
|
||||||
|
|
||||||
IPC_InitLog(">>>");
|
IPC_InitLog(">>>");
|
||||||
|
|
||||||
|
@ -542,9 +707,6 @@ IPC_Init()
|
||||||
if (!gClientState)
|
if (!gClientState)
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
// IPC_Shutdown will decrement
|
|
||||||
gInitCount++;
|
|
||||||
|
|
||||||
nsresult rv = TryConnect();
|
nsresult rv = TryConnect();
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
IPC_Shutdown();
|
IPC_Shutdown();
|
||||||
|
@ -555,16 +717,13 @@ IPC_Init()
|
||||||
nsresult
|
nsresult
|
||||||
IPC_Shutdown()
|
IPC_Shutdown()
|
||||||
{
|
{
|
||||||
NS_ENSURE_TRUE(gInitCount, NS_ERROR_NOT_INITIALIZED);
|
NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
|
||||||
|
|
||||||
if (--gInitCount > 0)
|
|
||||||
return NS_OK;
|
|
||||||
|
|
||||||
if (gClientState->connected)
|
if (gClientState->connected)
|
||||||
IPC_Disconnect();
|
IPC_Disconnect();
|
||||||
|
|
||||||
delete gClientState;
|
delete gClientState;
|
||||||
gClientState = NULL;
|
gClientState = nsnull;
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -573,7 +732,8 @@ IPC_Shutdown()
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
IPC_DefineTarget(const nsID &aTarget,
|
IPC_DefineTarget(const nsID &aTarget,
|
||||||
ipcIMessageObserver *aObserver)
|
ipcIMessageObserver *aObserver,
|
||||||
|
PRBool aOnCurrentThread)
|
||||||
{
|
{
|
||||||
NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
|
NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
|
||||||
|
|
||||||
|
@ -590,11 +750,11 @@ IPC_DefineTarget(const nsID &aTarget,
|
||||||
// the observer is released on the main thread.
|
// the observer is released on the main thread.
|
||||||
{
|
{
|
||||||
nsAutoMonitor mon(td->monitor);
|
nsAutoMonitor mon(td->monitor);
|
||||||
td->observer = aObserver;
|
td->SetObserver(aObserver, aOnCurrentThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove target outside of td's monitor to avoid holding the monitor
|
// remove target outside of td's monitor to avoid holding the monitor
|
||||||
// while entering the client state's lock.
|
// while entering the client state's monitor.
|
||||||
if (!aObserver)
|
if (!aObserver)
|
||||||
RemoveTarget(aTarget, PR_TRUE);
|
RemoveTarget(aTarget, PR_TRUE);
|
||||||
|
|
||||||
|
@ -603,7 +763,7 @@ IPC_DefineTarget(const nsID &aTarget,
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (aObserver)
|
if (aObserver)
|
||||||
rv = DefineTarget(aTarget, aObserver, PR_TRUE, nsnull);
|
rv = DefineTarget(aTarget, aObserver, aOnCurrentThread, PR_TRUE, nsnull);
|
||||||
else
|
else
|
||||||
rv = NS_ERROR_INVALID_ARG; // unknown target
|
rv = NS_ERROR_INVALID_ARG; // unknown target
|
||||||
}
|
}
|
||||||
|
@ -611,6 +771,32 @@ IPC_DefineTarget(const nsID &aTarget,
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
IPC_DisableMessageObserver(const nsID &aTarget)
|
||||||
|
{
|
||||||
|
NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
|
||||||
|
|
||||||
|
// do not permit modifications to the IPCM protocol's target.
|
||||||
|
if (aTarget.Equals(IPCM_TARGET))
|
||||||
|
return NS_ERROR_INVALID_ARG;
|
||||||
|
|
||||||
|
DisableMessageObserver(aTarget);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
IPC_EnableMessageObserver(const nsID &aTarget)
|
||||||
|
{
|
||||||
|
NS_ENSURE_TRUE(gClientState, NS_ERROR_NOT_INITIALIZED);
|
||||||
|
|
||||||
|
// do not permit modifications to the IPCM protocol's target.
|
||||||
|
if (aTarget.Equals(IPCM_TARGET))
|
||||||
|
return NS_ERROR_INVALID_ARG;
|
||||||
|
|
||||||
|
EnableMessageObserver(aTarget);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
IPC_SendMessage(PRUint32 aReceiverID,
|
IPC_SendMessage(PRUint32 aReceiverID,
|
||||||
const nsID &aTarget,
|
const nsID &aTarget,
|
||||||
|
@ -797,10 +983,10 @@ IPC_ClientExists(PRUint32 aClientID, PRBool *aResult)
|
||||||
nsresult
|
nsresult
|
||||||
IPC_SpawnDaemon(const char *path)
|
IPC_SpawnDaemon(const char *path)
|
||||||
{
|
{
|
||||||
PRFileDesc *readable = NULL, *writable = NULL;
|
PRFileDesc *readable = nsnull, *writable = nsnull;
|
||||||
PRProcessAttr *attr = NULL;
|
PRProcessAttr *attr = nsnull;
|
||||||
nsresult rv = NS_ERROR_FAILURE;
|
nsresult rv = NS_ERROR_FAILURE;
|
||||||
char *const argv[] = { (char *const) path, NULL };
|
char *const argv[] = { (char *const) path, nsnull };
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
// setup an anonymous pipe that we can use to determine when the daemon
|
// setup an anonymous pipe that we can use to determine when the daemon
|
||||||
|
@ -819,7 +1005,7 @@ IPC_SpawnDaemon(const char *path)
|
||||||
if (PR_ProcessAttrSetInheritableFD(attr, writable, IPC_STARTUP_PIPE_NAME) != PR_SUCCESS)
|
if (PR_ProcessAttrSetInheritableFD(attr, writable, IPC_STARTUP_PIPE_NAME) != PR_SUCCESS)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
if (PR_CreateProcessDetached(path, argv, NULL, attr) != PR_SUCCESS)
|
if (PR_CreateProcessDetached(path, argv, nsnull, attr) != PR_SUCCESS)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
if ((PR_Read(readable, &c, 1) != 1) && (c != IPC_STARTUP_PIPE_MAGIC))
|
if ((PR_Read(readable, &c, 1) != 1) && (c != IPC_STARTUP_PIPE_MAGIC))
|
||||||
|
@ -838,89 +1024,6 @@ end:
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
class ipcEvent_ClientState : public PLEvent
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ipcEvent_ClientState(PRUint32 aClientID, PRUint32 aClientState)
|
|
||||||
: mClientID(aClientID)
|
|
||||||
, mClientState(aClientState)
|
|
||||||
{
|
|
||||||
PL_InitEvent(this, nsnull, HandleEvent, DestroyEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
PR_STATIC_CALLBACK(void *) HandleEvent(PLEvent *ev)
|
|
||||||
{
|
|
||||||
// maybe we've been shutdown!
|
|
||||||
if (!gClientState)
|
|
||||||
return nsnull;
|
|
||||||
|
|
||||||
ipcEvent_ClientState *self = (ipcEvent_ClientState *) ev;
|
|
||||||
|
|
||||||
for (PRInt32 i=0; i<gClientState->clientObservers.Count(); ++i)
|
|
||||||
gClientState->clientObservers[i]->OnClientStateChange(self->mClientID,
|
|
||||||
self->mClientState);
|
|
||||||
return nsnull;
|
|
||||||
}
|
|
||||||
|
|
||||||
PR_STATIC_CALLBACK(void) DestroyEvent(PLEvent *ev)
|
|
||||||
{
|
|
||||||
delete (ipcEvent_ClientState *) ev;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
PRUint32 mClientID;
|
|
||||||
PRUint32 mClientState;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
class ipcEvent_ProcessPendingQ : public PLEvent
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ipcEvent_ProcessPendingQ(const nsID &aTarget)
|
|
||||||
: mTarget(aTarget)
|
|
||||||
{
|
|
||||||
PL_InitEvent(this, nsnull, HandleEvent, DestroyEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
PR_STATIC_CALLBACK(void *) HandleEvent(PLEvent *ev)
|
|
||||||
{
|
|
||||||
ProcessPendingQ(((ipcEvent_ProcessPendingQ *) ev)->mTarget);
|
|
||||||
return nsnull;
|
|
||||||
}
|
|
||||||
|
|
||||||
PR_STATIC_CALLBACK(void) DestroyEvent(PLEvent *ev)
|
|
||||||
{
|
|
||||||
delete (ipcEvent_ProcessPendingQ *) ev;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const nsID mTarget;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
static void
|
|
||||||
PostEvent(PLEvent *ev)
|
|
||||||
{
|
|
||||||
if (!ev)
|
|
||||||
return;
|
|
||||||
|
|
||||||
nsCOMPtr<nsIEventQueue> eventQ;
|
|
||||||
NS_GetMainEventQ(getter_AddRefs(eventQ));
|
|
||||||
if (!eventQ)
|
|
||||||
return;
|
|
||||||
|
|
||||||
nsresult rv = eventQ->PostEvent(ev);
|
|
||||||
if (NS_FAILED(rv))
|
|
||||||
{
|
|
||||||
NS_WARNING("PostEvent failed");
|
|
||||||
PL_DestroyEvent(ev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
PR_STATIC_CALLBACK(PLDHashOperator)
|
PR_STATIC_CALLBACK(PLDHashOperator)
|
||||||
EnumerateTargetMapAndNotify(const nsID &aKey,
|
EnumerateTargetMapAndNotify(const nsID &aKey,
|
||||||
ipcTargetData *aData,
|
ipcTargetData *aData,
|
||||||
|
@ -928,12 +1031,8 @@ EnumerateTargetMapAndNotify(const nsID &aKey,
|
||||||
{
|
{
|
||||||
nsAutoMonitor mon(aData->monitor);
|
nsAutoMonitor mon(aData->monitor);
|
||||||
|
|
||||||
// this flag needs to be set while we are inside the monitor, since it is one
|
|
||||||
// of the conditions under which WaitTarget may block waiting for messages.
|
|
||||||
gClientState->connected = PR_FALSE;
|
|
||||||
|
|
||||||
// wake up anyone waiting on this target.
|
// wake up anyone waiting on this target.
|
||||||
mon.Notify();
|
mon.NotifyAll();
|
||||||
|
|
||||||
return PL_DHASH_NEXT;
|
return PL_DHASH_NEXT;
|
||||||
}
|
}
|
||||||
|
@ -945,7 +1044,8 @@ IPC_OnConnectionEnd(nsresult error)
|
||||||
// now, go through the target map, and tickle each monitor. that should
|
// now, go through the target map, and tickle each monitor. that should
|
||||||
// unblock any calls to WaitTarget.
|
// unblock any calls to WaitTarget.
|
||||||
|
|
||||||
nsAutoLock lock(gClientState->lock);
|
nsAutoMonitor mon(gClientState->monitor);
|
||||||
|
gClientState->connected = PR_FALSE;
|
||||||
gClientState->targetMap.EnumerateRead(EnumerateTargetMapAndNotify, nsnull);
|
gClientState->targetMap.EnumerateRead(EnumerateTargetMapAndNotify, nsnull);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -978,8 +1078,8 @@ IPC_OnMessageAvailable(ipcMessage *msg)
|
||||||
case IPCM_MSG_PSH_CLIENT_STATE:
|
case IPCM_MSG_PSH_CLIENT_STATE:
|
||||||
{
|
{
|
||||||
ipcMessageCast<ipcmMessageClientState> status(msg);
|
ipcMessageCast<ipcmMessageClientState> status(msg);
|
||||||
PostEvent(new ipcEvent_ClientState(status->ClientID(),
|
PostEventToMainThread(new ipcEvent_ClientState(status->ClientID(),
|
||||||
status->ClientState()));
|
status->ClientState()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -990,17 +1090,23 @@ IPC_OnMessageAvailable(ipcMessage *msg)
|
||||||
{
|
{
|
||||||
nsAutoMonitor mon(td->monitor);
|
nsAutoMonitor mon(td->monitor);
|
||||||
|
|
||||||
|
// we only want to dispatch a 'ProcessPendingQ' event if we have not
|
||||||
|
// already done so.
|
||||||
PRBool dispatchEvent = td->pendingQ.IsEmpty();
|
PRBool dispatchEvent = td->pendingQ.IsEmpty();
|
||||||
|
|
||||||
// put this message on our pending queue
|
// put this message on our pending queue
|
||||||
td->pendingQ.Append(msg);
|
td->pendingQ.Append(msg);
|
||||||
|
|
||||||
|
// make copy of target since |msg| may end up pointing to free'd memory
|
||||||
|
// once we notify the monitor.
|
||||||
|
const nsID target = msg->Target();
|
||||||
|
|
||||||
// wake up anyone waiting on this queue
|
// wake up anyone waiting on this queue
|
||||||
mon.Notify();
|
mon.Notify();
|
||||||
|
|
||||||
// proxy call to target's message procedure
|
// proxy call to target's message procedure
|
||||||
if (dispatchEvent)
|
if (dispatchEvent)
|
||||||
PostEvent(new ipcEvent_ProcessPendingQ(msg->Target()));
|
CallProcessPendingQ(target, td);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -40,34 +40,25 @@
|
||||||
interface ipcILockNotify;
|
interface ipcILockNotify;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This service provides named interprocess locking with either synchronous
|
* This service provides named interprocess locking.
|
||||||
* or asynchronous waiting.
|
|
||||||
*/
|
*/
|
||||||
[scriptable, uuid(9f6dbe15-d851-4b00-912a-5ac0be88a409)]
|
[scriptable, uuid(9f6dbe15-d851-4b00-912a-5ac0be88a409)]
|
||||||
interface ipcILockService : nsISupports
|
interface ipcILockService : nsISupports
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Call this method to acquire a named lock. Pass a notification handler
|
* Call this method to acquire a named interprocess lock.
|
||||||
* to be notified asynchronously when the lock is acquired. Otherwise,
|
|
||||||
* this function will block until the lock is acquired.
|
|
||||||
*
|
*
|
||||||
* @param aLockName
|
* @param aLockName
|
||||||
* specifies the name of the lock
|
* specifies the name of the lock
|
||||||
* @param aNotify
|
|
||||||
* notification callback (NULL to synchronously acquire lock)
|
|
||||||
* @param aWaitIfBusy
|
* @param aWaitIfBusy
|
||||||
* wait for the lock to become available; otherwise, fail if lock
|
* wait for the lock to become available; otherwise, fail if lock
|
||||||
* is already held by some other process.
|
* is already held by some other process.
|
||||||
*/
|
*/
|
||||||
void acquireLock(in string aLockName,
|
void acquireLock(in string aLockName,
|
||||||
in ipcILockNotify aNotify,
|
|
||||||
in boolean aWaitIfBusy);
|
in boolean aWaitIfBusy);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this method to release a named lock. This method can be called
|
* Call this method to release a named lock.
|
||||||
* before OnAcquireLockComplete has been called, which will effectively
|
|
||||||
* cancel the request to acquire the named lock. OnAcquireLockComplete
|
|
||||||
* will not be called after a call to ReleaseLock.
|
|
||||||
*
|
*
|
||||||
* @param aLockName
|
* @param aLockName
|
||||||
* specifies the name of the lock
|
* specifies the name of the lock
|
||||||
|
|
|
@ -66,7 +66,7 @@ IPC_FlattenLockMsg(const ipcLockMsg *msg, PRUint32 *bufLen)
|
||||||
+ strlen(msg->key) // key
|
+ strlen(msg->key) // key
|
||||||
+ 1; // null terminator
|
+ 1; // null terminator
|
||||||
|
|
||||||
PRUint8 *buf = (PRUint8 *) malloc(len);
|
PRUint8 *buf = (PRUint8 *) ::operator new(len);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include <stdlib.h>
|
/* vim:set ts=4 sw=4 sts=4 et cindent: */
|
||||||
/* ***** 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
|
||||||
*
|
*
|
||||||
|
@ -36,7 +36,10 @@
|
||||||
*
|
*
|
||||||
* ***** END LICENSE BLOCK ***** */
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
#include "nsIServiceManager.h"
|
#include <stdlib.h>
|
||||||
|
#include "nsDependentString.h"
|
||||||
|
#include "nsHashKeys.h"
|
||||||
|
#include "nsAutoPtr.h"
|
||||||
#include "ipcILockNotify.h"
|
#include "ipcILockNotify.h"
|
||||||
#include "ipcLockService.h"
|
#include "ipcLockService.h"
|
||||||
#include "ipcLockProtocol.h"
|
#include "ipcLockProtocol.h"
|
||||||
|
@ -49,22 +52,23 @@ static const nsID kLockTargetID = IPC_LOCK_TARGETID;
|
||||||
nsresult
|
nsresult
|
||||||
ipcLockService::Init()
|
ipcLockService::Init()
|
||||||
{
|
{
|
||||||
nsresult rv;
|
if (!mResultMap.Init())
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
rv = IPC_Init();
|
// Configure OnMessageAvailable to be called on the IPC thread. This is
|
||||||
if (NS_FAILED(rv))
|
// done to allow us to proxy OnAcquireLockComplete events to the right
|
||||||
return rv;
|
// thread immediately even if the main thread is blocked waiting to acquire
|
||||||
|
// some other lock synchronously.
|
||||||
|
|
||||||
return IPC_DefineTarget(kLockTargetID, this);
|
return IPC_DefineTarget(kLockTargetID, this, PR_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS2(ipcLockService, ipcILockService, ipcIMessageObserver)
|
NS_IMPL_THREADSAFE_ISUPPORTS2(ipcLockService, ipcILockService, ipcIMessageObserver)
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
ipcLockService::AcquireLock(const char *lockName, ipcILockNotify *notify, PRBool waitIfBusy)
|
ipcLockService::AcquireLock(const char *lockName, PRBool waitIfBusy)
|
||||||
{
|
{
|
||||||
LOG(("ipcLockService::AcquireLock [lock=%s sync=%u wait=%u]\n",
|
LOG(("ipcLockService::AcquireLock [lock=%s wait=%u]\n", lockName, waitIfBusy));
|
||||||
lockName, notify == nsnull, waitIfBusy));
|
|
||||||
|
|
||||||
ipcLockMsg msg;
|
ipcLockMsg msg;
|
||||||
msg.opcode = IPC_LOCK_OP_ACQUIRE;
|
msg.opcode = IPC_LOCK_OP_ACQUIRE;
|
||||||
|
@ -72,30 +76,31 @@ ipcLockService::AcquireLock(const char *lockName, ipcILockNotify *notify, PRBool
|
||||||
msg.key = lockName;
|
msg.key = lockName;
|
||||||
|
|
||||||
PRUint32 bufLen;
|
PRUint32 bufLen;
|
||||||
PRUint8 *buf = IPC_FlattenLockMsg(&msg, &bufLen);
|
nsAutoPtr<PRUint8> buf( IPC_FlattenLockMsg(&msg, &bufLen) );
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
|
nsresult lockStatus = NS_ERROR_UNEXPECTED;
|
||||||
|
nsDependentCString lockNameStr(lockName);
|
||||||
|
nsCStringHashKey hashKey(&lockNameStr);
|
||||||
|
|
||||||
|
if (!mResultMap.Put(hashKey.GetKey(), &lockStatus))
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
|
// prevent our OnMessageAvailable from being called until we explicitly ask
|
||||||
|
// for it to be called via IPC_WaitMessage.
|
||||||
|
IPC_DISABLE_MESSAGE_OBSERVER_FOR_SCOPE(kLockTargetID);
|
||||||
|
|
||||||
nsresult rv = IPC_SendMessage(0, kLockTargetID, buf, bufLen);
|
nsresult rv = IPC_SendMessage(0, kLockTargetID, buf, bufLen);
|
||||||
free(buf);
|
if (NS_SUCCEEDED(rv)) {
|
||||||
if (NS_FAILED(rv)) {
|
// block the calling thread until we get a response from the daemon
|
||||||
LOG((" SendMessage failed [rv=%x]\n", rv));
|
rv = IPC_WaitMessage(0, kLockTargetID, this, PR_INTERVAL_NO_TIMEOUT);
|
||||||
return rv;
|
if (NS_SUCCEEDED(rv))
|
||||||
|
rv = lockStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notify) {
|
mResultMap.Remove(hashKey.GetKey());
|
||||||
nsCStringKey hashKey(lockName);
|
return rv;
|
||||||
mPendingTable.Put(&hashKey, notify);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// block the calling thread until we get a response from the daemon
|
|
||||||
|
|
||||||
mSyncLockName = lockName;
|
|
||||||
rv = IPC_WaitMessage(0, kLockTargetID, nsnull, PR_INTERVAL_NO_TIMEOUT);
|
|
||||||
mSyncLockName = nsnull;
|
|
||||||
|
|
||||||
return NS_FAILED(rv) ? rv : mSyncLockStatus;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
|
@ -114,12 +119,11 @@ ipcLockService::ReleaseLock(const char *lockName)
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
nsresult rv = IPC_SendMessage(0, kLockTargetID, buf, bufLen);
|
nsresult rv = IPC_SendMessage(0, kLockTargetID, buf, bufLen);
|
||||||
free(buf);
|
delete buf;
|
||||||
|
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv))
|
||||||
|
return rv;
|
||||||
|
|
||||||
nsCStringKey hashKey(lockName);
|
|
||||||
mPendingTable.Remove(&hashKey);
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,37 +134,18 @@ ipcLockService::OnMessageAvailable(PRUint32 unused, const nsID &target,
|
||||||
ipcLockMsg msg;
|
ipcLockMsg msg;
|
||||||
IPC_UnflattenLockMsg(data, dataLen, &msg);
|
IPC_UnflattenLockMsg(data, dataLen, &msg);
|
||||||
|
|
||||||
LOG(("ipcLockService::OnMessageAvailable [lock=%s opcode=%u sync-lock=%s]\n", msg.key, msg.opcode, mSyncLockName));
|
LOG(("ipcLockService::OnMessageAvailable [lock=%s opcode=%u]\n", msg.key, msg.opcode));
|
||||||
|
|
||||||
|
nsDependentCString lockNameStr(msg.key);
|
||||||
|
nsCStringHashKey hashKey(&lockNameStr);
|
||||||
|
|
||||||
|
nsresult *status;
|
||||||
|
mResultMap.Get(hashKey.GetKey(), &status);
|
||||||
|
|
||||||
nsresult status;
|
|
||||||
if (msg.opcode == IPC_LOCK_OP_STATUS_ACQUIRED)
|
if (msg.opcode == IPC_LOCK_OP_STATUS_ACQUIRED)
|
||||||
status = NS_OK;
|
*status = NS_OK;
|
||||||
else
|
else
|
||||||
status = NS_ERROR_FAILURE;
|
*status = NS_ERROR_FAILURE;
|
||||||
|
|
||||||
// handle synchronous waiting case first
|
|
||||||
if (mSyncLockName) {
|
|
||||||
if (strcmp(mSyncLockName, msg.key) == 0) {
|
|
||||||
mSyncLockStatus = status;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
return IPC_WAIT_NEXT_MESSAGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, this is an asynchronous notification
|
|
||||||
NotifyComplete(msg.key, status);
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
ipcLockService::NotifyComplete(const char *lockName, nsresult status)
|
|
||||||
{
|
|
||||||
nsCStringKey hashKey(lockName);
|
|
||||||
nsISupports *obj = mPendingTable.Get(&hashKey); // ADDREFS
|
|
||||||
if (obj) {
|
|
||||||
nsCOMPtr<ipcILockNotify> notify = do_QueryInterface(obj);
|
|
||||||
NS_RELEASE(obj);
|
|
||||||
if (notify)
|
|
||||||
notify->OnAcquireLockComplete(lockName, status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* vim:set ts=4 sw=4 sts=4 et cindent: */
|
||||||
/* ***** 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
|
||||||
*
|
*
|
||||||
|
@ -42,7 +43,14 @@
|
||||||
#include "ipcList.h"
|
#include "ipcList.h"
|
||||||
#include "ipcdclient.h"
|
#include "ipcdclient.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsHashtable.h"
|
#include "nsDataHashtable.h"
|
||||||
|
#include "nsHashKeys.h"
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
typedef nsDataHashtableMT<nsCStringHashKey, nsresult*> ipcLockResultMap;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
class ipcLockService : public ipcILockService
|
class ipcLockService : public ipcILockService
|
||||||
, public ipcIMessageObserver
|
, public ipcIMessageObserver
|
||||||
|
@ -52,20 +60,14 @@ public:
|
||||||
NS_DECL_IPCILOCKSERVICE
|
NS_DECL_IPCILOCKSERVICE
|
||||||
NS_DECL_IPCIMESSAGEOBSERVER
|
NS_DECL_IPCIMESSAGEOBSERVER
|
||||||
|
|
||||||
ipcLockService() : mSyncLockName(nsnull) {}
|
|
||||||
~ipcLockService() { IPC_Shutdown(); }
|
|
||||||
NS_HIDDEN_(nsresult) Init();
|
NS_HIDDEN_(nsresult) Init();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NS_HIDDEN_(void) NotifyComplete(const char *lockName, nsresult status);
|
// maps lockname to the address of a nsresult, which will be assigned a
|
||||||
|
// value once a STATUS event is received.
|
||||||
// map from lockname to locknotify for pending notifications
|
ipcLockResultMap mResultMap;
|
||||||
nsSupportsHashtable mPendingTable;
|
|
||||||
|
|
||||||
// if non-null, then this is the name of the lock we are trying to
|
|
||||||
// synchronously acquire.
|
|
||||||
const char *mSyncLockName;
|
|
||||||
nsresult mSyncLockStatus;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
#endif // !ipcLockService_h__
|
#endif // !ipcLockService_h__
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "ipcModuleUtil.h"
|
#include "ipcModuleUtil.h"
|
||||||
#include "ipcLockProtocol.h"
|
#include "ipcLockProtocol.h"
|
||||||
#include "plhash.h"
|
#include "plhash.h"
|
||||||
|
#include "plstr.h"
|
||||||
|
|
||||||
static const nsID kLockTargetID = IPC_LOCK_TARGETID;
|
static const nsID kLockTargetID = IPC_LOCK_TARGETID;
|
||||||
|
|
||||||
|
@ -75,6 +76,42 @@ struct ipcLockContext
|
||||||
, mNextPending(NULL) {}
|
, mNextPending(NULL) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
PR_STATIC_CALLBACK(void *)
|
||||||
|
ipcLockModule_AllocTable(void *pool, PRSize size)
|
||||||
|
{
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
PR_STATIC_CALLBACK(void)
|
||||||
|
ipcLockModule_FreeTable(void *pool, void *item)
|
||||||
|
{
|
||||||
|
free(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
PR_STATIC_CALLBACK(PLHashEntry *)
|
||||||
|
ipcLockModule_AllocEntry(void *pool, const void *key)
|
||||||
|
{
|
||||||
|
return (PLHashEntry *) malloc(sizeof(PLHashEntry));
|
||||||
|
}
|
||||||
|
|
||||||
|
PR_STATIC_CALLBACK(void)
|
||||||
|
ipcLockModule_FreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
|
||||||
|
{
|
||||||
|
PL_strfree((char *) he->key);
|
||||||
|
free(he);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const PLHashAllocOps ipcLockModule_AllocOps = {
|
||||||
|
ipcLockModule_AllocTable,
|
||||||
|
ipcLockModule_FreeTable,
|
||||||
|
ipcLockModule_AllocEntry,
|
||||||
|
ipcLockModule_FreeEntry
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ipcLockModule_AcquireLock(PRUint32 cid, PRUint8 flags, const char *key)
|
ipcLockModule_AcquireLock(PRUint32 cid, PRUint8 flags, const char *key)
|
||||||
{
|
{
|
||||||
|
@ -113,7 +150,7 @@ ipcLockModule_AcquireLock(PRUint32 cid, PRUint8 flags, const char *key)
|
||||||
if (!ctx)
|
if (!ctx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
PL_HashTableAdd(gLockTable, key, ctx);
|
PL_HashTableAdd(gLockTable, PL_strdup(key), ctx);
|
||||||
|
|
||||||
ipcLockModule_Send(cid, key, IPC_LOCK_OP_STATUS_ACQUIRED);
|
ipcLockModule_Send(cid, key, IPC_LOCK_OP_STATUS_ACQUIRED);
|
||||||
}
|
}
|
||||||
|
@ -187,8 +224,16 @@ PR_STATIC_CALLBACK(PRIntn)
|
||||||
ipcLockModule_ReleaseByCID(PLHashEntry *he, PRIntn i, void *arg)
|
ipcLockModule_ReleaseByCID(PLHashEntry *he, PRIntn i, void *arg)
|
||||||
{
|
{
|
||||||
PRUint32 cid = *(PRUint32 *) arg;
|
PRUint32 cid = *(PRUint32 *) arg;
|
||||||
ipcLockModule_ReleaseLockHelper(cid, (const char *) he->key,
|
|
||||||
(ipcLockContext *) he->value);
|
printf("$$$ ipcLockModule_ReleaseByCID [cid=%u key=%s he=%p]\n", cid, (char*)he->key, (void*)he);
|
||||||
|
|
||||||
|
ipcLockContext *ctx = (ipcLockContext *) he->value;
|
||||||
|
if (ctx->mOwnerID != cid)
|
||||||
|
return HT_ENUMERATE_NEXT;
|
||||||
|
|
||||||
|
if (ipcLockModule_ReleaseLockHelper(cid, (const char *) he->key, ctx))
|
||||||
|
return HT_ENUMERATE_REMOVE;
|
||||||
|
|
||||||
return HT_ENUMERATE_NEXT;
|
return HT_ENUMERATE_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +248,7 @@ ipcLockModule_Init()
|
||||||
PL_HashString,
|
PL_HashString,
|
||||||
PL_CompareStrings,
|
PL_CompareStrings,
|
||||||
PL_CompareValues,
|
PL_CompareValues,
|
||||||
NULL,
|
&ipcLockModule_AllocOps,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,8 +97,6 @@ tmTransactionService::~tmTransactionService() {
|
||||||
if (qmap)
|
if (qmap)
|
||||||
delete qmap;
|
delete qmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC_Shutdown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -116,11 +114,7 @@ tmTransactionService::Init(const nsACString & aNamespace) {
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
rv = IPC_Init();
|
rv = IPC_DefineTarget(kTransModuleID, this, PR_TRUE);
|
||||||
if (NS_FAILED(rv))
|
|
||||||
return rv;
|
|
||||||
|
|
||||||
rv = IPC_DefineTarget(kTransModuleID, this);
|
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return rv;
|
return rv;
|
||||||
|
|
||||||
|
@ -189,7 +183,7 @@ tmTransactionService::Attach(const nsACString & aDomainName,
|
||||||
|
|
||||||
// acquire a lock if neccessary
|
// acquire a lock if neccessary
|
||||||
if (aLockingCall)
|
if (aLockingCall)
|
||||||
lockService->AcquireLock(joinedQueueName, nsnull, PR_TRUE);
|
lockService->AcquireLock(joinedQueueName, PR_TRUE);
|
||||||
// XXX need to handle lock failures
|
// XXX need to handle lock failures
|
||||||
|
|
||||||
if (NS_SUCCEEDED(trans.Init(0, // no IPC client
|
if (NS_SUCCEEDED(trans.Init(0, // no IPC client
|
||||||
|
@ -224,7 +218,7 @@ tmTransactionService::Flush(const nsACString & aDomainName,
|
||||||
PRBool aLockingCall) {
|
PRBool aLockingCall) {
|
||||||
// acquire a lock if neccessary
|
// acquire a lock if neccessary
|
||||||
if (aLockingCall)
|
if (aLockingCall)
|
||||||
lockService->AcquireLock(GetJoinedQueueName(aDomainName), nsnull, PR_TRUE);
|
lockService->AcquireLock(GetJoinedQueueName(aDomainName), PR_TRUE);
|
||||||
|
|
||||||
// synchronous flush
|
// synchronous flush
|
||||||
nsresult rv = SendDetachOrFlush(GetQueueID(aDomainName), TM_FLUSH, PR_TRUE);
|
nsresult rv = SendDetachOrFlush(GetQueueID(aDomainName), TM_FLUSH, PR_TRUE);
|
||||||
|
|
|
@ -50,7 +50,9 @@
|
||||||
// T *mNext;
|
// T *mNext;
|
||||||
// };
|
// };
|
||||||
//
|
//
|
||||||
// objects added to the list must be allocated with operator new.
|
// objects added to the list must be allocated with operator new. class T may
|
||||||
|
// optionally inherit from ipcListNode<T> if it doesn't wish to define mNext
|
||||||
|
// explicitly.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
|
@ -184,4 +186,13 @@ protected:
|
||||||
T *mTail;
|
T *mTail;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class ipcListNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ipcListNode() : mNext(nsnull) {}
|
||||||
|
|
||||||
|
T *mNext;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // !ipcList_h__
|
#endif // !ipcList_h__
|
||||||
|
|
|
@ -169,6 +169,7 @@ myIpcClientQueryHandler::OnQueryComplete(PRUint32 aQueryID,
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if 0
|
||||||
class myIpcLockNotify : public ipcILockNotify
|
class myIpcLockNotify : public ipcILockNotify
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -185,6 +186,7 @@ myIpcLockNotify::OnAcquireLockComplete(const char *lockName, nsresult status)
|
||||||
gIpcLockServ->ReleaseLock(lockName);
|
gIpcLockServ->ReleaseLock(lockName);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -306,10 +308,10 @@ int main(int argc, char **argv)
|
||||||
RETURN_IF_FAILED(rv, "do_GetService(ipcLockServ)");
|
RETURN_IF_FAILED(rv, "do_GetService(ipcLockServ)");
|
||||||
NS_ADDREF(gIpcLockServ = lockService);
|
NS_ADDREF(gIpcLockServ = lockService);
|
||||||
|
|
||||||
nsCOMPtr<ipcILockNotify> notify(new myIpcLockNotify());
|
//nsCOMPtr<ipcILockNotify> notify(new myIpcLockNotify());
|
||||||
gIpcLockServ->AcquireLock("blah", notify, PR_TRUE);
|
gIpcLockServ->AcquireLock("blah", PR_TRUE);
|
||||||
|
|
||||||
rv = gIpcLockServ->AcquireLock("foo", nsnull, PR_TRUE);
|
rv = gIpcLockServ->AcquireLock("foo", PR_TRUE);
|
||||||
printf("*** sync AcquireLock returned [rv=%x]\n", rv);
|
printf("*** sync AcquireLock returned [rv=%x]\n", rv);
|
||||||
|
|
||||||
PLEvent *ev;
|
PLEvent *ev;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче