러스트로는 이런거 못 짠다. ㅎㅎㅎ
러빨러들한테 속지 마라
// -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*-
/*
c-loop.c
This file is part of Clair.
Copyright (C) 2021-2025 Hodong Kim, All rights reserved.
Unauthorized copying of this software, via any medium is strictly prohibited.
Proprietary and confidential.
Written by Hodong Kim <hodong@nimfsoft.art>
*/
#if defined(__FreeBSD__) || defined(__OpenBSD__)
#include <sys/param.h>
#include <sys/event.h>
#ifdef __FreeBSD_version
#if __FreeBSD_version >= 1400000
#define __C_TIMER_FD__
#include <sys/timerfd.h>
#endif
#endif
#elif defined(__linux__)
#define _POSIX_C_SOURCE 199309L // CLOCK_MONOTONIC
#define __C_TIMER_FD__
#include <sys/timerfd.h>
#include <sys/epoll.h>
#endif
#include "c-loop.h"
#include "c-mem.h"
#include <stdlib.h>
#include <unistd.h>
#include "c-log.h"
#include <errno.h>
#include <string.h>
#include <assert.h>
static CLoop* c_loop;
CSource* c_source_new ()
{
CSource* source = c_calloc (1, sizeof (CSource));
return source;
}
void c_source_free (CSource* source)
{
free (source);
}
CLoop* c_loop_get_default ()
{
if (!c_loop)
c_loop = c_loop_new ();
return c_loop;
}
static uintptr_t idle_key_hash (const uintptr_t* key, uint32_t seed)
{
return c_murmur3_32 ((const uint8_t*) key, 2 * sizeof (uintptr_t), seed);
}
static bool idle_key_equal (const uintptr_t* a, const uintptr_t* b)
{
return !memcmp (a, b, 2 * sizeof (uintptr_t));
}
static void idle_key_free (void* key)
{
free (key);
}
CLoop* c_loop_new ()
{
#if defined(__FreeBSD__) || defined(__OpenBSD__)
int fd = kqueue ();
#elif defined __linux__
int fd = epoll_create1 (EPOLL_CLOEXEC);
#else
#error "This platform is not supported"
#endif
if (fd == -1)
{
c_log_warning ("%s", strerror (errno));
return nullptr;
}
CLoop* loop = c_calloc (1, sizeof (CLoop));
loop->timeout = -1;
loop->timeout_idle = 250;
loop->capa = 4;
#if defined(__FreeBSD__) || defined(__OpenBSD__)
loop->kq = fd;
loop->events = c_calloc (1, loop->capa * sizeof (struct kevent));
#elif defined __linux__
loop->epfd = fd;
loop->events = c_calloc (1, loop->capa * sizeof (struct epoll_event));
#else
#error "This platform is not supported"
#endif
loop->sources = c_hash_map_new (c_ptr_hash, c_ptr_equal, nullptr, nullptr);
loop->idles = c_hash_map_new ((CHashFunc) idle_key_hash,
(CEqualFunc) idle_key_equal,
(CFreeFunc) idle_key_free,
nullptr);
return loop;
}
void c_loop_free (CLoop* loop)
{
if (!loop)
return;
c_hash_map_free (loop->idles);
c_hash_map_free (loop->sources);
#if defined(__FreeBSD__) || defined(__OpenBSD__)
free (loop->events);
close (loop->kq);
#elif defined __linux__
free (loop->events);
close (loop->epfd);
#else
#error "This platform is not supported"
#endif
free (loop);
}
void c_loop_quit (CLoop* loop)
{
loop->run = false;
}
int c_loop_iteration (CLoop* loop)
{
loop->depth++;
// dispatch pending events
CHashMapIter iter;
c_hash_map_iter_init (&iter, loop->sources);
CSource* source;
while (c_hash_map_iter_next (&iter, NULL, (void**) &source))
if (source->pending && source->pending (source) && source->dispatch)
{
source->dispatch (source);
loop->depth--;
return 1;
}
int timeout;
if (loop->idles && c_hash_map_size (loop->idles))
{
if (loop->timeout > -1)
timeout = C_MIN (loop->timeout, loop->timeout_idle);
else
timeout = loop->timeout_idle;
}
else
{
timeout = loop->timeout;
}
int retval;
do {
#if defined(__FreeBSD__) || defined(__OpenBSD__)
struct timespec* ts;
struct timespec ts2;
if (timeout == -1)
{
ts = nullptr;
}
else
{
ts2.tv_sec = timeout * 1000000 / 1000000000;
ts2.tv_nsec = timeout * 1000000 % 1000000000;
ts = &ts2;
}
loop->n_revents = kevent (loop->kq, nullptr, 0, loop->events, loop->n_events, ts);
#elif defined __linux__
loop->n_revents = epoll_wait (loop->epfd, loop->events, loop->n_events, timeout);
#else
#error "This platform is not supported"
#endif
retval = loop->n_revents;
} while (loop->n_revents == -1 && errno == EINTR && loop->run);
while (loop->n_revents > 0)
{
loop->n_revents--;
int i = loop->n_revents;
short revents = 0;
#if defined(__FreeBSD__) || defined(__OpenBSD__)
if (loop->events[i].flags & EV_EOF)
revents |= POLLHUP;
if (loop->events[i].flags & EV_ERROR)
revents |= POLLERR;
if (loop->events[i].filter == EVFILT_READ)
revents |= POLLIN;
else if (loop->events[i].filter == EVFILT_WRITE)
revents |= POLLOUT;
#elif defined(__linux__)
if (loop->events[i].events & EPOLLHUP)
revents |= POLLHUP;
if (loop->events[i].events & EPOLLERR)
revents |= POLLERR;
if (loop->events[i].events & EPOLLIN)
revents |= POLLIN;
else if (loop->events[i].events & EPOLLOUT)
revents |= POLLOUT;
#else
#error "This platform is not supported"
#endif
if (revents)
{
if ((source = c_hash_map_lookup (loop->sources,
#if defined(__FreeBSD__) || defined(__OpenBSD__)
C_INT_TO_VOIDP (loop->events[i].ident))))
#elif defined(__linux__)
C_INT_TO_VOIDP (loop->events[i].data.fd))))
#else
#error "This platform is not supported"
#endif
{
source->revents = revents;
if (source->dispatch)
source->dispatch (source);
}
}
}
if (loop->depth == 1 && loop->idles && c_hash_map_size (loop->idles))
{
uintptr_t* key;
void* user_data;
c_hash_map_iter_init (&iter, loop->idles);
while (c_hash_map_iter_next (&iter, (void**) &key, &user_data))
{
CCallback1 callback = (CCallback1) key[0];
callback (user_data);
}
}
loop->depth--;
return retval;
}
bool c_loop_run (CLoop* loop)
{
loop->run = true;
while (loop->run)
{
if (c_loop_iteration (loop) == -1)
{
if (errno == EINTR)
c_log_info ("%s", strerror (errno));
else
c_log_critical ("%s", strerror (errno));
return false;
}
}
return true;
}
void c_loop_add_source (CLoop* loop, CSource* source)
{
if (source->fd < 0)
{
c_log_warning ("c_loop_add_source failed: source->fd: %d", source->fd);
return;
}
#if defined(__FreeBSD__) || defined(__OpenBSD__)
struct kevent kevents[2];
int status;
int n_events = 0;
if (source->events & POLLIN)
{
EV_SET (&kevents[n_events], source->fd, EVFILT_READ, EV_ADD, 0, 0, 0);
n_events++;
}
if (source->events & POLLOUT)
{
EV_SET (&kevents[n_events], source->fd, EVFILT_WRITE, EV_ADD, 0, 0, 0);
n_events++;
}
status = kevent (loop->kq, kevents, n_events, NULL, 0, NULL);
if (status == -1)
{
c_log_warning ("kevent (loop->kq, kevents, %d, NULL, 0, NULL) failed: %s",
n_events, strerror (errno));
return;
}
#elif defined __linux__
struct epoll_event eevent = { 0 };
int status;
int n_events = 1;
eevent.events = 0;
eevent.data.fd = source->fd;
if (source->events & POLLIN)
eevent.events |= EPOLLIN;
if (source->events & POLLOUT)
eevent.events = EPOLLOUT;
status = epoll_ctl (loop->epfd, EPOLL_CTL_ADD, source->fd, &eevent);
if (status == -1)
{
c_log_warning ("epoll_ctl EPOLL_CTL_ADD %d failed: %s",
source->fd, strerror (errno));
return;
}
#else
#error "This platform is not supported"
#endif
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux__)
loop->n_events += n_events;
if (loop->n_events == loop->capa)
#else
#error "This platform is not supported"
#endif
{
loop->capa *= 2;
#if defined(__FreeBSD__) || defined(__OpenBSD__)
loop->events = c_realloc (loop->events,
loop->capa * sizeof (struct kevent));
#elif defined __linux__
loop->events = c_realloc (loop->events,
loop->capa * sizeof (struct epoll_event));
#else
#error "This platform is not supported"
#endif
}
c_hash_map_insert (loop->sources, C_INT_TO_VOIDP (source->fd), source);
}
void c_loop_remove_source (CLoop* loop, CSource* source)
{
int status;
int n_events = 0;
#if defined(__FreeBSD__) || defined(__OpenBSD__)
struct kevent kevents[2];
if (source->events & POLLIN)
{
EV_SET (&kevents[n_events], source->fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
n_events++;
}
if (source->events & POLLOUT)
{
EV_SET (&kevents[n_events], source->fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
n_events++;
}
status = kevent (loop->kq, kevents, n_events, NULL, 0, NULL);
if (status == -1)
{
c_log_warning ("kevent (loop->kq, kevents, 1, NULL, 0, NULL) failed: %s",
strerror (errno));
return;
}
for (int i = 0; i < loop->n_revents; i++)
{
if (loop->events[i].ident == source->fd)
{
loop->events[i].ident = -1;
loop->events[i].filter = 0;
loop->events[i].flags = 0;
}
}
#elif defined __linux__
n_events = 1;
status = epoll_ctl (loop->epfd, EPOLL_CTL_DEL, source->fd, nullptr);
if (status == -1)
{
c_log_warning ("epoll_ctl EPOLL_CTL_DEL %d failed: %s",
source->fd, strerror (errno));
return;
}
for (int i = 0; i < loop->n_revents; i++)
{
if (loop->events[i].data.fd == source->fd)
{
loop->events[i].events = 0;
loop->events[i].data.fd = -1;
break;
}
}
#else
#error "This platform is not supported"
#endif
loop->n_events -= n_events;
if (loop->n_events < loop->capa / 4)
{
loop->capa /= 2;
#if defined(__FreeBSD__) || defined(__OpenBSD__)
loop->events = c_realloc (loop->events,
loop->capa * sizeof (struct kevent));
#elif defined __linux__
loop->events = c_realloc (loop->events,
loop->capa * sizeof (struct epoll_event));
#else
#error "This platform is not supported"
#endif
}
c_hash_map_remove (loop->sources, C_INT_TO_VOIDP (source->fd));
}
static void fd_dispatch (CSource* source)
{
if (source->callback)
{
CFdCallback callback = (CFdCallback) source->callback;
callback (source->fd, source->revents, source->user_data);
}
}
void c_loop_add_fd (CLoop* loop,
int fd,
short events,
CFdCallback callback,
void* user_data)
{
if (fd < 0)
{
c_log_warning ("c_loop_add_fd failed: fd: %d", fd);
return;
}
CSource* source = c_source_new ();
source->fd = fd;
source->events = events;
source->dispatch = fd_dispatch;
source->callback = (CCallback) callback;
source->user_data = user_data;
c_loop_add_source (loop, source);
}
void c_loop_remove_fd (CLoop* loop, int fd)
{
if (fd < 0)
{
c_log_warning ("c_loop_remove_fd failed: %d", fd);
return;
}
CSource* source = c_hash_map_lookup (loop->sources, C_INT_TO_VOIDP (fd));
c_loop_remove_source (loop, source);
c_source_free (source);
}
void c_loop_mod_fd (CLoop* loop, int fd, short events)
{
CSource* source = c_hash_map_lookup (loop->sources, C_INT_TO_VOIDP (fd));
#if defined(__FreeBSD__) || defined(__OpenBSD__)
struct kevent kevents[2];
int i = 0;
if ((source->events & POLLIN) - (events & POLLIN) > 0)
{
EV_SET (&kevents[i], fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
i++;
}
else if ((source->events & POLLIN) - (events & POLLIN) < 0)
{
EV_SET (&kevents[i], fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0);
i++;
}
if ((source->events & POLLOUT) - (events & POLLOUT) > 0)
{
EV_SET (&kevents[i], fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
i++;
}
else if ((source->events & POLLOUT) - (events & POLLOUT) < 0)
{
EV_SET (&kevents[i], fd, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, 0);
i++;
}
if (i > 0)
{
int status = kevent (loop->kq, kevents, i, NULL, 0, NULL);
if (status == -1)
{
c_log_warning ("kevent (loop->kq, kevents, i, NULL, 0, NULL) failed: %s",
i, strerror (errno));
}
}
#elif defined __linux__
struct epoll_event eevent = { 0 };
if (events & POLLIN)
eevent.events = EPOLLIN;
if (events & POLLOUT)
eevent.events = EPOLLOUT;
eevent.data.fd = fd;
int status = epoll_ctl (loop->epfd, EPOLL_CTL_MOD, fd, &eevent);
if (status == -1)
c_log_warning ("epoll_ctl EPOLL_CTL_MOD %d %u failed: %s",
fd, eevent.events, strerror (errno));
#else
#error "This platform is not supported"
#endif
source->events = events;
}
void* c_loop_add_idle (CLoop* loop, CCallback1 callback, void* user_data)
{
uintptr_t* key = c_malloc (2 * sizeof (uintptr_t));
key[0] = (uintptr_t) callback;
key[1] = (uintptr_t) user_data;
c_hash_map_insert (loop->idles, key, user_data);
return callback;
}
void c_loop_remove_idle (CLoop* loop, CCallback1 callback, void* user_data)
{
uintptr_t key[2] = { (uintptr_t) callback, (uintptr_t) user_data };
c_hash_map_remove (loop->idles, key);
}
static void timer_dispatch (CSource* source)
{
c_log_debug ("timer_dispatch: source->fd: %d", source->fd);
int status;
#if defined(__C_TIMER_FD__)
uint64_t n_expirations;
do {
status = read (source->fd, &n_expirations, sizeof (uint64_t));
} while (status == -1 && errno == EINTR);
#else
struct kevent event;
status = kevent (source->fd, NULL, 0, &event, 1, NULL);
#endif
if (status == -1)
{
c_log_critical ("%s", strerror (errno));
return;
}
else if (status > 0 && source->callback)
{
CCallback1 callback = (CCallback1) source->callback;
callback (source->user_data);
}
return;
}
int c_loop_add_timer (CLoop* loop, int ms, CCallback1 callback, void* user_data)
{
#if defined(__C_TIMER_FD__)
int fd = timerfd_create (CLOCK_MONOTONIC, TFD_CLOEXEC);
#else
int fd = kqueue ();
#endif
if (fd == -1)
{
c_log_warning ("%s", strerror (errno));
return -1;
}
if (!c_loop_mod_timer (loop, fd, ms))
{
close (fd);
return -1;
}
CSource* source = c_source_new ();
source->fd = fd;
source->events = POLLIN;
source->dispatch = timer_dispatch;
source->callback = (CCallback) callback;
source->user_data = user_data;
c_loop_add_source (loop, source);
return fd;
}
void c_loop_remove_timer (CLoop* loop, int fd)
{
if (fd < 0)
{
c_log_warning ("fd is less than 0: i%d", fd);
return;
}
CSource* source = c_hash_map_lookup (loop->sources, C_INT_TO_VOIDP (fd));
if (source)
{
#if defined(__C_TIMER_FD__)
struct itimerspec ts = { { 0, 0 }, { 0, 0 } };
if (timerfd_settime (fd, 0, &ts, nullptr))
c_log_warning ("%s", strerror (errno));
#else
struct kevent event;
EV_SET (&event, fd, EVFILT_TIMER, EV_DELETE | EV_DISABLE, 0, 0, 0);
int status = kevent (source->fd, &event, 1, NULL, 0, NULL);
if (status == -1)
{
c_log_warning ("kevent (%d, &event, 1, NULL, 0, NULL) failed: %s", fd,
strerror (errno));
}
#endif
c_loop_remove_source (loop, source);
c_source_free (source);
if (fd > -1)
close (fd);
}
}
bool c_loop_mod_timer (CLoop* loop, int fd, int ms)
{
if (fd < 0)
{
c_log_warning ("fd < 0: %d", fd);
return false;
}
#if defined(__C_TIMER_FD__)
long nsec;
if (ms > 0)
nsec = (long) ms * 1000000;
else if (ms == 0)
nsec = 1;
else
nsec = 0;
struct itimerspec its;
// 1000000000 ns = 1 sec
its.it_value.tv_sec = nsec / 1000000000;
its.it_value.tv_nsec = nsec % 1000000000;
its.it_interval.tv_sec = its.it_value.tv_sec;
its.it_interval.tv_nsec = its.it_value.tv_nsec;
if (timerfd_settime (fd, 0, &its, nullptr))
{
c_log_warning ("timerfd_settime failed: %s", strerror (errno));
return false;
}
#else
struct kevent event;
int status;
if (ms < 0)
EV_SET (&event, fd, EVFILT_TIMER, EV_ADD | EV_DISABLE, 0, 0, 0);
else
EV_SET (&event, fd, EVFILT_TIMER, EV_ADD | EV_ENABLE, 0, ms, 0);
status = kevent (fd, &event, 1, NULL, 0, NULL);
if (status == -1)
{
c_log_warning ("kevent (fd, &event, 1, NULL, 0, NULL) failed: %s",
strerror (errno));
return false;
}
#endif
return true;
}
댓글 영역
획득법
① NFT 발행
작성한 게시물을 NFT로 발행하면 일주일 동안 사용할 수 있습니다. (최초 1회)
② NFT 구매
다른 이용자의 NFT를 구매하면 한 달 동안 사용할 수 있습니다. (구매 시마다 갱신)
사용법
디시콘에서지갑연결시 바로 사용 가능합니다.