/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "securec.h"
#include "nstackx_epoll.h"
#include "nstackx_log.h"
#include "nstackx_error.h"
#include "nstackx_list.h"
#include "nstackx_socket.h"
#include "nstackx_timer.h"

#define TAG "nStackXEpoll"

#ifndef LWIP_LITEOS_A_COMPAT
#define IS_INVALID_SOCKET_DESC(fd) \
    (((fd) < LWIP_SOCKET_OFFSET) || ((fd) >= (LWIP_CONFIG_NUM_SOCKETS + LWIP_SOCKET_OFFSET)))
#define EVENT_PTR_IDX(fd) ((fd) - (LWIP_SOCKET_OFFSET))
#else
#define IS_INVALID_SOCKET_DESC(fd) ((fd) < 0)
#endif /* LWIP_LITEOS_A_COMPAT */

typedef struct {
    fd_set *readfds;
    fd_set *writefds;
    fd_set *exceptfds;
} EpollSetPtr;

typedef struct {
    struct EpollDescStr epollfd;
    int32_t maxfd;
    fd_set readfds;
    fd_set writefds;
    fd_set exceptfds;
    pthread_mutex_t mutex;
} EpollSet;

#ifdef LWIP_LITEOS_A_COMPAT
struct EpollTaskList {
    List list;
    EpollTask *taskPtr;
};

struct EpollEventPtr {
    List list;
    List taskList;
    EpollSet *epollSetPtr;
};
#else
struct EpollEventPtr {
    EpollSet *epollSetPtr;
    EpollTask *taskPtr;
};
#endif

typedef void (*EpollTraverseFunc)(EpollSet *epollSetPtr, void *param, int32_t fd);
typedef void (*EpollEventCtrlFunc)(EpollSet *epollSetPtr, uint32_t events, EpollTask *task);

static uint8_t g_epollInited = NSTACKX_FALSE;
static pthread_mutex_t g_epollEventPtrMutex;

#ifdef LWIP_LITEOS_A_COMPAT
static List g_epollEventPtrList;
#else
static struct EpollEventPtr *g_epollEventPtrArray = NULL;
#endif

static void EpollFdEventAdd(EpollSet *epollSetPtr, uint32_t events, EpollTask *task);
static void EpollFdEventDel(EpollSet *epollSetPtr, uint32_t events, EpollTask *task);
static void EpollFdEventMod(EpollSet *epollSetPtr, uint32_t events, EpollTask *task);

static int32_t CtlEpollDesc(EpollTask *task, int op, uint32_t events)
{
    struct EpollEvent event;
#ifdef NSTACKX_DEBUG
    static uint32_t evtCnt = 0;
#endif

    if ((task == NULL) || ((op != EPOLL_CTL_RUN) && IS_INVALID_SOCKET_DESC(task->taskfd)) ||
        (!IsEpollDescValid(task->epollfd))) {
        LOGE(TAG, "invalid params");
        return NSTACKX_EINVAL;
    }

    event.op = op;
    event.events = events;
    event.ptr = (void *)task;
#ifdef NSTACKX_DEBUG
    event.evtSeq = evtCnt++;
    LOGD(TAG, "%d op %d event seq: %u", task->epollfd->recvFd, op, event.evtSeq);
#endif
    if (sendto(task->epollfd->sendFd, (const void *)&event, sizeof(event), 0, NULL, 0) < 0) {
        LOGE(TAG, "ctrl epollfd failed: %d", errno);
        return NSTACKX_EFAILED;
    }

    return NSTACKX_EOK;
}

static int32_t CtlEpollDescSync(EpollTask *task, EpollEventCtrlFunc func, uint32_t events)
{
    EpollSet *epollSetPtr = NULL;
    if ((task == NULL) || IS_INVALID_SOCKET_DESC(task->taskfd) || (!IsEpollDescValid(task->epollfd))) {
        LOGE(TAG, "invalid params");
        return NSTACKX_EINVAL;
    }

    epollSetPtr = container_of(task->epollfd, EpollSet, epollfd);
    if (pthread_mutex_lock(&(epollSetPtr->mutex)) != 0) {
        LOGE(TAG, "pthread mutex lock error");
        return NSTACKX_EFAILED;
    }
    func(epollSetPtr, events, task);
    if (pthread_mutex_unlock(&(epollSetPtr->mutex)) != 0) {
        LOGE(TAG, "pthread mutex unlock error");
    }

    return NSTACKX_EOK;
}

int32_t RunEpollTask(void *task, uint32_t events)
{
    return CtlEpollDesc((EpollTask *)task, EPOLL_CTL_RUN, events);
}

int32_t RefreshEpollTask(EpollTask *task, uint32_t events)
{
    return CtlEpollDescSync(task, EpollFdEventMod, events);
}

int32_t RegisterEpollTask(EpollTask *task, uint32_t events)
{
    return CtlEpollDescSync(task, EpollFdEventAdd, events);
}

int32_t DeRegisterEpollTask(EpollTask *task)
{
    return CtlEpollDescSync(task, EpollFdEventDel, 0);
}

#ifdef LWIP_LITEOS_A_COMPAT
static EpollSet *EpollSetFindByFd(int32_t fd)
{
    List *curr = NULL;
    struct EpollEventPtr *ptr = NULL;
    LIST_FOR_EACH(curr, &g_epollEventPtrList) {
        ptr = container_of(curr, struct EpollEventPtr, list);
        if (FD_ISSET(fd, &(ptr->epollSetPtr->exceptfds))) {
            return ptr->epollSetPtr;
        }
    }
    return NULL;
}

static struct EpollEventPtr *EpollEventListFind(const EpollSet *epollSetPtr)
{
    List *curr = NULL;
    struct EpollEventPtr *ptr = NULL;
    LIST_FOR_EACH(curr, &g_epollEventPtrList) {
        ptr = container_of(curr, struct EpollEventPtr, list);
        if (ptr->epollSetPtr == epollSetPtr) {
            return ptr;
        }
    }
    return NULL;
}

static struct EpollTaskList *EpollTaskListFind(const struct EpollEventPtr *ptr, const EpollTask *task)
{
    List *curr = NULL;
    struct EpollTaskList *taskListPtr = NULL;
    LIST_FOR_EACH(curr, &(ptr->taskList)) {
        taskListPtr = container_of(curr, struct EpollTaskList, list);
        if (taskListPtr->taskPtr == task) {
            return taskListPtr;
        }
    }
    return NULL;
}

static struct EpollTaskList *EpollTaskListNew(EpollTask *task)
{
    struct EpollTaskList *taskListPtr = (struct EpollTaskList *)malloc(sizeof(struct EpollTaskList));
    if (taskListPtr == NULL) {
        LOGE(TAG, "alloc EpollTaskList failed");
        return NULL;
    }
    (void)memset_s(taskListPtr, sizeof(*taskListPtr), 0, sizeof(*taskListPtr));
    taskListPtr->taskPtr = task;
    ListInitHead(&(taskListPtr->list));
    return taskListPtr;
}

static int32_t EpollEventTaskListAdd(const EpollSet *epollSetPtr, EpollTask *task)
{
    struct EpollEventPtr *ptr = EpollEventListFind(epollSetPtr);
    struct EpollTaskList *taskListPtr = NULL;
    if (ptr == NULL) {
        LOGE(TAG, "EpollSet not in list");
        return NSTACKX_NOEXIST;
    }
    taskListPtr = EpollTaskListNew(task);
    if (taskListPtr == NULL) {
        return NSTACKX_ENOMEM;
    }
    ListInsertTail(&(ptr->taskList), &(taskListPtr->list));
    return NSTACKX_EOK;
}

static void EpollEventTaskListDel(const EpollSet *epollSetPtr, const EpollTask *task)
{
    struct EpollEventPtr *ptr = EpollEventListFind(epollSetPtr);
    struct EpollTaskList *taskListPtr = NULL;
    if (ptr == NULL) {
        LOGE(TAG, "EpollSet not in list");
        return;
    }
    taskListPtr = EpollTaskListFind(ptr, task);
    if (taskListPtr == NULL) {
        return;
    }
    ListRemoveNode(&(taskListPtr->list));
    free(taskListPtr);
    return;
}

static EpollTask *EpollTaskFindByFd(const struct EpollEventPtr *ptr, int32_t fd)
{
    List *curr = NULL;
    struct EpollTaskList *taskListPtr = NULL;
    LIST_FOR_EACH(curr, &(ptr->taskList)) {
        taskListPtr = container_of(curr, struct EpollTaskList, list);
        if (taskListPtr->taskPtr->taskfd == fd) {
            return taskListPtr->taskPtr;
        }
    }
    return NULL;
}

static EpollTask *EpollSetFindTaskByFd(const EpollSet *epollSetPtr, int32_t fd)
{
    struct EpollEventPtr *ptr = EpollEventListFind(epollSetPtr);
    if (ptr == NULL) {
        LOGE(TAG, "EpollSet not in list");
        return NULL;
    }
    return EpollTaskFindByFd(ptr, fd);
}
#endif /* LWIP_LITEOS_A_COMPAT */

static int32_t ConnectPeerFd(int32_t localFd, int32_t peerFd)
{
    struct sockaddr_in addr = {0};
    socklen_t addr_len = sizeof(addr);

    if (getsockname(peerFd, (struct sockaddr *)&addr, &addr_len) != 0) {
        LOGE(TAG, "getsockname failed: %d", errno);
        return NSTACKX_EFAILED;
    }
    if (connect(localFd, (struct sockaddr *)&addr, addr_len) != 0) {
        LOGE(TAG, "connect failed: %d", errno);
        return NSTACKX_EFAILED;
    }

    return NSTACKX_EOK;
}

static int32_t GetLoopbackFd(int32_t peerFd)
{
    int32_t fd;
    struct sockaddr_in addr = {0};
    struct sockaddr *sockaddr = NULL;
    socklen_t addr_len;

    addr.sin_family = AF_INET;
    addr.sin_port = 0;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    sockaddr = (struct sockaddr *)&addr;
    addr_len = sizeof(addr);

    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (fd < 0) {
        LOGE(TAG, "socket failed: %d", errno);
        return -1;
    }

    if (SetSocketNonBlock(fd) != NSTACKX_EOK) {
        LOGE(TAG, "set socket nonblock failed");
        close(fd);
        return -1;
    }

    if (bind(fd, sockaddr, addr_len) != 0) {
        LOGE(TAG, "bind failed: %d", errno);
        close(fd);
        return -1;
    }

    if (peerFd < 0) {
        /* if peerFd is invalid, there is no need to connect to it, just return */
        return fd;
    }

    if ((ConnectPeerFd(fd, peerFd) == NSTACKX_EFAILED) || (ConnectPeerFd(peerFd, fd) == NSTACKX_EFAILED)) {
        close(fd);
        return -1;
    }

    return fd;
}

int32_t CreateEpollFdPair(struct EpollDescStr *epollfd)
{
    if (epollfd == NULL) {
        LOGE(TAG, "invalid param");
        return NSTACKX_EINVAL;
    }

    epollfd->recvFd = GetLoopbackFd(-1);
    if (epollfd->recvFd < 0) {
        epollfd->recvFd = -1;
        epollfd->sendFd = -1;
        return NSTACKX_EFAILED;
    }

    epollfd->sendFd = GetLoopbackFd(epollfd->recvFd);
    if (epollfd->sendFd < 0) {
        close(epollfd->recvFd);
        epollfd->recvFd = -1;
        epollfd->sendFd = -1;
        return NSTACKX_EFAILED;
    }

    return NSTACKX_EOK;
}

void EpollEventPtrInit(void)
{
    if (g_epollInited == NSTACKX_TRUE) {
        return;
    }
    /* this function is called when init lwip thread */
    if (pthread_mutex_init(&g_epollEventPtrMutex, NULL) != 0) {
        LOGE(TAG, "pthread_mutex_init error");
        return;
    }
#ifdef LWIP_LITEOS_A_COMPAT
    ListInitHead(&g_epollEventPtrList);
#else
    /* as ported to LwIP, once the memory is allocated, it will not be freed ever */
    g_epollEventPtrArray = (struct EpollEventPtr *)malloc(LWIP_CONFIG_NUM_SOCKETS * sizeof(struct EpollEventPtr));
    if (g_epollEventPtrArray == NULL) {
        LOGE(TAG, "malloc epoll ptr array failed");
        return;
    }
    (void)memset_s(g_epollEventPtrArray, LWIP_CONFIG_NUM_SOCKETS * sizeof(struct EpollEventPtr), 0x0,
        LWIP_CONFIG_NUM_SOCKETS * sizeof(struct EpollEventPtr));
#endif
    g_epollInited = NSTACKX_TRUE;
    LOGD(TAG, "epoll event init success");
    return;
}

static int32_t EpollEventRecordAdd(EpollSet *epollSetPtr)
{
    if (pthread_mutex_lock(&g_epollEventPtrMutex) != 0) {
        LOGE(TAG, "pthread mutex lock error");
        return NSTACKX_EFAILED;
    }
#ifdef LWIP_LITEOS_A_COMPAT
    struct EpollEventPtr *ptr = (struct EpollEventPtr *)malloc(sizeof(struct EpollEventPtr));
    if (ptr == NULL) {
        if (pthread_mutex_unlock(&g_epollEventPtrMutex) != 0) {
            LOGE(TAG, "pthread mutex unlock error");
        }
        LOGE(TAG, "EpollEventPtr alloc failed");
        return NSTACKX_ENOMEM;
    }
    (void)memset_s(ptr, sizeof(struct EpollEventPtr), 0, sizeof(struct EpollEventPtr));
    ListInitHead(&(ptr->list));
    ListInitHead(&(ptr->taskList));
    ptr->epollSetPtr = epollSetPtr;
    ListInsertTail(&g_epollEventPtrList, &(ptr->list));
#else
    g_epollEventPtrArray[EVENT_PTR_IDX(epollSetPtr->epollfd.recvFd)].epollSetPtr = epollSetPtr;
    g_epollEventPtrArray[EVENT_PTR_IDX(epollSetPtr->epollfd.sendFd)].epollSetPtr = epollSetPtr;
#endif
    if (pthread_mutex_unlock(&g_epollEventPtrMutex) != 0) {
        /* just give log, no error returned */
        LOGE(TAG, "pthread mutex unlock error");
    }
    return NSTACKX_EOK;
}

EpollDesc CreateEpollDesc(void)
{
    EpollSet *epollSetPtr = NULL;

    if (g_epollInited != NSTACKX_TRUE) {
        LOGE(TAG, "Epoll Event Ptr Not Init");
        return NULL;
    }

    epollSetPtr = (EpollSet *)malloc(sizeof(EpollSet));
    if (epollSetPtr == NULL) {
        LOGE(TAG, "malloc EpollSet failed");
        return NULL;
    }
    (void)memset_s(epollSetPtr, sizeof(EpollSet), 0, sizeof(EpollSet));

    if (pthread_mutex_init(&(epollSetPtr->mutex), NULL) != 0) {
        LOGE(TAG, "pthread_mutex_init error");
        goto FAIL_FREE;
    }

    if (CreateEpollFdPair(&(epollSetPtr->epollfd)) != NSTACKX_EOK) {
        LOGE(TAG, "Create Epoll failed");
        goto FAIL_MUTEX;
    }

    FD_SET(epollSetPtr->epollfd.recvFd, &epollSetPtr->readfds);
    FD_SET(epollSetPtr->epollfd.recvFd, &epollSetPtr->exceptfds);
    epollSetPtr->maxfd = epollSetPtr->epollfd.recvFd;

    if (EpollEventRecordAdd(epollSetPtr) != NSTACKX_EOK) {
        goto FAIL_CLOSE;
    }

    return &(epollSetPtr->epollfd);
FAIL_CLOSE:
    close(epollSetPtr->epollfd.recvFd);
    close(epollSetPtr->epollfd.sendFd);
FAIL_MUTEX:
    if (pthread_mutex_destroy(&(epollSetPtr->mutex)) != 0) {
        LOGE(TAG, "pthread mutex destroy error: %d", errno);
    }
FAIL_FREE:
    free(epollSetPtr);
    return NULL;
}

static int32_t RearZeroBitNum(unsigned long x)
{
    int32_t n = 1;
    int bitMov = TYPE_BITS_NUM(x) >> 1;
    int bitNum = bitMov;

    /* through binarySearch */
    while (bitNum > 1) {
        if ((x << bitMov) == 0) {
            n = n + bitNum;
            x = x >> bitNum;
        }
        bitNum = bitNum >> 1;
        bitMov += bitNum;
    }

    n = n - (x & 1);
    return n;
}

static int32_t PreZeroBitNum(unsigned long x)
{
    int n = 1;
    int bitMov = TYPE_BITS_NUM(x) >> 1;
    int bitNum = bitMov;

    /* through binarySearch */
    while (bitNum > 1) {
        if ((x >> bitMov) == 0) {
            n = n + bitNum;
            x = x << bitNum;
        }
        bitNum = bitNum >> 1;
        bitMov += bitNum;
    }

    n = n - (x >> (TYPE_BITS_NUM(x) - 1));
    return n;
}

static void EpollTaskEventHandle(uint32_t events, EpollTask *task)
{
    if ((events & EPOLLERR) == EPOLLERR) {
        if (task->errorHandle != NULL) {
            task->errorHandle(task);
        }
        return;
    }

    if (((events & EPOLLIN) == EPOLLIN) && (task->readHandle != NULL)) {
        task->readHandle(task);
    }

    /* Caution: It is possible for xxxHandle to free the Timer struct part of which the `task` is pointing to.
     * See ClientSettingTimeoutHandle() and ServerSettingTimeoutHandle() functions for an example. Coders must
     * logically assert that the `task` pointer will never be dereferenced if its content is previously freed.
     */
    if (((events & EPOLLOUT) == EPOLLOUT) && (task->writeHandle != NULL)) {
        task->writeHandle(task);
    }
}

static int32_t EpollFdEventTaskAdd(EpollSet *epollSetPtr, EpollTask *task)
{
    int32_t ret = NSTACKX_EOK;
    if (pthread_mutex_lock(&g_epollEventPtrMutex) != 0) {
        LOGE(TAG, "pthread mutex lock error");
        return NSTACKX_EFAILED;
    }
#ifdef LWIP_LITEOS_A_COMPAT
    if (EpollEventTaskListAdd(epollSetPtr, task) != NSTACKX_EOK) {
        ret = NSTACKX_EFAILED;
    }
#else
    g_epollEventPtrArray[EVENT_PTR_IDX(task->taskfd)].epollSetPtr = epollSetPtr;
    g_epollEventPtrArray[EVENT_PTR_IDX(task->taskfd)].taskPtr = task;
#endif
    if (pthread_mutex_unlock(&g_epollEventPtrMutex) != 0) {
        LOGE(TAG, "pthread mutex unlock error");
    }
    return ret;
}

static void EpollFdEventAdd(EpollSet *epollSetPtr, uint32_t events, EpollTask *task)
{
#ifndef LWIP_LITEOS_A_COMPAT
    EpollSet *setPtr = g_epollEventPtrArray[EVENT_PTR_IDX(task->taskfd)].epollSetPtr;
    if ((setPtr != NULL) && (setPtr != epollSetPtr)) {
        LOGE(TAG, "ADD: fd %d ptr has been used", task->taskfd);
        return;
    }
#endif
    if (FD_ISSET(task->taskfd, &epollSetPtr->exceptfds)) {
        LOGD(TAG, "ADD: fd %d has in epoll ctl", task->taskfd);
        return;
    }
    if ((events & (EPOLLIN | EPOLLOUT)) == 0) {
        LOGI(TAG, "invalid events");
        return;
    }
    if ((events & EPOLLIN) == EPOLLIN) {
        FD_SET(task->taskfd, &epollSetPtr->readfds);
    }
    if ((events & EPOLLOUT) == EPOLLOUT) {
        FD_SET(task->taskfd, &epollSetPtr->writefds);
    }
    FD_SET(task->taskfd, &epollSetPtr->exceptfds);

    if (EpollFdEventTaskAdd(epollSetPtr, task) != NSTACKX_EOK) {
        FD_CLR(task->taskfd, &epollSetPtr->readfds);
        FD_CLR(task->taskfd, &epollSetPtr->writefds);
        FD_CLR(task->taskfd, &epollSetPtr->exceptfds);
        return;
    }
    if (task->taskfd > epollSetPtr->maxfd) {
        epollSetPtr->maxfd = task->taskfd;
    }
}

static void EpollSetMaxFdUpdate(EpollSet *epollSetPtr)
{
    int32_t i, fd;

#ifdef __LITEOS__
#ifdef FDSETSAFESET
    for (i = (epollSetPtr->maxfd - LWIP_SOCKET_OFFSET) / BYTE_BITS_NUM; i >= 0; i--) {
        uint8_t bits = epollSetPtr->exceptfds.fd_bits[i];
#else
    for (i = epollSetPtr->maxfd / NFDBITS; i >= LWIP_SOCKET_OFFSET / NFDBITS; i--) {
        unsigned long bits = epollSetPtr->exceptfds.fds_bits[i];
#endif
#else
    for (i = epollSetPtr->maxfd / __NFDBITS; i >= LWIP_SOCKET_OFFSET / __NFDBITS; i--) {
        unsigned long bits = (unsigned long)(__FDS_BITS(&(epollSetPtr->exceptfds))[i]);
#endif
        if (bits == 0) {
            continue;
        }
        int32_t bitIdx = PreZeroBitNum(bits);
#ifdef __LITEOS__
#ifdef FDSETSAFESET
        fd = i * BYTE_BITS_NUM + (TYPE_BITS_NUM(unsigned long) - 1 - bitIdx) + LWIP_SOCKET_OFFSET;
#else
        fd = i * NFDBITS + (TYPE_BITS_NUM(unsigned long) - 1 - bitIdx);
#endif
#else
        fd = i * __NFDBITS + (TYPE_BITS_NUM(unsigned long) - 1 - bitIdx);
#endif
        epollSetPtr->maxfd = fd;
        break;
    }

    return;
}

static void EpollFdEventTaskDel(const EpollSet *epollSetPtr, const EpollTask *task)
{
    if (pthread_mutex_lock(&g_epollEventPtrMutex) != 0) {
        LOGE(TAG, "pthread mutex lock error");
        return;
    }
#ifdef LWIP_LITEOS_A_COMPAT
    EpollEventTaskListDel(epollSetPtr, task);
#else
    g_epollEventPtrArray[EVENT_PTR_IDX(task->taskfd)].epollSetPtr = NULL;
    g_epollEventPtrArray[EVENT_PTR_IDX(task->taskfd)].taskPtr = NULL;
#endif
    if (pthread_mutex_unlock(&g_epollEventPtrMutex) != 0) {
        LOGE(TAG, "pthread mutex unlock error");
    }
}

static void EpollFdEventDel(EpollSet *epollSetPtr, uint32_t events, EpollTask *task)
{
#ifndef LWIP_LITEOS_A_COMPAT
    EpollSet *setPtr = g_epollEventPtrArray[EVENT_PTR_IDX(task->taskfd)].epollSetPtr;
    if ((setPtr != NULL) && (setPtr != epollSetPtr)) {
        LOGE(TAG, "DEL: fd %d ptr has been used", task->taskfd);
        return;
    }
#endif
    (void)events;
    if (!FD_ISSET(task->taskfd, &epollSetPtr->exceptfds)) {
        LOGD(TAG, "DEL: fd %d not in epoll ctl", task->taskfd);
        return;
    }
    FD_CLR(task->taskfd, &epollSetPtr->readfds);
    FD_CLR(task->taskfd, &epollSetPtr->writefds);
    FD_CLR(task->taskfd, &epollSetPtr->exceptfds);

    EpollFdEventTaskDel(epollSetPtr, task);

    if (task->taskfd == epollSetPtr->maxfd) {
        EpollSetMaxFdUpdate(epollSetPtr);
    }

    return;
}

static void EpollFdEventMod(EpollSet *epollSetPtr, uint32_t events, EpollTask *task)
{
#ifndef LWIP_LITEOS_A_COMPAT
    EpollSet *setPtr = g_epollEventPtrArray[EVENT_PTR_IDX(task->taskfd)].epollSetPtr;
    if ((setPtr != NULL) && (setPtr != epollSetPtr)) {
        LOGE(TAG, "MOD: fd %d ptr has been used", task->taskfd);
        return;
    }
#endif
    if (!FD_ISSET(task->taskfd, &epollSetPtr->exceptfds)) {
        LOGD(TAG, "MOD: fd %d not in epoll ctl", task->taskfd);
        return;
    }
    if ((events & (EPOLLIN | EPOLLOUT)) == 0) {
        LOGI(TAG, "invalid events");
        return;
    }
    FD_CLR(task->taskfd, &epollSetPtr->readfds);
    FD_CLR(task->taskfd, &epollSetPtr->writefds);
    if ((events & EPOLLIN) == EPOLLIN) {
        FD_SET(task->taskfd, &epollSetPtr->readfds);
    }
    if ((events & EPOLLOUT) == EPOLLOUT) {
        FD_SET(task->taskfd, &epollSetPtr->writefds);
    }

    return;
}

static void EpollFdEventRun(EpollSet *epollSetPtr, uint32_t events, EpollTask *task)
{
    (void)epollSetPtr;
    EpollTaskEventHandle(events, task);
}

static void EpollFdEventOpHandle(EpollSet *epollSetPtr, int op, uint32_t events, EpollTask *task)
{
    switch (op) {
        case EPOLL_CTL_ADD:
        case EPOLL_CTL_DEL:
        case EPOLL_CTL_MOD:
            break;
        case EPOLL_CTL_RUN:
            EpollFdEventRun(epollSetPtr, events, task);
            break;
        default:
            LOGI(TAG, "unsupported op %u", op);
            break;
    }
}

static void EpollFdEventHandle(EpollSet *epollSetPtr, fd_set *readfds)
{
    int32_t ret;
    EpollTask *task = NULL;
    struct EpollEvent event;
    if (!FD_ISSET(epollSetPtr->epollfd.recvFd, readfds)) {
        return;
    }

    while (NSTACKX_TRUE) {
        ret = recvfrom(epollSetPtr->epollfd.recvFd, (void *)&event, sizeof(event), 0, NULL, NULL);
        if (ret < (int32_t)(sizeof(event))) {
            break;
        }
#ifdef NSTACKX_DEBUG
        LOGD(TAG, "Handle task %d event seq: %u", epollSetPtr->epollfd.recvFd, event.evtSeq);
#endif
        task = (EpollTask *)event.ptr;
        if (task == NULL) {
            continue;
        }
        EpollFdEventOpHandle(epollSetPtr, event.op, event.events, task);
    }
}

static void EpollEventHandle(EpollSet *epollSetPtr, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, int32_t fd)
{
    uint32_t events = 0;
    EpollTask *task = NULL;

#ifdef LWIP_LITEOS_A_COMPAT
    task = EpollSetFindTaskByFd(epollSetPtr, fd);
#else
    EpollSet *setPtr = g_epollEventPtrArray[EVENT_PTR_IDX(fd)].epollSetPtr;
    if (setPtr != epollSetPtr) {
        return;
    }
    task = g_epollEventPtrArray[EVENT_PTR_IDX(fd)].taskPtr;
#endif
    if (task == NULL) {
        return;
    }

    if (FD_ISSET(fd, readfds)) {
        events |= EPOLLIN;
    }
    if (FD_ISSET(fd, writefds)) {
        events |= EPOLLOUT;
    }
    if (FD_ISSET(fd, exceptfds)) {
        events |= EPOLLERR;
    }
    if (events == 0) {
        return;
    }

    EpollTaskEventHandle(events, task);
}

static void EpollSetFdHandle(EpollSet *epollSetPtr, void *param, int32_t fd)
{
    EpollSetPtr *setPtr = (EpollSetPtr *)param;
    fd_set *readfds = setPtr->readfds;
    fd_set *writefds = setPtr->writefds;
    fd_set *exceptfds = setPtr->exceptfds;
    if (fd == epollSetPtr->epollfd.recvFd) {
        EpollFdEventHandle(epollSetPtr, readfds);
#ifdef LWIP_LITEOS_A_COMPAT
    } else {
#else
    } else if (g_epollEventPtrArray[EVENT_PTR_IDX(fd)].epollSetPtr == epollSetPtr) {
#endif
        EpollEventHandle(epollSetPtr, readfds, writefds, exceptfds, fd);
    }
}

static void EpollSetTraverse(EpollSet *epollSetPtr, EpollTraverseFunc func, void *param)
{
#ifdef __LITEOS__
#ifdef FDSETSAFESET
    for (int32_t i = 0; i <= (epollSetPtr->maxfd - LWIP_SOCKET_OFFSET) / BYTE_BITS_NUM; i++) {
        uint8_t bits = epollSetPtr->exceptfds.fd_bits[i];
#else
    for (int32_t i = LWIP_SOCKET_OFFSET / NFDBITS; i <= epollSetPtr->maxfd / NFDBITS; i++) {
        unsigned long bits = epollSetPtr->exceptfds.fds_bits[i];
#endif
#else
    for (int32_t i = LWIP_SOCKET_OFFSET / __NFDBITS; i <= epollSetPtr->maxfd / __NFDBITS; i++) {
        unsigned long bits = (unsigned long)(__FDS_BITS(&(epollSetPtr->exceptfds))[i]);
#endif
        while (bits != 0) {
            int32_t fd;
            int32_t bitIdx = RearZeroBitNum(bits);
#ifdef __LITEOS__
#ifdef FDSETSAFESET
            fd = i * BYTE_BITS_NUM + bitIdx + LWIP_SOCKET_OFFSET;
#else
            fd = i * NFDBITS + bitIdx;
#endif
#else
            fd = i * __NFDBITS + bitIdx;
#endif
            func(epollSetPtr, param, fd);
            bits &= (bits - 1);
        }
    }
}

int32_t EpollLoop(EpollDesc epollfd, int32_t timeout)
{
    EpollSet *epollSetPtr = NULL;
    fd_set readfds, writefds, exceptfds;
    EpollSetPtr param = {&readfds, &writefds, &exceptfds};
    int32_t ret;
    struct timeval tv;
    struct timeval *tvp = NULL;

    if (!IsEpollDescValid(epollfd)) {
        return NSTACKX_EFAILED;
    }

    if (timeout != -1) {
        tv.tv_sec = timeout / NSTACKX_MILLI_TICKS;
        tv.tv_usec = (timeout % NSTACKX_MILLI_TICKS) * NSTACKX_MICRO_SEC_PER_MILLI_SEC;
        tvp = &tv;
    }

    epollSetPtr = container_of(epollfd, EpollSet, epollfd);
    if (memcpy_s(&readfds, sizeof(fd_set), &epollSetPtr->readfds, sizeof(fd_set)) != EOK ||
        memcpy_s(&writefds, sizeof(fd_set), &epollSetPtr->writefds, sizeof(fd_set)) != EOK ||
        memcpy_s(&exceptfds, sizeof(fd_set), &epollSetPtr->exceptfds, sizeof(fd_set)) != EOK) {
        return NSTACKX_EFAILED;
    }

    ret = select(epollSetPtr->maxfd + 1, &readfds, &writefds, &exceptfds, tvp);
    if (ret < 0) {
        if (errno == EINTR) {
            return NSTACKX_EINTR;
        }
        LOGE(TAG, "epoll %d select error", epollfd->recvFd);
        return NSTACKX_EFAILED;
    } else if (ret == 0) {
        return NSTACKX_ETIMEOUT;
    }

    EpollSetTraverse(epollSetPtr, EpollSetFdHandle, &param);

    return ret;
}

#ifndef LWIP_LITEOS_A_COMPAT
static void EpollSetClearHandle(EpollSet *epollSetPtr, void *param, int32_t fd)
{
    EpollSet *setPtr = g_epollEventPtrArray[EVENT_PTR_IDX(fd)].epollSetPtr;
    (void)param;
    if ((setPtr != NULL) && (setPtr != epollSetPtr)) {
        LOGE(TAG, "Clear: fd %d ptr has been used", fd);
        return;
    }
    if (pthread_mutex_lock(&g_epollEventPtrMutex) != 0) {
        LOGE(TAG, "pthread mutex lock error");
        return;
    }
    g_epollEventPtrArray[EVENT_PTR_IDX(fd)].epollSetPtr = NULL;
    g_epollEventPtrArray[EVENT_PTR_IDX(fd)].taskPtr = NULL;
    if (pthread_mutex_unlock(&g_epollEventPtrMutex) != 0) {
        LOGE(TAG, "pthread mutex unlock error");
        return;
    }
}

static void EpollEventCleanup(const EpollSet *epollSetPtr)
{
    g_epollEventPtrArray[EVENT_PTR_IDX(epollSetPtr->epollfd.recvFd)].epollSetPtr = NULL;
    g_epollEventPtrArray[EVENT_PTR_IDX(epollSetPtr->epollfd.recvFd)].taskPtr = NULL;
    g_epollEventPtrArray[EVENT_PTR_IDX(epollSetPtr->epollfd.sendFd)].epollSetPtr = NULL;
    g_epollEventPtrArray[EVENT_PTR_IDX(epollSetPtr->epollfd.sendFd)].taskPtr = NULL;
}
#else
static void EpollEventCleanup(const EpollSet *epollSetPtr)
{
    List *pos = NULL;
    List *tmp = NULL;
    struct EpollTaskList *taskListPtr = NULL;
    struct EpollEventPtr *ptr = EpollEventListFind(epollSetPtr);
    if (ptr == NULL) {
        return;
    }
    ListRemoveNode(&(ptr->list));
    LIST_FOR_EACH_SAFE(pos, tmp, &(ptr->taskList)) {
        taskListPtr = container_of(pos, struct EpollTaskList, list);
        ListRemoveNode(&taskListPtr->list);
        free(taskListPtr);
    }
    free(ptr);
}
#endif /* LWIP_LITEOS_A_COMPAT */

void CloseEpollDescInner(EpollDesc epollfd)
{
    EpollSet *epollSetPtr = NULL;
    if (!(IsEpollDescValid(epollfd))) {
        return;
    }
    epollSetPtr = container_of(epollfd, EpollSet, epollfd);
#ifndef LWIP_LITEOS_A_COMPAT
    EpollSetTraverse(epollSetPtr, EpollSetClearHandle, NULL);
#endif /* LWIP_LITEOS_A_COMPAT */
    if (pthread_mutex_lock(&g_epollEventPtrMutex) != 0) {
        LOGE(TAG, "pthread mutex lock error");
        goto FAIL_CLOSE;
    }
    EpollEventCleanup(epollSetPtr);
    if (pthread_mutex_unlock(&g_epollEventPtrMutex) != 0) {
        LOGE(TAG, "pthread mutex unlock error");
    }
FAIL_CLOSE:
    close(epollfd->recvFd);
    close(epollfd->sendFd);
    if (pthread_mutex_destroy(&(epollSetPtr->mutex)) != 0) {
        LOGE(TAG, "pthread mutex destroy error: %d", errno);
    }
    free(epollSetPtr);

    return;
}

#ifdef LWIP_LITEOS_A_COMPAT
static EpollSet *CloseDescEpollHandle(int32_t desc)
{
    return EpollSetFindByFd(desc);
}
#else
static EpollSet *CloseDescEpollHandle(int32_t desc)
{
    EpollSet *epollSetPtr = g_epollEventPtrArray[EVENT_PTR_IDX(desc)].epollSetPtr;
    g_epollEventPtrArray[EVENT_PTR_IDX(desc)].epollSetPtr = NULL;
    g_epollEventPtrArray[EVENT_PTR_IDX(desc)].taskPtr = NULL;
    return epollSetPtr;
}
#endif

void CloseDescClearEpollPtr(int32_t desc)
{
    EpollSet *epollSetPtr = NULL;
    if (IS_INVALID_SOCKET_DESC(desc)) {
        LOGE(TAG, "invalid socket : %d", desc);
        return;
    }
    if (g_epollInited != NSTACKX_TRUE) {
        LOGE(TAG, "Epoll Event Ptr Not Init");
        return;
    }

    if (pthread_mutex_lock(&g_epollEventPtrMutex) != 0) {
        LOGE(TAG, "pthread mutex lock error");
        return;
    }
    epollSetPtr = CloseDescEpollHandle(desc);
    if (pthread_mutex_unlock(&g_epollEventPtrMutex) != 0) {
        LOGE(TAG, "pthread mutex unlock error");
        return;
    }

    if (epollSetPtr == NULL) {
        return;
    }

    if (pthread_mutex_lock(&(epollSetPtr->mutex)) != 0) {
        LOGE(TAG, "pthread mutex lock error");
        return;
    }
    FD_CLR(desc, &epollSetPtr->readfds);
    FD_CLR(desc, &epollSetPtr->writefds);
    FD_CLR(desc, &epollSetPtr->exceptfds);
    if (desc == epollSetPtr->maxfd) {
        EpollSetMaxFdUpdate(epollSetPtr);
    }
    if (pthread_mutex_unlock(&(epollSetPtr->mutex)) != 0) {
        LOGE(TAG, "pthread mutex unlock error");
        return;
    }
}