summaryrefslogtreecommitdiff
path: root/src/lib/ecore_con/efl_net_socket_windows.c
diff options
context:
space:
mode:
authorGustavo Sverzut Barbieri <barbieri@profusion.mobi>2017-03-22 04:29:16 -0300
committerGustavo Sverzut Barbieri <barbieri@profusion.mobi>2017-03-29 12:44:19 -0300
commitfa0e2865a1a53cd05298b8368fa9abdb7c83bff8 (patch)
treeec445601f6df26f124851b56dcde2b6a870f2684 /src/lib/ecore_con/efl_net_socket_windows.c
parent17444557deab4f1ecb62e0fd03a7090a3a4bcbfd (diff)
implement efl_net_{socket,dialer,server}_windows
This is the local socket for windows, analogous to AF_UNIX. `Efl_Net_Socket_Windows` is the base class doing `ReadFile()` and `WriteFile()` using overlapped I/O, as well as the close procedure (`FlushFileBuffers()`, `DisconnectNamedPipe()` and `CloseHandle()`). These are done on top of an existing HANDLE that is set by `Efl_Net_Dialer_Windows` (from `CreateFile()`) or `Efl_Net_Server_Windows` (from `CreateNamedPipe()`). The overlapped I/O will return immediately, either with operation completed or `ERROR_IO_PENDING`, which means the kernel will execute that asynchronously and will later `SetEvent(overlapped.hEvent)` which is an event we wait on our main loop. That `overlapped` handle must exist during the call lifetime, thus cannot be bound to `pd`, as we may call `CancelIo()` but there is no guarantee the memory won't be touched, in that case we keep the overlapped around, but without an associated object. Windows provides no notification "can read without blocking" or non-blocking calls that returns partial data. The way to go is to use these overlapped I/O, with an initial `ReadFile()` to an internal buffer, once that operation finishes, we callback the user to says there is something to read (`efl_io_reader_can_read_set()`) and wait until `efl_io_reader_read()` is called to consume the available data, then `ReadFile()` is called again to read more data to the same internal buffer. Likewise, there is no "can write without blocking" or non-blocking calls that sends only partial data. The way to go is to get user bytes in `efl_io_writer_write()` and copy them in an internal buffer, then call `WriteFile()` on that and inform the user nothing else can be written until that operation completes (`efl_io_writer_can_write_set()`). This is cumbersome since we say we "sent" stuff when we actually didn't, it's still in our internal buffer (`pd->send.bytes`), but nonetheless the kernel and the other peer may be adding even more buffers, in this case we need to do a best effort to get it delivery. A particular case is troublesome: `write() -> close()`, this may result in `WriteFile()` pending, in this case we wait using `GetOverlappedResult()`, *this is nasty and may block*, but it's the only way I see to cope with such common use case. Other operations, like ongoing `ReadFile()` or `ConnectNamedPipe()` will be canceled using `CancelIo()`. Q: Why no I/O Completion Port (IOCP) was used? Why no CreateThreadpoolIo()? These perform much better! A: These will call back from secondary threads, but in EFL we must report back to the user in order to process incoming data or get more data to send. That is, we serialize everything to the main thread, making it impossible to use the benefits of IOCP and similar such as CreateThreadpoolIo(). Since we'd need to wakeup the main thread anyways, using `OVERLAPPED.hEvent` with `ecore_main_win32_handler_add()` does the job as we expect. Thanks to Vincent Torri (vtorri) for his help getting this code done with an example on how to do the NamedPipe handling on Windows.
Diffstat (limited to 'src/lib/ecore_con/efl_net_socket_windows.c')
-rw-r--r--src/lib/ecore_con/efl_net_socket_windows.c929
1 files changed, 929 insertions, 0 deletions
diff --git a/src/lib/ecore_con/efl_net_socket_windows.c b/src/lib/ecore_con/efl_net_socket_windows.c
new file mode 100644
index 0000000000..02c4b05c90
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_socket_windows.c
@@ -0,0 +1,929 @@
1#define EFL_NET_SOCKET_WINDOWS_PROTECTED 1
2#define EFL_IO_READER_PROTECTED 1
3#define EFL_IO_WRITER_PROTECTED 1
4#define EFL_IO_CLOSER_PROTECTED 1
5#define EFL_NET_SOCKET_PROTECTED 1
6
7#ifdef HAVE_CONFIG_H
8# include <config.h>
9#endif
10
11#include "Ecore.h"
12#include "Ecore_Con.h"
13#include "ecore_con_private.h"
14
15#define MY_CLASS EFL_NET_SOCKET_WINDOWS_CLASS
16
17#define BUFFER_SIZE (4 * 4096)
18
19typedef struct _Efl_Net_Socket_Windows_Data
20{
21 Eina_Stringshare *address_local;
22 Eina_Stringshare *address_remote;
23 Eina_List *pending_ops;
24 struct {
25 union {
26 uint8_t *bytes;
27 void *mem;
28 };
29 DWORD len;
30 DWORD used;
31 DWORD base;
32 Efl_Net_Socket_Windows_Operation *pending;
33 } recv;
34 struct {
35 union {
36 uint8_t *bytes;
37 void *mem;
38 };
39 DWORD len;
40 DWORD used;
41 Efl_Net_Socket_Windows_Operation *pending;
42 } send;
43 HANDLE handle;
44 Eina_Bool can_read;
45 Eina_Bool eos;
46 Eina_Bool pending_eos;
47 Eina_Bool can_write;
48 Eina_Bool io_started;
49 Eina_Bool close_on_exec;
50 Eina_Bool close_on_destructor;
51} Efl_Net_Socket_Windows_Data;
52
53struct _Efl_Net_Socket_Windows_Operation
54{
55 OVERLAPPED base;
56 Efl_Net_Socket_Windows_Operation_Success_Cb success_cb;
57 Efl_Net_Socket_Windows_Operation_Failure_Cb failure_cb;
58 const void *data;
59 Ecore_Win32_Handler *event_handler;
60 Eo *o;
61 Eina_Bool deleting;
62};
63
64/*
65 * differences to evil_format_message():
66 * - shorter string
67 * - no newline
68 * - fallback to error code if format fails
69 */
70char *
71_efl_net_windows_error_msg_get(DWORD win32err)
72{
73 LPTSTR msg;
74 char *str;
75 char *disp;
76 int len, reqlen;
77
78 if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
79 NULL,
80 win32err,
81 0, /* Default language */
82 (LPTSTR)&msg,
83 0,
84 NULL))
85 goto fallback;
86
87#ifdef UNICODE
88 str = evil_wchar_to_char(msg);
89#else
90 str = msg;
91#endif /* UNICODE */
92
93 len = strlen(str);
94 if ((len > 0) && (str[len - 1] == '\n'))
95 str[--len] = '\0';
96 if ((len > 0) && (str[len - 1] == '\r'))
97 str[--len] = '\0';
98 if ((len > 0) && (str[len - 1] == '.'))
99 str[--len] = '\0';
100
101 reqlen = snprintf("", 0, "%lu(%s)", win32err, str);
102 if (reqlen < 1) goto error;
103 disp = malloc(reqlen + 1);
104 if (!disp) goto error;
105 snprintf(disp, reqlen + 1, "%lu(%s)", win32err, str);
106
107#ifdef UNICODE
108 free(str);
109#endif /* UNICODE */
110 LocalFree(msg);
111
112 return disp;
113
114 error:
115#ifdef UNICODE
116 free(str);
117#endif /* UNICODE */
118 LocalFree(msg);
119 fallback:
120 {
121 char buf[64];
122 snprintf(buf, sizeof(buf), "%ld", win32err);
123 return strdup(buf);
124 }
125}
126
127static void
128_efl_net_socket_windows_handle_close(HANDLE h)
129{
130 if (!FlushFileBuffers(h))
131 {
132 DWORD win32err = GetLastError();
133 if (win32err != ERROR_PIPE_NOT_CONNECTED)
134 {
135 char *msg = _efl_net_windows_error_msg_get(GetLastError());
136 WRN("HANDLE=%p could not flush buffers: %s", h, msg);
137 free(msg);
138 }
139 }
140 if (!DisconnectNamedPipe(h))
141 {
142 DWORD win32err = GetLastError();
143 if ((win32err != ERROR_NOT_SUPPORTED) && /* dialer socket don't support it */
144 (win32err != ERROR_PIPE_NOT_CONNECTED))
145 {
146 char *msg = _efl_net_windows_error_msg_get(win32err);
147 WRN("HANDLE=%p could not disconnect: %s", h, msg);
148 free(msg);
149 }
150 }
151 CloseHandle(h);
152 DBG("HANDLE=%p closed", h);
153}
154
155static Eina_Bool _efl_net_socket_windows_operation_event(void *, Ecore_Win32_Handler *wh);
156
157Efl_Net_Socket_Windows_Operation *
158_efl_net_socket_windows_operation_new(Eo *o, Efl_Net_Socket_Windows_Operation_Success_Cb success_cb, Efl_Net_Socket_Windows_Operation_Failure_Cb failure_cb, const void *data)
159{
160 Efl_Net_Socket_Windows_Operation *op;
161 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, EFL_NET_SOCKET_WINDOWS_CLASS);
162
163 EINA_SAFETY_ON_NULL_RETURN_VAL(pd, NULL);
164 EINA_SAFETY_ON_NULL_RETURN_VAL(success_cb, NULL);
165 EINA_SAFETY_ON_NULL_RETURN_VAL(failure_cb, NULL);
166
167 op = calloc(1, sizeof(*op));
168 EINA_SAFETY_ON_NULL_RETURN_VAL(op, NULL);
169
170 op->base.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
171 if (!op->base.hEvent)
172 {
173 char *msg = _efl_net_windows_error_msg_get(GetLastError());
174 ERR("socket=%p success_cb=%p failure_cb=%p data=%p: error=%s",
175 op->o, op->success_cb, op->failure_cb, op->data, msg);
176 free(msg);
177 goto error_event;
178 }
179
180 op->event_handler = ecore_main_win32_handler_add(op->base.hEvent, _efl_net_socket_windows_operation_event, op);
181 if (!op->event_handler)
182 {
183 ERR("socket=%p success_cb=%p failure_cb=%p data=%p: could not create event handler",
184 op->o, op->success_cb, op->failure_cb, op->data);
185 goto error_handler;
186 }
187
188 op->success_cb = success_cb;
189 op->failure_cb = failure_cb;
190 op->data = data;
191 op->o = o;
192 pd->pending_ops = eina_list_append(pd->pending_ops, op);
193
194 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p",
195 op, op->o, op->success_cb, op->failure_cb, op->data);
196
197 return op;
198
199 error_handler:
200 CloseHandle(op->base.hEvent);
201 error_event:
202 free(op);
203 return NULL;
204}
205
206static Eina_Error
207_efl_net_socket_windows_operation_done(Efl_Net_Socket_Windows_Operation *op, DWORD win32err, DWORD used_size)
208{
209 Efl_Net_Socket_Windows_Data *pd;
210 Eina_Error err = 0;
211
212 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
213 {
214 char *msg = _efl_net_windows_error_msg_get(win32err);
215 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p error=%s used_size=%lu",
216 op, op->o, op->success_cb, op->failure_cb, op->data, msg, used_size);
217 free(msg);
218 }
219
220 op->deleting = EINA_TRUE;
221
222 efl_ref(op->o);
223 pd = efl_data_scope_get(op->o, EFL_NET_SOCKET_WINDOWS_CLASS);
224 if (pd)
225 pd->pending_ops = eina_list_remove(pd->pending_ops, op);
226
227 if (win32err)
228 err = op->failure_cb((void *)op->data, op->o, win32err);
229 else
230 op->success_cb((void *)op->data, op->o, used_size);
231
232 if (op->event_handler)
233 {
234 if (WaitForSingleObject(pd->handle, 0) != WAIT_OBJECT_0)
235 {
236 DWORD used_size = 0;
237 if (GetOverlappedResult(pd->handle, &op->base, &used_size, FALSE))
238 {
239 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, FALSE)",
240 op, op->o, op->success_cb, op->failure_cb, op->data, pd->handle, &op->base, used_size);
241 }
242 else
243 {
244 win32err = GetLastError();
245 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
246 {
247 char *msg = _efl_net_windows_error_msg_get(win32err);
248 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, TRUE)=%s",
249 op, op->o, op->success_cb, op->failure_cb, op->data, pd->handle, &op->base, used_size, msg);
250 free(msg);
251 }
252
253 if (win32err == ERROR_IO_INCOMPLETE)
254 {
255 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p: still pending I/O...",
256 op, op->o, op->success_cb, op->failure_cb, op->data);
257 efl_unref(op->o);
258 op->o = NULL;
259 return 0;
260 }
261 }
262 }
263
264 ecore_main_win32_handler_del(op->event_handler);
265 }
266
267#ifndef ERROR_HANDLES_CLOSED
268#define ERROR_HANDLES_CLOSED 676
269#endif
270 if ((win32err == ERROR_HANDLES_CLOSED) && !efl_io_closer_closed_get(op->o))
271 efl_io_closer_close(op->o);
272 efl_unref(op->o);
273
274 CloseHandle(op->base.hEvent);
275 free(op);
276 return err;
277}
278
279Eina_Error
280_efl_net_socket_windows_operation_failed(Efl_Net_Socket_Windows_Operation *op, DWORD win32err)
281{
282 EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
283 EINA_SAFETY_ON_TRUE_RETURN_VAL(op->deleting, EINVAL);
284
285 return _efl_net_socket_windows_operation_done(op, win32err, 0);
286}
287
288Eina_Error
289_efl_net_socket_windows_operation_succeeded(Efl_Net_Socket_Windows_Operation *op, DWORD used_size)
290{
291 EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
292 EINA_SAFETY_ON_TRUE_RETURN_VAL(op->deleting, EINVAL);
293
294 return _efl_net_socket_windows_operation_done(op, 0, used_size);
295}
296
297static Eina_Bool
298_efl_net_socket_windows_operation_event(void *data, Ecore_Win32_Handler *wh EINA_UNUSED)
299{
300 Efl_Net_Socket_Windows_Operation *op = data;
301 HANDLE h = _efl_net_socket_windows_handle_get(op->o);
302 DWORD used_size = 0;
303
304 if ((op->deleting) || (h == INVALID_HANDLE_VALUE))
305 {
306 DBG("op=%p was deleted and pending I/O completed!", op);
307 CloseHandle(op->base.hEvent);
308 free(op);
309 return ECORE_CALLBACK_CANCEL;
310 }
311
312 op->event_handler = NULL;
313
314 if (GetOverlappedResult(h, &op->base, &used_size, FALSE))
315 {
316 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, FALSE)",
317 op, op->o, op->success_cb, op->failure_cb, op->data, h, &op->base, used_size);
318 _efl_net_socket_windows_operation_succeeded(op, used_size);
319 }
320 else
321 {
322 DWORD win32err = GetLastError();
323 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
324 {
325 char *msg = _efl_net_windows_error_msg_get(win32err);
326 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, FALSE)=%s",
327 op, op->o, op->success_cb, op->failure_cb, op->data, h, &op->base, used_size, msg);
328 free(msg);
329 }
330
331 _efl_net_socket_windows_operation_failed(op, win32err);
332 }
333
334 return ECORE_CALLBACK_CANCEL;
335}
336
337Eina_Error
338_efl_net_socket_windows_init(Eo *o, HANDLE h)
339{
340 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
341
342 EINA_SAFETY_ON_TRUE_RETURN_VAL(h == INVALID_HANDLE_VALUE, EINVAL);
343 EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
344 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->handle != INVALID_HANDLE_VALUE, EALREADY);
345
346 pd->handle = h;
347
348 DBG("socket=%p adopted handle=%p", o, h);
349 return 0;
350}
351
352static Eina_Error _efl_net_socket_windows_recv(Eo *o, Efl_Net_Socket_Windows_Data *pd);
353
354static Eina_Error
355_efl_net_socket_windows_recv_success(void *data EINA_UNUSED, Eo *o, DWORD used_size)
356{
357 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
358
359 EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
360
361 pd->recv.pending = NULL;
362 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->recv.used + used_size > pd->recv.len, EINVAL);
363
364 pd->recv.used += used_size;
365
366 /* calls back the user, may read()/write()/close() */
367 efl_io_reader_can_read_set(o, pd->recv.used > 0);
368
369 if (pd->handle == INVALID_HANDLE_VALUE) return 0;
370 if (pd->recv.used == pd->recv.len) return 0;
371 if (pd->recv.pending) return 0;
372
373 return _efl_net_socket_windows_recv(o, pd);
374}
375
376static Eina_Error
377_efl_net_socket_windows_recv_failure(void *data EINA_UNUSED, Eo *o, DWORD win32err)
378{
379 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
380
381 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
382 {
383 char *msg = _efl_net_windows_error_msg_get(win32err);
384 DBG("socket=%p recv error=%s", o, msg);
385 free(msg);
386 }
387
388 pd->recv.pending = NULL;
389
390 if (pd->recv.used > 0)
391 pd->pending_eos = EINA_TRUE; /* eos when buffer drains */
392 else
393 {
394 efl_io_writer_can_write_set(o, EINA_FALSE);
395 efl_io_reader_can_read_set(o, EINA_FALSE);
396 efl_io_reader_eos_set(o, EINA_TRUE);
397 }
398
399 switch (win32err)
400 {
401 case ERROR_INVALID_USER_BUFFER: return EFAULT;
402 case ERROR_NOT_ENOUGH_MEMORY: return ENOMEM;
403 case ERROR_OPERATION_ABORTED: return ECANCELED;
404 case ERROR_BROKEN_PIPE: return EPIPE;
405 default: return EIO;
406 }
407}
408
409static Eina_Error
410_efl_net_socket_windows_recv(Eo *o, Efl_Net_Socket_Windows_Data *pd)
411{
412 Efl_Net_Socket_Windows_Operation *op;
413 OVERLAPPED *ovl;
414 DWORD used_size = 0, win32err;
415 BOOL r;
416
417 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->recv.pending != NULL, EINPROGRESS);
418
419 if (pd->handle == INVALID_HANDLE_VALUE) return EBADF;
420 if (pd->recv.len == 0) return ENOMEM;
421
422 if (pd->recv.base > 0)
423 {
424 DWORD todo = pd->recv.used - pd->recv.base;
425 if (todo > 0)
426 memmove(pd->recv.bytes, pd->recv.bytes + pd->recv.base, todo);
427 pd->recv.used -= pd->recv.base;
428 pd->recv.base = 0;
429 }
430
431 if (pd->recv.used == pd->recv.len) return ENOSPC;
432
433 op = _efl_net_socket_windows_operation_new(o,
434 _efl_net_socket_windows_recv_success,
435 _efl_net_socket_windows_recv_failure,
436 NULL);
437 EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
438 ovl = _efl_net_socket_windows_operation_overlapped_get(op);
439 r = ReadFile(pd->handle,
440 pd->recv.bytes + pd->recv.used,
441 pd->recv.len - pd->recv.used,
442 &used_size, ovl);
443 win32err = GetLastError();
444 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
445 {
446 char *msg = _efl_net_windows_error_msg_get(win32err);
447 DBG("socket=%p ReadFile(%p, %p, %lu, &size=%lu, %p)=%s",
448 o, pd->handle, pd->recv.bytes + pd->recv.used,
449 pd->recv.len - pd->recv.used, used_size, ovl, msg);
450 free(msg);
451 }
452
453 if (!r)
454 {
455 if (win32err == ERROR_IO_PENDING)
456 {
457 pd->recv.pending = op;
458 return 0;
459 }
460 else
461 {
462 return _efl_net_socket_windows_operation_failed(op, win32err);
463 }
464 }
465
466 return _efl_net_socket_windows_operation_succeeded(op, used_size);
467}
468
469static Eina_Error _efl_net_socket_windows_send(Eo *o, Efl_Net_Socket_Windows_Data *pd);
470
471static Eina_Error
472_efl_net_socket_windows_send_success(void *data EINA_UNUSED, Eo *o, DWORD used_size)
473{
474 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
475
476 EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
477
478 pd->send.pending = NULL;
479 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->send.used < used_size, EINVAL);
480
481 if (used_size > 0)
482 {
483 DWORD todo = pd->send.used - used_size;
484 if (todo > 0)
485 memmove(pd->send.bytes, pd->send.bytes + used_size, todo);
486 pd->send.used -= used_size;
487 }
488
489 /* calls back the user, may read()/write()/close() */
490 /* only can_write if we're fully done with previous request! */
491 efl_io_writer_can_write_set(o, pd->send.used == 0);
492
493 if (pd->handle == INVALID_HANDLE_VALUE) return 0;
494 if (pd->send.used == 0) return 0;
495 if (pd->send.pending) return 0;
496
497 return _efl_net_socket_windows_send(o, pd);
498}
499
500static Eina_Error
501_efl_net_socket_windows_send_failure(void *data EINA_UNUSED, Eo *o, DWORD win32err)
502{
503 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
504
505 EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
506
507 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
508 {
509 char *msg = _efl_net_windows_error_msg_get(win32err);
510 DBG("socket=%p send error=%s", o, msg);
511 free(msg);
512 }
513
514 pd->send.pending = NULL;
515
516 efl_io_writer_can_write_set(o, EINA_FALSE);
517
518 if (pd->recv.used > 0)
519 pd->pending_eos = EINA_TRUE; /* eos when buffer drains */
520 else
521 {
522 efl_io_reader_can_read_set(o, EINA_FALSE);
523 efl_io_reader_eos_set(o, EINA_TRUE);
524 }
525
526 switch (win32err)
527 {
528 case ERROR_INVALID_USER_BUFFER: return EFAULT;
529 case ERROR_NOT_ENOUGH_MEMORY: return ENOMEM;
530 case ERROR_OPERATION_ABORTED: return ECANCELED;
531 case ERROR_BROKEN_PIPE: return EPIPE;
532 default: return EIO;
533 }
534}
535
536static Eina_Error
537_efl_net_socket_windows_send(Eo *o, Efl_Net_Socket_Windows_Data *pd)
538{
539 Efl_Net_Socket_Windows_Operation *op;
540 OVERLAPPED *ovl;
541 DWORD used_size = 0, win32err;
542 BOOL r;
543
544 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->send.pending != NULL, EINPROGRESS);
545
546 if (pd->handle == INVALID_HANDLE_VALUE) return EBADF;
547 if (pd->send.used == 0) return 0;
548
549 op = _efl_net_socket_windows_operation_new(o,
550 _efl_net_socket_windows_send_success,
551 _efl_net_socket_windows_send_failure,
552 NULL);
553 EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
554
555 ovl = _efl_net_socket_windows_operation_overlapped_get(op);
556
557 r = WriteFile(pd->handle,
558 pd->send.bytes,
559 pd->send.used,
560 &used_size, ovl);
561 win32err = GetLastError();
562 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
563 {
564 char *msg = _efl_net_windows_error_msg_get(win32err);
565 DBG("socket=%p WriteFile(%p, %p, %lu, &size=%lu, %p)=%s",
566 o, pd->handle, pd->send.bytes, pd->send.used, used_size, ovl, msg);
567 free(msg);
568 }
569
570 if (!r)
571 {
572 if (win32err == ERROR_IO_PENDING)
573 {
574 pd->send.pending = op;
575 return 0;
576 }
577 else
578 {
579 return _efl_net_socket_windows_operation_failed(op, win32err);
580 }
581 }
582
583 return _efl_net_socket_windows_operation_succeeded(op, used_size);
584}
585
586Eina_Error
587_efl_net_socket_windows_io_start(Eo *o)
588{
589 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
590 Eina_Error err;
591
592 EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
593 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->io_started, EALREADY);
594
595 if (!pd->recv.mem)
596 {
597 pd->recv.mem = malloc(BUFFER_SIZE);
598 EINA_SAFETY_ON_NULL_RETURN_VAL(pd->recv.mem, ENOMEM);
599 pd->recv.len = BUFFER_SIZE;
600 pd->recv.used = 0;
601 }
602
603 if (!pd->send.mem)
604 {
605 pd->send.mem = malloc(BUFFER_SIZE);
606 EINA_SAFETY_ON_NULL_RETURN_VAL(pd->send.mem, ENOMEM);
607 pd->send.len = BUFFER_SIZE;
608 pd->send.used = 0;
609 }
610
611 DBG("socket=%p starting I/O...", o);
612 err = _efl_net_socket_windows_recv(o, pd);
613 if (err) return err;
614
615 efl_io_writer_can_write_set(o, EINA_TRUE);
616 pd->io_started = EINA_TRUE;
617 return 0;
618}
619
620HANDLE
621_efl_net_socket_windows_handle_get(const Eo *o)
622{
623 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
624 EINA_SAFETY_ON_NULL_RETURN_VAL(pd, INVALID_HANDLE_VALUE);
625 return pd->handle;
626}
627
628EOLIAN static Eo *
629_efl_net_socket_windows_efl_object_constructor(Eo *o, Efl_Net_Socket_Windows_Data *pd)
630{
631 pd->handle = INVALID_HANDLE_VALUE;
632
633 return efl_constructor(efl_super(o, MY_CLASS));
634}
635
636EOLIAN static void
637_efl_net_socket_windows_efl_object_destructor(Eo *o, Efl_Net_Socket_Windows_Data *pd)
638{
639 if (efl_io_closer_close_on_destructor_get(o) &&
640 (!efl_io_closer_closed_get(o)))
641 {
642 efl_event_freeze(o);
643 efl_io_closer_close(o);
644 efl_event_thaw(o);
645 }
646
647 efl_destructor(efl_super(o, MY_CLASS));
648
649 eina_stringshare_replace(&pd->address_local, NULL);
650 eina_stringshare_replace(&pd->address_remote, NULL);
651
652 free(pd->recv.mem);
653 pd->recv.mem = NULL;
654 pd->recv.len = 0;
655 pd->recv.used = 0;
656 pd->recv.base = 0;
657
658 free(pd->send.mem);
659 pd->send.mem = NULL;
660 pd->send.len = 0;
661 pd->send.used = 0;
662}
663
664EOLIAN static Eina_Error
665_efl_net_socket_windows_efl_io_closer_close(Eo *o, Efl_Net_Socket_Windows_Data *pd)
666{
667 HANDLE h;
668
669 EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EBADF);
670
671 DBG("socket=%p closing handle=%p", o, pd->handle);
672
673 if (pd->pending_ops)
674 {
675 if (pd->send.pending)
676 {
677 Efl_Net_Socket_Windows_Operation *op = pd->send.pending;
678 DWORD used_size = 0;
679 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p: wait %lu bytes pending write...",
680 op, op->o, op->success_cb, op->failure_cb, op->data, pd->send.used);
681 if (GetOverlappedResult(pd->handle, &op->base, &used_size, TRUE))
682 {
683 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, TRUE)",
684 op, op->o, op->success_cb, op->failure_cb, op->data, pd->handle, &op->base, used_size);
685 }
686 else
687 {
688 DWORD win32err = GetLastError();
689 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
690 {
691 char *msg = _efl_net_windows_error_msg_get(win32err);
692 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, TRUE)=%s",
693 op, op->o, op->success_cb, op->failure_cb, op->data, pd->handle, &op->base, used_size, msg);
694 free(msg);
695 }
696 }
697 }
698 if (!FlushFileBuffers(pd->handle))
699 {
700 char *msg = _efl_net_windows_error_msg_get(GetLastError());
701 WRN("HANDLE=%p could not flush buffers: %s", pd->handle, msg);
702 free(msg);
703 }
704 if (CancelIo(pd->handle))
705 DBG("socket=%p CancelIo(%p)", o, pd->handle);
706 else if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
707 {
708 char *msg = _efl_net_windows_error_msg_get(GetLastError());
709 WRN("socket=%p CancelIo(%p)=%s", o, pd->handle, msg);
710 free(msg);
711 }
712 }
713
714 while (pd->pending_ops)
715 _efl_net_socket_windows_operation_failed(pd->pending_ops->data, ERROR_OPERATION_ABORTED);
716
717 efl_io_writer_can_write_set(o, EINA_FALSE);
718 efl_io_reader_can_read_set(o, EINA_FALSE);
719 efl_io_reader_eos_set(o, EINA_TRUE);
720
721 h = InterlockedExchangePointer(&pd->handle, INVALID_HANDLE_VALUE);
722 if (h != INVALID_HANDLE_VALUE)
723 _efl_net_socket_windows_handle_close(h);
724
725 efl_event_callback_call(o, EFL_IO_CLOSER_EVENT_CLOSED, NULL);
726
727 return 0;
728}
729
730EOLIAN static Eina_Bool
731_efl_net_socket_windows_efl_io_closer_closed_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
732{
733 return pd->handle == INVALID_HANDLE_VALUE;
734}
735
736EOLIAN static Eina_Bool
737_efl_net_socket_windows_efl_io_closer_close_on_exec_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd, Eina_Bool close_on_exec)
738{
739 DBG("close on exec is not supported on windows");
740 pd->close_on_exec = close_on_exec;
741 return EINA_FALSE;
742}
743
744EOLIAN static Eina_Bool
745_efl_net_socket_windows_efl_io_closer_close_on_exec_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
746{
747 return pd->close_on_exec;
748}
749
750EOLIAN static void
751_efl_net_socket_windows_efl_io_closer_close_on_destructor_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd, Eina_Bool close_on_destructor)
752{
753 pd->close_on_destructor = close_on_destructor;
754}
755
756EOLIAN static Eina_Bool
757_efl_net_socket_windows_efl_io_closer_close_on_destructor_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
758{
759 return pd->close_on_destructor;
760}
761
762EOLIAN static Eina_Error
763_efl_net_socket_windows_efl_io_reader_read(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Rw_Slice *rw_slice)
764{
765 Eina_Slice ro_slice;
766 DWORD remaining;
767
768 EINA_SAFETY_ON_NULL_RETURN_VAL(rw_slice, EINVAL);
769 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->recv.base > pd->recv.used, EINVAL);
770
771 ro_slice.len = pd->recv.used - pd->recv.base;
772 if (ro_slice.len == 0)
773 {
774 rw_slice->len = 0;
775 if (pd->pending_eos)
776 {
777 efl_io_reader_eos_set(o, EINA_TRUE);
778 return 0;
779 }
780 return EAGAIN;
781 }
782 ro_slice.bytes = pd->recv.bytes + pd->recv.base;
783
784 *rw_slice = eina_rw_slice_copy(*rw_slice, ro_slice);
785
786 pd->recv.base += rw_slice->len;
787 remaining = pd->recv.used - pd->recv.base;
788
789 efl_io_reader_can_read_set(o, remaining > 0);
790
791 if ((pd->pending_eos) && (remaining == 0))
792 {
793 efl_io_reader_eos_set(o, EINA_TRUE);
794 return 0;
795 }
796
797 if (!pd->recv.pending)
798 {
799 DBG("recv more from socket=%p", o);
800 _efl_net_socket_windows_recv(o, pd);
801 return 0;
802 }
803
804 return 0;
805}
806
807EOLIAN static void
808_efl_net_socket_windows_efl_io_reader_can_read_set(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Bool can_read)
809{
810 EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o) && can_read);
811 if (pd->can_read == can_read) return;
812 pd->can_read = can_read;
813 efl_event_callback_call(o, EFL_IO_READER_EVENT_CAN_READ_CHANGED, NULL);
814}
815
816EOLIAN static Eina_Bool
817_efl_net_socket_windows_efl_io_reader_can_read_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
818{
819 return pd->can_read;
820}
821
822EOLIAN static void
823_efl_net_socket_windows_efl_io_reader_eos_set(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Bool is_eos)
824{
825 if (pd->eos == is_eos) return;
826 pd->eos = is_eos;
827 if (is_eos)
828 efl_event_callback_call(o, EFL_IO_READER_EVENT_EOS, NULL);
829}
830
831EOLIAN static Eina_Bool
832_efl_net_socket_windows_efl_io_reader_eos_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
833{
834 return pd->eos;
835}
836
837EOLIAN static Eina_Error
838_efl_net_socket_windows_efl_io_writer_write(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Slice *slice, Eina_Slice *remaining)
839{
840 Eina_Error err = EINVAL;
841 DWORD available, todo;
842
843 EINA_SAFETY_ON_NULL_RETURN_VAL(slice, EINVAL);
844 EINA_SAFETY_ON_NULL_GOTO(slice->mem, error);
845 EINA_SAFETY_ON_TRUE_GOTO(efl_io_closer_closed_get(o), error);
846 err = ENOMEM;
847 EINA_SAFETY_ON_NULL_GOTO(pd->send.mem, error);
848
849 /* note: do not queue more data if one is already pending,
850 * otherwise we over-commit a lot and on write() -> close(), it
851 * would take us more than simply waiting on the pending send to
852 * complete.
853 */
854 if (pd->send.pending)
855 {
856 efl_io_writer_can_write_set(o, EINA_FALSE);
857 err = EAGAIN;
858 goto error;
859 }
860 err = EINVAL;
861 EINA_SAFETY_ON_TRUE_GOTO(pd->send.used != 0, error);
862
863 available = pd->send.len - pd->send.used;
864 if (slice->len < available)
865 todo = slice->len;
866 else
867 todo = available;
868
869 memcpy(pd->send.bytes + pd->send.used, slice->mem, todo);
870 if (remaining)
871 {
872 remaining->len = slice->len - todo;
873 remaining->bytes = slice->bytes + todo;
874 }
875 slice->len = todo;
876 pd->send.used += todo;
877
878 efl_io_writer_can_write_set(o, EINA_FALSE);
879
880 DBG("send %lu bytes more to socket=%p", pd->send.used, o);
881 return _efl_net_socket_windows_send(o, pd);
882
883 error:
884 if (remaining) *remaining = *slice;
885 slice->len = 0;
886 slice->mem = NULL;
887 return err;
888}
889
890EOLIAN static void
891_efl_net_socket_windows_efl_io_writer_can_write_set(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Bool can_write)
892{
893 EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o) && can_write);
894 if (pd->can_write == can_write) return;
895 pd->can_write = can_write;
896 efl_event_callback_call(o, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, NULL);
897}
898
899EOLIAN static Eina_Bool
900_efl_net_socket_windows_efl_io_writer_can_write_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
901{
902 return pd->can_write;
903}
904
905EOLIAN static void
906_efl_net_socket_windows_efl_net_socket_address_local_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd, const char *address)
907{
908 eina_stringshare_replace(&pd->address_local, address);
909}
910
911EOLIAN static const char *
912_efl_net_socket_windows_efl_net_socket_address_local_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
913{
914 return pd->address_local;
915}
916
917EOLIAN static void
918_efl_net_socket_windows_efl_net_socket_address_remote_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd, const char *address)
919{
920 eina_stringshare_replace(&pd->address_remote, address);
921}
922
923EOLIAN static const char *
924_efl_net_socket_windows_efl_net_socket_address_remote_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
925{
926 return pd->address_remote;
927}
928
929#include "efl_net_socket_windows.eo.c"