Merge pull request #642 from Ventero/CleanCopyLocal
[mono.git] / mono / utils / mono-semaphore.c
1 /*
2  * mono-semaphore.c: mono-semaphore functions
3  *
4  * Author:
5  *      Gonzalo Paniagua Javier  <gonzalo@novell.com>
6  *
7  * (C) 2010 Novell, Inc.
8  */
9
10 #include <config.h>
11 #include <errno.h>
12 #include "utils/mono-semaphore.h"
13 #ifdef HAVE_SYS_TIME_H
14 #include <sys/time.h>
15 #endif
16 #ifdef HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
19
20 #if (defined (HAVE_SEMAPHORE_H) || defined (USE_MACH_SEMA)) && !defined(HOST_WIN32)
21 /* sem_* or semaphore_* functions in use */
22 #  ifdef USE_MACH_SEMA
23 #    define TIMESPEC mach_timespec_t
24 #    define WAIT_BLOCK(a,b) semaphore_timedwait (*(a), *(b))
25 #  elif defined(__native_client__) && defined(USE_NEWLIB)
26 #    define TIMESPEC struct timespec
27 #    define WAIT_BLOCK(a, b) sem_trywait(a)
28 #  elif defined(__OpenBSD__)
29 #    define TIMESPEC struct timespec
30 #    define WAIT_BLOCK(a) sem_trywait(a)
31 #  else
32 #    define TIMESPEC struct timespec
33 #    define WAIT_BLOCK(a,b) sem_timedwait (a, b)
34 #  endif
35
36 #define NSEC_PER_SEC 1000000000
37 int
38 mono_sem_timedwait (MonoSemType *sem, guint32 timeout_ms, gboolean alertable)
39 {
40         TIMESPEC ts, copy;
41         struct timeval t;
42         int res = 0;
43 #if defined(__OpenBSD__)
44         int timeout;
45 #endif
46
47 #ifndef USE_MACH_SEMA
48         if (timeout_ms == 0)
49                 return sem_trywait (sem);
50 #endif
51         if (timeout_ms == (guint32) 0xFFFFFFFF)
52                 return mono_sem_wait (sem, alertable);
53
54 #ifdef USE_MACH_SEMA
55         memset (&t, 0, sizeof (TIMESPEC));
56 #else
57         gettimeofday (&t, NULL);
58 #endif
59         ts.tv_sec = timeout_ms / 1000 + t.tv_sec;
60         ts.tv_nsec = (timeout_ms % 1000) * 1000000 + t.tv_usec * 1000;
61         while (ts.tv_nsec > NSEC_PER_SEC) {
62                 ts.tv_nsec -= NSEC_PER_SEC;
63                 ts.tv_sec++;
64         }
65 #if defined(__OpenBSD__)
66         timeout = ts.tv_sec;
67         while (timeout) {
68                 if ((res = WAIT_BLOCK (sem)) == 0)
69                         return res;
70
71                 if (alertable)
72                         return -1;
73
74                 usleep (ts.tv_nsec / 1000);
75                 timeout--;
76         }
77 #else
78         copy = ts;
79         while ((res = WAIT_BLOCK (sem, &ts)) == -1 && errno == EINTR) {
80                 struct timeval current;
81                 if (alertable)
82                         return -1;
83 #ifdef USE_MACH_SEMA
84                 memset (&current, 0, sizeof (TIMESPEC));
85 #else
86                 gettimeofday (&current, NULL);
87 #endif
88                 ts = copy;
89                 ts.tv_sec -= (current.tv_sec - t.tv_sec);
90                 ts.tv_nsec -= (current.tv_usec - t.tv_usec) * 1000;
91                 if (ts.tv_nsec < 0) {
92                         if (ts.tv_sec <= 0) {
93                                 ts.tv_nsec = 0;
94                         } else {
95                                 ts.tv_sec--;
96                                 ts.tv_nsec += NSEC_PER_SEC;
97                         }
98                 }
99                 if (ts.tv_sec < 0) {
100                         ts.tv_sec = 0;
101                         ts.tv_nsec = 0;
102                 }
103         }
104 #endif
105         /* OSX might return > 0 for error */
106         if (res != 0)
107                 res = -1;
108         return res;
109 }
110
111 int
112 mono_sem_wait (MonoSemType *sem, gboolean alertable)
113 {
114         int res;
115 #ifndef USE_MACH_SEMA
116         while ((res = sem_wait (sem)) == -1 && errno == EINTR)
117 #else
118         while ((res = semaphore_wait (*sem)) == KERN_ABORTED)
119 #endif
120         {
121                 if (alertable)
122                         return -1;
123         }
124         /* OSX might return > 0 for error */
125         if (res != 0)
126                 res = -1;
127         return res;
128 }
129
130 int
131 mono_sem_post (MonoSemType *sem)
132 {
133         int res;
134 #ifndef USE_MACH_SEMA
135         while ((res = sem_post (sem)) == -1 && errno == EINTR);
136 #else
137         res = semaphore_signal (*sem);
138         /* OSX might return > 0 for error */
139         if (res != KERN_SUCCESS)
140                 res = -1;
141 #endif
142         return res;
143 }
144
145 #else
146 /* Windows or io-layer functions in use */
147 int
148 mono_sem_wait (MonoSemType *sem, gboolean alertable)
149 {
150         return mono_sem_timedwait (sem, INFINITE, alertable);
151 }
152
153 int
154 mono_sem_timedwait (MonoSemType *sem, guint32 timeout_ms, gboolean alertable)
155 {
156         gboolean res;
157
158         while ((res = WaitForSingleObjectEx (*sem, timeout_ms, alertable)) == WAIT_IO_COMPLETION) {
159                 if (alertable) {
160                         errno = EINTR;
161                         return -1;
162                 }
163         }
164         switch (res) {
165         case WAIT_OBJECT_0:
166                 return 0;    
167         // WAIT_TIMEOUT and WAIT_FAILED
168         default:
169                 return -1;
170         }
171 }
172
173 int
174 mono_sem_post (MonoSemType *sem)
175 {
176         if (!ReleaseSemaphore (*sem, 1, NULL))
177                 return -1;
178         return 0;
179 }
180 #endif
181