librnr  1.14.5
RoadNarrows Robotics Common Library 1
shm.c
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: RoadNarrows Robotics Common Library 1
4 //
5 // Library: librnr
6 //
7 // File: shm.c
8 //
9 /*! \file
10  *
11  * $LastChangedDate: 2013-02-07 16:51:44 -0700 (Thu, 07 Feb 2013) $
12  * $Rev: 2675 $
13  *
14  * \brief Shared memory routines.
15  *
16  * \li Basic convenience funtions.
17  * \li Shared memory implementation of the POSIX thread mutex.
18  * With shared memory, the mutex may span mutilple applications, not just
19  * threads within one application.
20  * \li Other functions as needed.
21  *
22  * \author Robin Knight (robin.knight@roadnarrows.com)
23  *
24  * \pkgcopyright{2013-2018,RoadNarrows LLC.,http://www.roadnarrows.com}
25  */
26 // Permission is hereby granted, without written agreement and without
27 // license or royalty fees, to use, copy, modify, and distribute this
28 // software and its documentation for any purpose, provided that
29 // (1) The above copyright notice and the following two paragraphs
30 // appear in all copies of the source code and (2) redistributions
31 // including binaries reproduces these notices in the supporting
32 // documentation. Substantial modifications to this software may be
33 // copyrighted by their authors and need not follow the licensing terms
34 // described here, provided that the new terms are clearly indicated in
35 // all files where they apply.
36 //
37 // IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY MEMBERS/EMPLOYEES
38 // OF ROADNARROW LLC OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
39 // PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
40 // DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
41 // EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
42 // THE POSSIBILITY OF SUCH DAMAGE.
43 //
44 // THE AUTHOR AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
45 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
46 // FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
47 // "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
48 // PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
49 //
50 ////////////////////////////////////////////////////////////////////////////////
51 
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <sys/ipc.h>
55 #include <sys/shm.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <unistd.h>
61 #include <errno.h>
62 #include <fcntl.h>
63 #include <pthread.h>
64 
65 #include "rnr/rnrconfig.h"
66 #include "rnr/log.h"
67 #include "rnr/shm.h"
68 
69 
70 //------------------------------------------------------------------------------
71 // Basic Shared Memory Functions
72 //------------------------------------------------------------------------------
73 
74 int shm_open(key_t key,
75  size_t size,
76  shm_mem_init_func_t mem_init,
77  shm_t *pshm)
78 {
79  int perm = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
80  int shmId;
81  void *addr;
82  int tmp;
83 
84  if( pshm == NULL )
85  {
86  LOGERROR("shm_t structure is NULL.");
87  errno = EINVAL;
88  return RC_ERROR;
89  }
90 
91  // zzzsst
92  pshm->m_shmKey = 0;
93  pshm->m_shmId = 0;
94  pshm->m_shmSize = 0;
95  pshm->m_shmAddr = NULL;
96 
97  //
98  // Try to create shared memory with the given key, returning the identifier.
99  // Fails if already created.
100  //
101  shmId = shmget(key, size, IPC_CREAT | IPC_EXCL | perm);
102 
103  //
104  // Created.
105  //
106  if( shmId >= 0 )
107  {
108  // attach shared memory to process address space
109  addr = shmat(shmId, NULL, 0);
110 
111  if( addr == (void *)(-1) )
112  {
113  LOGSYSERROR("shmgat(%d, NULL, 0)", shmId);
114  tmp = errno;
115  shmctl(shmId, IPC_RMID, NULL);
116  errno = tmp;
117  return RC_ERROR;
118  }
119 
120  //
121  // Initialize the shared memory. Only the caller that creates the shared
122  // memory, initializes it, preventing race condition between caller
123  // threads or applications.
124  //
125  if( mem_init != NULL )
126  {
127  mem_init(key, addr, size);
128  }
129  }
130 
131  //
132  // Already created, try to attach.
133  //
134  else if( errno == EEXIST )
135  {
136  // already exist, get the identifier
137  shmId = shmget(key, size, perm);
138 
139  if( shmId < 0 )
140  {
141  LOGSYSERROR("shmget(%d=0x%x, %zu, ...)", key, key, size);
142  return RC_ERROR;
143  }
144 
145  // attach shared memory to process address space
146  addr = shmat(shmId, NULL, 0);
147 
148  if( addr == (void *)(-1) )
149  {
150  LOGSYSERROR("shmgat(%d, NULL, 0)", shmId);
151  tmp = errno;
152  shmctl(shmId, IPC_RMID, NULL);
153  errno = tmp;
154  return RC_ERROR;
155  }
156  }
157 
158  //
159  // Failed to create or get shared memory.
160  //
161  else
162  {
163  LOGSYSERROR("shmget(%d=0x%x, %zu, ...)", key, key, size);
164  return RC_ERROR;
165  }
166 
167  // set values
168  pshm->m_shmKey = key;
169  pshm->m_shmId = shmId;
170  pshm->m_shmSize = size;
171  pshm->m_shmAddr = addr;
172 
173  LOGDIAG3("Opened shared memory (id=%d, size=%zu).", key, size);
174 
175  return OK;
176 }
177 
178 int shm_close(shm_t *pshm)
179 {
180  if( pshm == NULL )
181  {
182  LOGERROR("shm_t structure is NULL.");
183  errno = EINVAL;
184  return RC_ERROR;
185  }
186 
187  // mark shared memory to be detroyed
188  else if( shmctl(pshm->m_shmId, IPC_RMID, NULL) < 0 )
189  {
190  LOGSYSERROR("shmctl(%d, ...)", pshm->m_shmId);
191  return RC_ERROR;
192  }
193 
194  // detach shared memory from application address space
195  else if( shmdt(pshm->m_shmAddr) < 0 )
196  {
197  LOGSYSERROR("shmdt(%p)", pshm->m_shmAddr);
198  return RC_ERROR;
199  }
200 
201  pshm->m_shmKey = 0;
202  pshm->m_shmId = 0;
203  pshm->m_shmSize = 0;
204  pshm->m_shmAddr = NULL;
205 
206  LOGDIAG3("Closed shared memory (id=%d).", pshm->m_shmId);
207 
208  return OK;
209 }
210 
211 
212 //------------------------------------------------------------------------------
213 // Mutex Shared Memory Functions
214 //------------------------------------------------------------------------------
215 
216 /*!
217  * \brief Create the share memory mutex.
218  *
219  * \param key Shared memory key.
220  * \param addr Starting address of attached shared memory.
221  * \param size Shared memory size.
222  */
223 static void shm_mutex_create(key_t key, void *addr, size_t size)
224 {
225  pthread_mutex_t *pmutex = (pthread_mutex_t *)addr;
226  pthread_mutexattr_t mutexAttr;
227 
228  pthread_mutexattr_init(&mutexAttr);
229 
230  //
231  // Set the attribute to share between applications.
232  //
233  if(pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED) < 0)
234  {
235  LOGSYSERROR("pthread_mutexattr_setpshared()");
236  }
237 
238 #ifndef ARCH_overo
239  //
240  // Set mutex robust attribute. If the owning thread or application terminates,
241  // the next thread/application acquiring the mutex is notified by the
242  // return value EOWNERDEAD.
243  //
244  else if( pthread_mutexattr_setrobust(&mutexAttr, PTHREAD_MUTEX_ROBUST) < 0 )
245  {
246  LOGSYSERROR("pthread_mutexattr_setrobust()");
247  }
248 
249  //
250  // Allow recursive locks by the owning thread or application.
251  //
252  else if( pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE) < 0 )
253  {
254  LOGSYSERROR("pthread_mutexattr_settype()");
255  }
256 #endif // ARCH_overo
257 
258  //
259  // Initialize the pthread mutex with the give attributes
260  //
261  else if( pthread_mutex_init(pmutex, &mutexAttr) < 0 )
262  {
263  LOGSYSERROR("pthread_mutex_init()");
264  }
265 
266  //
267  // Success.
268  //
269  else
270  {
271  LOGDIAG4("Shared memory mutex created.");
272  }
273 
274  pthread_mutexattr_destroy(&mutexAttr);
275 }
276 
277 int shm_mutex_init(key_t key, shm_mutex_t *pshmmutex)
278 {
279  size_t size = sizeof(pthread_mutex_t);
280 
281  if( pshmmutex == NULL )
282  {
283  LOGERROR("shm_mutex_t structure is NULL.");
284  errno = EINVAL;
285  return RC_ERROR;
286  }
287 
288  // create/attach to shared memory mutex
289  if( shm_open(key, size, shm_mutex_create, pshmmutex) < 0 )
290  {
291  LOGSYSERROR("shm_mutex_init(%d=0x%x, ...)", key, key);
292  return RC_ERROR;
293  }
294 
295  LOGDIAG3("Shared memory mutex initialized (key=%d=0x%x, ...)", key, key);
296 
297  return OK;
298 }
299 
301 {
302  return shm_close(pshmmutex);
303 }
304 
306 {
307  pthread_mutex_t *pmutex;
308  int ntries;
309  int rc;
310 
311  if( pshmmutex == NULL )
312  {
313  LOGERROR("shm_mutex_t structure is NULL.");
314  errno = EINVAL;
315  return errno;
316  }
317 
318  pmutex = (pthread_mutex_t *)(pshmmutex->m_shmAddr);
319 
320  for(ntries=0; ntries<SHM_MUTEX_N_TRIES; ++ntries)
321  {
322  if( (rc = pthread_mutex_lock(pmutex)) == 0 )
323  {
324  return OK;
325  }
326 
327  switch( rc )
328  {
329  case EOWNERDEAD: // owner of mutex went to a better place
330 #ifndef ARCH_overo
331  pthread_mutex_consistent(pmutex);
332 #endif // ARCH_overo
333  return OK;
334  case EINVAL: // mutex not initialized by creating thread/application
335  usleep(SHM_MUTEX_T_TRIES);
336  break;
337  case EAGAIN: // maximum recursive locks exceeded
338  usleep(SHM_MUTEX_T_TRIES);
339  break;
340  default: // other error
341  errno = rc;
342  return rc;
343  }
344  }
345 
346  errno = rc;
347  return rc;
348 }
349 
351 {
352  pthread_mutex_t *pmutex;
353  int ntries;
354  int rc;
355 
356  if( pshmmutex == NULL )
357  {
358  LOGERROR("shm_mutex_t structure is NULL.");
359  errno = EINVAL;
360  return errno;
361  }
362 
363  pmutex = (pthread_mutex_t *)(pshmmutex->m_shmAddr);
364 
365  for(ntries=0; ntries<SHM_MUTEX_N_TRIES; ++ntries)
366  {
367  if( (rc = pthread_mutex_trylock(pmutex)) == 0 )
368  {
369  return OK;
370  }
371 
372  switch( rc )
373  {
374  case EOWNERDEAD: // owner of mutex went to a better place
375 #ifndef ARCH_overo
376  pthread_mutex_consistent(pmutex);
377 #endif // ARCH_overo
378  return OK;
379  case EINVAL: // mutex not initialized by creating thread/application
380  usleep(SHM_MUTEX_T_TRIES);
381  break;
382  case EAGAIN: // maximum recursive locks exceeded
383  usleep(SHM_MUTEX_T_TRIES);
384  break;
385  case EBUSY: // already locked
386  default: // other error
387  errno = rc;
388  return rc;
389  }
390  }
391 
392  errno = rc;
393  return rc;
394 }
395 
397 {
398  pthread_mutex_t *pmutex;
399 
400  if( pshmmutex == NULL )
401  {
402  LOGERROR("shm_mutex_t structure is NULL.");
403  errno = EINVAL;
404  return errno;
405  }
406 
407  pmutex = (pthread_mutex_t *)(pshmmutex->m_shmAddr);
408 
409  return pthread_mutex_unlock(pmutex);
410 }
411 
412 #if 0
413 static pthread_mutex_t * GlobalMutex;
414 static char * myLine = PROG1;
415 static int shmid;
416 
417 key_t key = 0x11d7;
418 
419 void * getSharedMemPtr(key_t key)
420 {
421  FILE * fp;
422 
423  shmid = shmget(key,
424  sizeof(pthread_mutex_t) + sizeof(pthread_once_t),
425  IPC_CREAT | IPC_EXCL | 0x1b6);
426 
427  if( shmid < 0 )
428  {
429  switch( errno )
430  {
431  case EEXIST:
432  printf("Could not create shared mem.\n");
433  printf("Will attempt to find it.\n");
434 
435  shmid = shmget(key,
436  sizeof(pthread_mutex_t) + sizeof(pthread_once_t),
437  0x1b6);
438  break;
439  }
440 
441  if(0 > shmid)
442  {
443  printf("\tCouldnt find it either\n");
444  }
445 
446  else
447  {
448 
449  fp = fopen("MutextestFile.txt", "r+");
450  fclose(fp);
451 
452  myLine = PROG2;
453  printf("\tFound shared memory");
454  void * const poSharedMem = shmat(shmid, NULL, 0);
455 
456  if(((void *)-1) == poSharedMem)
457  {
458  printf("Could not attatch shared memory to address space.\n");
459  return NULL;
460  }
461  else
462  {
463  //printf("Shared memory attached and marked for deletion.\n");
464  //shmctl(shmid, IPC_RMID, NULL);
465  printf("Shared memory attached\n");
466  return poSharedMem;
467  }
468  }
469  }
470  else
471  {
472  fp = fopen("MutextestFile.txt", "w+");
473  fclose(fp);
474 
475  myLine = PROG1;
476  printf("Shared memory created.\n");
477 
478  void * const poSharedMem = shmat(shmid, NULL, 0);
479 
480  if(((void *)-1) == poSharedMem)
481  {
482  printf("Could not attatch shared memory to address space.\n");
483  return NULL;
484  }
485  else
486  {
487  //printf("Shared memory attached and marked for deletion.\n");
488  //shmctl(shmid, IPC_RMID, NULL);
489  printf("Shared memory attached\n");
490  return poSharedMem;
491  }
492  }
493 
494  return NULL;
495 }
496 
497 int detatchFromSharedMem(void * poSharedMem)
498 {
499  printf("Marking shared memory for destruction and detaching from it.\n");
500  shmctl(shmid, IPC_RMID, NULL);
501  return shmdt(poSharedMem);
502 }
503 
504 
505 int main(void)
506 {
507 
508  FILE * fp;
509 
510  void * poSharedMem = getSharedMemPtr();
511  GlobalMutex = (pthread_mutex_t *)poSharedMem;
512 
513  pthread_once_t * pOnce = (pthread_once_t *)( ((char *)poSharedMem) + sizeof(pthread_mutex_t) );
514 
515  pthread_once(pOnce, sharedMemoryMutexInit);
516 
517  if (GlobalMutex == NULL)
518  {
519  return 0;
520  }
521 
522  int mutexLockResult;
523 
524  for (int i =0; i < 100000; i ++)
525  {
526  mutexLockResult = pthread_mutex_lock(GlobalMutex);
527  if (0 == mutexLockResult)
528  {
529  fp = fopen("MutextestFile.txt", "r+");
530  if (NULL != fp)
531  {
532  fseek(fp, 0, SEEK_END);
533  fprintf(fp, "%4d - ", i+1);
534  fprintf(fp, myLine);
535  fclose(fp);
536  fp = NULL;
537  }
538  pthread_mutex_unlock(GlobalMutex);
539  }
540  else if (EOWNERDEAD == mutexLockResult)
541  {
542  fp = fopen("MutextestFile.txt", "r+");
543  if (NULL != fp)
544  {
545  fseek(fp, 0, SEEK_END);
546  fprintf(fp, "%4d - ", i+1);
547  fprintf(fp, myLine);
548  fclose(fp);
549  fp = NULL;
550  }
551  pthread_mutex_consistent_np(GlobalMutex);
552  pthread_mutex_unlock(GlobalMutex);
553  }
554  }
555 
556 
557  if(NULL != GlobalMutex)
558  {
559  detatchFromSharedMem(GlobalMutex);
560  }
561 }
562 #endif
int shm_open(key_t key, size_t size, shm_mem_init_func_t mem_init, shm_t *pshm)
Open shared memory segement.
Definition: shm.c:74
Shared memory interface.
int shm_mutex_unlock(shm_mutex_t *pshmmutex)
Unlock a lock the mutex.
Definition: shm.c:396
#define LOGDIAG3(fmt,...)
Standard Diagnostic Level 3 logging.
Definition: log.h:393
#define OK
Okay.
Definition: rnrconfig.h:301
#define NULL
null pointer
Definition: rnrconfig.h:199
int shm_mutex_lock(shm_mutex_t *pshmmutex)
Lock the mutex.
Definition: shm.c:305
int shm_close(shm_t *pshm)
Close shared memory segement.
Definition: shm.c:178
#define LOGSYSERROR(fmt,...)
Standard System Error logging.
Definition: log.h:509
#define RC_ERROR
common function error return code
Definition: rnrconfig.h:316
#define LOGERROR(fmt,...)
Standard Error logging.
Definition: log.h:488
void(* shm_mem_init_func_t)(key_t key, void *add, size_t size)
User-supplied shared memory initialization function.
Definition: shm.h:59
Shared memory structure type.
Definition: shm.h:39
#define LOGDIAG4(fmt,...)
Standard Diagnostic Level 4 logging.
Definition: log.h:386
int main(int argc, char *argv[])
Example main.
static void shm_mutex_create(key_t key, void *addr, size_t size)
Create the share memory mutex.
Definition: shm.c:223
RoadNarrows Robotics common configuration file.
int m_shmId
shared memory identifier
Definition: shm.h:42
int shm_mutex_init(key_t key, shm_mutex_t *pshmmutex)
Create and initialize a shared memory mutex.
Definition: shm.c:277
int shm_mutex_destroy(shm_mutex_t *pshmmutex)
Destroy a shared memory mutex.
Definition: shm.c:300
#define SHM_MUTEX_N_TRIES
maximum number of tries to acquire lock
Definition: shm.h:63
#define SHM_MUTEX_T_TRIES
usec time between tries to acquire lock
Definition: shm.h:67
key_t m_shmKey
shared memory key
Definition: shm.h:41
size_t m_shmSize
shared memory size in bytes
Definition: shm.h:43
void * m_shmAddr
starting addressed of attached shared memory
Definition: shm.h:44
Logger declarations.
int shm_mutex_trylock(shm_mutex_t *pshmmutex)
Try to lock the mutex.
Definition: shm.c:350