mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
fixing IPC MT bugs. adding aggressive MT testcase for ipcILockService.
This commit is contained in:
parent
f2f5743420
commit
23e1dd8eaf
@ -145,8 +145,14 @@ interface ipcIService : nsISupports
|
||||
* @param aObserver
|
||||
* the message observer to receive incoming messages for the
|
||||
* specified target. pass null to remove the existing observer.
|
||||
* @param aOnCurrentThread
|
||||
* if true, then the message observer will be called on the same
|
||||
* thread that calls defineTarget. otherwise, aObserver will be
|
||||
* called on a background thread.
|
||||
*/
|
||||
void defineTarget(in nsIDRef aTarget, in ipcIMessageObserver aObserver);
|
||||
void defineTarget(in nsIDRef aTarget,
|
||||
in ipcIMessageObserver aObserver,
|
||||
in boolean aOnCurrentThread);
|
||||
|
||||
/**
|
||||
* send message asynchronously to a client or a module in the IPC daemon.
|
||||
@ -202,6 +208,16 @@ interface ipcIService : nsISupports
|
||||
in nsIDRef aTarget,
|
||||
in ipcIMessageObserver aObserver,
|
||||
in unsigned long aTimeout);
|
||||
|
||||
/**
|
||||
* Call this method to disable the default message observer for a target.
|
||||
*/
|
||||
void disableMessageObserver(in nsIDRef aTarget);
|
||||
|
||||
/**
|
||||
* Call this method to re-enable the default message observer for a target.
|
||||
*/
|
||||
void enableMessageObserver(in nsIDRef aTarget);
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
@ -136,7 +136,7 @@ IPC_METHOD IPC_DisableMessageObserver(
|
||||
|
||||
/**
|
||||
* Call this method to re-enable the message observer configured for a
|
||||
* message target that was disabled by a call to IPC_DisableMessageObserver.
|
||||
* message target.
|
||||
*/
|
||||
IPC_METHOD IPC_EnableMessageObserver(
|
||||
const nsID &aTarget
|
||||
|
@ -170,6 +170,7 @@ ConnCreate(PRFileDesc *fd)
|
||||
s->fds[POLL].fd = PR_NewPollableEvent();
|
||||
s->send_offset = 0;
|
||||
s->in_msg = NULL;
|
||||
s->shutdown = PR_FALSE;
|
||||
|
||||
if (!s->lock || !s->fds[1].fd)
|
||||
{
|
||||
@ -331,32 +332,17 @@ ConnThread(void *arg)
|
||||
// check if something has been added to the send queue. if so, then
|
||||
// acknowledge pollable event (wait should not block), and configure
|
||||
// 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)
|
||||
{
|
||||
PR_WaitForPollableEvent(s->fds[POLL].fd);
|
||||
PR_Lock(s->lock);
|
||||
|
||||
PRBool delayShutdown = PR_FALSE;
|
||||
|
||||
if (!s->send_queue.IsEmpty())
|
||||
{
|
||||
delayShutdown = PR_TRUE;
|
||||
s->fds[SOCK].in_flags |= PR_POLL_WRITE;
|
||||
}
|
||||
|
||||
if (!s->callback_queue.IsEmpty())
|
||||
{
|
||||
delayShutdown = PR_TRUE;
|
||||
s->callback_queue.MoveTo(cbs_to_run);
|
||||
}
|
||||
|
||||
if (!delayShutdown && s->shutdown)
|
||||
rv = NS_ERROR_ABORT;
|
||||
|
||||
PR_Unlock(s->lock);
|
||||
}
|
||||
@ -376,6 +362,14 @@ ConnThread(void *arg)
|
||||
(cb->func)(cb->arg);
|
||||
cbs_to_run.DeleteFirst();
|
||||
}
|
||||
|
||||
// check if we should exit this thread. delay processing a shutdown
|
||||
// request until after all queued up messages have been sent and until
|
||||
// after all queued up callbacks have been run.
|
||||
PR_Lock(s->lock);
|
||||
if (s->shutdown && s->send_queue.IsEmpty() && s->callback_queue.IsEmpty())
|
||||
rv = NS_ERROR_ABORT;
|
||||
PR_Unlock(s->lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -85,9 +85,10 @@ ipcService::ClientExists(PRUint32 aClientID, PRBool *aResult)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ipcService::DefineTarget(const nsID &aTarget, ipcIMessageObserver *aObserver)
|
||||
ipcService::DefineTarget(const nsID &aTarget, ipcIMessageObserver *aObserver,
|
||||
PRBool aOnCurrentThread)
|
||||
{
|
||||
return IPC_DefineTarget(aTarget, aObserver);
|
||||
return IPC_DefineTarget(aTarget, aObserver, aOnCurrentThread);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -102,5 +103,18 @@ ipcService::WaitMessage(PRUint32 aSenderID, const nsID &aTarget,
|
||||
ipcIMessageObserver *aObserver,
|
||||
PRUint32 aTimeout)
|
||||
{
|
||||
return IPC_WaitMessage(aSenderID, aTarget, aObserver, PR_MillisecondsToInterval(aTimeout));
|
||||
return IPC_WaitMessage(aSenderID, aTarget, aObserver,
|
||||
PR_MillisecondsToInterval(aTimeout));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ipcService::DisableMessageObserver(const nsID &aTarget)
|
||||
{
|
||||
return IPC_DisableMessageObserver(aTarget);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ipcService::EnableMessageObserver(const nsID &aTarget)
|
||||
{
|
||||
return IPC_EnableMessageObserver(aTarget);
|
||||
}
|
||||
|
@ -334,31 +334,21 @@ WaitTarget(const nsID &aTarget,
|
||||
{
|
||||
NS_ASSERTION(!lastChecked, "oops");
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
//
|
||||
// NOTE:
|
||||
//
|
||||
// we must start at the top of the pending queue, possibly revisiting
|
||||
// messages that our selector has already rejected. this is necessary
|
||||
// because the queue may have been modified while we were waiting on
|
||||
// the monitor. the impact of this on performance remains to be seen.
|
||||
//
|
||||
// one cheap solution is to keep a counter that is incremented each
|
||||
// time a message is removed from the pending queue. that way we can
|
||||
// avoid revisiting all messages sometimes.
|
||||
//
|
||||
|
||||
if (isValid)
|
||||
lastChecked = beforeLastChecked->mNext;
|
||||
else
|
||||
{
|
||||
lastChecked = td->pendingQ.First();
|
||||
beforeLastChecked = nsnull;
|
||||
}
|
||||
}
|
||||
else
|
||||
lastChecked = td->pendingQ.First();
|
||||
lastChecked = td->pendingQ.First();
|
||||
beforeLastChecked = nsnull;
|
||||
|
||||
// loop over pending queue until we find a message that our selector likes.
|
||||
while (lastChecked)
|
||||
@ -400,6 +390,9 @@ WaitTarget(const nsID &aTarget,
|
||||
break;
|
||||
}
|
||||
mon.Wait(timeEnd - t);
|
||||
|
||||
LOG(("woke up from sleep [pendingQempty=%d connected=%d]\n",
|
||||
td->pendingQ.IsEmpty(), gClientState->connected));
|
||||
}
|
||||
|
||||
return rv;
|
||||
@ -1051,10 +1044,26 @@ IPC_OnConnectionEnd(nsresult error)
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
#ifdef IPC_LOGGING
|
||||
#include "prprf.h"
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
|
||||
// called on a background thread
|
||||
void
|
||||
IPC_OnMessageAvailable(ipcMessage *msg)
|
||||
{
|
||||
#ifdef IPC_LOGGING
|
||||
if (LOG_ENABLED())
|
||||
{
|
||||
char *targetStr = msg->Target().ToString();
|
||||
LOG(("got message for target: %s\n", targetStr));
|
||||
nsMemory::Free(targetStr);
|
||||
|
||||
IPC_LogBinary((const PRUint8 *) msg->Data(), msg->DataLen());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (msg->Target().Equals(IPCM_TARGET))
|
||||
{
|
||||
switch (IPCM_GetType(msg))
|
||||
@ -1101,8 +1110,10 @@ IPC_OnMessageAvailable(ipcMessage *msg)
|
||||
// once we notify the monitor.
|
||||
const nsID target = msg->Target();
|
||||
|
||||
LOG(("placed message on pending queue for target and notifying all...\n"));
|
||||
|
||||
// wake up anyone waiting on this queue
|
||||
mon.Notify();
|
||||
mon.NotifyAll();
|
||||
|
||||
// proxy call to target's message procedure
|
||||
if (dispatchEvent)
|
||||
@ -1113,123 +1124,3 @@ IPC_OnMessageAvailable(ipcMessage *msg)
|
||||
NS_WARNING("message target is undefined");
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
#ifdef BUILD_IPCDCLIENT_STANDALONE
|
||||
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsIEventQueueService.h"
|
||||
|
||||
static PRBool gKeepGoing = PR_TRUE;
|
||||
|
||||
static const nsID kTestTargetID =
|
||||
{ /* e628fc6e-a6a7-48c7-adba-f241d1128fb8 */
|
||||
0xe628fc6e,
|
||||
0xa6a7,
|
||||
0x48c7,
|
||||
{0xad, 0xba, 0xf2, 0x41, 0xd1, 0x12, 0x8f, 0xb8}
|
||||
};
|
||||
|
||||
class TestMessageObserver : public ipcIMessageObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD OnMessageAvailable(PRUint32 aSenderID,
|
||||
const nsID &aTarget,
|
||||
const PRUint8 *aData,
|
||||
PRUint32 aDataLen)
|
||||
{
|
||||
LOG(("got message from %d: \"%s\"\n", aSenderID, aData));
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
NS_IMPL_ISUPPORTS1(TestMessageObserver, ipcIMessageObserver)
|
||||
|
||||
class TestClientObserver : public ipcIClientObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD OnClientStateChange(PRUint32 aClientID,
|
||||
PRUint32 aClientStatus)
|
||||
{
|
||||
LOG(("got client status change [cid=%u status=%u]\n", aClientID, aClientStatus));
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
NS_IMPL_ISUPPORTS1(TestClientObserver, ipcIClientObserver)
|
||||
|
||||
int main()
|
||||
{
|
||||
NS_InitXPCOM2(nsnull, nsnull, nsnull);
|
||||
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIEventQueueService> eqs = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
rv = eqs->CreateMonitoredThreadEventQueue();
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
nsCOMPtr<nsIEventQueue> eq;
|
||||
rv = eqs->ResolveEventQueue(NS_CURRENT_EVENTQ, getter_AddRefs(eq));
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
rv = IPC_Init();
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
rv = IPC_AddClientObserver(new TestClientObserver());
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
PRUint32 cID;
|
||||
rv = IPC_GetID(&cID);
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
LOG(("current process's client id = %lu\n", cID));
|
||||
|
||||
// add test code here
|
||||
|
||||
rv = IPC_AddName("ipcdclient");
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
// test valid lookup
|
||||
PRUint32 resolvedID;
|
||||
rv = IPC_ResolveClientName("ipcdclient", &resolvedID);
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
LOG(("resolved ID is %lu\n", resolvedID));
|
||||
if (resolvedID != cID)
|
||||
NS_NOTREACHED("resolved ID is not what was expected");
|
||||
|
||||
// test bogus lookup
|
||||
rv = IPC_ResolveClientName("foopy", &resolvedID);
|
||||
LOG(("resolving \"foopy\" returned rv=%x\n", rv));
|
||||
if (NS_SUCCEEDED(rv))
|
||||
NS_NOTREACHED("expected client name lookup to fail");
|
||||
|
||||
rv = IPC_DefineTarget(kTestTargetID, new TestMessageObserver());
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
const PRUint8 kData[] = "hello world\n";
|
||||
rv = IPC_SendMessage(0, kTestTargetID, kData, sizeof(kData));
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
PLEvent *ev;
|
||||
while (gKeepGoing)
|
||||
{
|
||||
eq->WaitForEvent(&ev);
|
||||
if (!ev)
|
||||
break;
|
||||
eq->HandleEvent(ev);
|
||||
}
|
||||
|
||||
IPC_Shutdown();
|
||||
}
|
||||
|
||||
NS_ShutdownXPCOM(nsnull);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // BUILD_IPCDCLIENT_STANDALONE
|
||||
|
@ -45,4 +45,8 @@ include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
DIRS = public src
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
DIRS += test
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
@ -43,15 +43,25 @@
|
||||
#include "ipcLockService.h"
|
||||
#include "ipcLockProtocol.h"
|
||||
#include "ipcLog.h"
|
||||
#include "prthread.h"
|
||||
|
||||
static const nsID kLockTargetID = IPC_LOCK_TARGETID;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct ipcPendingLock
|
||||
{
|
||||
const char *name;
|
||||
nsresult status;
|
||||
PRBool complete;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsresult
|
||||
ipcLockService::Init()
|
||||
{
|
||||
if (!mResultMap.Init())
|
||||
if (PR_NewThreadPrivateIndex(&mTPIndex, nsnull) != PR_SUCCESS)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
// Configure OnMessageAvailable to be called on the IPC thread. This is
|
||||
@ -79,12 +89,12 @@ ipcLockService::AcquireLock(const char *lockName, PRBool waitIfBusy)
|
||||
if (!buf)
|
||||
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;
|
||||
ipcPendingLock pendingLock;
|
||||
pendingLock.name = lockName;
|
||||
pendingLock.status = 0xDEADBEEF; // something bogus
|
||||
pendingLock.complete = PR_FALSE;
|
||||
if (PR_SetThreadPrivate(mTPIndex, &pendingLock) != PR_SUCCESS)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
// prevent our OnMessageAvailable from being called until we explicitly ask
|
||||
// for it to be called via IPC_WaitMessage.
|
||||
@ -92,13 +102,18 @@ ipcLockService::AcquireLock(const char *lockName, PRBool waitIfBusy)
|
||||
|
||||
nsresult rv = IPC_SendMessage(0, kLockTargetID, buf, bufLen);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// block the calling thread until we get a response from the daemon
|
||||
rv = IPC_WaitMessage(0, kLockTargetID, this, PR_INTERVAL_NO_TIMEOUT);
|
||||
do {
|
||||
// block the calling thread until we get a response from the daemon
|
||||
rv = IPC_WaitMessage(0, kLockTargetID, this, PR_INTERVAL_NO_TIMEOUT);
|
||||
}
|
||||
while (NS_SUCCEEDED(rv) && !pendingLock.complete);
|
||||
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = lockStatus;
|
||||
rv = pendingLock.status;
|
||||
}
|
||||
|
||||
mResultMap.Remove(hashKey.GetKey());
|
||||
// we could clear the TPD, but that isn't really necessary.
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -126,6 +141,7 @@ ipcLockService::ReleaseLock(const char *lockName)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// called on the same thread that called IPC_WaitMessage
|
||||
NS_IMETHODIMP
|
||||
ipcLockService::OnMessageAvailable(PRUint32 unused, const nsID &target,
|
||||
const PRUint8 *data, PRUint32 dataLen)
|
||||
@ -135,16 +151,18 @@ ipcLockService::OnMessageAvailable(PRUint32 unused, const nsID &target,
|
||||
|
||||
LOG(("ipcLockService::OnMessageAvailable [lock=%s opcode=%u]\n", msg.key, msg.opcode));
|
||||
|
||||
nsDependentCString lockNameStr(msg.key);
|
||||
nsCStringHashKey hashKey(&lockNameStr);
|
||||
ipcPendingLock *pendingLock = (ipcPendingLock *) PR_GetThreadPrivate(mTPIndex);
|
||||
if (strcmp(pendingLock->name, msg.key) == 0) {
|
||||
pendingLock->complete = PR_TRUE;
|
||||
if (msg.opcode == IPC_LOCK_OP_STATUS_ACQUIRED)
|
||||
pendingLock->status = NS_OK;
|
||||
else
|
||||
pendingLock->status = NS_ERROR_FAILURE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult *status;
|
||||
mResultMap.Get(hashKey.GetKey(), &status);
|
||||
LOG(("message does not match; waiting for another...\n"));
|
||||
|
||||
if (msg.opcode == IPC_LOCK_OP_STATUS_ACQUIRED)
|
||||
*status = NS_OK;
|
||||
else
|
||||
*status = NS_ERROR_FAILURE;
|
||||
|
||||
return NS_OK;
|
||||
// else, we got a message that another thread is waiting to receive.
|
||||
return IPC_WAIT_NEXT_MESSAGE;
|
||||
}
|
||||
|
@ -40,15 +40,7 @@
|
||||
#define ipcLockService_h__
|
||||
|
||||
#include "ipcILockService.h"
|
||||
#include "ipcList.h"
|
||||
#include "ipcdclient.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
typedef nsDataHashtableMT<nsCStringHashKey, nsresult*> ipcLockResultMap;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@ -63,9 +55,7 @@ public:
|
||||
NS_HIDDEN_(nsresult) Init();
|
||||
|
||||
private:
|
||||
// maps lockname to the address of a nsresult, which will be assigned a
|
||||
// value once a STATUS event is received.
|
||||
ipcLockResultMap mResultMap;
|
||||
PRUintn mTPIndex;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -225,12 +225,13 @@ ipcLockModule_ReleaseByCID(PLHashEntry *he, PRIntn i, void *arg)
|
||||
{
|
||||
PRUint32 cid = *(PRUint32 *) arg;
|
||||
|
||||
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;
|
||||
|
||||
printf("$$$ ipcLockModule_ReleaseByCID [cid=%u key=%s he=%p]\n",
|
||||
cid, (char*)he->key, (void*)he);
|
||||
|
||||
if (ipcLockModule_ReleaseLockHelper(cid, (const char *) he->key, ctx))
|
||||
return HT_ENUMERATE_REMOVE;
|
||||
|
||||
|
67
ipc/ipcd/extensions/lock/test/Makefile.in
Normal file
67
ipc/ipcd/extensions/lock/test/Makefile.in
Normal file
@ -0,0 +1,67 @@
|
||||
# vim:set ts=8 sw=8 noet:
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla IPC.
|
||||
#
|
||||
# The Initial Developer of the Original Code is IBM Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2004
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Darin Fisher <darin@meer.net>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = dconnect
|
||||
|
||||
REQUIRES = ipcd \
|
||||
nspr \
|
||||
string \
|
||||
xpcom \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
TestIPCLocks.cpp \
|
||||
$(NULL)
|
||||
|
||||
SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=$(BIN_SUFFIX))
|
||||
|
||||
include $(topsrcdir)/config/config.mk
|
||||
|
||||
LIBS = \
|
||||
$(EXTRA_DSO_LIBS) \
|
||||
$(XPCOM_LIBS) \
|
||||
$(NSPR_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
244
ipc/ipcd/extensions/lock/test/TestIPCLocks.cpp
Normal file
244
ipc/ipcd/extensions/lock/test/TestIPCLocks.cpp
Normal file
@ -0,0 +1,244 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla IPC.
|
||||
*
|
||||
* The Initial Developer of the Original Code is IBM Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2004
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@meer.net>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// This test program spawns N copies of itself, and each copy spawns M threads.
|
||||
// Each thread acquires and releases a named, interprocess lock.
|
||||
// Randomized delays are injected at various points to exercise the system, and
|
||||
// help expose any race conditions that may exist.
|
||||
//
|
||||
// Usage: TestIPCLocks [-N]
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "ipcILockService.h"
|
||||
#include "ipcLockCID.h"
|
||||
#include "nsIServiceManagerUtils.h"
|
||||
#include "nsIEventQueueService.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsXPCOM.h"
|
||||
#include "prproces.h"
|
||||
#include "prprf.h"
|
||||
|
||||
#if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS)
|
||||
#include <unistd.h>
|
||||
static unsigned GetPID()
|
||||
{
|
||||
return (unsigned) getpid();
|
||||
}
|
||||
#elif defined(XP_WIN)
|
||||
#include <windows.h>
|
||||
static unsigned GetPID()
|
||||
{
|
||||
return (unsigned) GetCurrentProcessId();
|
||||
}
|
||||
#else
|
||||
static unsigned int GetPID()
|
||||
{
|
||||
return 0; // implement me!
|
||||
}
|
||||
#endif
|
||||
|
||||
static void LOG(const char *fmt, ... )
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
PRUint32 nb = 0;
|
||||
char buf[512];
|
||||
|
||||
nb = PR_snprintf(buf, sizeof(buf), "[%u:%p] ", GetPID(), PR_GetCurrentThread());
|
||||
|
||||
PR_vsnprintf(buf + nb, sizeof(buf) - nb, fmt, ap);
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
|
||||
fwrite(buf, strlen(buf), 1, stdout);
|
||||
fflush(stdout);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void RandomSleep(PRUint32 fromMS, PRUint32 toMS)
|
||||
{
|
||||
PRUint32 ms = fromMS + (PRUint32) ((toMS - fromMS) * ((double) rand() / RAND_MAX));
|
||||
//LOG("putting thread to sleep for %u ms\n", ms);
|
||||
PR_Sleep(PR_MillisecondsToInterval(ms));
|
||||
}
|
||||
|
||||
static ipcILockService *gLockService;
|
||||
|
||||
PR_STATIC_CALLBACK(void) TestThread(void *arg)
|
||||
{
|
||||
const char *lockName = (const char *) arg;
|
||||
|
||||
LOG("entering TestThread [lock=%s]\n", lockName);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
RandomSleep(1000, 1100);
|
||||
|
||||
//LOG("done sleeping\n");
|
||||
|
||||
rv = gLockService->AcquireLock(lockName, PR_TRUE);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
{
|
||||
//LOG("acquired lock \"%s\"\n", lockName);
|
||||
RandomSleep(500, 1000);
|
||||
//LOG("releasing lock \"%s\"\n", lockName);
|
||||
rv = gLockService->ReleaseLock(lockName);
|
||||
if (NS_FAILED(rv))
|
||||
{
|
||||
LOG("failed to release lock [rv=%x]\n", rv);
|
||||
NS_ERROR("failed to release lock");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("failed to acquire lock [rv=%x]\n", rv);
|
||||
NS_NOTREACHED("failed to acquire lock");
|
||||
}
|
||||
|
||||
LOG("exiting TestThread [lock=%s rv=%x]\n", lockName, rv);
|
||||
}
|
||||
|
||||
static const char *kLockNames[] = {
|
||||
"foopy",
|
||||
"test",
|
||||
"1",
|
||||
"xyz",
|
||||
"moz4ever",
|
||||
nsnull
|
||||
};
|
||||
|
||||
static nsresult DoTest()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIEventQueueService> eqs =
|
||||
do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
rv = eqs->CreateMonitoredThreadEventQueue();
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsIEventQueue> eq;
|
||||
rv = eqs->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(eq));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<ipcILockService> lockService =
|
||||
do_GetService(IPC_LOCKSERVICE_CONTRACTID);
|
||||
|
||||
gLockService = lockService;
|
||||
|
||||
PRThread *threads[10] = {0};
|
||||
int i = 0;
|
||||
|
||||
for (const char **lockName = kLockNames; *lockName; ++lockName, ++i)
|
||||
{
|
||||
threads[i] = PR_CreateThread(PR_USER_THREAD,
|
||||
TestThread,
|
||||
(void *) *lockName,
|
||||
PR_PRIORITY_NORMAL,
|
||||
PR_GLOBAL_THREAD,
|
||||
PR_JOINABLE_THREAD,
|
||||
0);
|
||||
}
|
||||
|
||||
for (i=0; threads[i]; ++i)
|
||||
{
|
||||
PR_JoinThread(threads[i]);
|
||||
threads[i] = nsnull;
|
||||
}
|
||||
|
||||
gLockService = nsnull;
|
||||
|
||||
LOG("joined with all threads; exiting DoTest\n");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
LOG("entering main\n");
|
||||
|
||||
int numProcs = 10;
|
||||
|
||||
// if this is a child process, then just run the test
|
||||
if (argc > 1)
|
||||
{
|
||||
if (strcmp(argv[1], "-child") == 0)
|
||||
{
|
||||
RandomSleep(1000, 1000);
|
||||
LOG("running child test\n");
|
||||
NS_InitXPCOM2(nsnull, nsnull, nsnull);
|
||||
DoTest();
|
||||
NS_ShutdownXPCOM(nsnull);
|
||||
return 0;
|
||||
}
|
||||
else if (argv[1][0] == '-')
|
||||
{
|
||||
// argument is a number
|
||||
numProcs = atoi(argv[1] + 1);
|
||||
if (numProcs == 0)
|
||||
{
|
||||
printf("### usage: TestIPCLocks [-N]\n"
|
||||
"where, N is the number of test processes to spawn.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG("sleeping for 1 second\n");
|
||||
PR_Sleep(PR_SecondsToInterval(1));
|
||||
|
||||
PRProcess **procs = (PRProcess **) malloc(sizeof(PRProcess*) * numProcs);
|
||||
int i;
|
||||
|
||||
// else, spawn the child processes
|
||||
for (i=0; i<numProcs; ++i)
|
||||
{
|
||||
char *const argv[] = {"./TestIPCLocks", "-child", nsnull};
|
||||
LOG("spawning child test\n");
|
||||
procs[i] = PR_CreateProcess("./TestIPCLocks", argv, nsnull, nsnull);
|
||||
}
|
||||
|
||||
PRInt32 exitCode;
|
||||
for (i=0; i<numProcs; ++i)
|
||||
PR_WaitProcess(procs[i], &exitCode);
|
||||
|
||||
return 0;
|
||||
}
|
@ -40,9 +40,11 @@
|
||||
#ifdef IPC_LOGGING
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "prenv.h"
|
||||
#include "prprf.h"
|
||||
#include "prthread.h"
|
||||
#include "plstr.h"
|
||||
|
||||
PRBool ipcLogEnabled = PR_FALSE;
|
||||
@ -58,8 +60,9 @@ char ipcLogPrefix[10] = {0};
|
||||
static inline PRUint32
|
||||
WritePrefix(char *buf, PRUint32 bufLen)
|
||||
{
|
||||
return PR_snprintf(buf, bufLen, "[%u] %s ",
|
||||
return PR_snprintf(buf, bufLen, "[%u:%p] %s ",
|
||||
(unsigned) getpid(),
|
||||
PR_GetCurrentThread(),
|
||||
ipcLogPrefix);
|
||||
}
|
||||
#endif
|
||||
@ -73,9 +76,9 @@ WritePrefix(char *buf, PRUint32 bufLen)
|
||||
static inline PRUint32
|
||||
WritePrefix(char *buf, PRUint32 bufLen)
|
||||
{
|
||||
return PR_snprintf(buf, bufLen, "[%u:%u] %s ",
|
||||
return PR_snprintf(buf, bufLen, "[%u:%p] %s ",
|
||||
GetCurrentProcessId(),
|
||||
GetCurrentThreadId(),
|
||||
PR_GetCurrentThread(),
|
||||
ipcLogPrefix);
|
||||
}
|
||||
#endif
|
||||
@ -112,4 +115,33 @@ IPC_Log(const char *fmt, ... )
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
IPC_LogBinary(const PRUint8 *data, PRUint32 len)
|
||||
{
|
||||
PRUint32 i, j, ln;
|
||||
for (i=0; i<len; ) {
|
||||
char line[100] = "";
|
||||
const PRUint8 *p;
|
||||
|
||||
ln = 0;
|
||||
|
||||
p = &data[i];
|
||||
for (j=0; j<PR_MIN(8, len - i); ++j, ++p)
|
||||
ln += PR_snprintf(line + ln, sizeof(line) - ln, "%02x ", *p);
|
||||
|
||||
for (; ln < 32; ++ln)
|
||||
line[ln] = ' ';
|
||||
|
||||
p = &data[i];
|
||||
for (j=0; j<PR_MIN(8, len - i); ++j, ++p)
|
||||
ln += PR_snprintf(line + ln, sizeof(line) - ln, "%c", isprint(*p) ? *p : '.');
|
||||
|
||||
line[ln] = '\0';
|
||||
|
||||
i += (p - &data[i]);
|
||||
|
||||
LOG(("%s\n", line));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -50,6 +50,7 @@
|
||||
extern PRBool ipcLogEnabled;
|
||||
extern NS_HIDDEN_(void) IPC_InitLog(const char *prefix);
|
||||
extern NS_HIDDEN_(void) IPC_Log(const char *fmt, ...);
|
||||
extern NS_HIDDEN_(void) IPC_LogBinary(const PRUint8 *data, PRUint32 len);
|
||||
|
||||
#define IPC_LOG(_args) \
|
||||
PR_BEGIN_MACRO \
|
||||
@ -62,6 +63,7 @@ extern NS_HIDDEN_(void) IPC_Log(const char *fmt, ...);
|
||||
|
||||
#else
|
||||
#define IPC_InitLog(prefix)
|
||||
#define IPC_LogBinary(data, len)
|
||||
#define LOG(args)
|
||||
#define LOG_ENABLED() (0)
|
||||
#endif
|
||||
|
@ -222,7 +222,7 @@ int main(int argc, char **argv)
|
||||
gIpcServ->AddName(argv[1]);
|
||||
}
|
||||
|
||||
ipcServ->DefineTarget(kTestTargetID, new myIpcMessageObserver());
|
||||
ipcServ->DefineTarget(kTestTargetID, new myIpcMessageObserver(), PR_TRUE);
|
||||
|
||||
const char data[] =
|
||||
"01 this is a really long message.\n"
|
||||
|
Loading…
Reference in New Issue
Block a user