-void *socket_listen_main(void *ptr)
-{
- if(ptr) { ; }
-
- info("WEB SERVER thread created with task id %d", gettid());
-
- struct web_client *w;
- struct timeval tv;
- int retval;
-
- if(ptr) { ; }
-
- if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
- error("Cannot set pthread cancel type to DEFERRED.");
-
- if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
- error("Cannot set pthread cancel state to ENABLE.");
-
- web_client_timeout = config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);
- web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip);
-
- if(listen_fd < 0) fatal("LISTENER: Listen socket is not ready.");
-
- fd_set ifds, ofds, efds;
- int fdmax = listen_fd;
-
- FD_ZERO (&ifds);
- FD_ZERO (&ofds);
- FD_ZERO (&efds);
-
- for(;;) {
- tv.tv_sec = 0;
- tv.tv_usec = 200000;
-
- if(listen_fd >= 0) {
- FD_SET(listen_fd, &ifds);
- FD_SET(listen_fd, &efds);
- }
-
- // debug(D_WEB_CLIENT, "LISTENER: Waiting...");
- retval = select(fdmax+1, &ifds, &ofds, &efds, &tv);
-
- if(retval == -1) {
- error("LISTENER: select() failed.");
- continue;
- }
- else if(retval) {
- // check for new incoming connections
- if(FD_ISSET(listen_fd, &ifds)) {
- w = web_client_create(listen_fd);
- if(unlikely(!w)) {
- // no need for error log - web_client_create already logged the error
- continue;
- }
-
- if(pthread_create(&w->thread, NULL, web_client_main, w) != 0) {
- error("%llu: failed to create new thread for web client.");
- w->obsolete = 1;
- }
- else if(pthread_detach(w->thread) != 0) {
- error("%llu: Cannot request detach of newly created web client thread.", w->id);
- w->obsolete = 1;
- }
- }
- else debug(D_WEB_CLIENT, "LISTENER: select() didn't do anything.");
-
- }
- //else {
- // debug(D_WEB_CLIENT, "LISTENER: select() timeout.");
- //}
-
- // cleanup unused clients
- for(w = web_clients; w ; w = w?w->next:NULL) {
- if(w->obsolete) {
- debug(D_WEB_CLIENT, "%llu: Removing client.", w->id);
- // pthread_join(w->thread, NULL);
- w = web_client_free(w);
- log_allocations();
- }
- }
- }
-
- error("LISTENER: exit!");
-
- if(listen_fd >= 0) close(listen_fd);
- exit(2);
-
- return NULL;
+#define CLEANUP_EVERY_EVENTS 100
+
+void *socket_listen_main_multi_threaded(void *ptr) {
+ struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
+
+ web_server_mode = WEB_SERVER_MODE_MULTI_THREADED;
+ info("Multi-threaded WEB SERVER thread created with task id %d", gettid());
+
+ struct web_client *w;
+ int retval, counter = 0;
+
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+ error("Cannot set pthread cancel type to DEFERRED.");
+
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+ error("Cannot set pthread cancel state to ENABLE.");
+
+ if(!listen_fds_count)
+ fatal("LISTENER: No sockets to listen to.");
+
+ struct pollfd *fds = callocz(sizeof(struct pollfd), listen_fds_count);
+
+ size_t i;
+ for(i = 0; i < listen_fds_count ;i++) {
+ fds[i].fd = listen_fds[i];
+ fds[i].events = POLLIN;
+ fds[i].revents = 0;
+
+ info("Listening on '%s'", (listen_fds_names[i])?listen_fds_names[i]:"UNKNOWN");
+ }
+
+ int timeout = 10 * 1000;
+
+ for(;;) {
+ // debug(D_WEB_CLIENT, "LISTENER: Waiting...");
+ retval = poll(fds, listen_fds_count, timeout);
+
+ if(unlikely(retval == -1)) {
+ error("LISTENER: poll() failed.");
+ continue;
+ }
+ else if(unlikely(!retval)) {
+ debug(D_WEB_CLIENT, "LISTENER: select() timeout.");
+ counter = 0;
+ cleanup_web_clients();
+ continue;
+ }
+
+ for(i = 0 ; i < listen_fds_count ; i++) {
+ short int revents = fds[i].revents;
+
+ // check for new incoming connections
+ if(revents & POLLIN || revents & POLLPRI) {
+ fds[i].revents = 0;
+
+ w = web_client_create(fds[i].fd);
+ if(unlikely(!w)) {
+ // no need for error log - web_client_create already logged the error
+ continue;
+ }
+
+ if(pthread_create(&w->thread, NULL, web_client_main, w) != 0) {
+ error("%llu: failed to create new thread for web client.", w->id);
+ w->obsolete = 1;
+ }
+ else if(pthread_detach(w->thread) != 0) {
+ error("%llu: Cannot request detach of newly created web client thread.", w->id);
+ w->obsolete = 1;
+ }
+ }
+ }
+
+ // cleanup unused clients
+ counter++;
+ if(counter >= CLEANUP_EVERY_EVENTS) {
+ counter = 0;
+ cleanup_web_clients();
+ }
+ }
+
+ debug(D_WEB_CLIENT, "LISTENER: exit!");
+ close_listen_sockets();
+
+ freez(fds);
+
+ static_thread->enabled = 0;
+ pthread_exit(NULL);
+ return NULL;
+}
+
+struct web_client *single_threaded_clients[FD_SETSIZE];
+
+static inline int single_threaded_link_client(struct web_client *w, fd_set *ifds, fd_set *ofds, fd_set *efds, int *max) {
+ if(unlikely(w->obsolete || w->dead || (!w->wait_receive && !w->wait_send)))
+ return 1;
+
+ if(unlikely(w->ifd < 0 || w->ifd >= FD_SETSIZE || w->ofd < 0 || w->ofd >= FD_SETSIZE)) {
+ error("%llu: invalid file descriptor, ifd = %d, ofd = %d (required 0 <= fd < FD_SETSIZE (%d)", w->id, w->ifd, w->ofd, FD_SETSIZE);
+ return 1;
+ }
+
+ FD_SET(w->ifd, efds);
+ if(unlikely(*max < w->ifd)) *max = w->ifd;
+
+ if(unlikely(w->ifd != w->ofd)) {
+ if(*max < w->ofd) *max = w->ofd;
+ FD_SET(w->ofd, efds);
+ }
+
+ if(w->wait_receive) FD_SET(w->ifd, ifds);
+ if(w->wait_send) FD_SET(w->ofd, ofds);
+
+ single_threaded_clients[w->ifd] = w;
+ single_threaded_clients[w->ofd] = w;
+
+ return 0;
+}
+
+static inline int single_threaded_unlink_client(struct web_client *w, fd_set *ifds, fd_set *ofds, fd_set *efds) {
+ FD_CLR(w->ifd, efds);
+ if(unlikely(w->ifd != w->ofd)) FD_CLR(w->ofd, efds);
+
+ if(w->wait_receive) FD_CLR(w->ifd, ifds);
+ if(w->wait_send) FD_CLR(w->ofd, ofds);
+
+ single_threaded_clients[w->ifd] = NULL;
+ single_threaded_clients[w->ofd] = NULL;
+
+ if(unlikely(w->obsolete || w->dead || (!w->wait_receive && !w->wait_send)))
+ return 1;
+
+ return 0;