From pats, 2 Months ago, written in C.
  1. // gcc -std=c99 -Wall -Wextra epoll_test.c -pthread
  2.  
  3. /*
  4.  * Sequence of actions:
  5.  * 1. Server tries to read() from client, gets EAGAIN.
  6.  * 2. Client calls write() succesfully.
  7.  * 3. Server adds the client to epoll (after client's write) with edge-triggered flag.
  8.  * 4. Server calls epoll_wait().
  9.  * Will the server get a notification?
  10.  */
  11.  
  12. #define _GNU_SOURCE
  13.  
  14. #include <arpa/inet.h>
  15. #include <errno.h>
  16. #include <pthread.h>
  17. #include <semaphore.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <sys/epoll.h>
  22. #include <sys/socket.h>
  23. #include <sys/types.h>
  24. #include <unistd.h>
  25.  
  26. #define HOST "127.0.0.1"
  27. #define PORT 8080
  28.  
  29. static int got_event;
  30.  
  31. static sem_t server_sem;
  32. static sem_t client_sem;
  33.  
  34. static void *server(void *param)
  35. {
  36.   (void)param;
  37.  
  38.   int server_fd = socket(AF_INET, SOCK_STREAM, 0);
  39.   if (server_fd < 0) {
  40.     perror("Server: socket()");
  41.     exit(EXIT_FAILURE);
  42.   }
  43.  
  44.   if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) != 0) {
  45.     perror("Server: setsockopt()");
  46.     exit(EXIT_FAILURE);
  47.   }
  48.  
  49.   struct sockaddr_in addr;
  50.   memset(&addr, 0, sizeof(addr));
  51.   addr.sin_family = AF_INET;
  52.   addr.sin_port = htons(PORT);
  53.   addr.sin_addr.s_addr = inet_addr(HOST);
  54.   if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
  55.     perror("Server: bind()");
  56.     exit(EXIT_FAILURE);
  57.   }
  58.  
  59.   if (listen(server_fd, 1024) != 0) {
  60.     perror("Server: listen()");
  61.     exit(EXIT_FAILURE);
  62.   }
  63.  
  64.   int epoll_fd = epoll_create1(0);
  65.   if (epoll_fd < 0) {
  66.     perror("Server: epoll_create1()");
  67.     exit(EXIT_FAILURE);
  68.   }
  69.  
  70.   puts("Server: ready");
  71.   sem_post(&client_sem);
  72.   sem_wait(&server_sem);
  73.  
  74.   int client_fd = accept4(server_fd, NULL, NULL, SOCK_NONBLOCK);
  75.   if (client_fd < 0) {
  76.     perror("Server: accept4()");
  77.     exit(EXIT_FAILURE);
  78.   }
  79.   puts("Server: accepted connection");
  80.  
  81.   char buf[16];
  82.   ssize_t num_read = read(client_fd, buf, sizeof(buf));
  83.   if (num_read != -1 || errno != EAGAIN) {
  84.     fputs("Server: read() did not return EAGAIN\n", stderr);
  85.     exit(EXIT_FAILURE);
  86.   }
  87.   puts("Server: read() returned EAGAIN");
  88.  
  89.   sem_post(&client_sem);
  90.   sem_wait(&server_sem);
  91.  
  92.   struct epoll_event event = {
  93.     .events = EPOLLIN | EPOLLET,
  94.   };
  95.   if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) != 0) {
  96.     perror("Server: epoll_ctl()");
  97.     exit(EXIT_FAILURE);
  98.   }
  99.   puts("Server: event added");
  100.  
  101.   struct epoll_event events[16];
  102.   int num_events = epoll_wait(epoll_fd, events, sizeof(events) / sizeof(*events), 0);
  103.   if (num_events < 0) {
  104.     perror("Server: epoll_wait()");
  105.     exit(EXIT_FAILURE);
  106.   }
  107.   printf("Server: got %i events\n", num_events);
  108.  
  109.   got_event = num_events == 1;
  110.  
  111.   return NULL;
  112. }
  113.  
  114. static void *client(void *param)
  115. {
  116.   (void)param;
  117.  
  118.   sem_wait(&client_sem);
  119.  
  120.   int server_fd = socket(AF_INET, SOCK_STREAM, 0);
  121.   if (server_fd < 0) {
  122.     perror("Client: socket()");
  123.     exit(EXIT_FAILURE);
  124.   }
  125.  
  126.   struct sockaddr_in addr;
  127.   memset(&addr, 0, sizeof(addr));
  128.   addr.sin_family = AF_INET;
  129.   addr.sin_port = htons(PORT);
  130.   addr.sin_addr.s_addr = inet_addr(HOST);
  131.   if (connect(server_fd, &addr, sizeof(addr)) != 0) {
  132.     perror("Client: connect()");
  133.     exit(EXIT_FAILURE);
  134.   }
  135.  
  136.   puts("Client: connected");
  137.   sem_post(&server_sem);
  138.   sem_wait(&client_sem);
  139.  
  140.   char buf[16];
  141.   ssize_t num_written = write(server_fd, buf, sizeof(buf));
  142.   if (num_written < 0) {
  143.     perror("Client: write()");
  144.     exit(EXIT_FAILURE);
  145.   }
  146.   printf("Client: wrote %zi bytes\n", num_written);
  147.  
  148.   sem_post(&server_sem);
  149.  
  150.   return NULL;
  151. }
  152.  
  153. int main(void)
  154. {
  155.   if (sem_init(&server_sem, 0, 0) != 0) {
  156.     perror("sem_init()");
  157.     return EXIT_FAILURE;
  158.   }
  159.  
  160.   if (sem_init(&client_sem, 0, 0) != 0) {
  161.     perror("sem_init()");
  162.     return EXIT_FAILURE;
  163.   }
  164.  
  165.   pthread_t server_thr, client_thr;
  166.  
  167.   if (pthread_create(&server_thr, NULL, server, NULL) != 0) {
  168.     perror("pthread_create()");
  169.     return EXIT_FAILURE;
  170.   }
  171.  
  172.   if (pthread_create(&client_thr, NULL, client, NULL) != 0) {
  173.     perror("pthread_create()");
  174.     return EXIT_FAILURE;
  175.   }
  176.  
  177.   pthread_join(server_thr, NULL);
  178.   pthread_join(client_thr, NULL);
  179.  
  180.   printf("Test %s\n", got_event ? "OK" : "FAILED");
  181.  
  182.   return EXIT_SUCCESS;
  183. }
captcha