#include <iostream>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <sched.h> 
#include <exception>
#include <string.h>
#include <thread>
#include <atomic>
#include <arpa/inet.h>
#include <sys/fcntl.h>

int safe_call(int ret, const char * ctx) {
//   fprintf(stderr, "%s == %d, errno(%d) == %s\n", ctx, ret, errno, strerror(errno));
  if(ret == -1) throw std::runtime_error(std::string(ctx) +  " == " + std::to_string(ret) + ", errno(" + std::to_string(errno) + ')'  + " == " + strerror(errno));
  return ret;
}
#define safe_call(call) safe_call(call, #call)



class alignas(64) worker {
public:
  
  void run(int listen_fd) {
    this->listen_fd = listen_fd;
    std::thread([this]() {
      work();
    }).detach();
  }
  
protected:

  void work() {
    fprintf(stderr, "start thread(%lu)\n", id);
    
    while(int accept_fd = safe_call(accept(listen_fd, nullptr, nullptr))) {
      fprintf(stderr, "accept thread(%lu)\n", id);
      uint64_t buff[(1024 * 10) / sizeof(uint64_t)];
      safe_call(read(accept_fd, buff, sizeof(uint64_t)));
      for(auto & x : buff) x = buff[0];
      auto it = std::begin(buff), end = std::end(buff);
      do {
        safe_call(write(accept_fd, it, 1024));
      } while((it += (1024 / sizeof(uint64_t))) < end);
      close(accept_fd);
      sleep(10);
    }
  }
  
protected:
  size_t id = get_tid();
  int listen_fd;
protected:
  size_t get_tid() {
    return tid_count++;
  }
  static inline size_t tid_count = 0;;
};


int listen_to(in_addr_t addr, uint16_t port) {
  constexpr size_t n = 100;
  sockaddr_in saddr{};
  saddr.sin_family = AF_INET;
  saddr.sin_port = htons(port);
  saddr.sin_addr.s_addr = htonl(addr);
  int listen_fd = safe_call(socket(AF_INET, SOCK_STREAM, 0));
  int option = 1;
  safe_call(setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)));
  safe_call(bind(listen_fd, (__CONST_SOCKADDR_ARG)&saddr, sizeof(saddr)));
  safe_call(listen(listen_fd, n));
  return listen_fd;
}

worker workers[10000];

int main() {
  int listen_fd = listen_to(INADDR_LOOPBACK, 8888);
  
  for(auto & w : workers) w.run(listen_fd);
  while(1) {sleep(1);};
}