/ src / libnml / buffer / memsem.cc
memsem.cc
  1  /********************************************************************
  2  * Description: memsem.cc
  3  *   Uses a block of memory to implement a mutual exclusion semaphore.
  4  *   With LynxOs and SunOs using semop is very inefficient if the semaphore 
  5  *   will usually be available. Other platforms may give you no semaphore
  6  *   operations.
  7  *
  8  *   Example: You are communicating with another process via shared memory.
  9  *   You need a mutual exclusion semaphore because you don't want the reader
 10  *   to occasionally read the buffer while the writer has written only part
 11  *   of the message, but most of the time when the writer won't be using
 12  *   buffer and the reader should get access immediately. semop will take
 13  *   100 to 150 microseconds to return access, while mem_get_access should
 14  *   take less than a microsecond with less than 10 total_connections.
 15  *
 16  *   Derived from a work by Fred Proctor & Will Shackleford
 17  *
 18  * Author:
 19  * License: LGPL Version 2
 20  * System: Linux
 21  *    
 22  * Copyright (c) 2004 All rights reserved.
 23  *
 24  * Last change: 
 25  ********************************************************************/
 26  
 27  #ifdef  __cplusplus
 28  extern "C" {
 29  #endif
 30  
 31  #include <stddef.h>		/* NULL */
 32  
 33  #ifdef  __cplusplus
 34  };
 35  #endif
 36  
 37  #include "timer.hh"		/* etime(), esleep() */
 38  #include "memsem.hh"		/* struct mem_access_object */
 39  #include "rcs_print.hh"		// rcs_print_error()
 40  
 41  #define TIMEOUT_MIN ((double) 1.0E-6)
 42  
 43  /******************************************************************
 44  * Take the mutual exclusion semaphore.
 45  *
 46  * Parameters:
 47  * data should point to an area of memory accesable to all processes that
 48  *   is at least "total_connections" bytes long,
 49  * connection_number is a unique identifier for this process it should
 50  *  be between 0 and ("total_connections"-1)
 51  * total_connections is the maximum number of processes that can use this
 52  *  block of memory as a semaphore.
 53  * timeout - is the time allowed for getting the semaphore.
 54  *    (If timeout is negative then wait forever.)
 55  * semdelay - is the time to wait between tries for the semaphore.
 56  * read_only - Will the access to the buffer only to read the memory.
 57  * split_buffer - The buffer will be split so that one area may be read
 58  * while the other area is written to.
 59  * toggle_bit - If the buffer is split the toggle bit determines which area
 60  * will be read from and which may be written to. (O.K. its really a byte but
 61  * think of it as 1 bit since it should only be 0 or 1)
 62  *
 63  * Returns: 0 for success: -1 for invalid parameters: or -2 if it timed out.
 64  ***********************************************************************/
 65  /*! \todo Another #if 0 */
 66  #if 0
 67  int mem_get_access(void *data, long connection_number,
 68      long total_connections, double timeout, double semdelay,
 69      int read_only, int split_buffer, char &toggle_bit)
 70  #endif
 71       int mem_get_access(struct mem_access_object *mo)
 72  {
 73      char *mylock;
 74      char current_lock;
 75      char *plock;
 76      char *lastlock;
 77      int semaphores_clear;
 78      double start_time, time;
 79      int split_buffer = 0;
 80      int read_only;
 81      int total_connections;
 82      int connection_number;
 83      double timeout;
 84  #ifdef DEBUG
 85      rcs_print("mem_get_access: - Time = %lf\n", etime());
 86      static int count = 0;
 87      count++;
 88  #endif
 89  
 90      /* Check Parameters. */
 91      if ((total_connections = mo->total_connections) <=
 92  	(connection_number = mo->connection_number) || connection_number < 0)
 93      {
 94  	return -1;
 95      }
 96      if (NULL == mo->data) {
 97  	return -1;
 98      }
 99  
100      /* Check for a request for me to sleep */
101      int wait_requested = 1;
102      lastlock = ((char *) mo->data) + total_connections;
103      mylock = ((char *) mo->data) + connection_number;
104      time = start_time = etime();	/* get start time */
105      while (wait_requested
106  	&& (time - start_time < mo->timeout / 2 || mo->timeout < 0)) {
107  	wait_requested = 0;
108  	for (plock = (char *) mo->data; plock < lastlock; plock++) {
109  	    if (5 == (current_lock = *plock) && plock != mylock) {
110  		wait_requested = 1;
111  	    }
112  	}
113  	if (wait_requested) {
114  	    esleep(mo->sem_delay);
115  	}
116      }
117  
118      /* Try the locks one time before checking time because checking the locks 
119         generally takes much less time than checking the time. */
120      *mylock = 4;
121      mo->toggle_bit = ((char *) mo->data)[total_connections];
122      read_only = mo->read_only;
123  #ifdef DEBUG
124      if (read_only) {
125  	rcs_print("added sleep: %d - Time = %lf\n", __LINE__, etime());
126  	esleep(0.02);
127      }
128  #endif
129      if (read_only) {
130  	split_buffer = mo->split_buffer;
131  	if (split_buffer) {
132  	    *mylock = 2 + mo->toggle_bit;
133  	    return 0;
134  	}
135  	*mylock = 2;
136      } else {
137  	*mylock = 1;
138      }
139  
140  #ifdef debug
141      if (read_only) {
142  	rcs_print("added sleep: %d - time = %lf\n", __line__, etime());
143  	esleep(0.01);
144      }
145  #endif
146      semaphores_clear = 1;
147      lastlock = ((char *) mo->data) + total_connections;
148      mo->toggle_bit = ((char *) mo->data)[total_connections];
149      for (plock = (char *) mo->data; plock < lastlock; plock++) {
150  	if (0 != (current_lock = *plock)) {
151  	    if (plock != mylock) {
152  		if (!(read_only && current_lock > 1) &&
153  		    !(split_buffer && current_lock == 2 + mo->toggle_bit)
154  		    && (current_lock != 5)) {
155  		    semaphores_clear = 0;
156  		}
157  	    }
158  	}
159      }
160  #ifdef debug
161      if (0)			// read_only && 0 == count % 2)
162      {
163  	rcs_print("added sleep: %d - time = %lf\n", __line__, etime());
164  	esleep(0.01);
165      }
166  #endif
167      if (semaphores_clear) {
168  	return 0;
169      }
170      timeout = mo->timeout;
171      if (timeout < TIMEOUT_MIN && timeout > 0) {
172  	*mylock = 0;
173  	return (-2);
174      }
175      /* release the lock before going to sleep. */
176      *mylock = 5;
177  
178      if (NULL != mo->sem) {
179  	if (-1 == mo->sem->wait()) {
180  	    *mylock = 0;
181  	    return -1;
182  	}
183      } else {
184  	esleep(mo->sem_delay);	/* sleep for 100 microseconds */
185      }
186      if (read_only) {
187  	*mylock = 2;
188      } else {
189  	*mylock = 1;
190      }
191  
192  #ifdef debug
193      if (0)			// read_only && 0 == count % 7)
194      {
195  	rcs_print("added sleep: %d - time = %lf\n", __line__, etime());
196  	esleep(0.01);
197      }
198  #endif
199      while ((timeout >= 0) ? ((time - start_time) < timeout) : 1) {
200  	if (split_buffer) {
201  	    mo->toggle_bit = ((char *) mo->data)[total_connections];
202  	}
203  	semaphores_clear = 1;
204  	plock = (char *) mo->data;
205  	mo->toggle_bit = ((char *) mo->data)[total_connections];
206  	for (; plock < lastlock; plock++) {
207  	    current_lock = *plock;
208  	    if (0 != current_lock) {
209  		if (plock != mylock &&
210  		    !(read_only && current_lock > 1) &&
211  		    !(split_buffer && current_lock == 2 + mo->toggle_bit)
212  		    && (current_lock != 5)) {
213  		    semaphores_clear = 0;
214  		}
215  	    }
216  	}
217  	if (semaphores_clear) {
218  	    return 0;
219  	}
220  	if (NULL != mo->sem) {
221  	    *mylock = 5;
222  	    mo->sem->wait();
223  	} else {
224  	    *mylock = 5;
225  	    esleep(mo->sem_delay);	/* sleep for 100 microseconds */
226  	}
227  	if (read_only) {
228  	    *mylock = 2;
229  	} else {
230  	    *mylock = 1;
231  	}
232  #ifdef DEBUG
233  	if (0)			// read_only && 0 == count % 4)
234  	{
235  	    rcs_print("added sleep: %d - Time = %lf\n", __LINE__, etime());
236  	    esleep(0.01);
237  	}
238  #endif
239  	time = etime();
240      }
241      *mylock = 0;
242      return (-2);
243  }
244  
245  /******************************************************************
246  * Give up the mutual exclusion semaphore.
247  *
248  * Parameters:
249  * data should point to an area of memory accesable to all processes that
250  *   is at least "total_connections" bytes long,
251  * connection_number is a unique identifier for this process it should
252  *  be between 0 and ("total_connections"-1)
253  *
254  * Returns: 0 for success: -1 for invalid parameters
255  ***********************************************************************/
256  int mem_release_access(struct mem_access_object *mo)
257  {
258      int i;
259      int process_waiting = 0;
260  #ifdef DEBUG
261      rcs_print("mem_release_access: - Time = %lf\n", etime());
262      static int count = 0;
263      count++;
264  #endif
265      if (NULL == mo) {
266  	rcs_print_error("mem_release_access: Invalid memory object.\n");
267  	return -1;
268      }
269      if (NULL == mo->data || mo->connection_number < 0) {
270  	rcs_print_error("mem_release_access: Invalid memory object.\n");
271  	return -1;
272      }
273  
274      if (mo->sem != NULL) {
275  	process_waiting = 0;
276  	for (i = 0; i < mo->total_connections; i++) {
277  	    if (((char *) mo->data)[i] == 5) {
278  		process_waiting = 1;
279  		break;
280  	    }
281  	}
282      }
283  #ifdef DEBUG
284      if (0)			// (0 == count % 5)
285      {
286  	rcs_print("added sleep: %d - Time = %lf\n", __LINE__, etime());
287  	esleep(0.01);
288      }
289  #endif
290  
291      /* If were using a split buffer and this is the end of a write toggle the 
292         toggle bit. */
293      if (mo->split_buffer && ((char *) mo->data)[mo->connection_number] == 1) {
294  	((char *) mo->data)[mo->total_connections] = !(mo->toggle_bit);
295      }
296  #ifdef DEBUG
297      if (0)			// 0 == count % 7)
298      {
299  	rcs_print("added sleep: %d - Time = %lf\n", __LINE__, etime());
300  	esleep(0.01);
301      }
302  #endif
303  
304      ((char *) mo->data)[mo->connection_number] = 0;
305  #ifdef DEBUG
306      if (0)			// 0 == count % 11)
307      {
308  	rcs_print("added sleep: %d - Time = %lf\n", __LINE__, etime());
309  	esleep(0.01);
310      }
311  #endif
312  
313      if (mo->sem != NULL) {
314  	if (process_waiting) {
315  	    mo->sem->post();
316  	}
317      }
318      return (0);
319  }