[System] Fixes UdpClient.Receive with IPv6 endpoint
[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 #  else
29 #    define TIMESPEC struct timespec
30 #    define WAIT_BLOCK(a,b) sem_timedwait (a, b)
31 #  endif
32
33 #ifndef NSEC_PER_SEC
34 #define NSEC_PER_SEC 1000000000
35 #endif
36
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
44 #ifndef USE_MACH_SEMA
45         if (timeout_ms == 0)
46                 return sem_trywait (sem);
47 #endif
48         if (timeout_ms == (guint32) 0xFFFFFFFF)
49                 return mono_sem_wait (sem, alertable);
50
51 #ifdef USE_MACH_SEMA
52         memset (&t, 0, sizeof (t));
53 #else
54         gettimeofday (&t, NULL);
55 #endif
56         ts.tv_sec = timeout_ms / 1000 + t.tv_sec;
57         ts.tv_nsec = (timeout_ms % 1000) * 1000000 + t.tv_usec * 1000;
58         while (ts.tv_nsec >= NSEC_PER_SEC) {
59                 ts.tv_nsec -= NSEC_PER_SEC;
60                 ts.tv_sec++;
61         }
62
63         copy = ts;
64 #ifdef USE_MACH_SEMA
65         gettimeofday (&t, NULL);
66         while ((res = WAIT_BLOCK (sem, &ts)) == KERN_ABORTED)
67 #else
68         while ((res = WAIT_BLOCK (sem, &ts)) == -1 && errno == EINTR)
69 #endif
70         {
71 #ifdef USE_MACH_SEMA
72                 struct timeval current;
73 #endif
74                 if (alertable)
75                         return -1;
76                 ts = copy;
77 #ifdef USE_MACH_SEMA
78                 gettimeofday (&current, NULL);
79                 ts.tv_sec -= (current.tv_sec - t.tv_sec);
80                 ts.tv_nsec -= (current.tv_usec - t.tv_usec) * 1000;
81                 if (ts.tv_nsec < 0) {
82                         if (ts.tv_sec <= 0) {
83                                 ts.tv_nsec = 0;
84                         } else {
85                                 ts.tv_sec--;
86                                 ts.tv_nsec += NSEC_PER_SEC;
87                         }
88                 }
89                 if (ts.tv_sec < 0) {
90                         ts.tv_sec = 0;
91                         ts.tv_nsec = 0;
92                 }
93 #endif
94         }
95
96         /* OSX might return > 0 for error */
97         if (res != 0)
98                 res = -1;
99         return res;
100 }
101
102 int
103 mono_sem_wait (MonoSemType *sem, gboolean alertable)
104 {
105         int res;
106 #ifndef USE_MACH_SEMA
107         while ((res = sem_wait (sem)) == -1 && errno == EINTR)
108 #else
109         while ((res = semaphore_wait (*sem)) == KERN_ABORTED)
110 #endif
111         {
112                 if (alertable)
113                         return -1;
114         }
115         /* OSX might return > 0 for error */
116         if (res != 0)
117                 res = -1;
118         return res;
119 }
120
121 int
122 mono_sem_post (MonoSemType *sem)
123 {
124         int res;
125 #ifndef USE_MACH_SEMA
126         while ((res = sem_post (sem)) == -1 && errno == EINTR);
127 #else
128         res = semaphore_signal (*sem);
129         /* OSX might return > 0 for error */
130         if (res != KERN_SUCCESS)
131                 res = -1;
132 #endif
133         return res;
134 }
135
136 #else
137 /* Windows or io-layer functions in use */
138 int
139 mono_sem_wait (MonoSemType *sem, gboolean alertable)
140 {
141         return mono_sem_timedwait (sem, INFINITE, alertable);
142 }
143
144 int
145 mono_sem_timedwait (MonoSemType *sem, guint32 timeout_ms, gboolean alertable)
146 {
147         gboolean res;
148
149         while ((res = WaitForSingleObjectEx (*sem, timeout_ms, alertable)) == WAIT_IO_COMPLETION) {
150                 if (alertable) {
151                         errno = EINTR;
152                         return -1;
153                 }
154         }
155         switch (res) {
156         case WAIT_OBJECT_0:
157                 return 0;    
158         // WAIT_TIMEOUT and WAIT_FAILED
159         default:
160                 return -1;
161         }
162 }
163
164 int
165 mono_sem_post (MonoSemType *sem)
166 {
167         if (!ReleaseSemaphore (*sem, 1, NULL))
168                 return -1;
169         return 0;
170 }
171 #endif
172