WinSockでepollをエミュレートする

細かいところはいい加減。なぜか動いてる。

echoサーバ

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <io.h>
#include "wsepoll.h"

#include <memory.h>

#define ECHO_PORT 7
#define MAX_BACKLOG 5
#define RCVBUFSIZE 256
#define MAX_EVENTS WSA_MAXIMUM_WAIT_EVENTS
#define EPOLL_TIMEOUT 1000

#define die(...) do { fprintf(stderr, __VA_ARGS__); WSACleanup(); exit(1); } while(0)
#define error(...) do { fprintf(stderr, __VA_ARGS__); } while(0)

static void wsa_startup(WORD version) {
  int n;
  WSADATA wsaData;

  if ((n = WSAStartup(version, &wsaData)) != 0) {
    die("WASStartup(): %d\n", n);
  }

  if (version != wsaData.wVersion) {
    die("WASStartup(): WinSock version %d.%d not supported\n", LOWORD(version), HIWORD(version));
  }
}

int echo(SOCKET sock) {
  char buf[RCVBUFSIZE + 1];
  size_t len;

  if ((len = recv(sock, buf, RCVBUFSIZE, 0)) < 0) {
    error("recv(): %d\n", WSAGetLastError());
    return -1;
  }

  if (len == 0) { return 0; }

  buf[len] = '\0';
  error("recv: %d '%s'\n", sock, buf);

  if (send(sock, buf, len, 0) < 0) {
    error("send(): %d\n", WSAGetLastError());
    return -1;
  }

  return len;
}


int main() {
  int epfd;
  SOCKET sock;
  struct sockaddr_in addr;
  struct epoll_event event;

  wsa_startup(MAKEWORD(2, 0));

  if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
    die("socket(): %d\n", WSAGetLastError());
  }

  addr.sin_family = AF_INET;
  addr.sin_port = htons(ECHO_PORT);
  addr.sin_addr.S_un.S_addr = INADDR_ANY;

  if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) != 0) {
    die("bind(): %d\n", WSAGetLastError());
  }

  if (listen(sock, MAX_BACKLOG) != 0) {
    die("listen(): %d\n", WSAGetLastError());
  }

  if((epfd = epoll_create(MAX_EVENTS)) < 0) {
    die("epoll_create()\n");
  }

  memset(&event, 0, sizeof(event));
  event.events  = EPOLLIN;
  event.data.fd = sock;

  if (epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &event) < 0) {
    die("epoll_ctl()\n");
  }

  while (1) {
    int i, nfds;
    struct sockaddr_in sa;
    socklen_t len = sizeof(sa);
    struct epoll_event events[MAX_EVENTS];

    if ((nfds = epoll_wait(epfd, events, MAX_EVENTS, EPOLL_TIMEOUT)) < 0) {
      die("epoll_wait()\n");
    }

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

      if (sock == fd) {
        int s;

        if ((s = accept(sock, (struct sockaddr*) &sa, &len)) < 0) {
          die("accept()\n");
        }

        error("accepted.\n");

        event.events  = EPOLLIN;
        event.data.fd = s;

        if (epoll_ctl(epfd, EPOLL_CTL_ADD, s, &event) < 0) {
          die("epoll_ctl()\n");
        }
      } else {
        if (echo(fd) < 1) {
          if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &event) < 0) {
            die("epoll_ctl()\n");
          }

          closesocket(fd);
        }
      }
    }
  }

  _close(epfd);

  closesocket(sock);
  WSACleanup();
  return 0;
}

wsepoll.h

#ifndef _WSEPOLL_H_
#define _WSEPOLL_H_

#include <winsock2.h>

#define EPOLLIN      (FD_READ | FD_ACCEPT | FD_CLOSE)
#define EPOLLOUT     (FD_WRITE | FD_CONNECT | FD_CLOSE)
#define EPOLLRDHUP   (FD_CLOSE)
#define EPOLLPRI     (FD_OOB)
#define EPOLLERR     0
#define EPOLLHUP     (FD_CLOSE)
#define EPOLLET      0
#define EPOLLONESHOT 0

#define EPOLL_CTL_ADD 1
#define EPOLL_CTL_DEL 2
#define EPOLL_CTL_MOD 3

struct epoll_data {
  int fd;
};

struct epoll_event {
  unsigned int events;
  struct epoll_data data;
};

int epoll_create(int size);
int epoll_ctl(int epfd, int op, SOCKET fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

#endif

wsepoll.c

#include <winsock2.h>
#include <windows.h>

#include <io.h>
#include <fcntl.h>
#include <share.h>
#include <sys/stat.h>
#include <sys/locking.h>
#include <malloc.h>
#include <errno.h>

#include "wsepoll.h"

struct wsepsock {
  SOCKET socket;
  unsigned int events;
};

#define LOCK_FILE "wseplck"

static struct wsepsock epsocks[WSA_MAXIMUM_WAIT_EVENTS];

static int mktmpfn(char *dst, int len, const char *src);
static int epoll_ctl_add(int epfd, SOCKET fd, struct epoll_event *event);
static int epoll_ctl_mod(int epfd, SOCKET fd, struct epoll_event *event);
static int epoll_ctl_del(int epfd, SOCKET fd, struct epoll_event *event);
static int epoll_wait_init(int *nfds, SOCKET *fds, HANDLE *hEvents);
static void epoll_wait_clean(SOCKET *fds, HANDLE *hEvents);

int epoll_create(int size) {
  int i, epfd;
  char tmpfn[MAX_PATH];

  if (!mktmpfn(tmpfn, MAX_PATH, LOCK_FILE)) {
    return -1;
  }

  if (_sopen_s(&epfd, tmpfn, _O_CREAT | _O_TEMPORARY, _SH_DENYNO, _S_IREAD | _S_IWRITE) != 0) {
    return -1;
  }

  if(_locking(epfd, _LK_NBLCK, 1) != 0) { return -1; }

  for (i = 0; i < WSA_MAXIMUM_WAIT_EVENTS; i++) {
    epsocks[i].socket = INVALID_SOCKET;
    epsocks[i].events = 0;
  }

  _locking(epfd, _LK_UNLCK, 1);
  
  return epfd;
}

int epoll_ctl(int epfd, int op, SOCKET fd, struct epoll_event *event) {
  int retval = -1;

  switch (op) {
  case EPOLL_CTL_ADD:
    retval = epoll_ctl_add(epfd, fd, event);
    break;
  case EPOLL_CTL_MOD:
    retval = epoll_ctl_mod(epfd, fd, event);
    break;
  case EPOLL_CTL_DEL:
    retval = epoll_ctl_del(epfd, fd, event);
    break;
  }

  return retval;
}

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) {
  int nfds;
  SOCKET fds[WSA_MAXIMUM_WAIT_EVENTS];
  HANDLE hEvents[WSA_MAXIMUM_WAIT_EVENTS];
  DWORD n;

  if (!epoll_wait_init(&nfds, fds, hEvents)) {
    epoll_wait_clean(fds, hEvents);
    return -1;
  }

  n = WSAWaitForMultipleEvents(nfds, hEvents, FALSE, timeout, FALSE);

  if (n == WSA_WAIT_FAILED) {
    epoll_wait_clean(fds, hEvents);
    return -1;
  } else if (n == WSA_WAIT_TIMEOUT) {
    epoll_wait_clean(fds, hEvents);
    return 0;
  } else {
    SOCKET fd;
    HANDLE hEvent;
    WSANETWORKEVENTS ev;
    
    n -= WSA_WAIT_EVENT_0;
    fd = fds[n];
    hEvent = hEvents[n];

    if (WSAEnumNetworkEvents(fd, hEvent, &ev) < 0) {
      epoll_wait_clean( fds, hEvents);
      return -1;
    }

    events[0].data.fd = fd;
    events[0].events = ev.lNetworkEvents;
    epoll_wait_clean(fds, hEvents);
    return 1;
  }
}

//-------------------------------------------------------------------

static int mktmpfn(char *dst, int len, const char *src) {
  char *buf = alloca(sizeof(char) * len);

  if (GetTempPathA(len, buf) == 0) {
    return 0;
  }

  if (GetTempFileNameA(buf, src, 0, dst) == 0) {
    return 0;
  }

  return 1;
}

static int epoll_ctl_add(int epfd, SOCKET fd, struct epoll_event *event) {
  int i;

  if(_locking(epfd, _LK_NBLCK, 1) != 0) { return -1; }

  for (i = 0; i < WSA_MAXIMUM_WAIT_EVENTS; i++) {
    if (epsocks[i].socket == INVALID_SOCKET) {
      epsocks[i].socket = fd;
      epsocks[i].events = event->events;
      break;
    }
  }

  _locking(epfd, _LK_UNLCK, 1);

  return (i < WSA_MAXIMUM_WAIT_EVENTS) ? 0 : -1;
}

static int epoll_ctl_mod(int epfd, SOCKET fd, struct epoll_event *event) {
  int i;

  if(_locking(epfd, _LK_NBLCK, 1) != 0) { return -1; }

  for (i = 0; i < WSA_MAXIMUM_WAIT_EVENTS; i++) {
    if (epsocks[i].socket == fd) {
      epsocks[i].events = event->events;
      break;
    }
  }

  _locking(epfd, _LK_UNLCK, 1);

  return (i < WSA_MAXIMUM_WAIT_EVENTS) ? 0 : -1;
}

static int epoll_ctl_del(int epfd, SOCKET fd, struct epoll_event *event) {
  int i;

  if(_locking(epfd, _LK_NBLCK, 1) != 0) { return -1; }

  for (i = 0; i < WSA_MAXIMUM_WAIT_EVENTS; i++) {
    if (epsocks[i].socket == fd) {
      epsocks[i].socket = INVALID_SOCKET;
      epsocks[i].events = 0;
      break;
    }
  }

  _locking(epfd, _LK_UNLCK, 1);

  return (i < WSA_MAXIMUM_WAIT_EVENTS) ? 0 : -1;
}

static int epoll_wait_init(int *nfds, SOCKET *fds, HANDLE *hEvents) {
  int i;
  (*nfds) = 0;

  for (i = 0; i < WSA_MAXIMUM_WAIT_EVENTS; i++) {
    fds[i] = INVALID_SOCKET;
    hEvents[i] = NULL;
  }

  for (i = 0; i < WSA_MAXIMUM_WAIT_EVENTS; i++) {
    int n = *nfds;
    if (epsocks[i].socket == INVALID_SOCKET) { continue; }

    fds[n] = epsocks[i].socket;
    hEvents[n] = WSACreateEvent();

    if (WSAEventSelect(fds[n], hEvents[n], epsocks[i].events) < 0) {
      errno = WSAGetLastError();
      return 0;
    }

    (*nfds)++;
  }

  return 1;
}

static void epoll_wait_clean(SOCKET *fds, HANDLE *hEvents) {
  int i;

  for (i = 0; i < WSA_MAXIMUM_WAIT_EVENTS; i++) {
    if (fds[i] != INVALID_SOCKET) {
      WSAEventSelect(fds[i], NULL, 0);
    }

    if (hEvents[i] != NULL) {
      WSACloseEvent(hEvents[i]);
    }
  }
}