tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

lock.c (15109B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 /*
      7 ** File:        lock.c
      8 ** Purpose:     test basic locking functions
      9 **
     10 ** Modification History:
     11 ** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
     12 **           The debug mode will print all of the printfs associated with this
     13 *test.
     14 **           The regress mode will be the default mode. Since the regress tool
     15 *limits
     16 **           the output to a one line status:PASS or FAIL,all of the printf
     17 *statements
     18 **           have been handled with an if (debug_mode) statement.
     19 ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been
     20 *updated to
     21 **          recognize the return code from tha main program.
     22 **
     23 ** 11-Aug-97 LarryH. Win16 port of NSPR.
     24 **           - Added "PASS", "FAIL" messages on completion.
     25 **           - Change stack variables to static scope variables
     26 **             because of shadow-stack use by Win16
     27 **           - Added PR_CALLBACK attribute to functions called by NSPR
     28 **           - Added command line arguments:
     29 **             - l <num> to control the number of loops
     30 **             - c <num> to control the number of CPUs.
     31 **             (was positional argv).
     32 **
     33 **
     34 ***********************************************************************/
     35 
     36 /***********************************************************************
     37 ** Includes
     38 ***********************************************************************/
     39 /* Used to get the command line option */
     40 #include "plgetopt.h"
     41 
     42 #include "prio.h"
     43 #include "prcmon.h"
     44 #include "prinit.h"
     45 #include "prinrval.h"
     46 #include "prprf.h"
     47 #include "prlock.h"
     48 #include "prlog.h"
     49 #include "prmon.h"
     50 #include "prmem.h"
     51 #include "prthread.h"
     52 #include "prtypes.h"
     53 
     54 #include "plstr.h"
     55 
     56 #include <stdlib.h>
     57 
     58 #if defined(XP_UNIX)
     59 #  include <string.h>
     60 #endif
     61 
     62 static PRIntn failed_already = 0;
     63 static PRFileDesc* std_err = NULL;
     64 static PRBool verbosity = PR_FALSE;
     65 static PRBool debug_mode = PR_FALSE;
     66 
     67 const static PRIntervalTime contention_interval = 50;
     68 
     69 typedef struct LockContentious_s {
     70  PRLock* ml;
     71  PRInt32 loops;
     72  PRUint32 contender;
     73  PRUint32 contentious;
     74  PRIntervalTime overhead;
     75  PRIntervalTime interval;
     76 } LockContentious_t;
     77 
     78 typedef struct MonitorContentious_s {
     79  PRMonitor* ml;
     80  PRInt32 loops;
     81  PRUint32 contender;
     82  PRUint32 contentious;
     83  PRIntervalTime overhead;
     84  PRIntervalTime interval;
     85 } MonitorContentious_t;
     86 
     87 static PRIntervalTime Sleeper(PRUint32 loops) {
     88  PRIntervalTime predicted = 0;
     89  while (loops-- > 0) {
     90    predicted += contention_interval;
     91    (void)PR_Sleep(contention_interval);
     92  }
     93  return predicted;
     94 } /* Sleeper */
     95 
     96 /*
     97 ** BASIC LOCKS
     98 */
     99 static PRIntervalTime MakeLock(PRUint32 loops) {
    100  PRLock* ml = NULL;
    101  while (loops-- > 0) {
    102    ml = PR_NewLock();
    103    PR_DestroyLock(ml);
    104    ml = NULL;
    105  }
    106  return 0;
    107 } /* MakeLock */
    108 
    109 static PRIntervalTime NonContentiousLock(PRUint32 loops) {
    110  PRLock* ml = NULL;
    111  ml = PR_NewLock();
    112  while (loops-- > 0) {
    113    PR_Lock(ml);
    114    PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(ml);
    115    PR_Unlock(ml);
    116  }
    117  PR_DestroyLock(ml);
    118  return 0;
    119 } /* NonContentiousLock */
    120 
    121 static void PR_CALLBACK LockContender(void* arg) {
    122  LockContentious_t* contention = (LockContentious_t*)arg;
    123  while (contention->loops-- > 0) {
    124    PR_Lock(contention->ml);
    125    PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
    126    contention->contender += 1;
    127    contention->overhead += contention->interval;
    128    PR_Sleep(contention->interval);
    129    PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
    130    PR_Unlock(contention->ml);
    131  }
    132 } /* LockContender */
    133 
    134 static PRIntervalTime ContentiousLock(PRUint32 loops) {
    135  PRStatus status;
    136  PRThread* thread = NULL;
    137  LockContentious_t* contention;
    138  PRIntervalTime rv, overhead, timein = PR_IntervalNow();
    139 
    140  contention = PR_NEWZAP(LockContentious_t);
    141  contention->loops = loops;
    142  contention->overhead = 0;
    143  contention->ml = PR_NewLock();
    144  contention->interval = contention_interval;
    145  thread =
    146      PR_CreateThread(PR_USER_THREAD, LockContender, contention,
    147                      PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
    148  PR_ASSERT(thread != NULL);
    149 
    150  overhead = PR_IntervalNow() - timein;
    151 
    152  while (contention->loops-- > 0) {
    153    PR_Lock(contention->ml);
    154    PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
    155    contention->contentious += 1;
    156    contention->overhead += contention->interval;
    157    PR_Sleep(contention->interval);
    158    PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
    159    PR_Unlock(contention->ml);
    160  }
    161 
    162  timein = PR_IntervalNow();
    163  status = PR_JoinThread(thread);
    164  PR_DestroyLock(contention->ml);
    165  overhead += (PR_IntervalNow() - timein);
    166  rv = overhead + contention->overhead;
    167  if (verbosity)
    168    PR_fprintf(std_err, "Access ratio: %u to %u\n", contention->contentious,
    169               contention->contender);
    170  PR_Free(contention);
    171  return rv;
    172 } /* ContentiousLock */
    173 
    174 /*
    175 ** MONITORS
    176 */
    177 static PRIntervalTime MakeMonitor(PRUint32 loops) {
    178  PRMonitor* ml = NULL;
    179  while (loops-- > 0) {
    180    ml = PR_NewMonitor();
    181    PR_DestroyMonitor(ml);
    182    ml = NULL;
    183  }
    184  return 0;
    185 } /* MakeMonitor */
    186 
    187 static PRIntervalTime NonContentiousMonitor(PRUint32 loops) {
    188  PRMonitor* ml = NULL;
    189  ml = PR_NewMonitor();
    190  while (loops-- > 0) {
    191    PR_EnterMonitor(ml);
    192    PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
    193    PR_ExitMonitor(ml);
    194  }
    195  PR_DestroyMonitor(ml);
    196  return 0;
    197 } /* NonContentiousMonitor */
    198 
    199 static void PR_CALLBACK TryEntry(void* arg) {
    200  PRMonitor* ml = (PRMonitor*)arg;
    201  if (debug_mode) {
    202    PR_fprintf(std_err, "Reentrant thread created\n");
    203  }
    204  PR_EnterMonitor(ml);
    205  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
    206  if (debug_mode) {
    207    PR_fprintf(std_err, "Reentrant thread acquired monitor\n");
    208  }
    209  PR_ExitMonitor(ml);
    210  if (debug_mode) {
    211    PR_fprintf(std_err, "Reentrant thread released monitor\n");
    212  }
    213 } /* TryEntry */
    214 
    215 static PRIntervalTime ReentrantMonitor(PRUint32 loops) {
    216  PRStatus status;
    217  PRThread* thread;
    218  PRMonitor* ml = PR_NewMonitor();
    219  if (debug_mode) {
    220    PR_fprintf(std_err, "\nMonitor created for reentrant test\n");
    221  }
    222 
    223  PR_EnterMonitor(ml);
    224  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
    225  PR_EnterMonitor(ml);
    226  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
    227  if (debug_mode) {
    228    PR_fprintf(std_err, "Monitor acquired twice\n");
    229  }
    230 
    231  thread = PR_CreateThread(PR_USER_THREAD, TryEntry, ml, PR_PRIORITY_LOW,
    232                           PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
    233  PR_ASSERT(thread != NULL);
    234  PR_Sleep(PR_SecondsToInterval(1));
    235  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
    236 
    237  PR_ExitMonitor(ml);
    238  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
    239  if (debug_mode) {
    240    PR_fprintf(std_err, "Monitor released first time\n");
    241  }
    242 
    243  PR_ExitMonitor(ml);
    244  if (debug_mode) {
    245    PR_fprintf(std_err, "Monitor released second time\n");
    246  }
    247 
    248  status = PR_JoinThread(thread);
    249  if (debug_mode)
    250    PR_fprintf(std_err, "Reentrant thread joined %s\n",
    251               (status == PR_SUCCESS) ? "successfully" : "in error");
    252 
    253  PR_DestroyMonitor(ml);
    254  return 0;
    255 } /* ReentrantMonitor */
    256 
    257 static void PR_CALLBACK MonitorContender(void* arg) {
    258  MonitorContentious_t* contention = (MonitorContentious_t*)arg;
    259  while (contention->loops-- > 0) {
    260    PR_EnterMonitor(contention->ml);
    261    PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
    262    contention->contender += 1;
    263    contention->overhead += contention->interval;
    264    PR_Sleep(contention->interval);
    265    PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
    266    PR_ExitMonitor(contention->ml);
    267  }
    268 } /* MonitorContender */
    269 
    270 static PRUint32 ContentiousMonitor(PRUint32 loops) {
    271  PRStatus status;
    272  PRThread* thread = NULL;
    273  MonitorContentious_t* contention;
    274  PRIntervalTime rv, overhead, timein = PR_IntervalNow();
    275 
    276  contention = PR_NEWZAP(MonitorContentious_t);
    277  contention->loops = loops;
    278  contention->overhead = 0;
    279  contention->ml = PR_NewMonitor();
    280  contention->interval = contention_interval;
    281  thread =
    282      PR_CreateThread(PR_USER_THREAD, MonitorContender, contention,
    283                      PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
    284  PR_ASSERT(thread != NULL);
    285 
    286  overhead = PR_IntervalNow() - timein;
    287 
    288  while (contention->loops-- > 0) {
    289    PR_EnterMonitor(contention->ml);
    290    PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
    291    contention->contentious += 1;
    292    contention->overhead += contention->interval;
    293    PR_Sleep(contention->interval);
    294    PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
    295    PR_ExitMonitor(contention->ml);
    296  }
    297 
    298  timein = PR_IntervalNow();
    299  status = PR_JoinThread(thread);
    300  PR_DestroyMonitor(contention->ml);
    301  overhead += (PR_IntervalNow() - timein);
    302  rv = overhead + contention->overhead;
    303  if (verbosity)
    304    PR_fprintf(std_err, "Access ratio: %u to %u\n", contention->contentious,
    305               contention->contender);
    306  PR_Free(contention);
    307  return rv;
    308 } /* ContentiousMonitor */
    309 
    310 /*
    311 ** CACHED MONITORS
    312 */
    313 static PRIntervalTime NonContentiousCMonitor(PRUint32 loops) {
    314  MonitorContentious_t contention;
    315  while (loops-- > 0) {
    316    PR_CEnterMonitor(&contention);
    317    PR_CExitMonitor(&contention);
    318  }
    319  return 0;
    320 } /* NonContentiousCMonitor */
    321 
    322 static void PR_CALLBACK Contender(void* arg) {
    323  MonitorContentious_t* contention = (MonitorContentious_t*)arg;
    324  while (contention->loops-- > 0) {
    325    PR_CEnterMonitor(contention);
    326    contention->contender += 1;
    327    contention->overhead += contention->interval;
    328    PR_Sleep(contention->interval);
    329    PR_CExitMonitor(contention);
    330  }
    331 } /* Contender */
    332 
    333 static PRIntervalTime ContentiousCMonitor(PRUint32 loops) {
    334  PRStatus status;
    335  PRThread* thread = NULL;
    336  MonitorContentious_t* contention;
    337  PRIntervalTime overhead, timein = PR_IntervalNow();
    338 
    339  contention = PR_NEWZAP(MonitorContentious_t);
    340  contention->ml = NULL;
    341  contention->loops = loops;
    342  contention->interval = contention_interval;
    343  thread =
    344      PR_CreateThread(PR_USER_THREAD, Contender, contention, PR_PRIORITY_LOW,
    345                      PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
    346  PR_ASSERT(thread != NULL);
    347 
    348  overhead = PR_IntervalNow() - timein;
    349 
    350  while (contention->loops-- > 0) {
    351    PR_CEnterMonitor(contention);
    352    contention->contentious += 1;
    353    contention->overhead += contention->interval;
    354    PR_Sleep(contention->interval);
    355    PR_CExitMonitor(contention);
    356  }
    357 
    358  timein = PR_IntervalNow();
    359  status = PR_JoinThread(thread);
    360  overhead += (PR_IntervalNow() - timein);
    361  overhead += overhead + contention->overhead;
    362  if (verbosity)
    363    PR_fprintf(std_err, "Access ratio: %u to %u\n", contention->contentious,
    364               contention->contender);
    365  PR_Free(contention);
    366  return overhead;
    367 } /* ContentiousCMonitor */
    368 
    369 static PRIntervalTime Test(const char* msg, PRUint32 (*test)(PRUint32 loops),
    370                           PRUint32 loops, PRIntervalTime overhead) {
    371  /*
    372   * overhead - overhead not measured by the test.
    373   * duration - wall clock time it took to perform test.
    374   * predicted - extra time test says should not be counted
    375   *
    376   * Time accountable to the test is duration - overhead - predicted
    377   * All times are Intervals and accumulated for all iterations.
    378   */
    379  PRFloat64 elapsed;
    380  PRIntervalTime accountable, duration;
    381  PRUintn spaces = PL_strlen(msg);
    382  PRIntervalTime timeout, timein = PR_IntervalNow();
    383  PRIntervalTime predicted = test(loops);
    384  timeout = PR_IntervalNow();
    385  duration = timeout - timein;
    386 
    387  if (debug_mode) {
    388    accountable = duration - predicted;
    389    accountable -= overhead;
    390    elapsed = (PRFloat64)PR_IntervalToMicroseconds(accountable);
    391    PR_fprintf(PR_STDOUT, "%s:", msg);
    392    while (spaces++ < 50) {
    393      PR_fprintf(PR_STDOUT, " ");
    394    }
    395    if ((PRInt32)accountable < 0) {
    396      PR_fprintf(PR_STDOUT, "*****.** usecs/iteration\n");
    397    } else {
    398      PR_fprintf(PR_STDOUT, "%8.2f usecs/iteration\n", elapsed / loops);
    399    }
    400  }
    401  return duration;
    402 } /* Test */
    403 
    404 int main(int argc, char** argv) {
    405  PRBool rv = PR_TRUE;
    406  PRIntervalTime duration;
    407  PRUint32 cpu, cpus = 2, loops = 100;
    408 
    409  PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
    410  {
    411    /* The command line argument: -d is used to determine if the test is being
    412    run in debug mode. The regress tool requires only one line output:PASS or
    413    FAIL. All of the printfs associated with this test has been handled with a
    414    if (debug_mode) test. Command line argument -l <num> sets the number of
    415    loops. Command line argument -c <num> sets the number of cpus. Usage: lock
    416    [-d] [-l <num>] [-c <num>]
    417    */
    418    PLOptStatus os;
    419    PLOptState* opt = PL_CreateOptState(argc, argv, "dvl:c:");
    420    while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
    421      if (PL_OPT_BAD == os) {
    422        continue;
    423      }
    424      switch (opt->option) {
    425        case 'd': /* debug mode */
    426          debug_mode = PR_TRUE;
    427          break;
    428        case 'v': /* debug mode */
    429          verbosity = PR_TRUE;
    430          break;
    431        case 'l': /* number of loops */
    432          loops = atoi(opt->value);
    433          break;
    434        case 'c': /* number of cpus */
    435          cpus = atoi(opt->value);
    436          break;
    437        default:
    438          break;
    439      }
    440    }
    441    PL_DestroyOptState(opt);
    442  }
    443 
    444  /* main test */
    445  PR_SetConcurrency(8);
    446 
    447  if (loops == 0) {
    448    loops = 100;
    449  }
    450  if (debug_mode) {
    451    std_err = PR_STDERR;
    452    PR_fprintf(std_err, "Lock: Using %d loops\n", loops);
    453  }
    454 
    455  if (cpus == 0) {
    456    cpus = 2;
    457  }
    458  if (debug_mode) {
    459    PR_fprintf(std_err, "Lock: Using %d cpu(s)\n", cpus);
    460  }
    461 
    462  (void)Sleeper(10); /* try filling in the caches */
    463 
    464  for (cpu = 1; cpu <= cpus; ++cpu) {
    465    if (debug_mode) {
    466      PR_fprintf(std_err, "\nLock: Using %d CPU(s)\n", cpu);
    467    }
    468    PR_SetConcurrency(cpu);
    469 
    470    duration = Test("Overhead of PR_Sleep", Sleeper, loops, 0);
    471    duration = 0;
    472 
    473    (void)Test("Lock creation/deletion", MakeLock, loops, 0);
    474    (void)Test("Lock non-contentious locking/unlocking", NonContentiousLock,
    475               loops, 0);
    476    (void)Test("Lock contentious locking/unlocking", ContentiousLock, loops,
    477               duration);
    478    (void)Test("Monitor creation/deletion", MakeMonitor, loops, 0);
    479    (void)Test("Monitor non-contentious locking/unlocking",
    480               NonContentiousMonitor, loops, 0);
    481    (void)Test("Monitor contentious locking/unlocking", ContentiousMonitor,
    482               loops, duration);
    483 
    484    (void)Test("Cached monitor non-contentious locking/unlocking",
    485               NonContentiousCMonitor, loops, 0);
    486    (void)Test("Cached monitor contentious locking/unlocking",
    487               ContentiousCMonitor, loops, duration);
    488 
    489    (void)ReentrantMonitor(loops);
    490  }
    491 
    492  if (debug_mode)
    493    PR_fprintf(std_err, "%s: test %s\n", "Lock(mutex) test",
    494               ((rv) ? "passed" : "failed"));
    495  else {
    496    if (!rv) {
    497      failed_already = 1;
    498    }
    499  }
    500 
    501  if (failed_already) {
    502    PR_fprintf(PR_STDOUT, "FAIL\n");
    503    return 1;
    504  } else {
    505    PR_fprintf(PR_STDOUT, "PASS\n");
    506    return 0;
    507  }
    508 
    509 } /* main */
    510 
    511 /* testlock.c */