Main Page   Class Hierarchy   Alphabetical List   Data Structures   File List   Data Fields   Globals  

memsem.cc

Go to the documentation of this file.
00001 /**********************************************************************
00002 * File: memsem.cc
00003 * Purpose: Uses a block of memory to implement a mutual exclusion semaphore.
00004 * With LynxOs and SunOs using semop is very inefficient if the semaphore will
00005 * ussually be available. Other platforms may give you no semaphore operations.
00006 *
00007 * Example: You are communicating with another process via shared memory. You
00008 * need a mutual exclusion semaphore because you don't want the reader to
00009 * occasionally read the buffer while the writer has written only part of the
00010 * message, but most of the time when the writer won't be using buffer and
00011 * the reader should get access immediately. semop will take 100 to 150
00012 * microseconds to return access, while mem_get_access should take less than
00013 * a microsecond with less than 10 total_connections.
00014 *
00015 * On 1/23/95 Will Shackleford began additions to allow for the buffer to
00016 * be split. Splitting the buffer between doubles the ammount of memory
00017 * required, but means that readers will never have to wait for a semaphore
00018 * and decreases the percentage of time a writer will have to wait.
00019 *
00020 * This code will be obsolete when all operating systems provide efficient
00021 * semaphore functions and all microprocessors have effective test and
00022 * set instructions.
00023 *************************************************************************/
00024 
00025 #include "rcs_defs.hh"          /* EXTERN_C_STD_HEADERS */
00026 
00027 #ifdef  EXTERN_C_STD_HEADERS
00028 extern "C"
00029 {
00030 #endif
00031 
00032 #ifndef UNDER_CE
00033 #include <stddef.h>             /* NULL */
00034 #endif
00035 
00036 #ifdef  EXTERN_C_STD_HEADERS
00037 };
00038 #endif
00039 
00040 #include "timer.hh"             /* etime(), esleep() */
00041 #include "memsem.hh"            /* struct mem_access_object */
00042 #include "rcs_prnt.hh"          // rcs_print_error()
00043 
00044 #define TIMEOUT_MIN ((double) 1.0E-6)
00045 
00046 /******************************************************************
00047 * Take the mutual exclusion semaphore.
00048 *
00049 * Parameters:
00050 * data should point to an area of memory accesable to all processes that
00051 *   is at least "total_connections" bytes long,
00052 * connection_number is a unique identifier for this process it should
00053 *  be between 0 and ("total_connections"-1)
00054 * total_connections is the maximum number of processes that can use this
00055 *  block of memory as a semaphore.
00056 * timeout - is the time allowed for getting the semaphore.
00057 *    (If timeout is negative then wait forever.)
00058 * semdelay - is the time to wait between tries for the semaphore.
00059 * read_only - Will the access to the buffer only to read the memory.
00060 * split_buffer - The buffer will be split so that one area may be read
00061 * while the other area is written to.
00062 * toggle_bit - If the buffer is split the toggle bit determines which area
00063 * will be read from and which may be written to. (O.K. its really a byte but
00064 * think of it as 1 bit since it should only be 0 or 1)
00065 *
00066 * Returns: 0 for success: -1 for invalid parameters: or -2 if it timed out.
00067 ***********************************************************************/
00068 #if 0
00069 int
00070 mem_get_access (void *data, long connection_number,
00071                 long total_connections, double timeout, double semdelay,
00072                 int read_only, int split_buffer, char &toggle_bit)
00073 #endif
00074      int mem_get_access (struct mem_access_object *mo)
00075 {
00076   register char *mylock;
00077   register char current_lock;
00078   register char *plock;
00079   char *lastlock;
00080   int semaphores_clear;
00081   double start_time, time;
00082   int split_buffer = 0;
00083   int read_only;
00084   int total_connections;
00085   int connection_number;
00086   double timeout;
00087 #ifdef DEBUG
00088   rcs_print ("mem_get_access: - Time = %lf\n", etime ());
00089 #endif
00090 #ifdef DEBUG
00091   static int count = 0;
00092   count++;
00093 #endif
00094 
00095   /* Check Parameters. */
00096   if ((total_connections = mo->total_connections) <=
00097       (connection_number = mo->connection_number) || connection_number < 0)
00098     {
00099       return -1;
00100     }
00101   if (NULL == mo->data)
00102     {
00103       return -1;
00104     }
00105 
00106   /* Check for a request for me to sleep */
00107   int wait_requested = 1;
00108   lastlock = ((char *) mo->data) + total_connections;
00109   mylock = ((char *) mo->data) + connection_number;
00110   time = start_time = etime (); /* get start time  */
00111   while (wait_requested
00112          && (time - start_time < mo->timeout / 2 || mo->timeout < 0))
00113     {
00114       wait_requested = 0;
00115       for (plock = (char *) mo->data; plock < lastlock; plock++)
00116         {
00117           if (5 == (current_lock = *plock) && plock != mylock)
00118             {
00119               wait_requested = 1;
00120             }
00121         }
00122       if (wait_requested)
00123         {
00124           esleep (mo->sem_delay);
00125         }
00126     }
00127 
00128   /* Try the locks one time before checking time because checking the
00129      locks generally takes much less time than checking the time. */
00130   *mylock = 4;
00131   mo->toggle_bit = ((char *) mo->data)[total_connections];
00132   read_only = mo->read_only;
00133 #ifdef DEBUG
00134   if (read_only)
00135     {
00136       rcs_print ("added sleep: %d - Time = %lf\n", __LINE__, etime ());
00137       esleep (0.02);
00138     }
00139 #endif
00140   if (read_only)
00141     {
00142       split_buffer = mo->split_buffer;
00143       if (split_buffer)
00144         {
00145           *mylock = 2 + mo->toggle_bit;
00146           return 0;
00147         }
00148       *mylock = 2;
00149     }
00150   else
00151     {
00152       *mylock = 1;
00153     }
00154 
00155 #ifdef debug
00156   if (read_only)
00157     {
00158       rcs_print ("added sleep: %d - time = %lf\n", __line__, etime ());
00159       esleep (0.01);
00160     }
00161 #endif
00162   semaphores_clear = 1;
00163   lastlock = ((char *) mo->data) + total_connections;
00164   mo->toggle_bit = ((char *) mo->data)[total_connections];
00165   for (plock = (char *) mo->data; plock < lastlock; plock++)
00166     {
00167       if (0 != (current_lock = *plock))
00168         {
00169           if (plock != mylock)
00170             {
00171               if (!(read_only && current_lock > 1) &&
00172                   !(split_buffer && current_lock == 2 + mo->toggle_bit)
00173                   && (current_lock != 5))
00174                 {
00175                   semaphores_clear = 0;
00176                 }
00177             }
00178         }
00179     }
00180 #ifdef debug
00181   if (0)                        //read_only && 0 == count % 2)
00182     {
00183       rcs_print ("added sleep: %d - time = %lf\n", __line__, etime ());
00184       esleep (0.01);
00185     }
00186 #endif
00187   if (semaphores_clear)
00188     {
00189       return 0;
00190     }
00191   timeout = mo->timeout;
00192   if (timeout < TIMEOUT_MIN && timeout > 0)
00193     {
00194       *mylock = 0;
00195       return (-2);
00196     }
00197   /* release the lock before going to sleep. */
00198   *mylock = 5;
00199 
00200   if (NULL != mo->sem)
00201     {
00202       if (-1 == mo->sem->wait ())
00203         {
00204           *mylock = 0;
00205           return -1;
00206         }
00207     }
00208   else
00209     {
00210       esleep (mo->sem_delay);   /* sleep for 100 microseconds */
00211     }
00212   if (read_only)
00213     {
00214       *mylock = 2;
00215     }
00216   else
00217     {
00218       *mylock = 1;
00219     }
00220 
00221 #ifdef debug
00222   if (0)                        //read_only && 0 == count % 7)
00223     {
00224       rcs_print ("added sleep: %d - time = %lf\n", __line__, etime ());
00225       esleep (0.01);
00226     }
00227 #endif
00228   while ((timeout >= 0) ? ((time - start_time) < timeout) : 1)
00229     {
00230       if (split_buffer)
00231         {
00232           mo->toggle_bit = ((char *) mo->data)[total_connections];
00233         }
00234       semaphores_clear = 1;
00235       plock = (char *) mo->data;
00236       mo->toggle_bit = ((char *) mo->data)[total_connections];
00237       for (; plock < lastlock; plock++)
00238         {
00239           current_lock = *plock;
00240           if (0 != current_lock)
00241             {
00242               if (plock != mylock &&
00243                   !(read_only && current_lock > 1) &&
00244                   !(split_buffer && current_lock == 2 + mo->toggle_bit)
00245                   && (current_lock != 5))
00246                 {
00247                   semaphores_clear = 0;
00248                 }
00249             }
00250         }
00251       if (semaphores_clear)
00252         {
00253           return 0;
00254         }
00255       if (NULL != mo->sem)
00256         {
00257           *mylock = 5;
00258           mo->sem->wait ();
00259         }
00260       else
00261         {
00262           *mylock = 5;
00263           esleep (mo->sem_delay);       /* sleep for 100 microseconds */
00264         }
00265       if (read_only)
00266         {
00267           *mylock = 2;
00268         }
00269       else
00270         {
00271           *mylock = 1;
00272         }
00273 #ifdef DEBUG
00274       if (0)                    //read_only && 0 == count % 4)
00275         {
00276           rcs_print ("added sleep: %d - Time = %lf\n", __LINE__, etime ());
00277           esleep (0.01);
00278         }
00279 #endif
00280       time = etime ();
00281     }
00282   *mylock = 0;
00283   return (-2);
00284 }
00285 
00286 
00287 /******************************************************************
00288 * Give up the mutual exclusion semaphore.
00289 *
00290 * Parameters:
00291 * data should point to an area of memory accesable to all processes that
00292 *   is at least "total_connections" bytes long,
00293 * connection_number is a unique identifier for this process it should
00294 *  be between 0 and ("total_connections"-1)
00295 *
00296 * Returns: 0 for success: -1 for invalid parameters
00297 ***********************************************************************/
00298 int
00299 mem_release_access (struct mem_access_object *mo)
00300 {
00301   int i;
00302   int process_waiting = 0;
00303 #ifdef DEBUG
00304   rcs_print ("mem_release_access: - Time = %lf\n", etime ());
00305 #endif
00306 #ifdef DEBUG
00307   static int count = 0;
00308   count++;
00309 #endif
00310   if (NULL == mo)
00311     {
00312       rcs_print_error ("mem_release_access: Invalid memory object.\n");
00313     }
00314   if (NULL == mo->data || mo->connection_number < 0)
00315     {
00316       rcs_print_error ("mem_release_access: Invalid memory object.\n");
00317       return -1;
00318     }
00319 
00320   if (mo->sem != NULL)
00321     {
00322       process_waiting = 0;
00323       for (i = 0; i < mo->total_connections; i++)
00324         {
00325           if (((char *) mo->data)[i] == 5)
00326             {
00327               process_waiting = 1;
00328               break;
00329             }
00330         }
00331     }
00332 #ifdef DEBUG
00333   if (0)                        //(0 == count % 5)
00334     {
00335       rcs_print ("added sleep: %d - Time = %lf\n", __LINE__, etime ());
00336       esleep (0.01);
00337     }
00338 #endif
00339 
00340   /* If were using a split buffer and this is the end of a write
00341      toggle the toggle bit. */
00342   if (mo->split_buffer && ((char *) mo->data)[mo->connection_number] == 1)
00343     {
00344       ((char *) mo->data)[mo->total_connections] = !(mo->toggle_bit);
00345     }
00346 #ifdef DEBUG
00347   if (0)                        // 0 == count % 7)
00348     {
00349       rcs_print ("added sleep: %d - Time = %lf\n", __LINE__, etime ());
00350       esleep (0.01);
00351     }
00352 #endif
00353 
00354   ((char *) mo->data)[mo->connection_number] = 0;
00355 #ifdef DEBUG
00356   if (0)                        // 0 == count % 11)
00357     {
00358       rcs_print ("added sleep: %d - Time = %lf\n", __LINE__, etime ());
00359       esleep (0.01);
00360     }
00361 #endif
00362 
00363   if (mo->sem != NULL)
00364     {
00365       if (process_waiting)
00366         {
00367           mo->sem->post ();
00368         }
00369     }
00370   return (0);
00371 }

Generated on Sun Dec 2 15:56:50 2001 for rcslib by doxygen1.2.11.1 written by Dimitri van Heesch, © 1997-2001