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