467caf5e9ffebccb0e67b752353cb6e41b591aa8
[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, *dir, *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         /* No need to check if the dir already exists or check
264          * mkdir() errors, because on any error the open() call will
265          * report the problem.
266          */
267         dir = g_path_get_dirname (file);
268         mkdir (dir, 0755);
269         g_free (dir);
270         
271         return file;
272 }
273
274 static int
275 _wapi_shm_file_open (const gchar *filename, guint32 wanted_size)
276 {
277         int fd;
278         struct stat statbuf;
279         int ret, tries = 0;
280         gboolean created = FALSE;
281         mode_t oldmask;
282
283 try_again:
284         if (tries++ > 10) {
285                 /* Just give up */
286                 return (-1);
287         } else if (tries > 5) {
288                 /* Break out of a loop */
289                 unlink (filename);
290         }
291         
292         /* Make sure future processes can open the shared data files */
293         oldmask = umask (066);
294
295         /* No O_CREAT yet, because we need to initialise the file if
296          * we have to create it.
297          */
298         fd = open (filename, O_RDWR, 0600);
299         umask (oldmask);
300         
301         if (fd == -1 && errno == ENOENT) {
302                 /* OK, its up to us to create it.  O_EXCL to avoid a
303                  * race condition where two processes can
304                  * simultaneously try and create the file
305                  */
306                 oldmask = umask (066);
307                 fd = open (filename, O_CREAT|O_EXCL|O_RDWR, 0600);
308                 umask (oldmask);
309                 
310                 if (fd == -1 && errno == EEXIST) {
311                         /* It's possible that the file was created in
312                          * between finding it didn't exist, and trying
313                          * to create it.  Just try opening it again
314                          */
315                         goto try_again;
316                 } else if (fd == -1) {
317                         g_critical ("%s: shared file [%s] open error: %s",
318                                     __func__, filename, g_strerror (errno));
319                         return -1;
320                 } else {
321                         /* We created the file, so we need to expand
322                          * the file.
323                          *
324                          * (wanted_size-1, because we're about to
325                          * write the other byte to actually expand the
326                          * file.)
327                          */
328                         if (lseek (fd, wanted_size-1, SEEK_SET) == -1) {
329                                 g_critical ("%s: shared file [%s] lseek error: %s", __func__, filename, g_strerror (errno));
330                                 close (fd);
331                                 unlink (filename);
332                                 return -1;
333                         }
334                         
335                         do {
336                                 ret = write (fd, "", 1);
337                         } while (ret == -1 && errno == EINTR);
338                                 
339                         if (ret == -1) {
340                                 g_critical ("%s: shared file [%s] write error: %s", __func__, filename, g_strerror (errno));
341                                 close (fd);
342                                 unlink (filename);
343                                 return -1;
344                         }
345                         
346                         created = TRUE;
347
348                         /* The contents of the file is set to all
349                          * zero, because it is opened up with lseek,
350                          * so we don't need to do any more
351                          * initialisation here
352                          */
353                 }
354         } else if (fd == -1) {
355                 g_critical ("%s: shared file [%s] open error: %s", __func__,
356                             filename, g_strerror (errno));
357                 return -1;
358         }
359         
360         /* Use stat to find the file size (instead of hard coding it)
361          * because we can expand the file later if needed (for more
362          * handles or scratch space.)
363          */
364         if (fstat (fd, &statbuf) == -1) {
365                 g_critical ("%s: fstat error: %s", __func__,
366                             g_strerror (errno));
367                 if (created == TRUE) {
368                         unlink (filename);
369                 }
370                 close (fd);
371                 return -1;
372         }
373
374         if (statbuf.st_size < wanted_size) {
375                 close (fd);
376                 if (created == TRUE) {
377                         g_critical ("%s: shared file [%s] is not big enough! (found %ld, need %d bytes)", __func__, filename, (long)statbuf.st_size, wanted_size);
378                         unlink (filename);
379                         return -1;
380                 } else {
381                         /* We didn't create it, so just try opening it again */
382                         _wapi_handle_spin (100);
383                         goto try_again;
384                 }
385         }
386         
387         return fd;
388 }
389
390 static gboolean
391 check_disabled (void)
392 {
393         if (_wapi_shm_disabled || g_getenv ("MONO_DISABLE_SHM")) {
394                 const char* val = g_getenv ("MONO_DISABLE_SHM");
395                 if (val == NULL || *val == '1' || *val == 'y' || *val == 'Y') {
396                         _wapi_shm_disabled = TRUE;
397                 }
398         }
399
400         return _wapi_shm_disabled;
401 }
402
403 /*
404  * _wapi_shm_attach:
405  * @success: Was it a success
406  *
407  * Attach to the shared memory file or create it if it did not exist.
408  * Returns the memory area the file was mmapped to.
409  */
410 gpointer
411 _wapi_shm_attach (_wapi_shm_t type)
412 {
413         gpointer shm_seg;
414         int fd;
415         struct stat statbuf;
416         gchar *filename = _wapi_shm_file (type), *shm_name;
417         guint32 size;
418         
419         switch(type) {
420         case WAPI_SHM_DATA:
421                 size = sizeof(struct _WapiHandleSharedLayout);
422                 break;
423                 
424         case WAPI_SHM_FILESHARE:
425                 size = sizeof(struct _WapiFileShareLayout);
426                 break;
427         default:
428                 g_error ("Invalid type in _wapi_shm_attach ()");
429                 return NULL;
430         }
431
432         if (check_disabled ()) {
433                 return g_malloc0 (size);
434         }
435
436 #ifdef USE_SHM
437         shm_name = _wapi_shm_shm_name (type);
438         fd = _wapi_shm_open (shm_name, size);
439         g_free (shm_name);
440 #else
441         fd = -1;
442 #endif
443
444         /* Fall back to files if POSIX shm fails (for example, because /dev/shm is not mounted */
445         if (fd == -1)
446                 fd = _wapi_shm_file_open (filename, size);
447         if (fd == -1) {
448                 g_critical ("%s: shared file [%s] open error", __func__,
449                             filename);
450                 return NULL;
451         }
452
453         if (fstat (fd, &statbuf)==-1) {
454                 g_critical ("%s: fstat error: %s", __func__,
455                             g_strerror (errno));
456                 close (fd);
457                 return NULL;
458         }
459         
460         shm_seg = mmap (NULL, statbuf.st_size, PROT_READ|PROT_WRITE,
461                         MAP_SHARED, fd, 0);
462         if (shm_seg == MAP_FAILED) {
463                 shm_seg = mmap (NULL, statbuf.st_size, PROT_READ|PROT_WRITE,
464                         MAP_PRIVATE, fd, 0);
465                 if (shm_seg == MAP_FAILED) {
466                         g_critical ("%s: mmap error: %s", __func__, g_strerror (errno));
467                         close (fd);
468                         return NULL;
469                 }
470         }
471                 
472         close (fd);
473         return shm_seg;
474 }
475
476 static void
477 shm_semaphores_init (void)
478 {
479         key_t key;
480         key_t oldkey;
481         int thr_ret;
482         struct _WapiHandleSharedLayout *tmp_shared;
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         key = ftok (_wapi_shm_file (WAPI_SHM_DATA), 'M');
523
524 again:
525         retries++;
526         oldkey = tmp_shared->sem_key;
527
528         if (oldkey == 0) {
529                 DEBUGLOG ("%s: Creating with new key (0x%x)", __func__, key);
530
531                 /*
532                  * The while loop attempts to make some sense of the
533                  * bonkers 'think of a random number' method of
534                  * picking a key without collision with other
535                  * applications
536                  */
537                 while ((_wapi_sem_id = semget (key, _WAPI_SHARED_SEM_COUNT,
538                                                IPC_CREAT | IPC_EXCL | 0600)) == -1) {
539                         if (errno == ENOMEM) {
540                                 g_error ("%s: semget error: %s", __func__,
541                                             g_strerror (errno));
542                         } else if (errno == ENOSPC) {
543                                 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));
544                         } else if (errno != EEXIST) {
545                                 if (retries > 3)
546                                         g_warning ("%s: semget error: %s key 0x%x - trying again", __func__,
547                                                         g_strerror (errno), key);
548                         }
549                         
550                         key++;
551                         DEBUGLOG ("%s: Got (%s), trying with new key (0x%x)", __func__, g_strerror (errno), key);
552                 }
553                 /*
554                  * Got a semaphore array, so initialise it and install
555                  * the key into the shared memory
556                  */
557                 
558                 if (semctl (_wapi_sem_id, 0, SETALL, defs) == -1) {
559                         if (retries > 3)
560                                 g_warning ("%s: semctl init error: %s - trying again", __func__, g_strerror (errno));
561
562                         /*
563                          * Something went horribly wrong, so try
564                          * getting a new set from scratch
565                          */
566                         semctl (_wapi_sem_id, 0, IPC_RMID);
567                         goto again;
568                 }
569
570                 if (InterlockedCompareExchange (&tmp_shared->sem_key,
571                                                 key, 0) != 0) {
572                         /*
573                          * Someone else created one and installed the
574                          * key while we were working, so delete the
575                          * array we created and fall through to the
576                          * 'key already known' case.
577                          */
578                         semctl (_wapi_sem_id, 0, IPC_RMID);
579                         oldkey = tmp_shared->sem_key;
580                 } else {
581                         /*
582                          * We've installed this semaphore set's key into
583                          * the shared memory
584                          */
585                         goto done;
586                 }
587         }
588         
589         DEBUGLOG ("%s: Trying with old key 0x%x", __func__, oldkey);
590
591         _wapi_sem_id = semget (oldkey, _WAPI_SHARED_SEM_COUNT, 0600);
592         if (_wapi_sem_id == -1) {
593                 if (retries > 3)
594                         g_warning ("%s: semget error opening old key 0x%x (%s) - trying again",
595                                         __func__, oldkey,g_strerror (errno));
596
597                 /*
598                  * Someone must have deleted the semaphore set, so
599                  * blow away the bad key and try again
600                  */
601                 InterlockedCompareExchange (&tmp_shared->sem_key, 0, oldkey);
602                 
603                 goto again;
604         }
605
606   done:
607         /* Increment the usage count of this semaphore set */
608         thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_PROCESS_COUNT_LOCK);
609         g_assert (thr_ret == 0);
610         
611         DEBUGLOG ("%s: Incrementing the process count (%d)", __func__, _wapi_getpid ());
612
613         /*
614          * We only ever _unlock_ this semaphore, letting the kernel
615          * restore (ie decrement) this unlock when this process exits.
616          * We lock another semaphore around it so we can serialise
617          * access when we're testing the value of this semaphore when
618          * we exit cleanly, so we can delete the whole semaphore set.
619          */
620         _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_PROCESS_COUNT);
621
622         DEBUGLOG ("%s: Process count is now %d (%d)", __func__, semctl (_wapi_sem_id, _WAPI_SHARED_SEM_PROCESS_COUNT, GETVAL), _wapi_getpid ());
623         
624         _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_PROCESS_COUNT_LOCK);
625
626         if (_wapi_shm_disabled)
627                 g_free (tmp_shared);
628         else
629                 munmap (tmp_shared, sizeof(struct _WapiHandleSharedLayout));
630 }
631
632 static void
633 shm_semaphores_remove (void)
634 {
635         int thr_ret;
636         int proc_count;
637         gchar *shm_name;
638         
639         DEBUGLOG ("%s: Checking process count (%d)", __func__, _wapi_getpid ());
640         
641         thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_PROCESS_COUNT_LOCK);
642         g_assert (thr_ret == 0);
643         
644         proc_count = semctl (_wapi_sem_id, _WAPI_SHARED_SEM_PROCESS_COUNT,
645                              GETVAL);
646
647         g_assert (proc_count > 0);
648         if (proc_count == 1) {
649                 /*
650                  * Just us, so blow away the semaphores and the shared
651                  * files
652                  */
653                 DEBUGLOG ("%s: Removing semaphores! (%d)", __func__, _wapi_getpid ());
654
655                 semctl (_wapi_sem_id, 0, IPC_RMID);
656 #ifdef USE_SHM
657                 shm_name = _wapi_shm_shm_name (WAPI_SHM_DATA);
658                 shm_unlink (shm_name);
659                 g_free (shm_name);
660
661                 shm_name = _wapi_shm_shm_name (WAPI_SHM_FILESHARE);
662                 shm_unlink (shm_name);
663                 g_free (shm_name);
664 #endif
665                 unlink (_wapi_shm_file (WAPI_SHM_DATA));
666                 unlink (_wapi_shm_file (WAPI_SHM_FILESHARE));
667         } else {
668                 /*
669                  * "else" clause, because there's no point unlocking
670                  * the semaphore if we've just blown it away...
671                  */
672                 _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_PROCESS_COUNT_LOCK);
673         }
674 }
675
676 static int
677 shm_sem_lock (int sem)
678 {
679         struct sembuf ops;
680         int ret;
681         
682         DEBUGLOG ("%s: locking sem %d", __func__, sem);
683
684         ops.sem_num = sem;
685         ops.sem_op = -1;
686         ops.sem_flg = SEM_UNDO;
687         
688   retry:
689         do {
690                 ret = semop (_wapi_sem_id, &ops, 1);
691         } while (ret == -1 && errno == EINTR);
692
693         if (ret == -1) {
694                 /*
695                  * EINVAL covers the case when the semaphore was
696                  * deleted before we started the semop
697                  */
698                 if (errno == EIDRM || errno == EINVAL) {
699                         /*
700                          * Someone blew away this semaphore set, so
701                          * get a new one and try again
702                          */
703                         DEBUGLOG ("%s: Reinitialising the semaphores!", __func__);
704
705                         _wapi_shm_semaphores_init ();
706                         goto retry;
707                 }
708                 
709                 /* Turn this into a pthreads-style return value */
710                 ret = errno;
711         }
712         
713         DEBUGLOG ("%s: returning %d (%s)", __func__, ret, g_strerror (ret));
714         
715         return ret;
716 }
717
718 static int
719 shm_sem_trylock (int sem)
720 {
721         struct sembuf ops;
722         int ret;
723         
724         DEBUGLOG ("%s: trying to lock sem %d", __func__, sem);
725         
726         ops.sem_num = sem;
727         ops.sem_op = -1;
728         ops.sem_flg = IPC_NOWAIT | SEM_UNDO;
729         
730   retry:
731         do {
732                 ret = semop (_wapi_sem_id, &ops, 1);
733         } while (ret == -1 && errno == EINTR);
734
735         if (ret == -1) {
736                 /*
737                  * EINVAL covers the case when the semaphore was
738                  * deleted before we started the semop
739                  */
740                 if (errno == EIDRM || errno == EINVAL) {
741                         /*
742                          * Someone blew away this semaphore set, so
743                          * get a new one and try again
744                          */
745                         DEBUGLOG ("%s: Reinitialising the semaphores!", __func__);
746
747                         _wapi_shm_semaphores_init ();
748                         goto retry;
749                 }
750                 
751                 /* Turn this into a pthreads-style return value */
752                 ret = errno;
753         }
754         
755         if (ret == EAGAIN) {
756                 /* But pthreads uses this code instead */
757                 ret = EBUSY;
758         }
759         
760         DEBUGLOG ("%s: returning %d (%s)", __func__, ret, g_strerror (ret));
761         
762         return ret;
763 }
764
765 static int
766 shm_sem_unlock (int sem)
767 {
768         struct sembuf ops;
769         int ret;
770         
771         DEBUGLOG ("%s: unlocking sem %d", __func__, sem);
772         
773         ops.sem_num = sem;
774         ops.sem_op = 1;
775         ops.sem_flg = SEM_UNDO;
776         
777   retry:
778         do {
779                 ret = semop (_wapi_sem_id, &ops, 1);
780         } while (ret == -1 && errno == EINTR);
781
782         if (ret == -1) {
783                 /* EINVAL covers the case when the semaphore was
784                  * deleted before we started the semop
785                  */
786                 if (errno == EIDRM || errno == EINVAL) {
787                         /* Someone blew away this semaphore set, so
788                          * get a new one and try again (we can't just
789                          * assume that the semaphore is now unlocked)
790                          */
791                         DEBUGLOG ("%s: Reinitialising the semaphores!", __func__);
792
793                         _wapi_shm_semaphores_init ();
794                         goto retry;
795                 }
796                 
797                 /* Turn this into a pthreads-style return value */
798                 ret = errno;
799         }
800         
801         DEBUGLOG ("%s: returning %d (%s)", __func__, ret, g_strerror (ret));
802
803         return ret;
804 }
805
806 void
807 _wapi_shm_semaphores_init (void)
808 {
809         if (check_disabled ()) 
810                 noshm_semaphores_init ();
811         else
812                 shm_semaphores_init ();
813 }
814
815 void
816 _wapi_shm_semaphores_remove (void)
817 {
818         if (!_wapi_shm_disabled) 
819                 shm_semaphores_remove ();
820 }
821
822 int
823 _wapi_shm_sem_lock (int sem)
824 {
825         if (_wapi_shm_disabled) 
826                 return noshm_sem_lock (sem);
827         else
828                 return shm_sem_lock (sem);
829 }
830
831 int
832 _wapi_shm_sem_trylock (int sem)
833 {
834         if (_wapi_shm_disabled) 
835                 return noshm_sem_trylock (sem);
836         else 
837                 return shm_sem_trylock (sem);
838 }
839
840 int
841 _wapi_shm_sem_unlock (int sem)
842 {
843         if (_wapi_shm_disabled) 
844                 return noshm_sem_unlock (sem);
845         else 
846                 return shm_sem_unlock (sem);
847 }
848 #endif /* !DISABLE_SHARED_HANDLES */