view seobeo/os/s_linux_edge.c @ 63:fff1b048dda6

[Postdog] Fixed a problem where string did not wrap.
author June Park <parkjune1995@gmail.com>
date Tue, 23 Dec 2025 14:00:37 -0800
parents ea9ef388ab97
children ecb6ee6a22c3
line wrap: on
line source

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


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

  // Each thread creates its own epoll to avoid race conditions
  int epfd = epoll_create1(0);
  if (epfd < 0) {
    perror("epoll_create1");
    return NULL;
  }

  // Add server socket to this thread's epoll
  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);
    if (n < 0) {
      if (errno == EINTR) continue;
      perror("epoll_wait");
      continue;
    }

    for (int i = 0; i < n; i++) {
      Seobeo_PHandle phandle = events[i].data.ptr;

      if (phandle == args->srv) {
        // Accept all pending connections (edge-triggered mode)
        while (1) {
          Seobeo_PHandle p_cli_handle = Seobeo_Stream_Handle_Server_Accept(args->srv);
          if (!p_cli_handle) break;

          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);
          }
        }
      } else {
        // Remove from epoll first
        epoll_ctl(epfd, EPOLL_CTL_DEL, phandle->socket, NULL);

        // Handle request (this function destroys the handle internally)
        Seobeo_Web_HandleClientRequest(phandle, args->cache);
      }
    }
  }

  close(epfd);
  return NULL;
}

void Seobeo_Web_Edge(
    Seobeo_PHandle p_server_handle,
    int thread_count,
    Dowa_PHashMap 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);
}


void Seobeo_Web_Edge_2(Seobeo_PHandle p_handle_server, Dowa_PHashMap cache)
{
  const int MAX_EVENTS = 1024;
  struct epoll_event events[MAX_EVENTS];
  char keybuf[32];

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

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

  Dowa_PHashMap handles = Dowa_HashMap_Create(1024);
  snprintf(keybuf, sizeof(keybuf), "%d", p_handle_server->socket);
  Dowa_HashMap_Push_Value(handles, keybuf, p_handle_server, sizeof(p_handle_server));

  while (1) {
    int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
    if (n < 0)
    {
      if (errno == EINTR) continue;
      perror("epoll_wait");
      break;
    }

    for (int i = 0; i < n; i++)
    {
      int fd = events[i].data.fd;

      if (fd == p_handle_server->socket)
      {
        while (1)
        {
          Seobeo_PHandle p_handle_client = Seobeo_Stream_Handle_Server_Accept(p_handle_server);
          if (!p_handle_client) break;

          struct epoll_event client_ev = {
            .events = EPOLLIN | EPOLLET,
            .data.fd = p_handle_client->socket
          };
          if (epoll_ctl(epfd, EPOLL_CTL_ADD, p_handle_client->socket, &client_ev) < 0)
          {
            perror("epoll_ctl ADD client");
            Seobeo_Handle_Destroy(p_handle_client); 
            continue;
          }

          snprintf(keybuf, sizeof(keybuf), "%d", p_handle_client->socket);
          if (p_handle_client)
              Dowa_HashMap_Push_Value_With_Type_NoCopy(handles, keybuf, p_handle_client,
                                                       sizeof(p_handle_client), DOWA_HASH_MAP_TYPE_HASHMAP);
        }
        continue;
      }

      snprintf(keybuf, sizeof(keybuf), "%d", fd);
      Seobeo_PHandle p_handle_client = Dowa_HashMap_Get(handles, keybuf);
      if (!p_handle_client)
      {
        // might happen if client closed between event and lookup
        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
        continue;
      }

      // Remove from epoll
      epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);

      // Handle request (this function destroys the handle internally)
      Seobeo_Web_HandleClientRequest(p_handle_client, cache);

      // Remove from hashmap (handle is already destroyed by HandleClientRequest)
      Dowa_HashMap_Pop_Key(handles, keybuf);
    }
  }

  close(epfd);
  Dowa_HashMap_Destroy(handles);
}