tor-browser

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

pollable.c (7260B)


      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 * A test for the pollable events.
      8 *
      9 * A number of threads are in a ring configuration, each waiting on
     10 * a pollable event that is set by its upstream neighbor.
     11 */
     12 
     13 #include "prinit.h"
     14 #include "prio.h"
     15 #include "prthread.h"
     16 #include "prerror.h"
     17 #include "prmem.h"
     18 #include "prlog.h"
     19 #include "prprf.h"
     20 
     21 #include "plgetopt.h"
     22 
     23 #include <stdlib.h>
     24 
     25 #define DEFAULT_THREADS 10
     26 #define DEFAULT_LOOPS 100
     27 
     28 PRIntn numThreads = DEFAULT_THREADS;
     29 PRIntn numIterations = DEFAULT_LOOPS;
     30 PRIntervalTime dally = PR_INTERVAL_NO_WAIT;
     31 PRFileDesc* debug_out = NULL;
     32 PRBool debug_mode = PR_FALSE;
     33 PRBool verbosity = PR_FALSE;
     34 
     35 typedef struct ThreadData {
     36  PRFileDesc* event;
     37  int index;
     38  struct ThreadData* next;
     39 } ThreadData;
     40 
     41 void ThreadRoutine(void* arg) {
     42  ThreadData* data = (ThreadData*)arg;
     43  PRIntn i;
     44  PRPollDesc pd;
     45  PRInt32 rv;
     46 
     47  pd.fd = data->event;
     48  pd.in_flags = PR_POLL_READ;
     49 
     50  for (i = 0; i < numIterations; i++) {
     51    rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
     52    if (rv == -1) {
     53      PR_fprintf(PR_STDERR, "PR_Poll failed\n");
     54      exit(1);
     55    }
     56    if (verbosity) {
     57      PR_fprintf(debug_out, "thread %d awakened\n", data->index);
     58    }
     59    PR_ASSERT(rv != 0);
     60    PR_ASSERT(pd.out_flags & PR_POLL_READ);
     61    if (PR_WaitForPollableEvent(data->event) == PR_FAILURE) {
     62      PR_fprintf(PR_STDERR, "consume event failed\n");
     63      exit(1);
     64    }
     65    if (dally != PR_INTERVAL_NO_WAIT) {
     66      PR_Sleep(dally);
     67    }
     68    if (verbosity) {
     69      PR_fprintf(debug_out, "thread %d posting event\n", data->index);
     70    }
     71    if (PR_SetPollableEvent(data->next->event) == PR_FAILURE) {
     72      PR_fprintf(PR_STDERR, "post event failed\n");
     73      exit(1);
     74    }
     75  }
     76 }
     77 
     78 static void Help(void) {
     79  debug_out = PR_STDOUT;
     80 
     81  PR_fprintf(debug_out,
     82             "Usage: pollable [-c n] [-t n] [-d] [-v] [-G] [-C n] [-D n]\n");
     83  PR_fprintf(debug_out, "-c n\tloops at thread level (default: %d)\n",
     84             DEFAULT_LOOPS);
     85  PR_fprintf(debug_out, "-t n\tnumber of threads (default: %d)\n",
     86             DEFAULT_THREADS);
     87  PR_fprintf(debug_out, "-d\tturn on debugging output (default: FALSE)\n");
     88  PR_fprintf(debug_out, "-v\tturn on verbose output (default: FALSE)\n");
     89  PR_fprintf(debug_out, "-G\tglobal threads only (default: FALSE)\n");
     90  PR_fprintf(debug_out, "-C n\tconcurrency setting (default: 1)\n");
     91  PR_fprintf(debug_out, "-D n\tdally setting (msecs) (default: 0)\n");
     92 } /* Help */
     93 
     94 int main(int argc, char** argv) {
     95  ThreadData selfData;
     96  ThreadData* data;
     97  PRThread** thread;
     98  void* block;
     99  PRIntn i;
    100  PRIntervalTime timeStart, timeEnd;
    101  PRPollDesc pd;
    102  PRInt32 rv;
    103  PRThreadScope thread_scope = PR_LOCAL_THREAD;
    104  PRBool help = PR_FALSE;
    105  PRUintn concurrency = 1;
    106  PRUintn average;
    107  PLOptStatus os;
    108  PLOptState* opt;
    109 
    110 
    111  opt = PL_CreateOptState(argc, argv, "hdvc:t:C:GD:");
    112  while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
    113    if (PL_OPT_BAD == os) {
    114      continue;
    115    }
    116    switch (opt->option) {
    117      case 'v': /* verbose mode */
    118        verbosity = PR_TRUE;
    119      case 'd': /* debug mode */
    120        debug_mode = PR_TRUE;
    121        break;
    122      case 'c': /* loop counter */
    123        numIterations = atoi(opt->value);
    124        break;
    125      case 't': /* thread limit */
    126        numThreads = atoi(opt->value);
    127        break;
    128      case 'C': /* Concurrency limit */
    129        concurrency = atoi(opt->value);
    130        break;
    131      case 'G': /* global threads only */
    132        thread_scope = PR_GLOBAL_THREAD;
    133        break;
    134      case 'D': /* dally */
    135        dally = PR_MillisecondsToInterval(atoi(opt->value));
    136        break;
    137      case 'h': /* help message */
    138        Help();
    139        help = PR_TRUE;
    140        break;
    141      default:
    142        break;
    143    }
    144  }
    145  PL_DestroyOptState(opt);
    146 
    147  if (help) {
    148    return 1;
    149  }
    150 
    151  if (concurrency > 1) {
    152    PR_SetConcurrency(concurrency);
    153  }
    154 
    155  if (PR_TRUE == debug_mode) {
    156    debug_out = PR_STDOUT;
    157    PR_fprintf(debug_out, "Test parameters\n");
    158    PR_fprintf(debug_out, "\tThreads involved: %d\n", numThreads);
    159    PR_fprintf(debug_out, "\tIteration limit: %d\n", numIterations);
    160    PR_fprintf(debug_out, "\tConcurrency: %d\n", concurrency);
    161    PR_fprintf(debug_out, "\tThread type: %s\n",
    162               (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL");
    163  }
    164 
    165  /*
    166   * Malloc a block of memory and divide it into data and thread.
    167   */
    168  block = PR_MALLOC(numThreads * (sizeof(ThreadData) + sizeof(PRThread*)));
    169  if (block == NULL) {
    170    PR_fprintf(PR_STDERR, "cannot malloc, failed\n");
    171    exit(1);
    172  }
    173  data = (ThreadData*)block;
    174  thread = (PRThread**)&data[numThreads];
    175 
    176  /* Pollable event */
    177  selfData.event = PR_NewPollableEvent();
    178  if (selfData.event == NULL) {
    179    PR_fprintf(PR_STDERR, "cannot create event: (%ld, %ld)\n", PR_GetError(),
    180               PR_GetOSError());
    181    exit(1);
    182  }
    183  selfData.next = &data[0];
    184  for (i = 0; i < numThreads; i++) {
    185    data[i].event = PR_NewPollableEvent();
    186    if (data[i].event == NULL) {
    187      PR_fprintf(PR_STDERR, "cannot create event: (%ld, %ld)\n", PR_GetError(),
    188                 PR_GetOSError());
    189      exit(1);
    190    }
    191    data[i].index = i;
    192    if (i != numThreads - 1) {
    193      data[i].next = &data[i + 1];
    194    } else {
    195      data[i].next = &selfData;
    196    }
    197 
    198    thread[i] = PR_CreateThread(PR_USER_THREAD, ThreadRoutine, &data[i],
    199                                PR_PRIORITY_NORMAL, thread_scope,
    200                                PR_JOINABLE_THREAD, 0);
    201    if (thread[i] == NULL) {
    202      PR_fprintf(PR_STDERR, "cannot create thread\n");
    203      exit(1);
    204    }
    205  }
    206 
    207  timeStart = PR_IntervalNow();
    208  pd.fd = selfData.event;
    209  pd.in_flags = PR_POLL_READ;
    210  for (i = 0; i < numIterations; i++) {
    211    if (dally != PR_INTERVAL_NO_WAIT) {
    212      PR_Sleep(dally);
    213    }
    214    if (verbosity) {
    215      PR_fprintf(debug_out, "main thread posting event\n");
    216    }
    217    if (PR_SetPollableEvent(selfData.next->event) == PR_FAILURE) {
    218      PR_fprintf(PR_STDERR, "set event failed\n");
    219      exit(1);
    220    }
    221    rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
    222    if (rv == -1) {
    223      PR_fprintf(PR_STDERR, "wait failed\n");
    224      exit(1);
    225    }
    226    PR_ASSERT(rv != 0);
    227    PR_ASSERT(pd.out_flags & PR_POLL_READ);
    228    if (verbosity) {
    229      PR_fprintf(debug_out, "main thread awakened\n");
    230    }
    231    if (PR_WaitForPollableEvent(selfData.event) == PR_FAILURE) {
    232      PR_fprintf(PR_STDERR, "consume event failed\n");
    233      exit(1);
    234    }
    235  }
    236  timeEnd = PR_IntervalNow();
    237 
    238  if (debug_mode) {
    239    average = PR_IntervalToMicroseconds(timeEnd - timeStart) /
    240              (numIterations * numThreads);
    241    PR_fprintf(debug_out, "Average switch times %d usecs for %d threads\n",
    242               average, numThreads);
    243  }
    244 
    245  for (i = 0; i < numThreads; i++) {
    246    if (PR_JoinThread(thread[i]) == PR_FAILURE) {
    247      PR_fprintf(PR_STDERR, "join thread failed\n");
    248      exit(1);
    249    }
    250    PR_DestroyPollableEvent(data[i].event);
    251  }
    252  PR_DELETE(block);
    253  PR_DestroyPollableEvent(selfData.event);
    254 
    255  PR_fprintf(PR_STDOUT, "PASSED\n");
    256  return 0;
    257 }