2009-03-29 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / io-layer / shared.c
1 /*
2  * shared.c:  Shared memory handling, and daemon launching
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002-2006 Novell, Inc.
8  */
9
10
11 #include <config.h>
12 #include <glib.h>
13 #include <stdio.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <sys/mman.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <sys/ipc.h>
22 #include <sys/sem.h>
23
24 #ifdef HAVE_SYS_UTSNAME_H
25 #include <sys/utsname.h>
26 #endif
27
28 #include <mono/io-layer/wapi.h>
29 #include <mono/io-layer/wapi-private.h>
30 #include <mono/io-layer/shared.h>
31 #include <mono/io-layer/handles-private.h>
32
33 /*
34  * Use POSIX shared memory if possible, it is simpler, and it has the advantage that 
35  * writes to the shared area does not need to be written to disk, avoiding spinning up 
36  * the disk every x secs on laptops.
37  */
38 #ifdef HAVE_SHM_OPEN
39 #define USE_SHM 1
40 #endif
41
42 #undef DEBUG
43
44 #ifdef DISABLE_SHARED_HANDLES
45 gboolean _wapi_shm_disabled = TRUE;
46 #else
47 gboolean _wapi_shm_disabled = FALSE;
48 #endif
49
50 static gchar *
51 _wapi_shm_base_name (_wapi_shm_t type)
52 {
53         gchar *name = NULL;
54         gchar machine_name[256];
55         const gchar *fake_name;
56         struct utsname ubuf;
57         int ret;
58         int len;
59         
60         ret = uname (&ubuf);
61         if (ret == -1) {
62                 ubuf.machine[0] = '\0';
63                 ubuf.sysname[0] = '\0';
64         } else {
65                 g_strdelimit (ubuf.sysname, "/", '_');
66                 g_strdelimit (ubuf.machine, "/", '_');
67         }
68
69         fake_name = g_getenv ("MONO_SHARED_HOSTNAME");
70         if (fake_name == NULL) {
71                 if (gethostname(machine_name, sizeof(machine_name)) != 0)
72                         machine_name[0] = '\0';
73         } else {
74                 len = MIN (strlen (fake_name), sizeof (machine_name) - 1);
75                 strncpy (machine_name, fake_name, len);
76                 machine_name [len] = '\0';
77         }
78         
79         switch (type) {
80         case WAPI_SHM_DATA:
81                 name = g_strdup_printf ("shared_data-%s-%s-%s-%d-%d-%d",
82                                         machine_name, ubuf.sysname,
83                                         ubuf.machine,
84                                         (int) sizeof(struct _WapiHandleShared),
85                                         _WAPI_HANDLE_VERSION, 0);
86                 break;
87                 
88         case WAPI_SHM_FILESHARE:
89                 name = g_strdup_printf ("shared_fileshare-%s-%s-%s-%d-%d-%d",
90                                         machine_name, ubuf.sysname,
91                                         ubuf.machine,
92                                         (int) sizeof(struct _WapiFileShare),
93                                         _WAPI_HANDLE_VERSION, 0);
94                 break;
95         }
96
97         return name;
98 }
99
100 #ifdef USE_SHM
101
102 static gchar *_wapi_shm_shm_name (_wapi_shm_t type)
103 {
104         char *base_name = _wapi_shm_base_name (type);
105
106         /* Also add the uid to avoid permission problems */
107         return g_strdup_printf ("/mono-shared-%d-%s", getuid (), base_name);
108 }
109
110 static int
111 _wapi_shm_open (const char *filename, int size)
112 {
113         int fd;
114
115         fd = shm_open (filename, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
116         if (fd == -1)
117                 /* Maybe /dev/shm is not mounted */
118                 return -1;
119         if (ftruncate (fd, size) != 0) {
120                 perror ("_wapi_shm_open (): ftruncate ()");
121                 g_assert_not_reached ();
122         }
123
124         return fd;
125 }
126
127 #endif
128
129 static gchar *_wapi_shm_file (_wapi_shm_t type)
130 {
131         static gchar file[_POSIX_PATH_MAX];
132         gchar *name = NULL, *filename, *dir, *wapi_dir;
133
134         name = _wapi_shm_base_name (type);
135
136         /* I don't know how nfs affects mmap.  If mmap() of files on
137          * nfs mounts breaks, then there should be an option to set
138          * the directory.
139          */
140         wapi_dir = getenv ("MONO_SHARED_DIR");
141         if (wapi_dir == NULL) {
142                 filename = g_build_filename (g_get_home_dir (), ".wapi", name,
143                                              NULL);
144         } else {
145                 filename = g_build_filename (wapi_dir, ".wapi", name, NULL);
146         }
147         g_free (name);
148
149         g_snprintf (file, _POSIX_PATH_MAX, "%s", filename);
150         g_free (filename);
151                 
152         /* No need to check if the dir already exists or check
153          * mkdir() errors, because on any error the open() call will
154          * report the problem.
155          */
156         dir = g_path_get_dirname (file);
157         mkdir (dir, 0755);
158         g_free (dir);
159         
160         return(file);
161 }
162
163 static int _wapi_shm_file_open (const gchar *filename, guint32 wanted_size)
164 {
165         int fd;
166         struct stat statbuf;
167         int ret, tries = 0;
168         gboolean created = FALSE;
169         mode_t oldmask;
170
171 try_again:
172         if (tries++ > 10) {
173                 /* Just give up */
174                 return (-1);
175         } else if (tries > 5) {
176                 /* Break out of a loop */
177                 unlink (filename);
178         }
179         
180         /* Make sure future processes can open the shared data files */
181         oldmask = umask (066);
182
183         /* No O_CREAT yet, because we need to initialise the file if
184          * we have to create it.
185          */
186         fd = open (filename, O_RDWR, 0600);
187         umask (oldmask);
188         
189         if (fd == -1 && errno == ENOENT) {
190                 /* OK, its up to us to create it.  O_EXCL to avoid a
191                  * race condition where two processes can
192                  * simultaneously try and create the file
193                  */
194                 oldmask = umask (066);
195                 fd = open (filename, O_CREAT|O_EXCL|O_RDWR, 0600);
196                 umask (oldmask);
197                 
198                 if (fd == -1 && errno == EEXIST) {
199                         /* It's possible that the file was created in
200                          * between finding it didn't exist, and trying
201                          * to create it.  Just try opening it again
202                          */
203                         goto try_again;
204                 } else if (fd == -1) {
205                         g_critical ("%s: shared file [%s] open error: %s",
206                                     __func__, filename, g_strerror (errno));
207                         return(-1);
208                 } else {
209                         /* We created the file, so we need to expand
210                          * the file.
211                          *
212                          * (wanted_size-1, because we're about to
213                          * write the other byte to actually expand the
214                          * file.)
215                          */
216                         if (lseek (fd, wanted_size-1, SEEK_SET) == -1) {
217                                 g_critical ("%s: shared file [%s] lseek error: %s", __func__, filename, g_strerror (errno));
218                                 close (fd);
219                                 unlink (filename);
220                                 return(-1);
221                         }
222                         
223                         do {
224                                 ret = write (fd, "", 1);
225                         } while (ret == -1 && errno == EINTR);
226                                 
227                         if (ret == -1) {
228                                 g_critical ("%s: shared file [%s] write error: %s", __func__, filename, g_strerror (errno));
229                                 close (fd);
230                                 unlink (filename);
231                                 return(-1);
232                         }
233                         
234                         created = TRUE;
235
236                         /* The contents of the file is set to all
237                          * zero, because it is opened up with lseek,
238                          * so we don't need to do any more
239                          * initialisation here
240                          */
241                 }
242         } else if (fd == -1) {
243                 g_critical ("%s: shared file [%s] open error: %s", __func__,
244                             filename, g_strerror (errno));
245                 return(-1);
246         }
247         
248         /* Use stat to find the file size (instead of hard coding it)
249          * because we can expand the file later if needed (for more
250          * handles or scratch space.)
251          */
252         if (fstat (fd, &statbuf) == -1) {
253                 g_critical ("%s: fstat error: %s", __func__,
254                             g_strerror (errno));
255                 if (created == TRUE) {
256                         unlink (filename);
257                 }
258                 close (fd);
259                 return(-1);
260         }
261
262         if (statbuf.st_size < wanted_size) {
263                 close (fd);
264                 if (created == TRUE) {
265                         g_critical ("%s: shared file [%s] is not big enough! (found %ld, need %d bytes)", __func__, filename, (long)statbuf.st_size, wanted_size);
266                         unlink (filename);
267                         return(-1);
268                 } else {
269                         /* We didn't create it, so just try opening it again */
270                         _wapi_handle_spin (100);
271                         goto try_again;
272                 }
273         }
274         
275         return(fd);
276 }
277
278 static gboolean check_disabled (void)
279 {
280         if (_wapi_shm_disabled || g_getenv ("MONO_DISABLE_SHM")) {
281                 const char* val = g_getenv ("MONO_DISABLE_SHM");
282                 if (val == NULL || *val == '1' || *val == 'y' || *val == 'Y') {
283                         _wapi_shm_disabled = TRUE;
284                 }
285         }
286
287         return(_wapi_shm_disabled);
288 }
289
290 /*
291  * _wapi_shm_attach:
292  * @success: Was it a success
293  *
294  * Attach to the shared memory file or create it if it did not exist.
295  * Returns the memory area the file was mmapped to.
296  */
297 gpointer _wapi_shm_attach (_wapi_shm_t type)
298 {
299         gpointer shm_seg;
300         int fd;
301         struct stat statbuf;
302         gchar *filename = _wapi_shm_file (type);
303         guint32 size;
304
305         switch(type) {
306         case WAPI_SHM_DATA:
307                 size = sizeof(struct _WapiHandleSharedLayout);
308                 break;
309                 
310         case WAPI_SHM_FILESHARE:
311                 size = sizeof(struct _WapiFileShareLayout);
312                 break;
313         default:
314                 g_error ("Invalid type in _wapi_shm_attach ()");
315                 return NULL;
316         }
317
318         if (check_disabled ()) {
319                 return g_malloc0 (size);
320         }
321
322 #ifdef USE_SHM
323         fd = _wapi_shm_open (_wapi_shm_shm_name (type), size);
324 #else
325         fd = -1;
326 #endif
327
328         /* Fall back to files if POSIX shm fails (for example, because /dev/shm is not mounted */
329         if (fd == -1)
330                 fd = _wapi_shm_file_open (filename, size);
331         if (fd == -1) {
332                 g_critical ("%s: shared file [%s] open error", __func__,
333                             filename);
334                 return(NULL);
335         }
336
337         if (fstat (fd, &statbuf)==-1) {
338                 g_critical ("%s: fstat error: %s", __func__,
339                             g_strerror (errno));
340                 close (fd);
341                 return(NULL);
342         }
343         
344         shm_seg = mmap (NULL, statbuf.st_size, PROT_READ|PROT_WRITE,
345                         MAP_SHARED, fd, 0);
346         if (shm_seg == MAP_FAILED) {
347                 shm_seg = mmap (NULL, statbuf.st_size, PROT_READ|PROT_WRITE,
348                         MAP_PRIVATE, fd, 0);
349                 if (shm_seg == MAP_FAILED) {
350                         g_critical ("%s: mmap error: %s", __func__, g_strerror (errno));
351                         close (fd);
352                         return(NULL);
353                 }
354         }
355                 
356         close (fd);
357         return(shm_seg);
358 }
359
360 static void shm_semaphores_init (void)
361 {
362         key_t key;
363         key_t oldkey;
364         int thr_ret;
365         struct _WapiHandleSharedLayout *tmp_shared;
366         
367         /* Yet more barmy API - this union is a well-defined parameter
368          * in a syscall, yet I still have to define it here as it
369          * doesn't appear in a header
370          */
371         union semun {
372                 int val;
373                 struct semid_ds *buf;
374                 ushort *array;
375         } defs;
376         ushort def_vals[_WAPI_SHARED_SEM_COUNT];
377         int i;
378         int retries = 0;
379         
380         for (i = 0; i < _WAPI_SHARED_SEM_COUNT; i++) {
381                 def_vals[i] = 1;
382         }
383
384         /* Process count must start at '0' - the 1 for all the others
385          * sets the semaphore to "unlocked"
386          */
387         def_vals[_WAPI_SHARED_SEM_PROCESS_COUNT] = 0;
388         
389         defs.array = def_vals;
390         
391         /* Temporarily attach the shared data so we can read the
392          * semaphore key.  We release this mapping and attach again
393          * after getting the semaphores to avoid a race condition
394          * where a terminating process can delete the shared files
395          * between a new process attaching the file and getting access
396          * to the semaphores (which increments the process count,
397          * preventing destruction of the shared data...)
398          */
399         tmp_shared = _wapi_shm_attach (WAPI_SHM_DATA);
400         g_assert (tmp_shared != NULL);
401         
402         key = ftok (_wapi_shm_file (WAPI_SHM_DATA), 'M');
403
404 again:
405         retries++;
406         oldkey = tmp_shared->sem_key;
407
408         if (oldkey == 0) {
409 #ifdef DEBUG
410                 g_message ("%s: Creating with new key (0x%x)", __func__, key);
411 #endif
412
413                 /* The while loop attempts to make some sense of the
414                  * bonkers 'think of a random number' method of
415                  * picking a key without collision with other
416                  * applications
417                  */
418                 while ((_wapi_sem_id = semget (key, _WAPI_SHARED_SEM_COUNT,
419                                                IPC_CREAT | IPC_EXCL | 0600)) == -1) {
420                         if (errno == ENOMEM) {
421                                 g_error ("%s: semget error: %s", __func__,
422                                             g_strerror (errno));
423                         } else if (errno == ENOSPC) {
424                                 g_error ("%s: semget error: %s.  Try deleting some semaphores with ipcs and ipcrm\nor increase the maximum number of semaphore in the system.", __func__, g_strerror (errno));
425                         } else if (errno != EEXIST) {
426                                 if (retries > 3)
427                                         g_warning ("%s: semget error: %s key 0x%x - trying again", __func__,
428                                                         g_strerror (errno), key);
429                         }
430                         
431                         key++;
432 #ifdef DEBUG
433                         g_message ("%s: Got (%s), trying with new key (0x%x)",
434                                    __func__, g_strerror (errno), key);
435 #endif
436                 }
437                 /* Got a semaphore array, so initialise it and install
438                  * the key into the shared memory
439                  */
440                 
441                 if (semctl (_wapi_sem_id, 0, SETALL, defs) == -1) {
442                         if (retries > 3)
443                                 g_warning ("%s: semctl init error: %s - trying again", __func__, g_strerror (errno));
444
445                         /* Something went horribly wrong, so try
446                          * getting a new set from scratch
447                          */
448                         semctl (_wapi_sem_id, 0, IPC_RMID);
449                         goto again;
450                 }
451
452                 if (InterlockedCompareExchange (&tmp_shared->sem_key,
453                                                 key, 0) != 0) {
454                         /* Someone else created one and installed the
455                          * key while we were working, so delete the
456                          * array we created and fall through to the
457                          * 'key already known' case.
458                          */
459                         semctl (_wapi_sem_id, 0, IPC_RMID);
460                         oldkey = tmp_shared->sem_key;
461                 } else {
462                         /* We've installed this semaphore set's key into
463                          * the shared memory
464                          */
465                         goto done;
466                 }
467         }
468         
469 #ifdef DEBUG
470         g_message ("%s: Trying with old key 0x%x", __func__, oldkey);
471 #endif
472
473         _wapi_sem_id = semget (oldkey, _WAPI_SHARED_SEM_COUNT, 0600);
474         if (_wapi_sem_id == -1) {
475                 if (retries > 3)
476                         g_warning ("%s: semget error opening old key 0x%x (%s) - trying again",
477                                         __func__, oldkey,g_strerror (errno));
478
479                 /* Someone must have deleted the semaphore set, so
480                  * blow away the bad key and try again
481                  */
482                 InterlockedCompareExchange (&tmp_shared->sem_key, 0, oldkey);
483                 
484                 goto again;
485         }
486
487   done:
488         /* Increment the usage count of this semaphore set */
489         thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_PROCESS_COUNT_LOCK);
490         g_assert (thr_ret == 0);
491         
492 #ifdef DEBUG
493         g_message ("%s: Incrementing the process count (%d)", __func__, _wapi_getpid ());
494 #endif
495
496         /* We only ever _unlock_ this semaphore, letting the kernel
497          * restore (ie decrement) this unlock when this process exits.
498          * We lock another semaphore around it so we can serialise
499          * access when we're testing the value of this semaphore when
500          * we exit cleanly, so we can delete the whole semaphore set.
501          */
502         _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_PROCESS_COUNT);
503
504 #ifdef DEBUG
505         g_message ("%s: Process count is now %d (%d)", __func__, semctl (_wapi_sem_id, _WAPI_SHARED_SEM_PROCESS_COUNT, GETVAL), _wapi_getpid ());
506 #endif
507         
508         _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_PROCESS_COUNT_LOCK);
509
510         if (_wapi_shm_disabled)
511                 g_free (tmp_shared);
512         else
513                 munmap (tmp_shared, sizeof(struct _WapiHandleSharedLayout));
514 }
515
516 static mono_mutex_t noshm_sems[_WAPI_SHARED_SEM_COUNT];
517
518 static void noshm_semaphores_init (void)
519 {
520         int i;
521
522         for (i = 0; i < _WAPI_SHARED_SEM_COUNT; i++) {
523                 mono_mutex_init (&noshm_sems[i], NULL);
524         }
525 }
526
527 static void shm_semaphores_remove (void)
528 {
529         int thr_ret;
530         int proc_count;
531         
532 #ifdef DEBUG
533         g_message ("%s: Checking process count (%d)", __func__,
534                    _wapi_getpid ());
535 #endif
536         
537         thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_PROCESS_COUNT_LOCK);
538         g_assert (thr_ret == 0);
539         
540         proc_count = semctl (_wapi_sem_id, _WAPI_SHARED_SEM_PROCESS_COUNT,
541                              GETVAL);
542
543         g_assert (proc_count > 0);
544         if (proc_count == 1) {
545                 /* Just us, so blow away the semaphores and the shared
546                  * files
547                  */
548 #ifdef DEBUG
549                 g_message ("%s: Removing semaphores! (%d)", __func__,
550                            _wapi_getpid ());
551 #endif
552
553                 semctl (_wapi_sem_id, 0, IPC_RMID);
554 #ifdef USE_SHM
555                 shm_unlink (_wapi_shm_shm_name (WAPI_SHM_DATA));
556                 shm_unlink (_wapi_shm_shm_name (WAPI_SHM_FILESHARE));
557 #endif
558                 unlink (_wapi_shm_file (WAPI_SHM_DATA));
559                 unlink (_wapi_shm_file (WAPI_SHM_FILESHARE));
560         } else {
561                 /* "else" clause, because there's no point unlocking
562                  * the semaphore if we've just blown it away...
563                  */
564                 _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_PROCESS_COUNT_LOCK);
565         }
566 }
567
568 static void noshm_semaphores_remove (void)
569 {
570         /* No need to do anything */
571 }
572
573 static int shm_sem_lock (int sem)
574 {
575         struct sembuf ops;
576         int ret;
577         
578 #ifdef DEBUG
579         g_message ("%s: locking sem %d", __func__, sem);
580 #endif
581
582         ops.sem_num = sem;
583         ops.sem_op = -1;
584         ops.sem_flg = SEM_UNDO;
585         
586   retry:
587         do {
588                 ret = semop (_wapi_sem_id, &ops, 1);
589         } while (ret == -1 && errno == EINTR);
590
591         if (ret == -1) {
592                 /* EINVAL covers the case when the semaphore was
593                  * deleted before we started the semop
594                  */
595                 if (errno == EIDRM || errno == EINVAL) {
596                         /* Someone blew away this semaphore set, so
597                          * get a new one and try again
598                          */
599 #ifdef DEBUG
600                         g_message ("%s: Reinitialising the semaphores!",
601                                    __func__);
602 #endif
603
604                         _wapi_shm_semaphores_init ();
605                         goto retry;
606                 }
607                 
608                 /* Turn this into a pthreads-style return value */
609                 ret = errno;
610         }
611         
612 #ifdef DEBUG
613         g_message ("%s: returning %d (%s)", __func__, ret, g_strerror (ret));
614 #endif
615         
616         return(ret);
617 }
618
619 static int noshm_sem_lock (int sem)
620 {
621         int ret;
622         
623 #ifdef DEBUG
624         g_message ("%s: locking nosem %d", __func__, sem);
625 #endif
626         
627         ret = mono_mutex_lock (&noshm_sems[sem]);
628         
629         return(ret);
630 }
631
632 static int shm_sem_trylock (int sem)
633 {
634         struct sembuf ops;
635         int ret;
636         
637 #ifdef DEBUG
638         g_message ("%s: trying to lock sem %d", __func__, sem);
639 #endif
640         
641         ops.sem_num = sem;
642         ops.sem_op = -1;
643         ops.sem_flg = IPC_NOWAIT | SEM_UNDO;
644         
645   retry:
646         do {
647                 ret = semop (_wapi_sem_id, &ops, 1);
648         } while (ret == -1 && errno == EINTR);
649
650         if (ret == -1) {
651                 /* EINVAL covers the case when the semaphore was
652                  * deleted before we started the semop
653                  */
654                 if (errno == EIDRM || errno == EINVAL) {
655                         /* Someone blew away this semaphore set, so
656                          * get a new one and try again
657                          */
658 #ifdef DEBUG
659                         g_message ("%s: Reinitialising the semaphores!",
660                                    __func__);
661 #endif
662
663                         _wapi_shm_semaphores_init ();
664                         goto retry;
665                 }
666                 
667                 /* Turn this into a pthreads-style return value */
668                 ret = errno;
669         }
670         
671         if (ret == EAGAIN) {
672                 /* But pthreads uses this code instead */
673                 ret = EBUSY;
674         }
675         
676 #ifdef DEBUG
677         g_message ("%s: returning %d (%s)", __func__, ret, g_strerror (ret));
678 #endif
679         
680         return(ret);
681 }
682
683 static int noshm_sem_trylock (int sem)
684 {
685         int ret;
686         
687 #ifdef DEBUG
688         g_message ("%s: trying to lock nosem %d", __func__, sem);
689 #endif
690         
691         ret = mono_mutex_trylock (&noshm_sems[sem]);
692         
693         return(ret);
694 }
695
696 static int shm_sem_unlock (int sem)
697 {
698         struct sembuf ops;
699         int ret;
700         
701 #ifdef DEBUG
702         g_message ("%s: unlocking sem %d", __func__, sem);
703 #endif
704         
705         ops.sem_num = sem;
706         ops.sem_op = 1;
707         ops.sem_flg = SEM_UNDO;
708         
709   retry:
710         do {
711                 ret = semop (_wapi_sem_id, &ops, 1);
712         } while (ret == -1 && errno == EINTR);
713
714         if (ret == -1) {
715                 /* EINVAL covers the case when the semaphore was
716                  * deleted before we started the semop
717                  */
718                 if (errno == EIDRM || errno == EINVAL) {
719                         /* Someone blew away this semaphore set, so
720                          * get a new one and try again (we can't just
721                          * assume that the semaphore is now unlocked)
722                          */
723 #ifdef DEBUG
724                         g_message ("%s: Reinitialising the semaphores!",
725                                    __func__);
726 #endif
727
728                         _wapi_shm_semaphores_init ();
729                         goto retry;
730                 }
731                 
732                 /* Turn this into a pthreads-style return value */
733                 ret = errno;
734         }
735         
736 #ifdef DEBUG
737         g_message ("%s: returning %d (%s)", __func__, ret, g_strerror (ret));
738 #endif
739
740         return(ret);
741 }
742
743 static int noshm_sem_unlock (int sem)
744 {
745         int ret;
746         
747 #ifdef DEBUG
748         g_message ("%s: unlocking nosem %d", __func__, sem);
749 #endif
750         
751         ret = mono_mutex_unlock (&noshm_sems[sem]);
752         
753         return(ret);
754 }
755
756 void _wapi_shm_semaphores_init (void)
757 {
758         if (check_disabled ()) {
759                 noshm_semaphores_init ();
760         } else {
761                 shm_semaphores_init ();
762         }
763 }
764
765 void _wapi_shm_semaphores_remove (void)
766 {
767         if (_wapi_shm_disabled) {
768                 noshm_semaphores_remove ();
769         } else {
770                 shm_semaphores_remove ();
771         }
772 }
773
774 int _wapi_shm_sem_lock (int sem)
775 {
776         if (_wapi_shm_disabled) {
777                 return(noshm_sem_lock (sem));
778         } else {
779                 return(shm_sem_lock (sem));
780         }
781 }
782
783 int _wapi_shm_sem_trylock (int sem)
784 {
785         if (_wapi_shm_disabled) {
786                 return(noshm_sem_trylock (sem));
787         } else {
788                 return(shm_sem_trylock (sem));
789         }
790 }
791
792 int _wapi_shm_sem_unlock (int sem)
793 {
794         if (_wapi_shm_disabled) {
795                 return(noshm_sem_unlock (sem));
796         } else {
797                 return(shm_sem_unlock (sem));
798         }
799 }