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