view seobeo/os/s_linux_edge.c @ 216:e82b80b24012 default tip

[MrJuneJune] Make webp translate background job.
author June Park <parkjune1995@gmail.com>
date Sat, 28 Feb 2026 21:04:43 -0800
parents a8976a008a9d
children
line wrap: on
line source

#include <sys/epoll.h>
#include <netinet/tcp.h>
#include "seobeo/seobeo.h"

// TCP keep-alive settings
#define KEEP_ALIVE_IDLE_SEC   30  // Start probes after 30s idle
#define KEEP_ALIVE_INTERVAL   5   // Probe every 5 seconds
#define KEEP_ALIVE_COUNT      3   // Close after 3 failed probes

// Configure TCP keep-alive on socket (kernel handles timeout)
static void configure_keep_alive(int socket)
{
  int enable = 1;
  setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));

  int idle = KEEP_ALIVE_IDLE_SEC;
  setsockopt(socket, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle));

  int interval = KEEP_ALIVE_INTERVAL;
  setsockopt(socket, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval));

  int count = KEEP_ALIVE_COUNT;
  setsockopt(socket, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));
}

void *Seobeo_Web_Edge_Worker(void *vargs)
{
  WorkerArgs *args = vargs;
  const int max_events = 64;
  struct epoll_event events[max_events];

  int epfd = epoll_create1(0);
  if (epfd < 0)
  {
    perror("epoll_create1");
    return NULL;
  }

  struct epoll_event ev = {
    .events = EPOLLIN | EPOLLET,
    .data.ptr = args->srv
  };
  if (epoll_ctl(epfd, EPOLL_CTL_ADD, args->srv->socket, &ev) < 0)
  {
    perror("epoll_ctl ADD server");
    close(epfd);
    return NULL;
  }

  while (1)
  {
    int n = epoll_wait(epfd, events, max_events, -1);  // Block indefinitely, kernel handles timeouts
    if (n < 0)
    {
      if (errno == EINTR)
        continue;
      perror("epoll_wait");
      continue;
    }

    for (int i = 0; i < n; i++)
    {
      Seobeo_Handle *p_handle = events[i].data.ptr;

      // Server socket - accept new connections
      if (p_handle == args->srv)
      {
        while (1)
        {
          Seobeo_Handle *p_cli_handle = Seobeo_Stream_Handle_Server_Accept(args->srv);
          if (!p_cli_handle) break;

          // Let kernel handle keep-alive timeout
          configure_keep_alive(p_cli_handle->socket);

          struct epoll_event client_ev = {
            .events = EPOLLIN | EPOLLET,
            .data.ptr = p_cli_handle
          };

          if (epoll_ctl(epfd, EPOLL_CTL_ADD, p_cli_handle->socket, &client_ev) < 0)
          {
            perror("epoll_ctl ADD client");
            Seobeo_Handle_Destroy(p_cli_handle);
          }
        }
      }
      // Client socket - handle request
      else
      {
        Seobeo_Handle *p_client_handle = p_handle;

        // Connection error or hangup - clean up
        if (events[i].events & (EPOLLERR | EPOLLHUP))
        {
          epoll_ctl(epfd, EPOLL_CTL_DEL, p_client_handle->socket, NULL);
          Seobeo_Handle_Destroy(p_client_handle);
          continue;
        }

        // Handle requests (loop for pipelined requests)
        boolean keep_alive = TRUE;
        while (keep_alive)
        {
          keep_alive = Seobeo_Web_ClientHandle_Request(p_client_handle, args->cache, TRUE);

          // No more data in buffer, wait for next epoll event
          if (keep_alive && p_client_handle->read_buffer_len == 0)
            break;
        }

        // Client wants to close
        if (!keep_alive)
        {
          epoll_ctl(epfd, EPOLL_CTL_DEL, p_client_handle->socket, NULL);
          Seobeo_Handle_Destroy(p_client_handle);
        }
        // Connection stays open, kernel will timeout if idle
      }
    }
  }

  close(epfd);
  return NULL;
}

void Seobeo_Web_Edge(
    Seobeo_Handle *p_server_handle,
    int thread_count,
    Seobeo_Cache_Entry *p_html_cache)
{
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setstacksize(&attr, 5 * 1024 * 1024); // 5 MB

  pthread_t threads[thread_count];
  for (int i = 0; i < thread_count; i++)
  {
    WorkerArgs *args = malloc(sizeof(WorkerArgs));
    *args = (WorkerArgs){ p_server_handle, p_html_cache };

    pthread_create(&threads[i], &attr, Seobeo_Web_Edge_Worker, args);
  }

  // Join threads instead of detaching for proper cleanup
  for (int i = 0; i < thread_count; i++)
    pthread_join(threads[i], NULL);

  pthread_attr_destroy(&attr);
}