This is the mail archive of the ecos-discuss@sources.redhat.com mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

few test patches for ecos-SMP


Hi Jifl,

Following the suggetions in your mail on 18 Oct 2002 regarding patch generation
(on ecos-discuss mailing list) I am sending the patches to make

packages/net/common/current/tests/multi_lo_select.c
packages/kernel/current/tests/bin_sem1.cxx
packages/kernel/current/tests/mqueue1.cxx

tests work on multiprocessor system (SMP scenario). The tests, as they stand
now, will fail to work otherwise.

More details on why the changes (additions mainly) are required, are provided in the comments around the changes.

In short, changes in kernel part are to take care of some race conditions in tests, and in multi_lo_select, to take care of some synchronisation issues.

--
regards
sandeep
--------------------------------------------------------------------------
Happiness, n.:
An agreeable sensation arising from contemplating the misery of
another.
-- Ambrose Bierce, "The Devil's Dictionary"
--------------------------------------------------------------------------
Index: multi_lo_select.c
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/common/current/tests/multi_lo_select.c,v
retrieving revision 1.2
diff -u -5 -p -r1.2 multi_lo_select.c
--- multi_lo_select.c	6 Oct 2002 13:07:53 -0000	1.2
+++ multi_lo_select.c	21 Oct 2002 11:19:10 -0000
@@ -57,10 +57,11 @@
 //==========================================================================
 
 #include <network.h>
 
 #include <cyg/infra/testcase.h>
+#include <cyg/kernel/kapi.h>
 
 #ifndef CYGPKG_LIBC_STDIO
 #define perror(s) diag_printf(#s ": %s\n", strerror(errno))
 #endif
 
@@ -130,23 +131,38 @@ static cyg_handle_t sender_thread_handle
 static cyg_sem_t listen_sema[NLISTENERS];
 
 static cyg_sem_t send_sema;
 static cyg_sem_t recv_sema;
 
+// Introduced to make sure that only one dummy allocates a socket and
+// binds, as intended in dummy(..) below. This also takes care of sanity
+// of static variables shared by all dummy instances.
+
+static cyg_sem_t dummy_sema;
+
 static cyg_thread_entry_t master;
 static cyg_thread_entry_t listener;
 static cyg_thread_entry_t sender;
 
+// Following variables are introduced to make sure that all the listeners are
+// up and listening before master thread fires the senders.
+// Later we reuse them, to make sure that master proceeds only after all the
+// listeners have finished.
+
+int liscnt = 0;
+cyg_mutex_t m0;
+cyg_cond_t cvar0;
+
+
 // ------------------------------------------------------------------------
 
 void
 pexit(char *s)
 {
     CYG_TEST_FAIL_FINISH( s );
 }
 
-
 #ifndef max
 #define max(a,b) (((a) > (b)) ? (a) : (b))
 #endif
 
 // ------------------------------------------------------------------------
@@ -164,25 +180,28 @@ void dummy( cyg_addrword_t which )
     CYG_TEST_CHECK( 0 <= which, "which under" );
     CYG_TEST_CHECK( NDUMMIES > which, "which over" );
 
     diag_printf( "Dummy %d alive\n", which );
 
+    cyg_semaphore_wait( &dummy_sema ); // only one dummy to ask for a socket
     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);
     }
+    cyg_semaphore_post( &dummy_sema);
 
     while (true) {
         FD_ZERO(&in_fds);
         FD_SET(s_s1, &in_fds);
         num = select( s_s1+1, &in_fds,0,0,0);
@@ -214,10 +233,11 @@ void listener( cyg_addrword_t which )
 
     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);
@@ -240,10 +260,17 @@ void listener( cyg_addrword_t which )
             pexit("bind /source_2/ error");
         }
         listen(s_s2, SOMAXCONN);
     }
 
+    // making sure that master doesn't fire senders before all the listeners
+    // are ready
+    cyg_mutex_lock(&m0);
+    liscnt++;
+    cyg_mutex_unlock(&m0);
+    cyg_cond_signal(&cvar0);	 // ready to listen now
+
     while (true) {
         FD_ZERO(&in_fds);
         FD_SET(s_s1, &in_fds);
         if ( dual )
             FD_SET(s_s2, &in_fds);
@@ -290,10 +317,17 @@ void listener( cyg_addrword_t which )
         close ( e_s2 );
 
     cyg_semaphore_post( &listen_sema[which] ); // Verify that I was here
     cyg_semaphore_post( &recv_sema );          // Count receptions
 
+    // making sure that master doesn't continue before all the listeners
+    // have finished
+    cyg_mutex_lock(&m0);
+    liscnt++;
+    cyg_mutex_unlock(&m0);
+    cyg_cond_signal(&cvar0);	 // let master know this listener is over
+
     cyg_thread_exit(); // explicitly
 }
 
 // ------------------------------------------------------------------------
 static void sender( cyg_addrword_t which ) // which means which set (odd/even) here...
@@ -338,10 +372,14 @@ 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 );
+    cyg_semaphore_init( &dummy_sema, 1 );
+
+    cyg_mutex_init(&m0);
+    cyg_cond_init(&cvar0, &m0);
 
     for ( i = 0 ; i < NLISTENERS; i++ )
         cyg_semaphore_init( &listen_sema[i], 0 );
 
     init_all_network_interfaces();
@@ -391,13 +429,35 @@ master(cyg_addrword_t param)
     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 );
+
+    // making sure all listeners are ready to listen
+    
+    while (NLISTENERS != liscnt) {
+	    cyg_mutex_lock(&m0);
+
+	    /* possible that some listener(s) managed to increment
+	     * liscnt by the time this thread got the lock in current
+	     * iteration. If we go into cyg_cond_wait directly then
+	     * it can lead into a situation where it can wait forever
+	     * as there is no listener to wake(signal) it up.
+	     */
+
+	    if (NLISTENERS != liscnt)
+	    	cyg_cond_wait(&cvar0);
+
+	    cyg_mutex_unlock(&m0);
+    }
+
     CYG_TEST_INFO("All listeners should be go now");
     cyg_thread_set_priority( self, PRIO_MASTERHIGH );
 
+	// Need to reset liscnt before resuming any sender 
+	liscnt = 0;
+
     for ( i = 0; i < NSENDERS; i++ ) {
         cyg_thread_create( (0 == i)
                            ?PRIO_SENDER_MID
                            : PRIO_SENDER_LOW,     // Priority
                            sender,                // entry
@@ -413,10 +473,29 @@ master(cyg_addrword_t param)
 
     // 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 );
+
+    // making sure all listeners have finished before master proceeds
+    // we are reusing liscnt, m0 and cvar0 for the purpose.
+    
+    while (NLISTENERS != liscnt) {
+	    cyg_mutex_lock(&m0);
+
+	    /* possible that some listener(s) managed to increment
+	     * liscnt by the time this thread got the lock in current
+	     * iteration. If we go into cyg_cond_wait directly then
+	     * it can lead into a situation where it can wait forever
+	     * as there is no listener to wake(signal) it up.
+	     */
+
+	    if (NLISTENERS != liscnt)
+	    	cyg_cond_wait(&cvar0);
+
+	    cyg_mutex_unlock(&m0);
+    }
 
     cyg_semaphore_peek( &recv_sema, &i );
     CYG_TEST_CHECK( NLISTENERS == i, "Not enough recvs occurred!" );
     
     cyg_semaphore_peek( &send_sema, &i );
Index: bin_sem1.cxx
===================================================================
RCS file: /cvs/ecos/ecos/packages/kernel/current/tests/bin_sem1.cxx,v
retrieving revision 1.7
diff -u -5 -p -r1.7 bin_sem1.cxx
--- bin_sem1.cxx	23 May 2002 23:06:56 -0000	1.7
+++ bin_sem1.cxx	21 Oct 2002 12:25:04 -0000
@@ -85,11 +85,16 @@ static void entry0( CYG_ADDRWORD data )
     CYG_TEST_PASS_FINISH("Binary Semaphore 1 OK");
 }
 
 static void entry1( CYG_ADDRWORD data )
 {
-    CHECK( s1.posted() );
+//    CHECK( s1.posted() );
+// In Multiprocessor scenario above check is likely to fail, because entry0
+// will take some time to finish with it's first s1.post() execution, but this
+// thread (entry1) will CHECK(s1.posted()) in the very beginning of it's
+// execution on another processor.
+
     s1.wait();
     CHECK( 1 == q++ );
     CHECK( ! s0.posted() );
     s0.post();
     s1.wait();
Index: mqueue1.cxx
===================================================================
RCS file: /cvs/ecos/ecos/packages/kernel/current/tests/mqueue1.cxx,v
retrieving revision 1.5
diff -u -5 -p -r1.5 mqueue1.cxx
--- mqueue1.cxx	23 May 2002 23:07:01 -0000	1.5
+++ mqueue1.cxx	21 Oct 2002 13:01:23 -0000
@@ -78,10 +78,25 @@ static char mempool[500];
 static size_t storedmempoollen;
 Cyg_Mqueue *mq;
 static Cyg_Binary_Semaphore t0sem, t1sem;
 static int calledback;
 
+// In multiprocessor scenario, t0 and t1 both will get scheduled to
+// run on different processors and a race condition crops up, where if
+// t1 checks for (mq == NULL) before the execution of my_free (called from
+// inside Cyg_Mqueue destructor for the object "the_mq" in thread t0) is
+// over, it will fail because mq won't be NULL by then.
+//
+// t0done is introduced to take care of this race condition b/w t0 and t1.
+// It is set to 1 at the end of my_free function.
+//
+// NOTE : Current solution will work as long as the size of mempool is kept
+// lesser than whatever you are passing to huge_mq(99999, 99999, ...) object
+// creation in thread t1, so that my_free gets called only once (as intended
+// in this test).
+
+int t0done = 0;
 
 /* FUNCTIONS */
 
 static int
 my_memcmp(const void *m1, const void *m2, size_t n)
@@ -112,10 +127,12 @@ static void
 my_free( void *ptr, size_t len )
 {
     CYG_TEST_PASS_FAIL( (ptr == &mempool[0]) && (len == storedmempoollen), 
                         "Freed pool correctly");
     mq = NULL; // invalidate
+
+    t0done = 1;	// t1 can proceed with checking of (mq == NULL) now
 }
 
 static void
 callback(Cyg_Mqueue &mq, CYG_ADDRWORD data)
 {
@@ -373,10 +390,17 @@ t1( CYG_ADDRWORD data )
 
 //------------------------------------------------------------------------
     
     t0sem.post();         // t0 should run straight away
     Cyg_Thread::yield();  // but just in case we have a funny sched
+
+    // making sure that thread t1 execution doesn't proceed till the
+    // destructor for Cyg_Mqueue object "the_mq" in thread t0 is done with
+    // it's work. The destructor is invoked because thread t0 has finished
+    // with it's execution.
+
+    while (0 == t0done) /* do nothing */ ;
 
     // check that mq was destroyed when t0 dropped off the end
     CYG_TEST_PASS_FAIL( NULL == mq, "queue destroyed correctly" );
     
     CYG_TEST_EXIT("kernel mqueue test 1");

-- 
Before posting, please read the FAQ: http://sources.redhat.com/fom/ecos
and search the list archive: http://sources.redhat.com/ml/ecos-discuss

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]