[mini] Add new JIT flag JIT_FLAG_DISCARD_RESULTS to reduce the noise when profiling...
[mono.git] / mono / metadata / socket-io-windows.c
1 /*
2 * socket-io-windows.c: Windows specific socket code.
3 *
4 * Copyright 2016 Microsoft
5 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
6 */
7 #include <config.h>
8 #include <glib.h>
9
10 #include "mono/metadata/socket-io-windows-internals.h"
11
12 #define LOGDEBUG(...)  
13
14 static gboolean set_blocking (SOCKET sock, gboolean block)
15 {
16         u_long non_block = block ? 0 : 1;
17         return ioctlsocket (sock, FIONBIO, &non_block) != SOCKET_ERROR;
18 }
19
20 static DWORD get_socket_timeout (SOCKET sock, int optname)
21 {
22         DWORD timeout = 0;
23         int optlen = sizeof (DWORD);
24         if (getsockopt (sock, SOL_SOCKET, optname, (char *)&timeout, &optlen) == SOCKET_ERROR) {
25                 WSASetLastError (0);
26                 return WSA_INFINITE;
27         }
28         if (timeout == 0)
29                 timeout = WSA_INFINITE; // 0 means infinite
30         return timeout;
31 }
32
33 /*
34 * Performs an alertable wait for the specified event (FD_ACCEPT_BIT,
35 * FD_CONNECT_BIT, FD_READ_BIT, FD_WRITE_BIT) on the specified socket.
36 * Returns TRUE if the event is fired without errors. Calls WSASetLastError()
37 * with WSAEINTR and returns FALSE if the thread is alerted. If the event is
38 * fired but with an error WSASetLastError() is called to set the error and the
39 * function returns FALSE.
40 */
41 static gboolean alertable_socket_wait (SOCKET sock, int event_bit)
42 {
43         static char *EVENT_NAMES[] = { "FD_READ", "FD_WRITE", NULL /*FD_OOB*/, "FD_ACCEPT", "FD_CONNECT", "FD_CLOSE" };
44         gboolean success = FALSE;
45         int error = -1;
46         DWORD timeout = WSA_INFINITE;
47         if (event_bit == FD_READ_BIT || event_bit == FD_WRITE_BIT) {
48                 timeout = get_socket_timeout (sock, event_bit == FD_READ_BIT ? SO_RCVTIMEO : SO_SNDTIMEO);
49         }
50         WSASetLastError (0);
51         WSAEVENT event = WSACreateEvent ();
52         if (event != WSA_INVALID_EVENT) {
53                 if (WSAEventSelect (sock, event, (1 << event_bit) | FD_CLOSE) != SOCKET_ERROR) {
54                         LOGDEBUG (g_message ("%06d - Calling WSAWaitForMultipleEvents () on socket %d", GetCurrentThreadId (), sock));
55                         DWORD ret = WSAWaitForMultipleEvents (1, &event, TRUE, timeout, TRUE);
56                         if (ret == WSA_WAIT_IO_COMPLETION) {
57                                 LOGDEBUG (g_message ("%06d - WSAWaitForMultipleEvents () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), sock));
58                                 error = WSAEINTR;
59                         } else if (ret == WSA_WAIT_TIMEOUT) {
60                                 error = WSAETIMEDOUT;
61                         } else {
62                                 g_assert (ret == WSA_WAIT_EVENT_0);
63                                 WSANETWORKEVENTS ne = { 0 };
64                                 if (WSAEnumNetworkEvents (sock, event, &ne) != SOCKET_ERROR) {
65                                         if (ne.lNetworkEvents & (1 << event_bit) && ne.iErrorCode[event_bit]) {
66                                                 LOGDEBUG (g_message ("%06d - %s error %d on socket %d", GetCurrentThreadId (), EVENT_NAMES[event_bit], ne.iErrorCode[event_bit], sock));
67                                                 error = ne.iErrorCode[event_bit];
68                                         } else if (ne.lNetworkEvents & FD_CLOSE_BIT && ne.iErrorCode[FD_CLOSE_BIT]) {
69                                                 LOGDEBUG (g_message ("%06d - FD_CLOSE error %d on socket %d", GetCurrentThreadId (), ne.iErrorCode[FD_CLOSE_BIT], sock));
70                                                 error = ne.iErrorCode[FD_CLOSE_BIT];
71                                         } else {
72                                                 LOGDEBUG (g_message ("%06d - WSAEnumNetworkEvents () finished successfully on socket %d", GetCurrentThreadId (), sock));
73                                                 success = TRUE;
74                                                 error = 0;
75                                         }
76                                 }
77                         }
78                         WSAEventSelect (sock, NULL, 0);
79                 }
80                 WSACloseEvent (event);
81         }
82         if (error != -1) {
83                 WSASetLastError (error);
84         }
85         return success;
86 }
87
88 #define ALERTABLE_SOCKET_CALL(event_bit, blocking, repeat, ret, op, sock, ...) \
89         LOGDEBUG (g_message ("%06d - Performing %s " #op " () on socket %d", GetCurrentThreadId (), blocking ? "blocking" : "non-blocking", sock)); \
90         if (blocking) { \
91                 if (set_blocking(sock, FALSE)) { \
92                         while (-1 == (int) (ret = op (sock, __VA_ARGS__))) { \
93                                 int _error = WSAGetLastError ();\
94                                 if (_error != WSAEWOULDBLOCK && _error != WSA_IO_PENDING) \
95                                         break; \
96                                 if (!alertable_socket_wait (sock, event_bit) || !repeat) \
97                                         break; \
98                         } \
99                         int _saved_error = WSAGetLastError (); \
100                         set_blocking (sock, TRUE); \
101                         WSASetLastError (_saved_error); \
102                 } \
103         } else { \
104                 ret = op (sock, __VA_ARGS__); \
105         } \
106         int _saved_error = WSAGetLastError (); \
107         LOGDEBUG (g_message ("%06d - Finished %s " #op " () on socket %d (ret = %d, WSAGetLastError() = %d)", GetCurrentThreadId (), \
108                 blocking ? "blocking" : "non-blocking", sock, ret, _saved_error)); \
109         WSASetLastError (_saved_error);
110
111 SOCKET alertable_accept (SOCKET s, struct sockaddr *addr, int *addrlen, gboolean blocking)
112 {
113         SOCKET newsock = INVALID_SOCKET;
114         ALERTABLE_SOCKET_CALL (FD_ACCEPT_BIT, blocking, TRUE, newsock, accept, s, addr, addrlen);
115         return newsock;
116 }
117
118 int alertable_connect (SOCKET s, const struct sockaddr *name, int namelen, gboolean blocking)
119 {
120         int ret = SOCKET_ERROR;
121         ALERTABLE_SOCKET_CALL (FD_CONNECT_BIT, blocking, FALSE, ret, connect, s, name, namelen);
122         ret = WSAGetLastError () != 0 ? SOCKET_ERROR : 0;
123         return ret;
124 }
125
126 int alertable_recv (SOCKET s, char *buf, int len, int flags, gboolean blocking)
127 {
128         int ret = SOCKET_ERROR;
129         ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, recv, s, buf, len, flags);
130         return ret;
131 }
132
133 int alertable_recvfrom (SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen, gboolean blocking)
134 {
135         int ret = SOCKET_ERROR;
136         ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, recvfrom, s, buf, len, flags, from, fromlen);
137         return ret;
138 }
139
140 int alertable_WSARecv (SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, gboolean blocking)
141 {
142         int ret = SOCKET_ERROR;
143         ALERTABLE_SOCKET_CALL (FD_READ_BIT, blocking, TRUE, ret, WSARecv, s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine);
144         return ret;
145 }
146
147 int alertable_send (SOCKET s, char *buf, int len, int flags, gboolean blocking)
148 {
149         int ret = SOCKET_ERROR;
150         ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, send, s, buf, len, flags);
151         return ret;
152 }
153
154 int alertable_sendto (SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen, gboolean blocking)
155 {
156         int ret = SOCKET_ERROR;
157         ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, sendto, s, buf, len, flags, to, tolen);
158         return ret;
159 }
160
161 int alertable_WSASend (SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, DWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, gboolean blocking)
162 {
163         int ret = SOCKET_ERROR;
164         ALERTABLE_SOCKET_CALL (FD_WRITE_BIT, blocking, FALSE, ret, WSASend, s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine);
165         return ret;
166 }
167
168 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT)
169 BOOL alertable_TransmitFile (SOCKET hSocket, HANDLE hFile, DWORD nNumberOfBytesToWrite, DWORD nNumberOfBytesPerSend, LPOVERLAPPED lpOverlapped, LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, DWORD dwReserved, gboolean blocking)
170 {
171         LOGDEBUG (g_message ("%06d - Performing %s TransmitFile () on socket %d", GetCurrentThreadId (), blocking ? "blocking" : "non-blocking", hSocket));
172
173         int error = 0;
174         if (blocking) {
175                 g_assert (lpOverlapped == NULL);
176                 OVERLAPPED overlapped = { 0 };
177                 overlapped.hEvent = WSACreateEvent ();
178                 if (overlapped.hEvent == WSA_INVALID_EVENT)
179                         return FALSE;
180                 if (!TransmitFile (hSocket, hFile, nNumberOfBytesToWrite, nNumberOfBytesPerSend, &overlapped, lpTransmitBuffers, dwReserved)) {
181                         error = WSAGetLastError ();
182                         if (error == WSA_IO_PENDING) {
183                                 error = 0;
184                                 // NOTE: .NET's Socket.SendFile() doesn't honor the Socket's SendTimeout so we shouldn't either
185                                 DWORD ret = WaitForSingleObjectEx (overlapped.hEvent, INFINITE, TRUE);
186                                 if (ret == WAIT_IO_COMPLETION) {
187                                         LOGDEBUG (g_message ("%06d - WaitForSingleObjectEx () returned WSA_WAIT_IO_COMPLETION for socket %d", GetCurrentThreadId (), hSocket));
188                                         error = WSAEINTR;
189                                 } else if (ret == WAIT_TIMEOUT) {
190                                         error = WSAETIMEDOUT;
191                                 } else if (ret != WAIT_OBJECT_0) {
192                                         error = GetLastError ();
193                                 }
194                         }
195                 }
196                 WSACloseEvent (overlapped.hEvent);
197         } else {
198                 if (!TransmitFile (hSocket, hFile, nNumberOfBytesToWrite, nNumberOfBytesPerSend, lpOverlapped, lpTransmitBuffers, dwReserved)) {
199                         error = WSAGetLastError ();
200                 }
201         }
202
203         LOGDEBUG (g_message ("%06d - Finished %s TransmitFile () on socket %d (ret = %d, WSAGetLastError() = %d)", GetCurrentThreadId (), \
204                 blocking ? "blocking" : "non-blocking", hSocket, error == 0, error));
205         WSASetLastError (error);
206
207         return error == 0;
208 }
209 #endif /* #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */