NSPR 2.0: Layered I/O

      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /*
      3  * The contents of this file are subject to the Netscape Public License
      4  * Version 1.0 (the "NPL"); you may not use this file except in
      5  * compliance with the NPL.  You may obtain a copy of the NPL at
      6  * http://www.mozilla.org/NPL/
      7  *
      8  * Software distributed under the NPL is distributed on an "AS IS" basis,
      9  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
     10  * for the specific language governing rights and limitations under the
     11  * NPL.
     12  *
     13  * The Initial Developer of this code under the NPL is Netscape
     14  * Communications Corporation.  Portions created by Netscape are
     15  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
     16  * Reserved.
     17  */
     18
     19 #include "prio.h"
     20 #include "prprf.h"
     21 #include "prlog.h"
     22 #include "prnetdb.h"
     23 #include "prthread.h"
     24
     25 #include "plerror.h"
     26 #include "plgetopt.h"
     27 #include "prwin16.h"
     28
     29 #include
     30
     31 /*
     32 ** Testing layering of I/O
     33 **
     34 **      The layered server
     35 ** A thread that acts as a server. It creates a TCP listener with a dummy
     36 ** layer pushed on top. Then listens for incoming connections. Each connection
     37 ** request for connection will be layered as well, accept one request, echo
     38 ** it back and close.
     39 **
     40 **      The layered client
     41 ** Pretty much what you'd expect.
     42 */
     43
     44 static PRFileDesc *logFile;
     45 static PRDescIdentity identity;
     46 static PRNetAddr server_address;
     47
     48 static PRIOMethods myMethods;
     49
     50 typedef enum Verbosity {silent, quiet, chatty, noisy} Verbosity;
     51
     52 static PRIntn minor_iterations = 5;
     53 static PRIntn major_iterations = 1;
     54 static Verbosity verbosity = quiet;
     55 static PRUint16 default_port = 12273;
     56
     57 static PRFileDesc *PushLayer(PRFileDesc *stack)
     58 {
     59     PRFileDesc *layer = PR_CreateIOLayerStub(identity, &myMethods);
     60     PRStatus rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer);
     61     if (verbosity > quiet)
     62         PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, stack);
     63     PR_ASSERT(PR_SUCCESS == rv);
     64     return stack;
     65 }  /* PushLayer */
     66
     67 #if 0
     68 static PRFileDesc *PopLayer(PRFileDesc *stack)
     69 {
     70     PRFileDesc *popped = PR_PopIOLayer(stack, identity);
     71     if (verbosity > quiet)
     72         PR_fprintf(logFile, "Popped layer(0x%x) from stack(0x%x)\n", popped, stack);
     73     popped->dtor(popped);
     74
     75     return stack;
     76 }  /* PopLayer */
     77 #endif
     78
     79 static void PR_CALLBACK Client(void *arg)
     80 {
     81     PRStatus rv;
     82     PRUint8 buffer[100];
     83     PRIntn empty_flags = 0;
     84     PRIntn bytes_read, bytes_sent;
     85     PRFileDesc *stack = (PRFileDesc*)arg;
     86
     87     rv = PR_Connect(stack, &server_address, PR_INTERVAL_NO_TIMEOUT);
     88     PR_ASSERT(PR_SUCCESS == rv);
     89     while (minor_iterations-- > 0)
     90     {
     91         bytes_sent = PR_Send(
     92             stack, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT);
     93         PR_ASSERT(sizeof(buffer) == bytes_sent);
     94         if (verbosity > chatty)
     95             PR_fprintf(logFile, "Client sending %d bytes\n", bytes_sent);
     96         bytes_read = PR_Recv(
     97             stack, buffer, bytes_sent, empty_flags, PR_INTERVAL_NO_TIMEOUT);
     98         if (verbosity > chatty)
     99             PR_fprintf(logFile, "Client receiving %d bytes\n", bytes_read);
    100         PR_ASSERT(bytes_read == bytes_sent);
    101     }
    102
    103     if (verbosity > quiet)
    104         PR_fprintf(logFile, "Client shutting down stack\n");
    105
    106     rv = PR_Shutdown(stack, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv);
    107 }  /* Client */
    108
    109 static void PR_CALLBACK Server(void *arg)
    110 {
    111     PRStatus rv;
    112     PRUint8 buffer[100];
    113     PRFileDesc *service;
    114     PRUintn empty_flags = 0;
    115     PRIntn bytes_read, bytes_sent;
    116     PRFileDesc *stack = (PRFileDesc*)arg;
    117     PRNetAddr any_address, client_address;
    118
    119     rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address);
    120     PR_ASSERT(PR_SUCCESS == rv);
    121
    122     rv = PR_Bind(stack, &any_address); PR_ASSERT(PR_SUCCESS == rv);
    123     rv = PR_Listen(stack, 10); PR_ASSERT(PR_SUCCESS == rv);
    124
    125     service = PR_Accept(stack, &client_address, PR_INTERVAL_NO_TIMEOUT);
    126     if (verbosity > quiet)
    127         PR_fprintf(logFile, "Server accepting connection\n");
    128
    129     do
    130     {
    131         bytes_read = PR_Recv(
    132             service, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT);
    133         if (0 != bytes_read)
    134         {
    135             if (verbosity > chatty)
    136                 PR_fprintf(logFile, "Server receiving %d bytes\n", bytes_read);
    137             PR_ASSERT(bytes_read > 0);
    138             bytes_sent = PR_Send(
    139                 service, buffer, bytes_read, empty_flags, PR_INTERVAL_NO_TIMEOUT);
    140             if (verbosity > chatty)
    141                 PR_fprintf(logFile, "Server sending %d bytes\n", bytes_sent);
    142             PR_ASSERT(bytes_read == bytes_sent);
    143         }
    144
    145     } while (0 != bytes_read);
    146
    147     if (verbosity > quiet)
    148         PR_fprintf(logFile, "Server shutting down and closing stack\n");
    149     rv = PR_Shutdown(service, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv);
    150     rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv);
    151
    152 }  /* Server */
    153
    154 static PRInt32 PR_CALLBACK MyRecv(
    155     PRFileDesc *fd, void *buf, PRInt32 amount,
    156     PRIntn flags, PRIntervalTime timeout)
    157 {
    158     char *b = (char*)buf;
    159     PRFileDesc *lo = fd->lower;
    160     PRInt32 rv, readin = 0, request;
    161     rv = lo->methods->recv(lo, &request, sizeof(request), flags, timeout);
    162     if (verbosity > chatty) PR_fprintf(
    163         logFile, "MyRecv sending permission for %d bytes\n", request);
    164     if (0 < rv)
    165     {
    166         if (verbosity > chatty) PR_fprintf(
    167             logFile, "MyRecv received permission request for %d bytes\n", request);
    168         rv = lo->methods->send(
    169             lo, &request, sizeof(request), flags, timeout);
    170         if (0 < rv)
    171         {
    172             if (verbosity > chatty) PR_fprintf(
    173                 logFile, "MyRecv sending permission for %d bytes\n", request);
    174             while (readin < request)
    175             {
    176                 rv = lo->methods->recv(
    177                     lo, b + readin, amount - readin, flags, timeout);
    178                 if (rv <= 0) break;
    179                 if (verbosity > chatty) PR_fprintf(
    180                     logFile, "MyRecv received %d bytes\n", rv);
    181                 readin += rv;
    182             }
    183             rv = readin;
    184         }
    185     }
    186     return rv;
    187 }  /* MyRecv */
    188
    189 static PRInt32 PR_CALLBACK MySend(
    190     PRFileDesc *fd, const void *buf, PRInt32 amount,
    191     PRIntn flags, PRIntervalTime timeout)
    192 {
    193     PRFileDesc *lo = fd->lower;
    194     const char *b = (const char*)buf;
    195     PRInt32 rv, wroteout = 0, request;
    196     if (verbosity > chatty) PR_fprintf(
    197         logFile, "MySend asking permission to send %d bytes\n", amount);
    198     rv = lo->methods->send(lo, &amount, sizeof(amount), flags, timeout);
    199     if (0 < rv)
    200     {
    201         rv = lo->methods->recv(
    202             lo, &request, sizeof(request), flags, timeout);
    203         if (0 < rv)
    204         {
    205             PR_ASSERT(request == amount);
    206             if (verbosity > chatty) PR_fprintf(
    207                 logFile, "MySend got permission to send %d bytes\n", request);
    208             while (wroteout < request)
    209             {
    210                 rv = lo->methods->send(
    211                     lo, b + wroteout, request - wroteout, flags, timeout);
    212                 if (rv <= 0) break;
    213                 if (verbosity > chatty) PR_fprintf(
    214                     logFile, "MySend wrote %d bytes\n", rv);
    215                 wroteout += rv;
    216             }
    217             rv = amount;
    218         }
    219     }
    220     return rv;
    221 }  /* MySend */
    222
    223 static Verbosity ChangeVerbosity(Verbosity verbosity, PRIntn delta)
    224 {
    225     PRIntn verbage = (PRIntn)verbosity + delta;
    226     if (verbage < (PRIntn)silent) verbage = (PRIntn)silent;
    227     else if (verbage > (PRIntn)noisy) verbage = (PRIntn)noisy;
    228     return (Verbosity)verbage;
    229 }  /* ChangeVerbosity */
    230
    231 PRIntn main(PRIntn argc, char **argv)
    232 {
    233     PRStatus rv;
    234     PRIntn mits;
    235     PLOptStatus os;
    236     PRFileDesc *client, *service;
    237     const char *server_name = NULL;
    238     const PRIOMethods *stubMethods;
    239     PRThread *client_thread, *server_thread;
    240     PRThreadScope thread_scope = PR_LOCAL_THREAD;
    241     PLOptState *opt = PL_CreateOptState(argc, argv, "dqGC:c:p:");
    242     while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
    243     {
    244         if (PL_OPT_BAD == os) continue;
    245         switch (opt->option)
    246         {
    247         case 0:
    248             server_name = opt->value;
    249             break;
    250         case 'd':  /* debug mode */
    251             if (verbosity < noisy)
    252                 verbosity = ChangeVerbosity(verbosity, 1);
    253             break;
    254         case 'q':  /* debug mode */
    255             if (verbosity > silent)
    256                 verbosity = ChangeVerbosity(verbosity, -1);
    257             break;
    258         case 'G':  /* use global threads */
    259             thread_scope = PR_GLOBAL_THREAD;
    260             break;
    261         case 'C':  /* number of threads waiting */
    262             major_iterations = atoi(opt->value);
    263             break;
    264         case 'c':  /* number of client threads */
    265             minor_iterations = atoi(opt->value);
    266             break;
    267         case 'p':  /* default port */
    268             default_port = atoi(opt->value);
    269             break;
    270         default:
    271             break;
    272         }
    273     }
    274     PL_DestroyOptState(opt);
    275     PR_STDIO_INIT();
    276
    277     logFile = PR_GetSpecialFD(PR_StandardError);
    278
    279     identity = PR_GetUniqueIdentity("Dummy");
    280     stubMethods = PR_GetDefaultIOMethods();
    281
    282     /*
    283     ** The protocol we're going to implement is one where in order to initiate
    284     ** a send, the sender must first solicit permission. Therefore, every
    285     ** send is really a send - receive - send sequence.
    286     */
    287     myMethods = *stubMethods;  /* first get the entire batch */
    288     myMethods.recv = MyRecv;  /* then override the ones we care about */
    289     myMethods.send = MySend;  /* then override the ones we care about */
    290
    291     if (NULL == server_name)
    292         rv = PR_InitializeNetAddr(
    293             PR_IpAddrLoopback, default_port, &server_address);
    294     else
    295     {
    296         rv = PR_StringToNetAddr(server_name, &server_address);
    297         PR_ASSERT(PR_SUCCESS == rv);
    298         rv = PR_InitializeNetAddr(
    299             PR_IpAddrNull, default_port, &server_address);
    300     }
    301     PR_ASSERT(PR_SUCCESS == rv);
    302
    303     /* one type w/o layering */
    304
    305     mits = minor_iterations;
    306     while (major_iterations-- > 0)
    307     {
    308         if (verbosity > silent)
    309             PR_fprintf(logFile, "Beginning non-layered test\n");
    310         client = PR_NewTCPSocket(); PR_ASSERT(NULL != client);
    311         service = PR_NewTCPSocket(); PR_ASSERT(NULL != service);
    312
    313         minor_iterations = mits;
    314         server_thread = PR_CreateThread(
    315             PR_USER_THREAD, Server, service,
    316             PR_PRIORITY_HIGH, thread_scope,
    317             PR_JOINABLE_THREAD, 16 * 1024);
    318         PR_ASSERT(NULL != server_thread);
    319
    320         client_thread = PR_CreateThread(
    321             PR_USER_THREAD, Client, client,
    322             PR_PRIORITY_NORMAL, thread_scope,
    323             PR_JOINABLE_THREAD, 16 * 1024);
    324         PR_ASSERT(NULL != client_thread);
    325
    326         rv = PR_JoinThread(client_thread);
    327         PR_ASSERT(PR_SUCCESS == rv);
    328         rv = PR_JoinThread(server_thread);
    329         PR_ASSERT(PR_SUCCESS == rv);
    330
    331         rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv);
    332         rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv);
    333         if (verbosity > silent)
    334             PR_fprintf(logFile, "Ending non-layered test\n");
    335
    336         /* with layering */
    337         if (verbosity > silent)
    338             PR_fprintf(logFile, "Beginning layered test\n");
    339         client = PR_NewTCPSocket(); PR_ASSERT(NULL != client);
    340         service = PR_NewTCPSocket(); PR_ASSERT(NULL != service);
    341
    342         minor_iterations = mits;
    343         server_thread = PR_CreateThread(
    344             PR_USER_THREAD, Server, PushLayer(service),
    345             PR_PRIORITY_HIGH, thread_scope,
    346             PR_JOINABLE_THREAD, 16 * 1024);
    347         PR_ASSERT(NULL != server_thread);
    348
    349         client_thread = PR_CreateThread(
    350             PR_USER_THREAD, Client, PushLayer(client),
    351             PR_PRIORITY_NORMAL, thread_scope,
    352             PR_JOINABLE_THREAD, 16 * 1024);
    353         PR_ASSERT(NULL != client_thread);
    354
    355         rv = PR_JoinThread(client_thread);
    356         PR_ASSERT(PR_SUCCESS == rv);
    357         rv = PR_JoinThread(server_thread);
    358         PR_ASSERT(PR_SUCCESS == rv);
    359
    360         rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv);
    361         rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv);
    362         if (verbosity > silent)
    363             PR_fprintf(logFile, "Ending layered test\n");
    364     }
    365     return 0;
    366 }  /* main */
    367
    368 /* layer.c */