Merge pull request #637 from LogosBible/enetdown
[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
21 #include "mono-mutex.h"
22
23 #ifndef HOST_WIN32
24
25 #if defined(__APPLE__)
26 #define _DARWIN_C_SOURCE
27 #include <pthread_spis.h>
28 #endif
29
30 #ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
31 /* Android does not implement pthread_mutex_timedlock(), but does provide an
32  * unusual declaration: http://code.google.com/p/android/issues/detail?id=7807
33  */
34 #ifdef PLATFORM_ANDROID
35 #define CONST_NEEDED
36 #else
37 #define CONST_NEEDED const
38 #endif
39
40 int pthread_mutex_timedlock (pthread_mutex_t *mutex,
41                             CONST_NEEDED struct timespec *timeout);
42 int
43 pthread_mutex_timedlock (pthread_mutex_t *mutex, CONST_NEEDED struct timespec *timeout)
44 {
45         struct timeval timenow;
46         struct timespec sleepytime;
47         int retcode;
48         
49         /* This is just to avoid a completely busy wait */
50         sleepytime.tv_sec = 0;
51         sleepytime.tv_nsec = 10000000;  /* 10ms */
52         
53         while ((retcode = pthread_mutex_trylock (mutex)) == EBUSY) {
54                 gettimeofday (&timenow, NULL);
55                 
56                 if (timenow.tv_sec >= timeout->tv_sec &&
57                     (timenow.tv_usec * 1000) >= timeout->tv_nsec) {
58                         return ETIMEDOUT;
59                 }
60                 
61                 nanosleep (&sleepytime, NULL);
62         }
63         
64         return retcode;
65 }
66 #endif /* HAVE_PTHREAD_MUTEX_TIMEDLOCK */
67
68
69 int
70 mono_once (mono_once_t *once, void (*once_init) (void))
71 {
72         int thr_ret;
73         
74         if (!once->complete) {
75                 pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
76                                       (void *)&once->mutex);
77                 thr_ret = pthread_mutex_lock (&once->mutex);
78                 g_assert (thr_ret == 0);
79                 
80                 if (!once->complete) {
81                         once_init ();
82                         once->complete = TRUE;
83                 }
84                 thr_ret = pthread_mutex_unlock (&once->mutex);
85                 g_assert (thr_ret == 0);
86                 
87                 pthread_cleanup_pop (0);
88         }
89         
90         return 0;
91 }
92
93 #endif
94
95 /*
96 Returns a recursive mutex that is safe under suspension.
97
98 A suspension safe mutex means one that can handle this scenario:
99
100 mutex M
101
102 thread 1:
103 1)lock M
104 2)suspend thread 2
105 3)unlock M
106 4)lock M
107
108 thread 2:
109 5)lock M
110
111 Say (1) happens before (5) and (5) happens before (2).
112 This means that thread 2 was suspended by the kernel because
113 it's waiting on mutext M.
114
115 Thread 1 then proceed to suspend thread 2 and unlock/lock the
116 mutex.
117
118 If the kernel implements mutexes with FIFO wait lists, this means
119 that thread 1 will be blocked waiting for thread 2 acquire the lock.
120 Since thread 2 is suspended, we have a deadlock.
121
122 A suspend safe mutex is an unfair lock but will schedule any runable
123 thread that is waiting for a the lock.
124
125 This problem was witnessed on OSX in mono/tests/thread-exit.cs.
126
127 */
128 int
129 mono_mutex_init_suspend_safe (mono_mutex_t *mutex)
130 {
131 #if defined(__APPLE__)
132         int res;
133         pthread_mutexattr_t attr;
134
135         pthread_mutexattr_init (&attr);
136         pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
137         pthread_mutexattr_setpolicy_np (&attr, _PTHREAD_MUTEX_POLICY_FIRSTFIT);
138         res = pthread_mutex_init (mutex, &attr);
139         pthread_mutexattr_destroy (&attr);
140
141         return res;
142 #else
143         return mono_mutex_init (mutex);
144 #endif
145 }