New tests.
[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))
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(__OpenBSD__)
26 #    define TIMESPEC struct timespec
27 #    define WAIT_BLOCK(a) sem_trywait(a)
28 #  else
29 #    define TIMESPEC struct timespec
30 #    define WAIT_BLOCK(a,b) sem_timedwait (a, b)
31 #  endif
32
33 #define NSEC_PER_SEC 1000000000
34 int
35 mono_sem_timedwait (MonoSemType *sem, guint32 timeout_ms, gboolean alertable)
36 {
37         TIMESPEC ts, copy;
38         struct timeval t;
39         int res = 0;
40 #if defined(__OpenBSD__)
41         int timeout;
42 #endif
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         gettimeofday (&t, NULL);
52         ts.tv_sec = timeout_ms / 1000 + t.tv_sec;
53         ts.tv_nsec = (timeout_ms % 1000) * 1000000 + t.tv_usec * 1000;
54         while (ts.tv_nsec > NSEC_PER_SEC) {
55                 ts.tv_nsec -= NSEC_PER_SEC;
56                 ts.tv_sec++;
57         }
58 #if defined(__OpenBSD__)
59         timeout = ts.tv_sec;
60         while (timeout) {
61                 if ((res = WAIT_BLOCK (sem)) == 0)
62                         return res;
63
64                 if (alertable)
65                         return -1;
66
67                 usleep (ts.tv_nsec / 1000);
68                 timeout--;
69         }
70 #else
71         copy = ts;
72         while ((res = WAIT_BLOCK (sem, &ts) == -1) && errno == EINTR) {
73                 struct timeval current;
74                 if (alertable)
75                         return -1;
76                 gettimeofday (&current, NULL);
77                 ts = copy;
78                 ts.tv_sec -= (current.tv_sec - t.tv_sec);
79                 ts.tv_nsec -= (current.tv_usec - t.tv_usec) * 1000;
80                 if (ts.tv_nsec < 0) {
81                         if (ts.tv_sec <= 0) {
82                                 ts.tv_nsec = 0;
83                         } else {
84                                 ts.tv_sec--;
85                                 ts.tv_nsec += NSEC_PER_SEC;
86                         }
87                 }
88                 if (ts.tv_sec < 0) {
89                         ts.tv_sec = 0;
90                         ts.tv_nsec = 0;
91                 }
92         }
93 #endif
94         return res;
95 }
96
97 int
98 mono_sem_wait (MonoSemType *sem, gboolean alertable)
99 {
100         int res;
101 #ifndef USE_MACH_SEMA
102         while ((res = sem_wait (sem) == -1) && errno == EINTR)
103 #else
104         while ((res = semaphore_wait (*sem) == -1) && errno == EINTR)
105 #endif
106         {
107                 if (alertable)
108                         return -1;
109         }
110         return res;
111 }
112
113 int
114 mono_sem_post (MonoSemType *sem)
115 {
116         int res;
117 #ifndef USE_MACH_SEMA
118         while ((res = sem_post (sem) == -1) && errno == EINTR);
119 #else
120         while ((res = semaphore_signal (*sem) == -1) && errno == EINTR);
121 #endif
122         return res;
123 }
124
125 #else
126 /* Windows or io-layer functions in use */
127 int
128 mono_sem_wait (MonoSemType *sem, gboolean alertable)
129 {
130         return mono_sem_timedwait (sem, INFINITE, alertable);
131 }
132
133 int
134 mono_sem_timedwait (MonoSemType *sem, guint32 timeout_ms, gboolean alertable)
135 {
136         gboolean res;
137
138         res = WaitForSingleObjectEx (*sem, timeout_ms, alertable);
139         switch (res) {
140         case WAIT_OBJECT_0:
141                 return 0;
142         case WAIT_IO_COMPLETION:
143                 errno = EINTR;
144                 return -1;            
145         // WAIT_TIMEOUT and WAIT_FAILED
146         default:
147                 return -1;
148         }
149 }
150
151 int
152 mono_sem_post (MonoSemType *sem)
153 {
154         if (!ReleaseSemaphore (*sem, 1, NULL))
155                 return -1;
156         return 0;
157 }
158 #endif
159