michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "plerror.h" michael@0: #include "plgetopt.h" michael@0: michael@0: #include "prinit.h" michael@0: #include "prprf.h" michael@0: #include "prio.h" michael@0: #include "prcvar.h" michael@0: #include "prmon.h" michael@0: #include "prcmon.h" michael@0: #include "prlock.h" michael@0: #include "prerror.h" michael@0: #include "prinit.h" michael@0: #include "prinrval.h" michael@0: #include "prthread.h" michael@0: michael@0: static PRLock *ml = NULL; michael@0: static PRIntervalTime base; michael@0: static PRFileDesc *err = NULL; michael@0: michael@0: typedef struct CMonShared michael@0: { michael@0: PRInt32 o1, o2; michael@0: } CMonShared; michael@0: michael@0: typedef struct MonShared michael@0: { michael@0: PRMonitor *o1, *o2; michael@0: } MonShared; michael@0: michael@0: typedef struct LockShared michael@0: { michael@0: PRLock *o1, *o2; michael@0: PRCondVar *cv1, *cv2; michael@0: } LockShared; michael@0: michael@0: static void LogNow(const char *msg, PRStatus rv) michael@0: { michael@0: PRIntervalTime now = PR_IntervalNow(); michael@0: PR_Lock(ml); michael@0: PR_fprintf(err, "%6ld: %s", (now - base), msg); michael@0: if (PR_FAILURE == rv) PL_FPrintError(err, " "); michael@0: else PR_fprintf(err, "\n"); michael@0: PR_Unlock(ml); michael@0: } /* LogNow */ michael@0: michael@0: static void Help(void) michael@0: { michael@0: PR_fprintf(err, "Usage: [-[d][l][m][c]] [-h]\n"); michael@0: PR_fprintf(err, "\t-d debug mode (default: FALSE)\n"); michael@0: PR_fprintf(err, "\t-l test with locks (default: FALSE)\n"); michael@0: PR_fprintf(err, "\t-m tests with monitors (default: FALSE)\n"); michael@0: PR_fprintf(err, "\t-c tests with cmonitors (default: FALSE)\n"); michael@0: PR_fprintf(err, "\t-h help\n"); michael@0: } /* Help */ michael@0: michael@0: static void PR_CALLBACK T2CMon(void *arg) michael@0: { michael@0: PRStatus rv; michael@0: CMonShared *shared = (CMonShared*)arg; michael@0: michael@0: PR_CEnterMonitor(&shared->o1); michael@0: LogNow("T2 waiting 5 seconds on o1", PR_SUCCESS); michael@0: rv = PR_CWait(&shared->o1, PR_SecondsToInterval(5)); michael@0: if (PR_SUCCESS == rv) LogNow("T2 resuming on o1", rv); michael@0: else LogNow("T2 wait failed on o1", rv); michael@0: michael@0: rv = PR_CNotify(&shared->o1); michael@0: if (PR_SUCCESS == rv) LogNow("T2 notified o1", rv); michael@0: else LogNow("T2 notify on o1 failed", rv); michael@0: michael@0: PR_CExitMonitor(&shared->o1); michael@0: } /* T2CMon */ michael@0: michael@0: static void PR_CALLBACK T3CMon(void *arg) michael@0: { michael@0: PRStatus rv; michael@0: CMonShared *shared = (CMonShared*)arg; michael@0: michael@0: PR_CEnterMonitor(&shared->o2); michael@0: LogNow("T3 waiting 5 seconds on o2", PR_SUCCESS); michael@0: rv = PR_CWait(&shared->o2, PR_SecondsToInterval(5)); michael@0: if (PR_SUCCESS == rv) LogNow("T3 resuming on o2", rv); michael@0: else LogNow("T3 wait failed on o2", rv); michael@0: rv = PR_CNotify(&shared->o2); michael@0: LogNow("T3 notify on o2", rv); michael@0: PR_CExitMonitor(&shared->o2); michael@0: michael@0: } /* T3CMon */ michael@0: michael@0: static CMonShared sharedCM; michael@0: michael@0: static void T1CMon(void) michael@0: { michael@0: PRStatus rv; michael@0: PRThread *t2, *t3; michael@0: michael@0: PR_fprintf(err, "\n**********************************\n"); michael@0: PR_fprintf(err, " CACHED MONITORS\n"); michael@0: PR_fprintf(err, "**********************************\n"); michael@0: michael@0: base = PR_IntervalNow(); michael@0: michael@0: PR_CEnterMonitor(&sharedCM.o1); michael@0: LogNow("T1 waiting 3 seconds on o1", PR_SUCCESS); michael@0: rv = PR_CWait(&sharedCM.o1, PR_SecondsToInterval(3)); michael@0: if (PR_SUCCESS == rv) LogNow("T1 resuming on o1", rv); michael@0: else LogNow("T1 wait on o1 failed", rv); michael@0: PR_CExitMonitor(&sharedCM.o1); michael@0: michael@0: LogNow("T1 creating T2", PR_SUCCESS); michael@0: t2 = PR_CreateThread( michael@0: PR_USER_THREAD, T2CMon, &sharedCM, PR_PRIORITY_NORMAL, michael@0: PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); michael@0: michael@0: LogNow("T1 creating T3", PR_SUCCESS); michael@0: t3 = PR_CreateThread( michael@0: PR_USER_THREAD, T3CMon, &sharedCM, PR_PRIORITY_NORMAL, michael@0: PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); michael@0: michael@0: PR_CEnterMonitor(&sharedCM.o2); michael@0: LogNow("T1 waiting forever on o2", PR_SUCCESS); michael@0: rv = PR_CWait(&sharedCM.o2, PR_INTERVAL_NO_TIMEOUT); michael@0: if (PR_SUCCESS == rv) LogNow("T1 resuming on o2", rv); michael@0: else LogNow("T1 wait on o2 failed", rv); michael@0: PR_CExitMonitor(&sharedCM.o2); michael@0: michael@0: (void)PR_JoinThread(t2); michael@0: (void)PR_JoinThread(t3); michael@0: michael@0: } /* T1CMon */ michael@0: michael@0: static void PR_CALLBACK T2Mon(void *arg) michael@0: { michael@0: PRStatus rv; michael@0: MonShared *shared = (MonShared*)arg; michael@0: michael@0: PR_EnterMonitor(shared->o1); michael@0: LogNow("T2 waiting 5 seconds on o1", PR_SUCCESS); michael@0: rv = PR_Wait(shared->o1, PR_SecondsToInterval(5)); michael@0: if (PR_SUCCESS == rv) LogNow("T2 resuming on o1", rv); michael@0: else LogNow("T2 wait failed on o1", rv); michael@0: michael@0: rv = PR_Notify(shared->o1); michael@0: if (PR_SUCCESS == rv) LogNow("T2 notified o1", rv); michael@0: else LogNow("T2 notify on o1 failed", rv); michael@0: michael@0: PR_ExitMonitor(shared->o1); michael@0: } /* T2Mon */ michael@0: michael@0: static void PR_CALLBACK T3Mon(void *arg) michael@0: { michael@0: PRStatus rv; michael@0: MonShared *shared = (MonShared*)arg; michael@0: michael@0: PR_EnterMonitor(shared->o2); michael@0: LogNow("T3 waiting 5 seconds on o2", PR_SUCCESS); michael@0: rv = PR_Wait(shared->o2, PR_SecondsToInterval(5)); michael@0: if (PR_SUCCESS == rv) LogNow("T3 resuming on o2", rv); michael@0: else LogNow("T3 wait failed on o2", rv); michael@0: rv = PR_Notify(shared->o2); michael@0: LogNow("T3 notify on o2", rv); michael@0: PR_ExitMonitor(shared->o2); michael@0: michael@0: } /* T3Mon */ michael@0: michael@0: static MonShared sharedM; michael@0: static void T1Mon(void) michael@0: { michael@0: PRStatus rv; michael@0: PRThread *t2, *t3; michael@0: michael@0: PR_fprintf(err, "\n**********************************\n"); michael@0: PR_fprintf(err, " MONITORS\n"); michael@0: PR_fprintf(err, "**********************************\n"); michael@0: michael@0: sharedM.o1 = PR_NewMonitor(); michael@0: sharedM.o2 = PR_NewMonitor(); michael@0: michael@0: base = PR_IntervalNow(); michael@0: michael@0: PR_EnterMonitor(sharedM.o1); michael@0: LogNow("T1 waiting 3 seconds on o1", PR_SUCCESS); michael@0: rv = PR_Wait(sharedM.o1, PR_SecondsToInterval(3)); michael@0: if (PR_SUCCESS == rv) LogNow("T1 resuming on o1", rv); michael@0: else LogNow("T1 wait on o1 failed", rv); michael@0: PR_ExitMonitor(sharedM.o1); michael@0: michael@0: LogNow("T1 creating T2", PR_SUCCESS); michael@0: t2 = PR_CreateThread( michael@0: PR_USER_THREAD, T2Mon, &sharedM, PR_PRIORITY_NORMAL, michael@0: PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); michael@0: michael@0: LogNow("T1 creating T3", PR_SUCCESS); michael@0: t3 = PR_CreateThread( michael@0: PR_USER_THREAD, T3Mon, &sharedM, PR_PRIORITY_NORMAL, michael@0: PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); michael@0: michael@0: PR_EnterMonitor(sharedM.o2); michael@0: LogNow("T1 waiting forever on o2", PR_SUCCESS); michael@0: rv = PR_Wait(sharedM.o2, PR_INTERVAL_NO_TIMEOUT); michael@0: if (PR_SUCCESS == rv) LogNow("T1 resuming on o2", rv); michael@0: else LogNow("T1 wait on o2 failed", rv); michael@0: PR_ExitMonitor(sharedM.o2); michael@0: michael@0: (void)PR_JoinThread(t2); michael@0: (void)PR_JoinThread(t3); michael@0: michael@0: PR_DestroyMonitor(sharedM.o1); michael@0: PR_DestroyMonitor(sharedM.o2); michael@0: michael@0: } /* T1Mon */ michael@0: michael@0: static void PR_CALLBACK T2Lock(void *arg) michael@0: { michael@0: PRStatus rv; michael@0: LockShared *shared = (LockShared*)arg; michael@0: michael@0: PR_Lock(shared->o1); michael@0: LogNow("T2 waiting 5 seconds on o1", PR_SUCCESS); michael@0: rv = PR_WaitCondVar(shared->cv1, PR_SecondsToInterval(5)); michael@0: if (PR_SUCCESS == rv) LogNow("T2 resuming on o1", rv); michael@0: else LogNow("T2 wait failed on o1", rv); michael@0: michael@0: rv = PR_NotifyCondVar(shared->cv1); michael@0: if (PR_SUCCESS == rv) LogNow("T2 notified o1", rv); michael@0: else LogNow("T2 notify on o1 failed", rv); michael@0: michael@0: PR_Unlock(shared->o1); michael@0: } /* T2Lock */ michael@0: michael@0: static void PR_CALLBACK T3Lock(void *arg) michael@0: { michael@0: PRStatus rv; michael@0: LockShared *shared = (LockShared*)arg; michael@0: michael@0: PR_Lock(shared->o2); michael@0: LogNow("T3 waiting 5 seconds on o2", PR_SUCCESS); michael@0: rv = PR_WaitCondVar(shared->cv2, PR_SecondsToInterval(5)); michael@0: if (PR_SUCCESS == rv) LogNow("T3 resuming on o2", rv); michael@0: else LogNow("T3 wait failed on o2", rv); michael@0: rv = PR_NotifyCondVar(shared->cv2); michael@0: LogNow("T3 notify on o2", rv); michael@0: PR_Unlock(shared->o2); michael@0: michael@0: } /* T3Lock */ michael@0: michael@0: /* michael@0: ** Make shared' a static variable for Win16 michael@0: */ michael@0: static LockShared sharedL; michael@0: michael@0: static void T1Lock(void) michael@0: { michael@0: PRStatus rv; michael@0: PRThread *t2, *t3; michael@0: sharedL.o1 = PR_NewLock(); michael@0: sharedL.o2 = PR_NewLock(); michael@0: sharedL.cv1 = PR_NewCondVar(sharedL.o1); michael@0: sharedL.cv2 = PR_NewCondVar(sharedL.o2); michael@0: michael@0: PR_fprintf(err, "\n**********************************\n"); michael@0: PR_fprintf(err, " LOCKS\n"); michael@0: PR_fprintf(err, "**********************************\n"); michael@0: michael@0: base = PR_IntervalNow(); michael@0: michael@0: PR_Lock(sharedL.o1); michael@0: LogNow("T1 waiting 3 seconds on o1", PR_SUCCESS); michael@0: rv = PR_WaitCondVar(sharedL.cv1, PR_SecondsToInterval(3)); michael@0: if (PR_SUCCESS == rv) LogNow("T1 resuming on o1", rv); michael@0: else LogNow("T1 wait on o1 failed", rv); michael@0: PR_Unlock(sharedL.o1); michael@0: michael@0: LogNow("T1 creating T2", PR_SUCCESS); michael@0: t2 = PR_CreateThread( michael@0: PR_USER_THREAD, T2Lock, &sharedL, PR_PRIORITY_NORMAL, michael@0: PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); michael@0: michael@0: LogNow("T1 creating T3", PR_SUCCESS); michael@0: t3 = PR_CreateThread( michael@0: PR_USER_THREAD, T3Lock, &sharedL, PR_PRIORITY_NORMAL, michael@0: PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); michael@0: michael@0: PR_Lock(sharedL.o2); michael@0: LogNow("T1 waiting forever on o2", PR_SUCCESS); michael@0: rv = PR_WaitCondVar(sharedL.cv2, PR_INTERVAL_NO_TIMEOUT); michael@0: if (PR_SUCCESS == rv) LogNow("T1 resuming on o2", rv); michael@0: else LogNow("T1 wait on o2 failed", rv); michael@0: PR_Unlock(sharedL.o2); michael@0: michael@0: (void)PR_JoinThread(t2); michael@0: (void)PR_JoinThread(t3); michael@0: michael@0: PR_DestroyLock(sharedL.o1); michael@0: PR_DestroyLock(sharedL.o2); michael@0: PR_DestroyCondVar(sharedL.cv1); michael@0: PR_DestroyCondVar(sharedL.cv2); michael@0: } /* T1Lock */ michael@0: michael@0: static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv ) michael@0: { michael@0: PLOptStatus os; michael@0: PLOptState *opt = PL_CreateOptState(argc, argv, "dhlmc"); michael@0: PRBool locks = PR_FALSE, monitors = PR_FALSE, cmonitors = PR_FALSE; michael@0: michael@0: err = PR_GetSpecialFD(PR_StandardError); michael@0: michael@0: while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) michael@0: { michael@0: if (PL_OPT_BAD == os) continue; michael@0: switch (opt->option) michael@0: { michael@0: case 'd': /* debug mode (noop) */ michael@0: break; michael@0: case 'l': /* locks */ michael@0: locks = PR_TRUE; michael@0: break; michael@0: case 'm': /* monitors */ michael@0: monitors = PR_TRUE; michael@0: break; michael@0: case 'c': /* cached monitors */ michael@0: cmonitors = PR_TRUE; michael@0: break; michael@0: case 'h': /* needs guidance */ michael@0: default: michael@0: Help(); michael@0: return 2; michael@0: } michael@0: } michael@0: PL_DestroyOptState(opt); michael@0: michael@0: ml = PR_NewLock(); michael@0: if (locks) T1Lock(); michael@0: if (monitors) T1Mon(); michael@0: if (cmonitors) T1CMon(); michael@0: michael@0: PR_DestroyLock(ml); michael@0: michael@0: PR_fprintf(err, "Done!\n"); michael@0: return 0; michael@0: } /* main */ michael@0: michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: PRIntn rv; michael@0: michael@0: PR_STDIO_INIT(); michael@0: rv = PR_Initialize(RealMain, argc, argv, 0); michael@0: return rv; michael@0: } /* main */ michael@0: /* xnotify.c */