a09ffd5f2f
Commit e2f5a9889a
was supposed to prevent
new scan request from pushing out the old one. However, it did not
really do that since eloop_deplete_timeout() returned 0 both for the
case where the old timeout existed (and was sooner) and if the old
timeout did not exist. It returned 1 only for the case where an old
timeout did exist and was larger than the new requested value. That case
used to result in wpa_supplicant_req_scan() rescheduling the timeout,
but hew code in eloop_deplete_timeout() did the exact same thing and as
such, did not really change anything apart from the debug log message.
Extend the eloop_deplete_timeout() (and eloop_replenish_timeout() for
that matter since it is very similar) to return three different values
based on whether the timeout existed or not and if yes, whether it was
modified. This allows wpa_supplicant_req_scan() to schedule a new
timeout only in the case there was no old timeout.
Signed-hostap: Jouni Malinen <j@w1.fi>
694 lines
15 KiB
C
694 lines
15 KiB
C
/*
|
|
* Event loop based on Windows events and WaitForMultipleObjects
|
|
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
|
*
|
|
* This software may be distributed under the terms of the BSD license.
|
|
* See README for more details.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include <winsock2.h>
|
|
|
|
#include "common.h"
|
|
#include "list.h"
|
|
#include "eloop.h"
|
|
|
|
|
|
struct eloop_sock {
|
|
int sock;
|
|
void *eloop_data;
|
|
void *user_data;
|
|
eloop_sock_handler handler;
|
|
WSAEVENT event;
|
|
};
|
|
|
|
struct eloop_event {
|
|
void *eloop_data;
|
|
void *user_data;
|
|
eloop_event_handler handler;
|
|
HANDLE event;
|
|
};
|
|
|
|
struct eloop_timeout {
|
|
struct dl_list list;
|
|
struct os_reltime time;
|
|
void *eloop_data;
|
|
void *user_data;
|
|
eloop_timeout_handler handler;
|
|
};
|
|
|
|
struct eloop_signal {
|
|
int sig;
|
|
void *user_data;
|
|
eloop_signal_handler handler;
|
|
int signaled;
|
|
};
|
|
|
|
struct eloop_data {
|
|
int max_sock;
|
|
size_t reader_count;
|
|
struct eloop_sock *readers;
|
|
|
|
size_t event_count;
|
|
struct eloop_event *events;
|
|
|
|
struct dl_list timeout;
|
|
|
|
int signal_count;
|
|
struct eloop_signal *signals;
|
|
int signaled;
|
|
int pending_terminate;
|
|
|
|
int terminate;
|
|
int reader_table_changed;
|
|
|
|
struct eloop_signal term_signal;
|
|
HANDLE term_event;
|
|
|
|
HANDLE *handles;
|
|
size_t num_handles;
|
|
};
|
|
|
|
static struct eloop_data eloop;
|
|
|
|
|
|
int eloop_init(void)
|
|
{
|
|
os_memset(&eloop, 0, sizeof(eloop));
|
|
dl_list_init(&eloop.timeout);
|
|
eloop.num_handles = 1;
|
|
eloop.handles = os_malloc(eloop.num_handles *
|
|
sizeof(eloop.handles[0]));
|
|
if (eloop.handles == NULL)
|
|
return -1;
|
|
|
|
eloop.term_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (eloop.term_event == NULL) {
|
|
printf("CreateEvent() failed: %d\n",
|
|
(int) GetLastError());
|
|
os_free(eloop.handles);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int eloop_prepare_handles(void)
|
|
{
|
|
HANDLE *n;
|
|
|
|
if (eloop.num_handles > eloop.reader_count + eloop.event_count + 8)
|
|
return 0;
|
|
n = os_realloc_array(eloop.handles, eloop.num_handles * 2,
|
|
sizeof(eloop.handles[0]));
|
|
if (n == NULL)
|
|
return -1;
|
|
eloop.handles = n;
|
|
eloop.num_handles *= 2;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int eloop_register_read_sock(int sock, eloop_sock_handler handler,
|
|
void *eloop_data, void *user_data)
|
|
{
|
|
WSAEVENT event;
|
|
struct eloop_sock *tmp;
|
|
|
|
if (eloop_prepare_handles())
|
|
return -1;
|
|
|
|
event = WSACreateEvent();
|
|
if (event == WSA_INVALID_EVENT) {
|
|
printf("WSACreateEvent() failed: %d\n", WSAGetLastError());
|
|
return -1;
|
|
}
|
|
|
|
if (WSAEventSelect(sock, event, FD_READ)) {
|
|
printf("WSAEventSelect() failed: %d\n", WSAGetLastError());
|
|
WSACloseEvent(event);
|
|
return -1;
|
|
}
|
|
tmp = os_realloc_array(eloop.readers, eloop.reader_count + 1,
|
|
sizeof(struct eloop_sock));
|
|
if (tmp == NULL) {
|
|
WSAEventSelect(sock, event, 0);
|
|
WSACloseEvent(event);
|
|
return -1;
|
|
}
|
|
|
|
tmp[eloop.reader_count].sock = sock;
|
|
tmp[eloop.reader_count].eloop_data = eloop_data;
|
|
tmp[eloop.reader_count].user_data = user_data;
|
|
tmp[eloop.reader_count].handler = handler;
|
|
tmp[eloop.reader_count].event = event;
|
|
eloop.reader_count++;
|
|
eloop.readers = tmp;
|
|
if (sock > eloop.max_sock)
|
|
eloop.max_sock = sock;
|
|
eloop.reader_table_changed = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void eloop_unregister_read_sock(int sock)
|
|
{
|
|
size_t i;
|
|
|
|
if (eloop.readers == NULL || eloop.reader_count == 0)
|
|
return;
|
|
|
|
for (i = 0; i < eloop.reader_count; i++) {
|
|
if (eloop.readers[i].sock == sock)
|
|
break;
|
|
}
|
|
if (i == eloop.reader_count)
|
|
return;
|
|
|
|
WSAEventSelect(eloop.readers[i].sock, eloop.readers[i].event, 0);
|
|
WSACloseEvent(eloop.readers[i].event);
|
|
|
|
if (i != eloop.reader_count - 1) {
|
|
os_memmove(&eloop.readers[i], &eloop.readers[i + 1],
|
|
(eloop.reader_count - i - 1) *
|
|
sizeof(struct eloop_sock));
|
|
}
|
|
eloop.reader_count--;
|
|
eloop.reader_table_changed = 1;
|
|
}
|
|
|
|
|
|
int eloop_register_event(void *event, size_t event_size,
|
|
eloop_event_handler handler,
|
|
void *eloop_data, void *user_data)
|
|
{
|
|
struct eloop_event *tmp;
|
|
HANDLE h = event;
|
|
|
|
if (event_size != sizeof(HANDLE) || h == INVALID_HANDLE_VALUE)
|
|
return -1;
|
|
|
|
if (eloop_prepare_handles())
|
|
return -1;
|
|
|
|
tmp = os_realloc_array(eloop.events, eloop.event_count + 1,
|
|
sizeof(struct eloop_event));
|
|
if (tmp == NULL)
|
|
return -1;
|
|
|
|
tmp[eloop.event_count].eloop_data = eloop_data;
|
|
tmp[eloop.event_count].user_data = user_data;
|
|
tmp[eloop.event_count].handler = handler;
|
|
tmp[eloop.event_count].event = h;
|
|
eloop.event_count++;
|
|
eloop.events = tmp;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void eloop_unregister_event(void *event, size_t event_size)
|
|
{
|
|
size_t i;
|
|
HANDLE h = event;
|
|
|
|
if (eloop.events == NULL || eloop.event_count == 0 ||
|
|
event_size != sizeof(HANDLE))
|
|
return;
|
|
|
|
for (i = 0; i < eloop.event_count; i++) {
|
|
if (eloop.events[i].event == h)
|
|
break;
|
|
}
|
|
if (i == eloop.event_count)
|
|
return;
|
|
|
|
if (i != eloop.event_count - 1) {
|
|
os_memmove(&eloop.events[i], &eloop.events[i + 1],
|
|
(eloop.event_count - i - 1) *
|
|
sizeof(struct eloop_event));
|
|
}
|
|
eloop.event_count--;
|
|
}
|
|
|
|
|
|
int eloop_register_timeout(unsigned int secs, unsigned int usecs,
|
|
eloop_timeout_handler handler,
|
|
void *eloop_data, void *user_data)
|
|
{
|
|
struct eloop_timeout *timeout, *tmp;
|
|
os_time_t now_sec;
|
|
|
|
timeout = os_zalloc(sizeof(*timeout));
|
|
if (timeout == NULL)
|
|
return -1;
|
|
if (os_get_reltime(&timeout->time) < 0) {
|
|
os_free(timeout);
|
|
return -1;
|
|
}
|
|
now_sec = timeout->time.sec;
|
|
timeout->time.sec += secs;
|
|
if (timeout->time.sec < now_sec) {
|
|
/*
|
|
* Integer overflow - assume long enough timeout to be assumed
|
|
* to be infinite, i.e., the timeout would never happen.
|
|
*/
|
|
wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to "
|
|
"ever happen - ignore it", secs);
|
|
os_free(timeout);
|
|
return 0;
|
|
}
|
|
timeout->time.usec += usecs;
|
|
while (timeout->time.usec >= 1000000) {
|
|
timeout->time.sec++;
|
|
timeout->time.usec -= 1000000;
|
|
}
|
|
timeout->eloop_data = eloop_data;
|
|
timeout->user_data = user_data;
|
|
timeout->handler = handler;
|
|
|
|
/* Maintain timeouts in order of increasing time */
|
|
dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
|
|
if (os_reltime_before(&timeout->time, &tmp->time)) {
|
|
dl_list_add(tmp->list.prev, &timeout->list);
|
|
return 0;
|
|
}
|
|
}
|
|
dl_list_add_tail(&eloop.timeout, &timeout->list);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void eloop_remove_timeout(struct eloop_timeout *timeout)
|
|
{
|
|
dl_list_del(&timeout->list);
|
|
os_free(timeout);
|
|
}
|
|
|
|
|
|
int eloop_cancel_timeout(eloop_timeout_handler handler,
|
|
void *eloop_data, void *user_data)
|
|
{
|
|
struct eloop_timeout *timeout, *prev;
|
|
int removed = 0;
|
|
|
|
dl_list_for_each_safe(timeout, prev, &eloop.timeout,
|
|
struct eloop_timeout, list) {
|
|
if (timeout->handler == handler &&
|
|
(timeout->eloop_data == eloop_data ||
|
|
eloop_data == ELOOP_ALL_CTX) &&
|
|
(timeout->user_data == user_data ||
|
|
user_data == ELOOP_ALL_CTX)) {
|
|
eloop_remove_timeout(timeout);
|
|
removed++;
|
|
}
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
|
|
|
|
int eloop_cancel_timeout_one(eloop_timeout_handler handler,
|
|
void *eloop_data, void *user_data,
|
|
struct os_reltime *remaining)
|
|
{
|
|
struct eloop_timeout *timeout, *prev;
|
|
int removed = 0;
|
|
struct os_reltime now;
|
|
|
|
os_get_reltime(&now);
|
|
remaining->sec = remaining->usec = 0;
|
|
|
|
dl_list_for_each_safe(timeout, prev, &eloop.timeout,
|
|
struct eloop_timeout, list) {
|
|
if (timeout->handler == handler &&
|
|
(timeout->eloop_data == eloop_data) &&
|
|
(timeout->user_data == user_data)) {
|
|
removed = 1;
|
|
if (os_reltime_before(&now, &timeout->time))
|
|
os_reltime_sub(&timeout->time, &now, remaining);
|
|
eloop_remove_timeout(timeout);
|
|
break;
|
|
}
|
|
}
|
|
return removed;
|
|
}
|
|
|
|
|
|
int eloop_is_timeout_registered(eloop_timeout_handler handler,
|
|
void *eloop_data, void *user_data)
|
|
{
|
|
struct eloop_timeout *tmp;
|
|
|
|
dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
|
|
if (tmp->handler == handler &&
|
|
tmp->eloop_data == eloop_data &&
|
|
tmp->user_data == user_data)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
|
|
eloop_timeout_handler handler, void *eloop_data,
|
|
void *user_data)
|
|
{
|
|
struct os_reltime now, requested, remaining;
|
|
struct eloop_timeout *tmp;
|
|
|
|
dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
|
|
if (tmp->handler == handler &&
|
|
tmp->eloop_data == eloop_data &&
|
|
tmp->user_data == user_data) {
|
|
requested.sec = req_secs;
|
|
requested.usec = req_usecs;
|
|
os_get_reltime(&now);
|
|
os_reltime_sub(&tmp->time, &now, &remaining);
|
|
if (os_reltime_before(&requested, &remaining)) {
|
|
eloop_cancel_timeout(handler, eloop_data,
|
|
user_data);
|
|
eloop_register_timeout(requested.sec,
|
|
requested.usec,
|
|
handler, eloop_data,
|
|
user_data);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
|
|
eloop_timeout_handler handler, void *eloop_data,
|
|
void *user_data)
|
|
{
|
|
struct os_reltime now, requested, remaining;
|
|
struct eloop_timeout *tmp;
|
|
|
|
dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
|
|
if (tmp->handler == handler &&
|
|
tmp->eloop_data == eloop_data &&
|
|
tmp->user_data == user_data) {
|
|
requested.sec = req_secs;
|
|
requested.usec = req_usecs;
|
|
os_get_reltime(&now);
|
|
os_reltime_sub(&tmp->time, &now, &remaining);
|
|
if (os_reltime_before(&remaining, &requested)) {
|
|
eloop_cancel_timeout(handler, eloop_data,
|
|
user_data);
|
|
eloop_register_timeout(requested.sec,
|
|
requested.usec,
|
|
handler, eloop_data,
|
|
user_data);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* TODO: replace with suitable signal handler */
|
|
#if 0
|
|
static void eloop_handle_signal(int sig)
|
|
{
|
|
int i;
|
|
|
|
eloop.signaled++;
|
|
for (i = 0; i < eloop.signal_count; i++) {
|
|
if (eloop.signals[i].sig == sig) {
|
|
eloop.signals[i].signaled++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
static void eloop_process_pending_signals(void)
|
|
{
|
|
int i;
|
|
|
|
if (eloop.signaled == 0)
|
|
return;
|
|
eloop.signaled = 0;
|
|
|
|
if (eloop.pending_terminate) {
|
|
eloop.pending_terminate = 0;
|
|
}
|
|
|
|
for (i = 0; i < eloop.signal_count; i++) {
|
|
if (eloop.signals[i].signaled) {
|
|
eloop.signals[i].signaled = 0;
|
|
eloop.signals[i].handler(eloop.signals[i].sig,
|
|
eloop.signals[i].user_data);
|
|
}
|
|
}
|
|
|
|
if (eloop.term_signal.signaled) {
|
|
eloop.term_signal.signaled = 0;
|
|
eloop.term_signal.handler(eloop.term_signal.sig,
|
|
eloop.term_signal.user_data);
|
|
}
|
|
}
|
|
|
|
|
|
int eloop_register_signal(int sig, eloop_signal_handler handler,
|
|
void *user_data)
|
|
{
|
|
struct eloop_signal *tmp;
|
|
|
|
tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
|
|
sizeof(struct eloop_signal));
|
|
if (tmp == NULL)
|
|
return -1;
|
|
|
|
tmp[eloop.signal_count].sig = sig;
|
|
tmp[eloop.signal_count].user_data = user_data;
|
|
tmp[eloop.signal_count].handler = handler;
|
|
tmp[eloop.signal_count].signaled = 0;
|
|
eloop.signal_count++;
|
|
eloop.signals = tmp;
|
|
|
|
/* TODO: register signal handler */
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifndef _WIN32_WCE
|
|
static BOOL eloop_handle_console_ctrl(DWORD type)
|
|
{
|
|
switch (type) {
|
|
case CTRL_C_EVENT:
|
|
case CTRL_BREAK_EVENT:
|
|
eloop.signaled++;
|
|
eloop.term_signal.signaled++;
|
|
SetEvent(eloop.term_event);
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
#endif /* _WIN32_WCE */
|
|
|
|
|
|
int eloop_register_signal_terminate(eloop_signal_handler handler,
|
|
void *user_data)
|
|
{
|
|
#ifndef _WIN32_WCE
|
|
if (SetConsoleCtrlHandler((PHANDLER_ROUTINE) eloop_handle_console_ctrl,
|
|
TRUE) == 0) {
|
|
printf("SetConsoleCtrlHandler() failed: %d\n",
|
|
(int) GetLastError());
|
|
return -1;
|
|
}
|
|
#endif /* _WIN32_WCE */
|
|
|
|
eloop.term_signal.handler = handler;
|
|
eloop.term_signal.user_data = user_data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int eloop_register_signal_reconfig(eloop_signal_handler handler,
|
|
void *user_data)
|
|
{
|
|
/* TODO */
|
|
return 0;
|
|
}
|
|
|
|
|
|
void eloop_run(void)
|
|
{
|
|
struct os_reltime tv, now;
|
|
DWORD count, ret, timeout_val, err;
|
|
size_t i;
|
|
|
|
while (!eloop.terminate &&
|
|
(!dl_list_empty(&eloop.timeout) || eloop.reader_count > 0 ||
|
|
eloop.event_count > 0)) {
|
|
struct eloop_timeout *timeout;
|
|
tv.sec = tv.usec = 0;
|
|
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
|
|
list);
|
|
if (timeout) {
|
|
os_get_reltime(&now);
|
|
if (os_reltime_before(&now, &timeout->time))
|
|
os_reltime_sub(&timeout->time, &now, &tv);
|
|
}
|
|
|
|
count = 0;
|
|
for (i = 0; i < eloop.event_count; i++)
|
|
eloop.handles[count++] = eloop.events[i].event;
|
|
|
|
for (i = 0; i < eloop.reader_count; i++)
|
|
eloop.handles[count++] = eloop.readers[i].event;
|
|
|
|
if (eloop.term_event)
|
|
eloop.handles[count++] = eloop.term_event;
|
|
|
|
if (timeout)
|
|
timeout_val = tv.sec * 1000 + tv.usec / 1000;
|
|
else
|
|
timeout_val = INFINITE;
|
|
|
|
if (count > MAXIMUM_WAIT_OBJECTS) {
|
|
printf("WaitForMultipleObjects: Too many events: "
|
|
"%d > %d (ignoring extra events)\n",
|
|
(int) count, MAXIMUM_WAIT_OBJECTS);
|
|
count = MAXIMUM_WAIT_OBJECTS;
|
|
}
|
|
#ifdef _WIN32_WCE
|
|
ret = WaitForMultipleObjects(count, eloop.handles, FALSE,
|
|
timeout_val);
|
|
#else /* _WIN32_WCE */
|
|
ret = WaitForMultipleObjectsEx(count, eloop.handles, FALSE,
|
|
timeout_val, TRUE);
|
|
#endif /* _WIN32_WCE */
|
|
err = GetLastError();
|
|
|
|
eloop_process_pending_signals();
|
|
|
|
/* check if some registered timeouts have occurred */
|
|
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
|
|
list);
|
|
if (timeout) {
|
|
os_get_reltime(&now);
|
|
if (!os_reltime_before(&now, &timeout->time)) {
|
|
void *eloop_data = timeout->eloop_data;
|
|
void *user_data = timeout->user_data;
|
|
eloop_timeout_handler handler =
|
|
timeout->handler;
|
|
eloop_remove_timeout(timeout);
|
|
handler(eloop_data, user_data);
|
|
}
|
|
|
|
}
|
|
|
|
if (ret == WAIT_FAILED) {
|
|
printf("WaitForMultipleObjects(count=%d) failed: %d\n",
|
|
(int) count, (int) err);
|
|
os_sleep(1, 0);
|
|
continue;
|
|
}
|
|
|
|
#ifndef _WIN32_WCE
|
|
if (ret == WAIT_IO_COMPLETION)
|
|
continue;
|
|
#endif /* _WIN32_WCE */
|
|
|
|
if (ret == WAIT_TIMEOUT)
|
|
continue;
|
|
|
|
while (ret >= WAIT_OBJECT_0 &&
|
|
ret < WAIT_OBJECT_0 + eloop.event_count) {
|
|
eloop.events[ret].handler(
|
|
eloop.events[ret].eloop_data,
|
|
eloop.events[ret].user_data);
|
|
ret = WaitForMultipleObjects(eloop.event_count,
|
|
eloop.handles, FALSE, 0);
|
|
}
|
|
|
|
eloop.reader_table_changed = 0;
|
|
for (i = 0; i < eloop.reader_count; i++) {
|
|
WSANETWORKEVENTS events;
|
|
if (WSAEnumNetworkEvents(eloop.readers[i].sock,
|
|
eloop.readers[i].event,
|
|
&events) == 0 &&
|
|
(events.lNetworkEvents & FD_READ)) {
|
|
eloop.readers[i].handler(
|
|
eloop.readers[i].sock,
|
|
eloop.readers[i].eloop_data,
|
|
eloop.readers[i].user_data);
|
|
if (eloop.reader_table_changed)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void eloop_terminate(void)
|
|
{
|
|
eloop.terminate = 1;
|
|
SetEvent(eloop.term_event);
|
|
}
|
|
|
|
|
|
void eloop_destroy(void)
|
|
{
|
|
struct eloop_timeout *timeout, *prev;
|
|
|
|
dl_list_for_each_safe(timeout, prev, &eloop.timeout,
|
|
struct eloop_timeout, list) {
|
|
eloop_remove_timeout(timeout);
|
|
}
|
|
os_free(eloop.readers);
|
|
os_free(eloop.signals);
|
|
if (eloop.term_event)
|
|
CloseHandle(eloop.term_event);
|
|
os_free(eloop.handles);
|
|
eloop.handles = NULL;
|
|
os_free(eloop.events);
|
|
eloop.events = NULL;
|
|
}
|
|
|
|
|
|
int eloop_terminated(void)
|
|
{
|
|
return eloop.terminate;
|
|
}
|
|
|
|
|
|
void eloop_wait_for_read_sock(int sock)
|
|
{
|
|
WSAEVENT event;
|
|
|
|
event = WSACreateEvent();
|
|
if (event == WSA_INVALID_EVENT) {
|
|
printf("WSACreateEvent() failed: %d\n", WSAGetLastError());
|
|
return;
|
|
}
|
|
|
|
if (WSAEventSelect(sock, event, FD_READ)) {
|
|
printf("WSAEventSelect() failed: %d\n", WSAGetLastError());
|
|
WSACloseEvent(event);
|
|
return ;
|
|
}
|
|
|
|
WaitForSingleObject(event, INFINITE);
|
|
WSAEventSelect(sock, event, 0);
|
|
WSACloseEvent(event);
|
|
}
|