fixing IPC MT bugs. adding aggressive MT testcase for ipcILockService.

This commit is contained in:
darin%meer.net 2004-05-11 21:27:28 +00:00
parent f2f5743420
commit 23e1dd8eaf
14 changed files with 476 additions and 203 deletions

View File

@ -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++

View File

@ -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

View File

@ -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
{

View File

@ -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);
}

View File

@ -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

View File

@ -45,4 +45,8 @@ include $(DEPTH)/config/autoconf.mk
DIRS = public src
ifdef ENABLE_TESTS
DIRS += test
endif
include $(topsrcdir)/config/rules.mk

View File

@ -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;
}

View File

@ -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;
};
//-----------------------------------------------------------------------------

View File

@ -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;

View 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

View 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;
}

View File

@ -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

View File

@ -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

View File

@ -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"