diff packages/net/common/current/tests/multi_lo_select.c @ 208:e0c0827131d1 ecos

Merge from eCos master repository on 2002-05-20-20:11:54-BST
author jlarmour
date Mon, 20 May 2002 22:19:26 +0000
parents
children ff7641b16d2a
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/net/common/current/tests/multi_lo_select.c
@@ -0,0 +1,446 @@
+//==========================================================================
+//
+//      tests/multi_lo_test.c
+// 
+//      Multiple selects-at-one-time test, using lo for portability.
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD or other sources
+// and are covered by the appropriate copyright disclaimers included herein.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    sorin@netappi.com, hmt
+// Contributors: gthomas,sorin@netappi.com
+// Date:         2000-05-24
+// Description:
+//
+//      This test is to test that the internal producer operation to select
+//      truly has broadcast semantics; there was a bug in there whereby it
+//      doesn't, so events get lost and/or the wrong thread is awakened.
+//
+//      We need to create N threads selecting on different sockets
+//      (different ports) (including one or two selecting on several
+//      sockets) and have a further thread or threads which send data to
+//      the those sockets in an order "random" with respect to the order in
+//      which the N selectors entered the wait, and their thread
+//      priorities.
+//
+//      If this all works, then we know that select always wakes the right
+//      thread in the right order.  I think...
+//
+//      I think 10 threads 0-9 where #2,#3,#6,#7 wait for multiple threads
+//      will do it.  #0-4 will be prio HI, #5-9 will be prio LO.  Sender
+//      thread A at prio MID will send to sockets 1,3,5,7,9.  Sender thread
+//      B at prio LOWEST will send to sockets 0,2,4,6,8.
+//
+//      Each sender thread will wait for a different semaphore signal
+//      before doing their next send, thus confirming correct ordering.
+//      Two common semaphores will also be signalled, one when a send
+//      occurs, the other when a recv happens.
+//
+//      The master thread will start off VERYHIGHPRI, then drop after
+//      starting all the others, to VERYLOW... when it next runs, those
+//      common semaphores should both have value 10 == NLISTENERS.
+//
+//
+//#####DESCRIPTIONEND#####
+//
+//==========================================================================
+
+#include <network.h>
+
+#include <cyg/infra/testcase.h>
+
+#ifndef CYGPKG_LIBC_STDIO
+#define perror(s) diag_printf(#s ": %s\n", strerror(errno))
+#endif
+
+#define SOURCE_PORT1 9900
+#define SOURCE_PORT2 9800 // for those who listen to multiple ports
+#define SOURCE_PORT3 9700 // for the dummy consumers of events
+
+#define PRIO_DUMMY 4 // Really high, so they're always right back there
+
+#define PRIO_LISTENER_HI 10
+#define PRIO_LISTENER_LO 15
+
+#define PRIO_SENDER_MID  12
+#define PRIO_SENDER_LOW  17
+
+#define PRIO_MASTERHIGH   3
+#define PRIO_MASTERLOW   25
+
+#ifndef CYGPKG_IO_FILEIO
+#if CYGPKG_IO_NFILE > 30
+#define NLISTENERS 10
+#else
+// fewer threads if not many sockets available
+#define NLISTENERS (CYGPKG_IO_NFILE/3)
+#endif
+#else
+#if CYGNUM_FILEIO_NFD > 30
+#define NLISTENERS 10
+#else
+// fewer threads if not many sockets available
+#define NLISTENERS (CYGNUM_FILEIO_NFD/3)
+#endif
+#endif
+
+#define NDUMMIES   10
+
+#define NSENDERS 2
+
+#define NUM_BUF  NLISTENERS
+#define MAX_BUF 100
+
+// buffers for receiving into:
+static unsigned char data_buf1[NUM_BUF][MAX_BUF];
+
+static unsigned char data_buf_write1[]="Client is alive";
+
+#define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL)
+#define MASTER_STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000)
+
+static char stack_master[MASTER_STACK_SIZE];
+static cyg_thread master_thread_data;
+static cyg_handle_t master_thread_handle;
+
+static char stack_dummy[NDUMMIES][STACK_SIZE];
+static cyg_thread dummy_thread_data[NDUMMIES]; 
+static cyg_handle_t dummy_thread_handle[NDUMMIES];  
+
+static char stack_listener[NLISTENERS][STACK_SIZE];
+static cyg_thread listener_thread_data[NLISTENERS]; 
+static cyg_handle_t listener_thread_handle[NLISTENERS];  
+
+static char stack_sender[NSENDERS][STACK_SIZE];
+static cyg_thread sender_thread_data[NSENDERS];
+static cyg_handle_t sender_thread_handle[NSENDERS];
+
+static cyg_sem_t listen_sema[NLISTENERS];
+
+static cyg_sem_t send_sema;
+static cyg_sem_t recv_sema;
+
+static cyg_thread_entry_t master;
+static cyg_thread_entry_t listener;
+static cyg_thread_entry_t sender;
+
+// ------------------------------------------------------------------------
+
+void
+pexit(char *s)
+{
+    CYG_TEST_FAIL_FINISH( s );
+}
+
+
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+// ------------------------------------------------------------------------
+
+void dummy( cyg_addrword_t which )
+{
+    // Share the same socket... we appear to run out otherwise.
+    static int s_s1 = -1;
+    static struct sockaddr_in local;
+
+    // locals...
+    fd_set in_fds;
+    int num;
+
+    CYG_TEST_CHECK( 0 <= which, "which under" );
+    CYG_TEST_CHECK( NDUMMIES > which, "which over" );
+
+    diag_printf( "Dummy %d alive\n", which );
+
+    if ( s_s1 < 0 ) {
+        s_s1 = socket(AF_INET, SOCK_STREAM, 0);
+        if (s_s1 < 0) {
+            pexit("stream socket 1");
+        }
+        memset(&local, 0, sizeof(local));
+        local.sin_family = AF_INET;
+        local.sin_len = sizeof(local);
+        local.sin_port = ntohs(SOURCE_PORT3 + which);
+        local.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+        if(bind(s_s1, (struct sockaddr *) &local, sizeof(local)) < 0) {
+            pexit("dummy bind /source_1/ error");
+        }
+        listen(s_s1, SOMAXCONN);
+    }
+
+    while (true) {
+        FD_ZERO(&in_fds);
+        FD_SET(s_s1, &in_fds);
+        num = select( s_s1+1, &in_fds,0,0,0);
+        
+        if (FD_ISSET(s_s1,&in_fds)) {
+            CYG_TEST_FAIL( "Activity on dummy port!" );
+        }
+    }   /* while (true) */ 
+}
+
+// ------------------------------------------------------------------------
+
+void listener( cyg_addrword_t which )
+{
+    int s_s1 = -1, e_s1 = 0, s_s2 = -1, e_s2 = 0;
+    struct sockaddr_in e_s1_addr,e_s2_addr,local;
+    fd_set in_fds;
+    int len;
+    int num;
+
+    // do we select on multiple sources?
+    int dual = (3 == (which & 3)) || (2 == (which & 3));
+    // then which is 2,3,6,7 so set up a 2nd listener
+    
+    CYG_TEST_CHECK( 0 <= which, "which under" );
+    CYG_TEST_CHECK( NLISTENERS > which, "which over" );
+
+    diag_printf( "Listener %d alive [%s]\n", which, dual ? "dual" : "single" );
+
+    s_s1 = socket(AF_INET, SOCK_STREAM, 0);
+    if (s_s1 < 0) {
+        pexit("stream socket 1");
+    }   
+    memset(&local, 0, sizeof(local));
+    local.sin_family = AF_INET;
+    local.sin_len = sizeof(local);
+    local.sin_port = ntohs(SOURCE_PORT1 + which);
+    local.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    if(bind(s_s1, (struct sockaddr *) &local, sizeof(local)) < 0) {
+        pexit("bind /source_1/ error");
+    }
+    listen(s_s1, SOMAXCONN);
+
+    if ( dual ) {
+        s_s2 = socket(AF_INET, SOCK_STREAM, 0);    
+        if (s_s2 < 0) {    
+            pexit("stream socket 2");
+        }  
+        memset(&local, 0, sizeof(local));
+        local.sin_family = AF_INET;
+        local.sin_len = sizeof(local);
+        local.sin_port = ntohs(SOURCE_PORT2 + which);
+        local.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+        if(bind(s_s2, (struct sockaddr *) &local, sizeof(local)) < 0) {    
+            pexit("bind /source_2/ error");
+        }
+        listen(s_s2, SOMAXCONN);
+    }
+
+    while (true) {
+        FD_ZERO(&in_fds);
+        FD_SET(s_s1, &in_fds);
+        if ( dual )
+            FD_SET(s_s2, &in_fds);
+        num = select ( max(s_s1,s_s2)+1, &in_fds,0,0,0);
+        
+        if (FD_ISSET(s_s1,&in_fds)) {
+            len = sizeof(e_s1_addr);
+            if ((e_s1 = accept(s_s1,(struct sockaddr *)&e_s1_addr,&len))<0) {
+                pexit("accept /source_1/");
+            }
+            diag_printf("TCP SERVER connection from %s: %d\n",
+                        inet_ntoa(e_s1_addr.sin_addr),ntohs(e_s1_addr.sin_port));
+        }
+
+        if ( dual ) {
+            if (FD_ISSET(s_s2,&in_fds)) {
+                len = sizeof(e_s2_addr);
+                if ((e_s2 = accept(s_s2,(struct sockaddr *)&e_s2_addr,&len))<0) {
+                    pexit("accept /source_2/");
+                }
+                diag_printf("TCP SERVER connection from %s: %d\n",
+                            inet_ntoa(e_s2_addr.sin_addr), ntohs(e_s2_addr.sin_port));
+            }
+        }
+        if ((e_s1 != 0) || ( e_s2 != 0)) {
+            break;
+        }
+    }   /* while (true) */ 
+
+    CYG_TEST_CHECK( 0 != e_s1, "No connection made on s1!" );
+    
+    if ((len = read(e_s1, data_buf1[which], MAX_BUF)) < 0  ) {
+        perror("I/O error s1");
+        CYG_TEST_FAIL( "Read s1 failed" );
+    }
+    diag_printf("Listener %d: %s\n", which, data_buf1[which]);
+
+    close( s_s1 );
+    if ( dual )
+        close( s_s2 );
+    if ( 0 != e_s1 )
+        close ( e_s1 );
+    if ( 0 != e_s2 )
+        close ( e_s2 );
+
+    cyg_semaphore_post( &listen_sema[which] ); // Verify that I was here
+    cyg_semaphore_post( &recv_sema );          // Count receptions
+
+    cyg_thread_exit(); // explicitly
+}
+
+// ------------------------------------------------------------------------
+static void sender( cyg_addrword_t which ) // which means which set (odd/even) here...
+{
+    int s_source;
+    struct sockaddr_in local;
+    int len;
+
+    diag_printf("client %d [%s] :started\n", which, (which & 1) ? "odd" : "even" );
+
+    for ( /* which as is */; which < NLISTENERS; which += 2 ) {
+
+        s_source = socket(AF_INET, SOCK_STREAM, 0);
+        if (s_source < 0) {
+            pexit("stream socket");
+        }
+        memset(&local, 0, sizeof(local));
+        local.sin_family = AF_INET;
+        local.sin_len = sizeof(local);
+        local.sin_port = htons( SOURCE_PORT1 + which );
+        local.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    
+        if (connect(s_source, (struct sockaddr *)&local, sizeof(local)) < 0) {
+            pexit("Can't connect to target");
+        }
+    
+        if ((len = write(s_source,data_buf_write1,sizeof(data_buf_write1) )) < 0) {
+            CYG_TEST_FAIL_FINISH("Error writing buffer");
+        } 
+        cyg_semaphore_wait( &listen_sema[which] ); // wait for the appropriate semaphore "reply"
+        cyg_semaphore_post( &send_sema ); // count up successful sends
+
+        close ( s_source );
+    }
+    cyg_thread_exit(); // explicitly
+}
+
+
+static void
+master(cyg_addrword_t param)
+{
+    int i;
+    cyg_handle_t self = cyg_thread_self();
+
+    cyg_semaphore_init( &send_sema, 0 );
+    cyg_semaphore_init( &recv_sema, 0 );
+
+    for ( i = 0 ; i < NLISTENERS; i++ )
+        cyg_semaphore_init( &listen_sema[i], 0 );
+
+    init_all_network_interfaces();
+    CYG_TEST_INFO("Start multiple loopback select test");
+#if NLOOP > 0
+    // We are currently running at high prio, so we can just go and make
+    // loads of threads:
+
+    // Some at higher prio
+    for ( i = 0; i < NLISTENERS/2; i++ )
+        cyg_thread_create(PRIO_LISTENER_HI,       // Priority
+                          listener,               // entry
+                          i,                      // entry parameter
+                          "listener",             // Name
+                          &stack_listener[i][0],  // Stack
+                          STACK_SIZE,             // Size
+                          &listener_thread_handle[i], // Handle
+                          &listener_thread_data[i] // Thread data structure
+            );
+    // the rest at lower prio
+    for (      ; i < NLISTENERS  ; i++ )
+        cyg_thread_create(PRIO_LISTENER_LO,       // Priority
+                          listener,               // entry
+                          i,                      // entry parameter
+                          "listener",             // Name
+                          &stack_listener[i][0],  // Stack
+                          STACK_SIZE,             // Size
+                          &listener_thread_handle[i], // Handle
+                          &listener_thread_data[i] // Thread data structure
+            );
+
+    // make the dummy event-grabber threads
+    for ( i = 0; i < NDUMMIES; i++ )
+        cyg_thread_create(PRIO_DUMMY,             // Priority
+                          dummy,                  // entry
+                          i,                      // entry parameter
+                          "dummy",                // Name
+                          &stack_dummy[i][0],     // Stack
+                          STACK_SIZE,             // Size
+                          &dummy_thread_handle[i], // Handle
+                          &dummy_thread_data[i]   // Thread data structure
+            );
+
+    // Start those threads
+    for ( i = 0; i < NLISTENERS; i++ )
+        cyg_thread_resume(listener_thread_handle[i]);
+    for ( i = 0; i < NDUMMIES; i++ )
+        cyg_thread_resume(   dummy_thread_handle[i]);
+
+    // and let them start up and start listening...
+    cyg_thread_set_priority( self, PRIO_MASTERLOW );
+    CYG_TEST_INFO("All listeners should be go now");
+    cyg_thread_set_priority( self, PRIO_MASTERHIGH );
+
+    for ( i = 0; i < NSENDERS; i++ ) {
+        cyg_thread_create( (0 == i)
+                           ?PRIO_SENDER_MID
+                           : PRIO_SENDER_LOW,     // Priority
+                           sender,                // entry
+                           i,                     // entry parameter
+                           "sender",              // Name
+                           &stack_sender[i][0],   // Stack
+                           STACK_SIZE,            // Size
+                           &sender_thread_handle[i], // Handle
+                           &sender_thread_data[i] // Thread data structure
+            );
+        cyg_thread_resume(sender_thread_handle[i]);
+    }
+
+    // Now we are still higher priority; so go low and let everyone else
+    // have their head.  When we next run after this, it should all be
+    // over.
+    cyg_thread_set_priority( self, PRIO_MASTERLOW );
+
+    cyg_semaphore_peek( &recv_sema, &i );
+    CYG_TEST_CHECK( NLISTENERS == i, "Not enough recvs occurred!" );
+    
+    cyg_semaphore_peek( &send_sema, &i );
+    CYG_TEST_CHECK( NLISTENERS == i, "Not enough sends occurred!" );
+
+    CYG_TEST_PASS_FINISH("Master returned OK");
+#endif
+    CYG_TEST_NA( "No loopback devs" );
+}
+
+void
+cyg_user_start(void)
+{
+    CYG_TEST_INIT();
+
+    cyg_thread_create(PRIO_MASTERHIGH,            // Priority
+                      master,                     // entry
+                      0,                          // entry parameter
+                      "master",                   // Name
+                      &stack_master[0],           // Stack
+                      MASTER_STACK_SIZE,          // Size
+                      &master_thread_handle,      // Handle
+                      &master_thread_data         // Thread data structure
+            );
+    cyg_thread_resume(master_thread_handle);      // Start it
+}
+
+// EOF multi_lo_select.c