tor-browser

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

sel_spd.c (13928B)


      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 * Test the speed of select within NSPR
      8 *
      9 */
     10 
     11 #include "nspr.h"
     12 #include "prpriv.h"
     13 
     14 #include <stdlib.h>
     15 #include <stdio.h>
     16 #include <errno.h>
     17 #include <string.h>
     18 
     19 #if defined(XP_UNIX)
     20 #  include <unistd.h>
     21 #endif
     22 
     23 #ifdef DEBUG
     24 #  define PORT_INC_DO +100
     25 #else
     26 #  define PORT_INC_DO
     27 #endif
     28 #ifdef IS_64
     29 #  define PORT_INC_3264 +200
     30 #else
     31 #  define PORT_INC_3264
     32 #endif
     33 
     34 #define PORT_BASE 19000 PORT_INC_DO PORT_INC_3264
     35 
     36 typedef struct timer_slot_t {
     37  unsigned long d_connect;
     38  unsigned long d_cl_data;
     39  unsigned long d_sv_data;
     40  unsigned long d_close;
     41  unsigned long d_total;
     42  unsigned long requests;
     43 } timer_slot_t;
     44 
     45 static long _iterations = 5;
     46 static long _client_data = 8192;
     47 
     48 static long _server_data = (128 * 1024);
     49 static long _threads_max = 10, _threads = 10;
     50 
     51 static int verbose = 0;
     52 static PRMonitor* exit_cv;
     53 static long _thread_exit_count;
     54 static timer_slot_t* timer_data;
     55 static PRThreadScope scope1, scope2;
     56 
     57 void tally_results(int);
     58 
     59 /* return the diff in microseconds */
     60 unsigned long _delta(PRIntervalTime* start, PRIntervalTime* stop) {
     61  /*
     62   * Will C do the right thing with unsigned arithemtic?
     63   */
     64  return PR_IntervalToMicroseconds(*stop - *start);
     65 }
     66 
     67 int _readn(PRFileDesc* sock, char* buf, int len) {
     68  int rem;
     69  int bytes;
     70 
     71  for (rem = len; rem; rem -= bytes) {
     72    bytes = PR_Recv(sock, buf + len - rem, rem, 0, PR_INTERVAL_NO_TIMEOUT);
     73    if (bytes <= 0) {
     74      return -1;
     75    }
     76  }
     77  return len;
     78 }
     79 
     80 void _thread_exit(int id) {
     81  PR_EnterMonitor(exit_cv);
     82 #ifdef DEBUG
     83  fprintf(stdout, "Thread %d EXIT\n", id);
     84 #endif
     85 
     86  _thread_exit_count--;
     87  if (_thread_exit_count == 0) {
     88 #ifdef DEBUG
     89    fprintf(stdout, "Thread %d EXIT triggered notify\n", id);
     90 #endif
     91    PR_Notify(exit_cv);
     92  }
     93  PR_ExitMonitor(exit_cv);
     94 }
     95 
     96 void _server_thread(void* arg_id) {
     97  void _client_thread(void*);
     98  int* id = (int*)arg_id;
     99  PRFileDesc* sock;
    100  PRSocketOptionData sockopt;
    101  PRNetAddr sa;
    102  PRFileDesc* newsock;
    103  char* data_buffer = NULL;
    104  int data_buffer_size;
    105  int index;
    106  PRIntervalTime start, connect_done, read_done, write_done, close_done;
    107 
    108 #ifdef DEBUG
    109  fprintf(stdout, "server thread %d alive\n", *id);
    110 #endif
    111 
    112  data_buffer_size =
    113      (_client_data > _server_data ? _client_data : _server_data);
    114 
    115  if ((data_buffer = (char*)PR_Malloc(data_buffer_size * sizeof(char))) ==
    116      NULL) {
    117    fprintf(stderr, "Error creating buffer in server thread %d\n", *id);
    118    goto done;
    119  }
    120 
    121  if ((sock = PR_NewTCPSocket()) == NULL) {
    122    fprintf(stderr, "Error creating socket in server thread %d\n", *id);
    123    goto done;
    124  }
    125 
    126  sockopt.option = PR_SockOpt_Reuseaddr;
    127  sockopt.value.reuse_addr = PR_TRUE;
    128  if (PR_SetSocketOption(sock, &sockopt) == PR_FAILURE) {
    129    fprintf(stderr, "Error setting socket option in server thread %d\n", *id);
    130    goto done;
    131  }
    132 
    133  memset(&sa, 0, sizeof(sa));
    134  sa.inet.family = PR_AF_INET;
    135  sa.inet.port = PR_htons(PORT_BASE + *id);
    136  sa.inet.ip = PR_htonl(PR_INADDR_ANY);
    137 
    138  if (PR_Bind(sock, &sa) < 0) {
    139    fprintf(stderr, "Error binding socket in server thread %d errno = %d\n",
    140            *id, errno);
    141    goto done;
    142  }
    143 
    144  if (PR_Listen(sock, 32) < 0) {
    145    fprintf(stderr, "Error listening to socket in server thread %d\n", *id);
    146    goto done;
    147  }
    148 
    149  /* Tell the client to start */
    150  if (PR_CreateThread(PR_USER_THREAD, _client_thread, id, PR_PRIORITY_NORMAL,
    151                      scope2, PR_UNJOINABLE_THREAD, 0) == NULL) {
    152    fprintf(stderr, "Error creating client thread %d\n", *id);
    153  }
    154 
    155  for (index = 0; index < _iterations; index++) {
    156 #ifdef DEBUG
    157    fprintf(stdout, "server thread %d loop %d\n", *id, index);
    158 #endif
    159 
    160    start = PR_IntervalNow();
    161 
    162    if ((newsock = PR_Accept(sock, &sa, PR_INTERVAL_NO_TIMEOUT)) == NULL) {
    163      fprintf(stderr, "Error accepting connection %d in server thread %d\n",
    164              index, *id);
    165      goto done;
    166    }
    167 #ifdef DEBUG
    168    fprintf(stdout, "server thread %d got connection %d\n", *id, newsock);
    169 #endif
    170 
    171    connect_done = PR_IntervalNow();
    172 
    173    if (_readn(newsock, data_buffer, _client_data) < _client_data) {
    174      fprintf(
    175          stderr,
    176          "Error reading client data for iteration %d in server thread %d\n",
    177          index, *id);
    178      goto done;
    179    }
    180 
    181 #ifdef DEBUG
    182    fprintf(stdout, "server thread %d read %d bytes\n", *id, _client_data);
    183 #endif
    184    read_done = PR_IntervalNow();
    185 
    186    if (PR_Send(newsock, data_buffer, _server_data, 0, PR_INTERVAL_NO_TIMEOUT) <
    187        _server_data) {
    188      fprintf(
    189          stderr,
    190          "Error sending client data for iteration %d in server thread %d\n",
    191          index, *id);
    192      goto done;
    193    }
    194 
    195 #ifdef DEBUG
    196    fprintf(stdout, "server thread %d write %d bytes\n", *id, _server_data);
    197 #endif
    198 
    199    write_done = PR_IntervalNow();
    200 
    201    PR_Close(newsock);
    202 
    203    close_done = PR_IntervalNow();
    204 
    205    timer_data[2 * (*id)].d_connect += _delta(&start, &connect_done);
    206    timer_data[2 * (*id)].d_cl_data += _delta(&connect_done, &read_done);
    207    timer_data[2 * (*id)].d_sv_data += _delta(&read_done, &write_done);
    208    timer_data[2 * (*id)].d_close += _delta(&write_done, &close_done);
    209    timer_data[2 * (*id)].d_total += _delta(&start, &close_done);
    210    timer_data[2 * (*id)].requests++;
    211 
    212 #ifdef DEBUG
    213    fprintf(stdout, "server: %d %d %d %d %d\n", _delta(&start, &connect_done),
    214            _delta(&connect_done, &read_done), _delta(&read_done, &write_done),
    215            _delta(&write_done, &close_done), _delta(&start, &close_done));
    216 #endif
    217  }
    218 
    219 done:
    220  if (data_buffer != NULL) {
    221    PR_Free(data_buffer);
    222  }
    223  if (sock) {
    224    PR_Close(sock);
    225  }
    226  _thread_exit(*id);
    227  return;
    228 }
    229 
    230 void _client_thread(void* arg_id) {
    231  int* id = (int*)arg_id;
    232  int index;
    233  PRNetAddr sa;
    234  PRFileDesc* sock_h;
    235  char* data_buffer = NULL;
    236  int data_buffer_size;
    237  int bytes;
    238  PRIntervalTime start, connect_done, read_done, write_done, close_done;
    239  PRStatus rv;
    240 
    241 #ifdef DEBUG
    242  fprintf(stdout, "client thread %d alive\n", *id);
    243 #endif
    244 
    245  data_buffer_size =
    246      (_client_data > _server_data ? _client_data : _server_data);
    247 
    248  if ((data_buffer = (char*)PR_Malloc(data_buffer_size * sizeof(char))) ==
    249      NULL) {
    250    fprintf(stderr, "Error creating buffer in server thread %d\n", *id);
    251    goto done;
    252  }
    253 
    254  memset(&sa, 0, sizeof(sa));
    255  rv = PR_InitializeNetAddr(PR_IpAddrLoopback, PORT_BASE + *id, &sa);
    256  PR_ASSERT(PR_SUCCESS == rv);
    257 
    258  for (index = 0; index < _iterations; index++) {
    259 #ifdef DEBUG
    260    fprintf(stdout, "client thread %d loop %d\n", *id, index);
    261 #endif
    262 
    263    start = PR_IntervalNow();
    264    if ((sock_h = PR_NewTCPSocket()) == NULL) {
    265      fprintf(stderr, "Error creating socket %d in client thread %d\n", index,
    266              *id);
    267      goto done;
    268    }
    269 
    270 #ifdef DEBUG
    271    fprintf(stdout, "client thread %d socket created %d\n", *id, sock_h);
    272 #endif
    273 
    274    if (PR_Connect(sock_h, &sa, PR_INTERVAL_NO_TIMEOUT) < 0) {
    275      fprintf(stderr, "Error accepting connection %d in client thread %d\n",
    276              index, *id);
    277      goto done;
    278    }
    279 
    280 #ifdef DEBUG
    281    fprintf(stdout, "client thread %d socket connected %d\n", *id, sock_h);
    282 #endif
    283 
    284    connect_done = PR_IntervalNow();
    285    if (PR_Send(sock_h, data_buffer, _client_data, 0, PR_INTERVAL_NO_TIMEOUT) <
    286        _client_data) {
    287      fprintf(
    288          stderr,
    289          "Error sending client data for iteration %d in client thread %d\n",
    290          index, *id);
    291      goto done;
    292    }
    293 
    294 #ifdef DEBUG
    295    fprintf(stdout, "client thread %d socket wrote %d\n", *id, _client_data);
    296 #endif
    297 
    298    write_done = PR_IntervalNow();
    299    if ((bytes = _readn(sock_h, data_buffer, _server_data)) < _server_data) {
    300      fprintf(stderr,
    301              "Error reading server data for iteration %d in client thread %d "
    302              "(read %d bytes)\n",
    303              index, *id, bytes);
    304      goto done;
    305    }
    306 
    307 #ifdef DEBUG
    308    fprintf(stdout, "client thread %d socket read %d\n", *id, _server_data);
    309 #endif
    310 
    311    read_done = PR_IntervalNow();
    312    PR_Close(sock_h);
    313    close_done = PR_IntervalNow();
    314 
    315    timer_data[2 * (*id) + 1].d_connect += _delta(&start, &connect_done);
    316    timer_data[2 * (*id) + 1].d_cl_data += _delta(&connect_done, &write_done);
    317    timer_data[2 * (*id) + 1].d_sv_data += _delta(&write_done, &read_done);
    318    timer_data[2 * (*id) + 1].d_close += _delta(&read_done, &close_done);
    319    timer_data[2 * (*id) + 1].d_total += _delta(&start, &close_done);
    320    timer_data[2 * (*id) + 1].requests++;
    321  }
    322 done:
    323  if (data_buffer != NULL) {
    324    PR_Free(data_buffer);
    325  }
    326  _thread_exit(*id);
    327 
    328  return;
    329 }
    330 
    331 static void do_work(void) {
    332  int index;
    333 
    334  _thread_exit_count = _threads * 2;
    335  for (index = 0; index < _threads; index++) {
    336    int* id = (int*)PR_Malloc(sizeof(int));
    337 
    338    *id = index;
    339 
    340    if (PR_CreateThread(PR_USER_THREAD, _server_thread, id, PR_PRIORITY_NORMAL,
    341                        scope1, PR_UNJOINABLE_THREAD, 0) == NULL) {
    342      fprintf(stderr, "Error creating server thread %d\n", index);
    343    }
    344  }
    345 
    346  PR_EnterMonitor(exit_cv);
    347  while (_thread_exit_count > 0) {
    348    PR_Wait(exit_cv, PR_INTERVAL_NO_TIMEOUT);
    349  }
    350  PR_ExitMonitor(exit_cv);
    351 
    352  fprintf(stdout, "TEST COMPLETE!\n");
    353 
    354  tally_results(verbose);
    355 }
    356 
    357 static void do_workUU(void) {
    358  scope1 = PR_LOCAL_THREAD;
    359  scope2 = PR_LOCAL_THREAD;
    360  do_work();
    361 }
    362 
    363 static void do_workUK(void) {
    364  scope1 = PR_LOCAL_THREAD;
    365  scope2 = PR_GLOBAL_THREAD;
    366  do_work();
    367 }
    368 
    369 static void do_workKU(void) {
    370  scope1 = PR_GLOBAL_THREAD;
    371  scope2 = PR_LOCAL_THREAD;
    372  do_work();
    373 }
    374 
    375 static void do_workKK(void) {
    376  scope1 = PR_GLOBAL_THREAD;
    377  scope2 = PR_GLOBAL_THREAD;
    378  do_work();
    379 }
    380 
    381 static void Measure(void (*func)(void), const char* msg) {
    382  PRIntervalTime start, stop;
    383  double d;
    384 
    385  start = PR_IntervalNow();
    386  (*func)();
    387  stop = PR_IntervalNow();
    388 
    389  d = (double)PR_IntervalToMicroseconds(stop - start);
    390 
    391  printf("%40s: %6.2f usec\n", msg, d / _iterations);
    392 }
    393 
    394 int main(int argc, char** argv) {
    395 #if defined(XP_UNIX)
    396  int opt;
    397  PR_IMPORT_DATA(char*) optarg;
    398 #endif
    399 
    400 #if defined(XP_UNIX)
    401  while ((opt = getopt(argc, argv, "c:s:i:t:v")) != EOF) {
    402    switch (opt) {
    403      case 'i':
    404        _iterations = atoi(optarg);
    405        break;
    406      case 't':
    407        _threads_max = _threads = atoi(optarg);
    408        break;
    409      case 'c':
    410        _client_data = atoi(optarg);
    411        break;
    412      case 's':
    413        _server_data = atoi(optarg);
    414        break;
    415      case 'v':
    416        verbose = 1;
    417        break;
    418      default:
    419        break;
    420    }
    421  }
    422 #endif
    423 
    424  PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
    425 
    426  fprintf(stdout,
    427          "Running test for %d iterations with %d simultaneous threads.\n",
    428          _iterations, _threads);
    429  fprintf(stdout,
    430          "\tWill send %d bytes of client data and %d bytes of server data\n",
    431          _client_data, _server_data);
    432 
    433  if ((exit_cv = PR_NewMonitor()) == NULL) {
    434    fprintf(stderr, "Error creating monitor for exit cv\n");
    435  }
    436  if ((timer_data = (timer_slot_t*)PR_Malloc(2 * _threads *
    437                                             sizeof(timer_slot_t))) == NULL) {
    438    fprintf(stderr, "error allocating thread time results array\n");
    439  }
    440  memset(timer_data, 0, 2 * _threads * sizeof(timer_slot_t));
    441 
    442  Measure(do_workUU, "select loop user/user");
    443  Measure(do_workUK, "select loop user/kernel");
    444  Measure(do_workKU, "select loop kernel/user");
    445  Measure(do_workKK, "select loop kernel/kernel");
    446 
    447  return 0;
    448 }
    449 
    450 void tally_results(int verbose) {
    451  int index;
    452  unsigned long tot_connect = 0;
    453  unsigned long tot_cl_data = 0;
    454  unsigned long tot_sv_data = 0;
    455  unsigned long tot_close = 0;
    456  unsigned long tot_all = 0;
    457  unsigned long tot_requests = 0;
    458 
    459  fprintf(stdout, "Server results:\n\n");
    460  for (index = 0; index < _threads_max * 2; index += 2) {
    461    if (verbose)
    462      fprintf(stdout, "server thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n", index,
    463              timer_data[index].requests, timer_data[index].d_connect,
    464              timer_data[index].d_cl_data, timer_data[index].d_sv_data,
    465              timer_data[index].d_close, timer_data[index].d_total);
    466 
    467    tot_connect += timer_data[index].d_connect / _threads;
    468    tot_cl_data += timer_data[index].d_cl_data / _threads;
    469    tot_sv_data += timer_data[index].d_sv_data / _threads;
    470    tot_close += timer_data[index].d_close / _threads;
    471    tot_all += timer_data[index].d_total / _threads;
    472    tot_requests += timer_data[index].requests / _threads;
    473  }
    474  fprintf(stdout, "----------\n");
    475  fprintf(stdout, "server per thread totals %u\t%u\t%u\t%u\t%u\n", tot_requests,
    476          tot_connect, tot_cl_data, tot_sv_data, tot_close);
    477  fprintf(stdout, "server per thread elapsed time %u\n", tot_all);
    478  fprintf(stdout, "----------\n");
    479 
    480  tot_connect = tot_cl_data = tot_sv_data = tot_close = tot_all = tot_requests =
    481      0;
    482  fprintf(stdout, "Client results:\n\n");
    483  for (index = 1; index < _threads_max * 2; index += 2) {
    484    if (verbose)
    485      fprintf(stdout, "client thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n", index,
    486              timer_data[index].requests, timer_data[index].d_connect,
    487              timer_data[index].d_cl_data, timer_data[index].d_sv_data,
    488              timer_data[index].d_close, timer_data[index].d_total);
    489 
    490    tot_connect += timer_data[index].d_connect / _threads;
    491    tot_cl_data += timer_data[index].d_cl_data / _threads;
    492    tot_sv_data += timer_data[index].d_sv_data / _threads;
    493    tot_close += timer_data[index].d_close / _threads;
    494    tot_all += timer_data[index].d_total / _threads;
    495    tot_requests += timer_data[index].requests / _threads;
    496  }
    497  fprintf(stdout, "----------\n");
    498  fprintf(stdout, "client per thread totals %u\t%u\t%u\t%u\t%u\n", tot_requests,
    499          tot_connect, tot_cl_data, tot_sv_data, tot_close);
    500  fprintf(stdout, "client per thread elapsed time %u\n", tot_all);
    501 }