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