Merge pull request #649 from DavidS/feature/implement-additional-reference-path
[mono.git] / mono / utils / mono-mutex.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * mono-mutex.h: Portability wrappers around POSIX Mutexes
4  *
5  * Authors: Jeffrey Stedfast <fejj@ximian.com>
6  *
7  * Copyright 2002 Ximian, Inc. (www.ximian.com)
8  */
9
10
11 #ifdef HAVE_CONFIG_H
12 #include <config.h>
13 #endif
14
15 #include <stdlib.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <assert.h>
19 #include <sys/time.h>
20 #include <mono/utils/mono-memory-model.h>
21
22 #include "mono-mutex.h"
23
24 #ifndef HOST_WIN32
25
26 #if defined(__APPLE__)
27 #define _DARWIN_C_SOURCE
28 #include <pthread_spis.h>
29 #include <dlfcn.h>
30 #endif
31
32 #ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
33 /* Android does not implement pthread_mutex_timedlock(), but does provide an
34  * unusual declaration: http://code.google.com/p/android/issues/detail?id=7807
35  */
36 #ifdef PLATFORM_ANDROID
37 #define CONST_NEEDED
38 #else
39 #define CONST_NEEDED const
40 #endif
41
42 int pthread_mutex_timedlock (pthread_mutex_t *mutex,
43                             CONST_NEEDED struct timespec *timeout);
44 int
45 pthread_mutex_timedlock (pthread_mutex_t *mutex, CONST_NEEDED struct timespec *timeout)
46 {
47         struct timeval timenow;
48         struct timespec sleepytime;
49         int retcode;
50         
51         /* This is just to avoid a completely busy wait */
52         sleepytime.tv_sec = 0;
53         sleepytime.tv_nsec = 10000000;  /* 10ms */
54         
55         while ((retcode = pthread_mutex_trylock (mutex)) == EBUSY) {
56                 gettimeofday (&timenow, NULL);
57                 
58                 if (timenow.tv_sec >= timeout->tv_sec &&
59                     (timenow.tv_usec * 1000) >= timeout->tv_nsec) {
60                         return ETIMEDOUT;
61                 }
62                 
63                 nanosleep (&sleepytime, NULL);
64         }
65         
66         return retcode;
67 }
68 #endif /* HAVE_PTHREAD_MUTEX_TIMEDLOCK */
69
70
71 int
72 mono_once (mono_once_t *once, void (*once_init) (void))
73 {
74         int thr_ret;
75         
76         if (!once->complete) {
77                 pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
78                                       (void *)&once->mutex);
79                 thr_ret = pthread_mutex_lock (&once->mutex);
80                 g_assert (thr_ret == 0);
81                 
82                 if (!once->complete) {
83                         once_init ();
84                         once->complete = TRUE;
85                 }
86                 thr_ret = pthread_mutex_unlock (&once->mutex);
87                 g_assert (thr_ret == 0);
88                 
89                 pthread_cleanup_pop (0);
90         }
91         
92         return 0;
93 }
94
95 #endif
96
97 /*
98 Returns a recursive mutex that is safe under suspension.
99
100 A suspension safe mutex means one that can handle this scenario:
101
102 mutex M
103
104 thread 1:
105 1)lock M
106 2)suspend thread 2
107 3)unlock M
108 4)lock M
109
110 thread 2:
111 5)lock M
112
113 Say (1) happens before (5) and (5) happens before (2).
114 This means that thread 2 was suspended by the kernel because
115 it's waiting on mutext M.
116
117 Thread 1 then proceed to suspend thread 2 and unlock/lock the
118 mutex.
119
120 If the kernel implements mutexes with FIFO wait lists, this means
121 that thread 1 will be blocked waiting for thread 2 acquire the lock.
122 Since thread 2 is suspended, we have a deadlock.
123
124 A suspend safe mutex is an unfair lock but will schedule any runable
125 thread that is waiting for a the lock.
126
127 This problem was witnessed on OSX in mono/tests/thread-exit.cs.
128
129 */
130 int
131 mono_mutex_init_suspend_safe (mono_mutex_t *mutex)
132 {
133 #if defined(__APPLE__)
134         int res;
135         pthread_mutexattr_t attr;
136         static gboolean inited;
137         static int (*setpolicy_np) (pthread_mutexattr_t *, int);
138
139         if (!inited) {
140                 setpolicy_np = dlsym (RTLD_NEXT, "pthread_mutexattr_setpolicy_np");
141                 mono_atomic_store_release (&inited, TRUE);
142         }
143
144         pthread_mutexattr_init (&attr);
145         pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
146         if (setpolicy_np)
147                 setpolicy_np (&attr, _PTHREAD_MUTEX_POLICY_FIRSTFIT);
148         res = pthread_mutex_init (mutex, &attr);
149         pthread_mutexattr_destroy (&attr);
150
151         return res;
152 #else
153         return mono_mutex_init (mutex);
154 #endif
155 }