/***************************************************************************//**
  @file ltrsrv_wait.c
  @author Borisov Alexey <borisov@lcard.ru>
  @date   1.02.2011

  Файл содержит функции управления очередью сокетов, на которых нужно ожидать
  некоторые события. Работа со всемы сокетами выполняется в одном потоке с
  помощью одной функции select().
  Сокеты, по которым нужно ждать события, добавляются в список ожидания с помощью
  функции ltrsrv_wait_add_sock(), которая регистрирует callback, вызываемый при
  возникновении заданных событий на сокете.
  После выполнения действий, главный цикл LTR Server'а вызывает функцию
  ltrsrv_wait_event(), которая ожидает событий по всем добаленным сокетам и
  для всех сокетов, для которых дождались события, вызывает callback
  *****************************************************************************/
#include "ltrsrv_wait.h"
#include "ltrsrv_defs.h"
#include "ltrsrv_log.h"
#include "ltimer.h"
#include <string.h>
#ifndef _WIN32
    #include <errno.h>
#endif


typedef struct {
    void *handle;
    t_socket sock;
    int flags; /* флаги из t_ltrsrv_wait_flags */
    t_ltimer tmr;
    t_ltrsrv_wait_cb cb;
    void *data;
    int event; /* флаги из t_ltrsrv_wait_event, определяющие, какое событие произошло */
#ifdef _WIN32
    HANDLE hEvent;
    HANDLE hWaitHandle;
#endif
} t_ltrsrv_wait;


static int f_wt_sock_cnt = 0;
static t_ltrsrv_wait f_wt_socks[LTRSRV_WAIT_SOCK_MAX];


#ifdef _WIN32
static HANDLE f_wait_event;

static VOID CALLBACK f_wait_event_cb(PVOID lpParameter, BOOLEAN TimerOrWaitFired) {    
    SetEvent(f_wait_event);
}
#endif



/* Удаляем сокеты, помеченые флагом LTRSRV_WAITFLG_DELETE */
static void f_delete_socks(void) {
    int i;
    for (i=f_wt_sock_cnt-1; i >= 0; i--) {
        if (f_wt_socks[i].flags & LTRSRV_WAITFLG_DELETE) {
#ifdef _WIN32
            if (f_wt_socks[i].hWaitHandle!=INVALID_HANDLE_VALUE) {
                UnregisterWait(f_wt_socks[i].hWaitHandle);
                f_wt_socks[i].hWaitHandle = INVALID_HANDLE_VALUE;
            }

            if (!(f_wt_socks[i].flags & LTRSRV_WAITFLG_RAW_EVENT) &&
                    (f_wt_socks[i].hEvent != INVALID_HANDLE_VALUE)) {
                WSACloseEvent(f_wt_socks[i].hEvent);
            }

#endif

            if (i!=(f_wt_sock_cnt-1)) {
                memmove(&f_wt_socks[i], &f_wt_socks[i+1],
                        (f_wt_sock_cnt-i-1)*sizeof(f_wt_socks[i]));
            }
            f_wt_sock_cnt--;
        }
    }
}

int ltrsrv_wait_add_tmr(const void *hnd, int flags, unsigned int tout,
                        t_ltrsrv_wait_cb cb, void *data) {
    int i, fnd;
    int err = 0;
    t_lclock_ticks tmr_clocks = LTIMER_MS_TO_CLOCK_TICKS(tout);
    /* проверяем - не зарегисрирован ли уже этот сокет */
    for (i = 0, fnd = 0; (i < f_wt_sock_cnt) && !fnd; ++i) {
        if ((f_wt_socks[i].handle == hnd) &&
                (f_wt_socks[i].flags & LTRSRV_WAITFLG_TIMER_HANDLE) &&
                !(f_wt_socks[i].flags & LTRSRV_WAITFLG_DELETE))
            fnd = 1;
    }

    /* если не нашли - добавляем новый сокет в конец */
    if (!fnd) {
        if (f_wt_sock_cnt == LTRSRV_WAIT_SOCK_MAX) {
            err = LTRSRV_ERR_NO_FREE_SOCK;
            ltrsrv_log_rec(LTRSRV_LOGLVL_ERR_FATAL, LTRSRV_LOGSRC_SERVER, err,
                           "No free resources in wait queue!!");
        } else {
            i = f_wt_sock_cnt++;
            /* для нового сокета сбрасываем события, для старого - оставляем,
               т.к. мб мы еще не вызвали на них callback */
            f_wt_socks[i].event = 0;
        }
    } else {
        i--;
    }



    if (!err && !(fnd && (flags & LTRSRV_WAITFLG_TIMER_ONLY_EARLIER) &&
                  (tmr_clocks > ltimer_expiration(&f_wt_socks[i].tmr)))) {
        f_wt_socks[i].sock = L_INVALID_SOCKET;
#ifdef _WIN32
        f_wt_socks[i].hEvent = INVALID_HANDLE_VALUE;
        f_wt_socks[i].hWaitHandle = INVALID_HANDLE_VALUE;
#endif
        f_wt_socks[i].handle = (void*)hnd;
        f_wt_socks[i].flags = (flags & LTRSRV_WAITFLG_AUTODEL) |
                LTRSRV_WAITFLG_TOUT | LTRSRV_WAITFLG_TIMER_HANDLE;
        f_wt_socks[i].cb = cb;
        f_wt_socks[i].data = data;        
        ltimer_set(&f_wt_socks[i].tmr, tmr_clocks);
    }

    return err;
}

#ifdef _WIN32
int ltrsrv_wait_add_evt(HANDLE event, int flags, unsigned int tout,
                        t_ltrsrv_wait_cb cb, void *data) {
    int i, fnd;
    int err = 0;
    /* проверяем - не зарегисрирован ли уже этот сокет */
    for (i=0, fnd=0; (i < f_wt_sock_cnt) && !fnd; i++) {
        if (!(f_wt_socks[i].flags & LTRSRV_WAITFLG_DELETE) &&
                (f_wt_socks[i].flags & LTRSRV_WAITFLG_RAW_EVENT) &&
                (f_wt_socks[i].hEvent == event))
            fnd = 1;
    }

    /* если не нашли - добавляем новый сокет в конец */
    if (!fnd) {
        if (f_wt_sock_cnt == LTRSRV_WAIT_SOCK_MAX) {
            err = LTRSRV_ERR_NO_FREE_SOCK;
            ltrsrv_log_rec(LTRSRV_LOGLVL_ERR_FATAL, LTRSRV_LOGSRC_SERVER, err,
                           "No free resources in wait queue!!");
        } else {
            i = f_wt_sock_cnt++;
            /* для нового сокета сбрасываем события, для старого - оставляем,
               т.к. мб мы еще не вызвали на них callback */
            f_wt_socks[i].event = 0;
            f_wt_socks[i].hEvent = event;
            f_wt_socks[i].hWaitHandle = INVALID_HANDLE_VALUE;
            f_wt_socks[i].sock = (t_socket)event;

        }
    } else {
        i--;
    }

    if (!err) {
        f_wt_socks[i].flags = flags | LTRSRV_WAITFLG_RAW_EVENT;
        f_wt_socks[i].data = data;
        f_wt_socks[i].cb = cb;
        if (flags & LTRSRV_WAITFLG_TOUT)
            ltimer_set(&f_wt_socks[i].tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
    }


    return err;
}

#endif



int ltrsrv_wait_add_sock(t_socket sock, int flags, unsigned int tout,
                         t_ltrsrv_wait_cb cb, void* data) {
    int i, fnd;
    int err = 0;

    /* проверяем - не зарегисрирован ли уже этот сокет */
    for (i=0, fnd=0; (i < f_wt_sock_cnt) && !fnd; i++) {
        if ((f_wt_socks[i].sock == sock) &&
                !(f_wt_socks[i].flags & (LTRSRV_WAITFLG_TIMER_HANDLE
                                         | LTRSRV_WAITFLG_RAW_EVENT
                                         | LTRSRV_WAITFLG_DELETE)))
            fnd = 1;            
    }

    /* если не нашли - добавляем новый сокет в конец */
    if (!fnd) {
        if (f_wt_sock_cnt == LTRSRV_WAIT_SOCK_MAX) {
            err = LTRSRV_ERR_NO_FREE_SOCK;
            ltrsrv_log_rec(LTRSRV_LOGLVL_ERR_FATAL, LTRSRV_LOGSRC_SERVER, err,
                           "No free resources in wait queue!!");
        } else {
            i = f_wt_sock_cnt;
            /* для нового сокета сбрасываем события, для старого - оставляем,
               т.к. мб мы еще не вызвали на них callback */
            f_wt_socks[i].event = 0;
#ifdef _WIN32
            f_wt_socks[i].hWaitHandle = INVALID_HANDLE_VALUE;
            f_wt_socks[i].hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
            if (f_wt_socks[i].hEvent==INVALID_HANDLE_VALUE)
                err = LTRSRV_ERR_CREATE_EVENT;
#endif
            if (!err)
                f_wt_sock_cnt++;

        }
    } else {
        i--;
    }

    if (!err) {
        f_wt_socks[i].handle = 0;
        f_wt_socks[i].sock = sock;
        f_wt_socks[i].flags = flags;
        f_wt_socks[i].cb = cb;
        f_wt_socks[i].data = data;
#ifdef _WIN32
        if (WSAEventSelect(f_wt_socks[i].sock, f_wt_socks[i].hEvent,
                           (flags & LTRSRV_WAITFLG_SEND ? FD_WRITE : 0) |
                           (flags & LTRSRV_WAITFLG_RECV ? FD_READ | FD_ACCEPT | FD_CLOSE : 0 )
                           ) != 0) {
            err = LTRSRV_ERR_EVENT_SELECT;
        }
#endif

        if (flags & LTRSRV_WAITFLG_TOUT)
            ltimer_set(&f_wt_socks[i].tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
    }


    return err;
}

void ltrsrv_wait_remove_sock(t_socket sock) {
    int i;
    for (i=0; (i < f_wt_sock_cnt); i++) {
        if ((f_wt_socks[i].sock == sock) &&
                !(f_wt_socks[i].flags& (LTRSRV_WAITFLG_TIMER_HANDLE
                                        | LTRSRV_WAITFLG_RAW_EVENT))) {
            f_wt_socks[i].flags |= LTRSRV_WAITFLG_DELETE;
        }
    }
}

void ltrsrv_wait_remove_tmr(const void* hnd) {
    int i;
    for (i=0; i < f_wt_sock_cnt; i++) {
        if ((f_wt_socks[i].handle == hnd) &&
                (f_wt_socks[i].flags&LTRSRV_WAITFLG_TIMER_HANDLE)) {
            f_wt_socks[i].flags |= LTRSRV_WAITFLG_DELETE;
        }
    }
}

#ifdef _WIN32
    void ltrsrv_wait_remove_evt(HANDLE event) {
        int i;
        for (i=0; i < f_wt_sock_cnt; i++) {
            if ((f_wt_socks[i].hEvent == event) &&
                    (f_wt_socks[i].flags&LTRSRV_WAITFLG_RAW_EVENT)) {
                f_wt_socks[i].flags |= LTRSRV_WAITFLG_DELETE;                
            }
        }
    }
#endif


static void f_wait_call_cb(void) {
    int i;
    /* вызываем callback-и по событиям отдельно, чтобы если внутри callback'а
       делаем операции с сокетами, они бы не повлияли на сохраненное событие */
    for (i=0; i < f_wt_sock_cnt; i++) {
        if (f_wt_socks[i].event && !(f_wt_socks[i].flags & LTRSRV_WAITFLG_DELETE)) {
            /* помечаем сокет на удаление, если стояло автоудаление после
               события. устанавливаем до callback'a, т.к. там может сокет быть
               заново зарегистрирован*/
            if (f_wt_socks[i].flags & LTRSRV_WAITFLG_AUTODEL) {
                f_wt_socks[i].flags |= LTRSRV_WAITFLG_DELETE;
            }
            f_wt_socks[i].cb(f_wt_socks[i].sock, f_wt_socks[i].event, f_wt_socks[i].data);
            f_wt_socks[i].event = 0;
        }
    }
}

#ifdef _WIN32
int ltrsrv_wait_event(uint32_t poll_tout) {
    int err = 0;
    int i;



    f_delete_socks();

    for (i=0; (i < f_wt_sock_cnt) && !err; i++) {
        /* если сокет закрыт и ждем приема - сразу должны вызывать событие */
        if ((f_wt_socks[i].flags & LTRSRV_WAITFLG_RECV) &&
             (f_wt_socks[i].flags & LTRSRV_WAITFLG_CLOSED)) {
            poll_tout = 0;
        } else if (!(f_wt_socks[i].flags & LTRSRV_WAITFLG_TIMER_HANDLE)) {

            if (f_wt_socks[i].hWaitHandle==INVALID_HANDLE_VALUE) {
                if (!RegisterWaitForSingleObject(&f_wt_socks[i].hWaitHandle, f_wt_socks[i].hEvent,
                                            f_wait_event_cb, NULL,
                                            INFINITE,
                                            (f_wt_socks[i].flags & LTRSRV_WAITFLG_RAW_EVENT)
                                                 ? WT_EXECUTEONLYONCE : 0)) {
                    err = LTRSRV_ERR_SOCKET_SELECT;
                }
            }
        } else {
            uint32_t tout = LTIMER_CLOCK_TICKS_TO_MS(ltimer_expiration(&f_wt_socks[i].tmr));
            if (tout < poll_tout)
                poll_tout = tout;
        }
    }

    if (!err) {
        if (WaitForSingleObject(f_wait_event, poll_tout)==WAIT_FAILED) {
            err = LTRSRV_ERR_SOCKET_SELECT;
        } else {
            ResetEvent(f_wait_event);
            /* при успешном завершении select, проверяем по каждому сокету, какое
               событие произошло */
            for (i=f_wt_sock_cnt-1; i >=0 ; i--) {
                int event = 0;
                if ((f_wt_socks[i].flags & LTRSRV_WAITFLG_RECV) &&
                     (f_wt_socks[i].flags & LTRSRV_WAITFLG_CLOSED)) {
                    event |= LTRSRV_EVENT_RCV_RDY;
                } else if (f_wt_socks[i].flags & LTRSRV_WAITFLG_RAW_EVENT) {

                    if (WaitForSingleObject(f_wt_socks[i].hEvent, 0) == WAIT_OBJECT_0) {
                        UnregisterWait(f_wt_socks[i].hWaitHandle);
                        f_wt_socks[i].hWaitHandle = INVALID_HANDLE_VALUE;
                        event |= LTRSRV_EVENT_RAW_EVT;
                    }
                } else if (!(f_wt_socks[i].flags & LTRSRV_WAITFLG_TIMER_HANDLE)) {
                    WSANETWORKEVENTS netevents;


                    if (WSAEnumNetworkEvents(f_wt_socks[i].sock,
                                             NULL, &netevents)==0) {
                        if ((f_wt_socks[i].flags & LTRSRV_WAITFLG_RECV)
                                && ((netevents.lNetworkEvents & (FD_ACCEPT | FD_CLOSE | FD_READ))
                                    || (f_wt_socks[i].flags & LTRSRV_WAITFLG_CLOSED))) {
                            event |= LTRSRV_EVENT_RCV_RDY;
                            /* так как в Windows FD_CLOSE приходит по фронту и
                             * может прийти с RD_READ, а закрытие определяется
                             * в обычных сокетах по приему 0 байт при готовности
                             * приема, то приходится явно сохранять
                             * флаг, чтобы при следующем ожидании вернуть
                             * сразу готовность */
                            /** @todo рассмотреть возможность сохранения флага
                             *  при удалении из очереди через AUTODELETE */
                            if (netevents.lNetworkEvents & FD_CLOSE)
                                f_wt_socks[i].flags |= LTRSRV_WAITFLG_CLOSED;
                        }

                        if ((f_wt_socks[i].flags & LTRSRV_WAITFLG_SEND)
                                && (netevents.lNetworkEvents & FD_WRITE)) {
                            event |= LTRSRV_EVENT_SEND_RDY;
                        }

                        //if (event) {
                        //    UnregisterWait(f_wt_socks[i].hWaitHandle);
                        //    f_wt_socks[i].hWaitHandle = INVALID_HANDLE_VALUE;
                        //}
                    }
                }

                /* таймаут устанавливается только если он был запрошен, истек и
                   при этом не произошло ни одного другого события по сокету */
                if (!event && (f_wt_socks[i].flags & LTRSRV_WAITFLG_TOUT) &&
                        ltimer_expired(&f_wt_socks[i].tmr)) {
                    event |= LTRSRV_EVENT_TOUT;
                    ltimer_reset(&f_wt_socks[i].tmr);
                }

                /* если произошло какое-либо событие - запоменаем событие */
                if (event)
                    f_wt_socks[i].event = event;
            }

            if (!err)
                f_wait_call_cb();
        }
    }

    return err;
}

#else
int ltrsrv_wait_event(uint32_t poll_tout) {
    t_lclock_ticks tout = LTIMER_MS_TO_CLOCK_TICKS(poll_tout);
    int tout_set = 1;
    int i, nfd=0;
    fd_set fd_r, fd_w, fd_e;
    struct timeval tv;
    int res, err = 0;

    f_delete_socks();

    /* заполняем наборы сокетов сокетами из f_wt_socks */
    FD_ZERO(&fd_r);
    FD_ZERO(&fd_w);
    FD_ZERO(&fd_e);
    for (i=0; i < f_wt_sock_cnt; i++) {
        if (!(f_wt_socks[i].flags & LTRSRV_WAITFLG_TIMER_HANDLE)) {
            if (f_wt_socks[i].flags & LTRSRV_WAITFLG_RECV) {
                FD_SET(f_wt_socks[i].sock, &fd_r);
            }
            if (f_wt_socks[i].flags & LTRSRV_WAITFLG_SEND) {
                FD_SET(f_wt_socks[i].sock, &fd_w);
            }
            /* exeption ожидаем для всех сокетов */
            FD_SET(f_wt_socks[i].sock, &fd_e);
        }

        /* если установлен таймаут - выбираем минимальный оставшийся */
        if (f_wt_socks[i].flags & LTRSRV_WAITFLG_TOUT) {
            t_lclock_ticks cur_tout = ltimer_expiration(&f_wt_socks[i].tmr);
            if (!tout_set || (tout > cur_tout)) {
                tout = cur_tout;
            }
            tout_set = 1;
        }

        f_wt_socks[i].event = 0;

#ifndef _WIN32
        /* в Berkley Socket нужно отслеживать номер старшего дескриптора */
        if ((f_wt_socks[i].sock+1) > nfd)
            nfd = f_wt_socks[i].sock+1;
#endif
    }

    if (tout_set) {
        tv.tv_sec = tout/1000;
        tv.tv_usec = (tout%1000) * 1000;
    }

    res = select(nfd, &fd_r, &fd_w, &fd_e, tout_set ? &tv : 0);

    if (res >= 0) {
        /* при успешном завершении select, проверяем по каждому сокету, какое
           событие произошло */
        for (i=0; i < f_wt_sock_cnt; i++) {
            int event = 0;

            if (!(f_wt_socks[i].flags & LTRSRV_WAITFLG_TIMER_HANDLE)) {
                if ((f_wt_socks[i].flags & LTRSRV_WAITFLG_RECV)
                        && (FD_ISSET(f_wt_socks[i].sock, &fd_r))) {
                    event |= LTRSRV_EVENT_RCV_RDY;
                }

                if ((f_wt_socks[i].flags & LTRSRV_WAITFLG_SEND) &&
                        (FD_ISSET(f_wt_socks[i].sock, &fd_w))) {
                    event |= LTRSRV_EVENT_SEND_RDY;
                }

                if (FD_ISSET(f_wt_socks[i].sock, &fd_e)) {
                    event |= LTRSRV_EVENT_EXCEPTION;
                }
            }

            /* таймаут устанавливается только если он был запрошен, истек и
               при этом не произошло ни одного другого события по сокету */
            if (!event && (f_wt_socks[i].flags & LTRSRV_WAITFLG_TOUT) &&
                    ltimer_expired(&f_wt_socks[i].tmr)) {
                event |= LTRSRV_EVENT_TOUT;
                ltimer_reset(&f_wt_socks[i].tmr);
            }

            /* если произошло какое-либо событие - запоменаем событие */
            if (event) {
                f_wt_socks[i].event = event;
            }
        }

        f_wait_call_cb();    
#ifdef _WIN32
    } else {
#else
    /* если select прерван по сигналу - то все ок, это не ошибка */
    } else if (errno!=EINTR) {
#endif    
        /* ошибка select... */
        err = LTRSRV_ERR_SOCKET_SELECT;
        ltrsrv_log_rec(LTRSRV_LOGLVL_ERR_FATAL, LTRSRV_LOGSRC_SERVER, err, "Select system error");
    }

    return err;
}

#endif


void ltrsrv_wait_close(void) {
    int i;
    f_delete_socks();

    for (i=0; i < f_wt_sock_cnt; i++) {        
        if (f_wt_socks[i].cb) {
            f_wt_socks[i].cb(f_wt_socks[i].sock, LTRSRV_EVENT_SRV_CLOSE,
                         f_wt_socks[i].data);
        }
    }

#ifdef _WIN32
    if (f_wait_event != INVALID_HANDLE_VALUE) {
        CloseHandle(f_wait_event);
        f_wait_event = INVALID_HANDLE_VALUE;
    }
#endif
}


#ifdef _WIN32
int inet_pton(int af, const char *src, void *dst) {
    struct sockaddr_storage ss;
    int sslen = sizeof(ss);
    int err = WSAStringToAddressA((char*)src, af, NULL, (LPSOCKADDR)&ss, &sslen);
    if (!err) {
        if (af==AF_INET) {
            memcpy(dst, &(((struct sockaddr_in *)&ss)->sin_addr), sizeof(struct in_addr));
#ifdef HAVE_SOCKADDR_IN6
        } else if (af==AF_INET6) {
            memcpy(dst, &(((struct sockaddr_in6 *)&ss)->sin6_addr), sizeof(struct in6_addr));
#endif
        } else {
            err = -1;
        }
    }
    return err ? -1 : 1;
}


#endif


int ltrsrv_addr_to_string(const struct sockaddr *saddr, size_t addr_len, char* str, size_t str_len) {
#ifdef _WIN32
    int err = WSAAddressToStringA((struct sockaddr *)saddr, addr_len, NULL, str, &str_len) ? LTRSRV_ERR_SOCKET_INET_ADDR : 0;
    if (!err)
        str[str_len-1]=0;
    return err;
#else
    void* addr=NULL;
    if (saddr->sa_family==AF_INET)
        addr = &(((struct sockaddr_in*)saddr)->sin_addr);
#ifdef HAVE_SOCKADDR_IN6
    else if (saddr->sa_family == AF_INET6)
        addr = &(((struct sockaddr_in6*)saddr)->sin6_addr);
#endif
    return inet_ntop(saddr->sa_family, addr, str, str_len) == NULL ? LTRSRV_ERR_SOCKET_INET_ADDR : 0;
#endif
}

/* преобразование кодов ошибок сокета (системных) в коды ошибок ltrd */
int ltrsrv_sock_conv_err(int sockerr, int def_err_code) {
    int err = def_err_code;
    if (L_SOCK_ERR_RESET(sockerr)) {
        err = LTRSRV_ERR_SOCKET_CON_RESET;
#ifdef L_SOCK_ERR_REFUSED
    } else if (L_SOCK_ERR_REFUSED(sockerr)) {
        err = LTRSRV_ERR_SOCKET_CON_REFUSED;
#endif
#ifdef L_SOCK_ERR_HOST_NOR_REACH
    } else if (L_SOCK_ERR_HOST_NOR_REACH(sockerr)) {
        err = LTRSRV_ERR_SOCKET_HOST_UNREACH;
#endif
    } else {
        ltrsrv_log_rec(LTRSRV_LOGLVL_WARN, LTRSRV_LOGSRC_SERVER, 0, "specific socket err %d", sockerr);
    }

    return err;
}

/* проверка завершилась установка соединения с ошибкой или нет (через getsockopt()) */
int ltrsrv_sock_con_done_check(t_socket sock) {
    int err = 0;
    int sockerr = 0;
    socklen_t optlen = sizeof(sockerr);
    if (L_SOCKET_ERROR == getsockopt(sock, SOL_SOCKET, SO_ERROR,
                                   (char*)&sockerr, &optlen)) {
        err = LTRSRV_ERR_SOCKET_GEN_ERR;
    } else {
        if (sockerr != 0) {
            err = ltrsrv_sock_conv_err(sockerr, LTRSRV_ERR_SOCKET_GEN_ERR);
        }
    }
    return err;
}


int ltrsrv_wait_init(void) {
    int err = 0;
#ifdef _WIN32
    f_wait_event = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (f_wait_event == INVALID_HANDLE_VALUE)
        err = LTRSRV_ERR_CREATE_EVENT;
#endif
    return err;
}

int ltrsrv_sock_recv(t_socket sock, void *data, int len, int *recvd_len) {
    int received;
    int err = 0;

    received = recv(sock, data,len, MSG_NOSIGNAL);

    if (received==L_SOCKET_ERROR) {
        if (L_SOCK_LAST_ERR_BLOCK()) {
            received = 0;
        } else {
            err = ltrsrv_sock_conv_err(LTRSRV_LAST_SYSERR, LTRSRV_ERR_SOCKET_RECV);
        }
    } else if (received == 0) {
        err = LTRSRV_ERR_SOCKET_CLOSED;
    } else {
        *recvd_len = received;
    }
    return err;
}
