efl/src/lib/ecore_con/efl_net_dialer_windows.c

159 lines
5.0 KiB
C
Raw Normal View History

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.
2017-03-22 00:29:16 -07:00
#define EFL_NET_SOCKET_WINDOWS_PROTECTED 1
#define EFL_IO_READER_PROTECTED 1
#define EFL_IO_WRITER_PROTECTED 1
#define EFL_IO_CLOSER_PROTECTED 1
#define EFL_NET_DIALER_PROTECTED 1
#define EFL_NET_SOCKET_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <evil_private.h> /* evil_format_message evil_last_error_get */
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.
2017-03-22 00:29:16 -07:00
#include "Ecore.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#define MY_CLASS EFL_NET_DIALER_WINDOWS_CLASS
typedef struct _Efl_Net_Dialer_Windows_Data
{
Eina_Stringshare *address_dial;
double timeout_dial;
Eina_Bool connected;
} Efl_Net_Dialer_Windows_Data;
EOLIAN static void
_efl_net_dialer_windows_efl_object_destructor(Eo *o, Efl_Net_Dialer_Windows_Data *pd)
{
efl_destructor(efl_super(o, MY_CLASS));
eina_stringshare_replace(&pd->address_dial, NULL);
}
EOLIAN static Eina_Error
_efl_net_dialer_windows_efl_net_dialer_dial(Eo *o, Efl_Net_Dialer_Windows_Data *pd, const char *address)
{
Eina_Error err;
HANDLE h;
char cstr[256], sstr[256];
ULONG cpid, spid;
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.
2017-03-22 00:29:16 -07:00
EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
EINA_SAFETY_ON_TRUE_RETURN_VAL(strchr(address, '/') != NULL, EINVAL);
EINA_SAFETY_ON_TRUE_RETURN_VAL(strchr(address, '\\') != NULL, EINVAL);
EINA_SAFETY_ON_TRUE_RETURN_VAL(strlen(PIPE_NS) + strlen(address) >= 256, EINVAL);
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_net_dialer_connected_get(o), EISCONN);
efl_net_dialer_address_dial_set(o, address);
h = CreateFile(pd->address_dial,
FILE_READ_ATTRIBUTES | FILE_READ_DATA |
FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, NULL);
if (h == INVALID_HANDLE_VALUE)
{
DWORD win32err = GetLastError();
switch (win32err)
{
case ERROR_ACCESS_DENIED: return EACCES;
case ERROR_INVALID_PARAMETER: return EINVAL;
case ERROR_SHARING_VIOLATION: return EINVAL;
case ERROR_FILE_EXISTS: return EEXIST;
case ERROR_FILE_NOT_FOUND: return ENOENT;
case ERROR_PIPE_BUSY: return EBUSY;
case ERROR_INVALID_NAME: return EADDRNOTAVAIL;
default:
{
ERR("CreateFile(%s): Unexpected win32err=%lu (%s)",
pd->address_dial, win32err, evil_format_message(win32err));
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.
2017-03-22 00:29:16 -07:00
return EINVAL;
}
}
}
err = _efl_net_socket_windows_init(o, h);
if (err)
{
CloseHandle(h);
return err;
}
if (GetNamedPipeClientProcessId(_efl_net_socket_windows_handle_get(o), &cpid))
snprintf(cstr, sizeof(cstr), "%s:%lu", address, cpid);
else
{
WRN("server=%p (%s) could not GetNamedPipeClientProcessId(o): %s",
o, address, evil_last_error_get());
eina_strlcpy(cstr, address, sizeof(cstr));
}
if (GetNamedPipeServerProcessId(_efl_net_socket_windows_handle_get(o), &spid))
snprintf(sstr, sizeof(sstr), "%s:%lu", address, spid);
else
{
WRN("server=%p (%s) could not GetNamedPipeServerProcessId(o): %s",
o, address, evil_last_error_get());
eina_strlcpy(sstr, address, sizeof(sstr));
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.
2017-03-22 00:29:16 -07:00
}
efl_net_socket_address_remote_set(o, sstr);
efl_net_socket_address_local_set(o, cstr);
efl_event_callback_call(o, EFL_NET_DIALER_EVENT_DIALER_RESOLVED, NULL);
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.
2017-03-22 00:29:16 -07:00
efl_net_dialer_connected_set(o, EINA_TRUE);
return _efl_net_socket_windows_io_start(o);
}
EOLIAN static void
_efl_net_dialer_windows_efl_net_dialer_address_dial_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd, const char *address)
{
const char *tmp = eina_stringshare_printf(PIPE_NS "%s", address);
eina_stringshare_del(pd->address_dial);
pd->address_dial = tmp;
}
EOLIAN static const char *
_efl_net_dialer_windows_efl_net_dialer_address_dial_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd)
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.
2017-03-22 00:29:16 -07:00
{
return pd->address_dial + strlen(PIPE_NS);
}
EOLIAN static void
_efl_net_dialer_windows_efl_net_dialer_connected_set(Eo *o, Efl_Net_Dialer_Windows_Data *pd, Eina_Bool connected)
{
if (pd->connected == connected) return;
pd->connected = connected;
if (connected) efl_event_callback_call(o, EFL_NET_DIALER_EVENT_DIALER_CONNECTED, NULL);
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.
2017-03-22 00:29:16 -07:00
}
EOLIAN static Eina_Bool
_efl_net_dialer_windows_efl_net_dialer_connected_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd)
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.
2017-03-22 00:29:16 -07:00
{
return pd->connected;
}
EOLIAN static void
_efl_net_dialer_windows_efl_net_dialer_timeout_dial_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd, double seconds)
{
pd->timeout_dial = seconds;
}
EOLIAN static double
_efl_net_dialer_windows_efl_net_dialer_timeout_dial_get(const Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd)
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.
2017-03-22 00:29:16 -07:00
{
return pd->timeout_dial;
}
EOLIAN static Eina_Error
_efl_net_dialer_windows_efl_io_closer_close(Eo *o, Efl_Net_Dialer_Windows_Data *pd EINA_UNUSED)
{
efl_net_dialer_connected_set(o, EINA_FALSE);
return efl_io_closer_close(efl_super(o, MY_CLASS));
}
#include "efl_net_dialer_windows.eo.c"