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