diff options
Diffstat (limited to 'web/server/single/single-threaded.c')
-rw-r--r-- | web/server/single/single-threaded.c | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/web/server/single/single-threaded.c b/web/server/single/single-threaded.c new file mode 100644 index 000000000..7e89ee683 --- /dev/null +++ b/web/server/single/single-threaded.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#define WEB_SERVER_INTERNALS 1 +#include "single-threaded.h" + +// -------------------------------------------------------------------------------------- +// the main socket listener - SINGLE-THREADED + +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(web_client_check_dead(w) || (!web_client_has_wait_receive(w) && !web_client_has_wait_send(w)))) { + return 1; + } + + if(unlikely(w->ifd < 0 || w->ifd >= (int)FD_SETSIZE || w->ofd < 0 || w->ofd >= (int)FD_SETSIZE)) { + error("%llu: invalid file descriptor, ifd = %d, ofd = %d (required 0 <= fd < FD_SETSIZE (%d)", w->id, w->ifd, w->ofd, (int)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(web_client_has_wait_receive(w)) FD_SET(w->ifd, ifds); + if(web_client_has_wait_send(w)) 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(web_client_has_wait_receive(w)) FD_CLR(w->ifd, ifds); + if(web_client_has_wait_send(w)) FD_CLR(w->ofd, ofds); + + single_threaded_clients[w->ifd] = NULL; + single_threaded_clients[w->ofd] = NULL; + + if(unlikely(web_client_check_dead(w) || (!web_client_has_wait_receive(w) && !web_client_has_wait_send(w)))) { + return 1; + } + + return 0; +} + +static void socket_listen_main_single_threaded_cleanup(void *data) { + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)data; + static_thread->enabled = NETDATA_MAIN_THREAD_EXITING; + + info("closing all sockets..."); + listen_sockets_close(&api_sockets); + + info("freeing web clients cache..."); + web_client_cache_destroy(); + + info("cleanup completed."); + static_thread->enabled = NETDATA_MAIN_THREAD_EXITED; +} + +void *socket_listen_main_single_threaded(void *ptr) { + netdata_thread_cleanup_push(socket_listen_main_single_threaded_cleanup, ptr); + web_server_mode = WEB_SERVER_MODE_SINGLE_THREADED; + web_server_is_multithreaded = 0; + + struct web_client *w; + + if(!api_sockets.opened) + fatal("LISTENER: no listen sockets available."); + + size_t i; + for(i = 0; i < (size_t)FD_SETSIZE ; i++) + single_threaded_clients[i] = NULL; + + fd_set ifds, ofds, efds, rifds, rofds, refds; + FD_ZERO (&ifds); + FD_ZERO (&ofds); + FD_ZERO (&efds); + int fdmax = 0; + + for(i = 0; i < api_sockets.opened ; i++) { + if (api_sockets.fds[i] < 0 || api_sockets.fds[i] >= (int)FD_SETSIZE) + fatal("LISTENER: Listen socket %d is not ready, or invalid.", api_sockets.fds[i]); + + info("Listening on '%s'", (api_sockets.fds_names[i])?api_sockets.fds_names[i]:"UNKNOWN"); + + FD_SET(api_sockets.fds[i], &ifds); + FD_SET(api_sockets.fds[i], &efds); + if(fdmax < api_sockets.fds[i]) + fdmax = api_sockets.fds[i]; + } + + while(!netdata_exit) { + debug(D_WEB_CLIENT_ACCESS, "LISTENER: single threaded web server waiting (fdmax = %d)...", fdmax); + + struct timeval tv = { .tv_sec = 1, .tv_usec = 0 }; + rifds = ifds; + rofds = ofds; + refds = efds; + int retval = select(fdmax+1, &rifds, &rofds, &refds, &tv); + + if(unlikely(retval == -1)) { + error("LISTENER: select() failed."); + continue; + } + else if(likely(retval)) { + debug(D_WEB_CLIENT_ACCESS, "LISTENER: got something."); + + for(i = 0; i < api_sockets.opened ; i++) { + if (FD_ISSET(api_sockets.fds[i], &rifds)) { + debug(D_WEB_CLIENT_ACCESS, "LISTENER: new connection."); + w = web_client_create_on_listenfd(api_sockets.fds[i]); + if(unlikely(!w)) + continue; + + if(api_sockets.fds_families[i] == AF_UNIX) + web_client_set_unix(w); + else + web_client_set_tcp(w); + + if (single_threaded_link_client(w, &ifds, &ofds, &ifds, &fdmax) != 0) { + web_client_release(w); + } + } + } + + for(i = 0 ; i <= (size_t)fdmax ; i++) { + if(likely(!FD_ISSET(i, &rifds) && !FD_ISSET(i, &rofds) && !FD_ISSET(i, &refds))) + continue; + + w = single_threaded_clients[i]; + if(unlikely(!w)) { + // error("no client on slot %zu", i); + continue; + } + + if(unlikely(single_threaded_unlink_client(w, &ifds, &ofds, &efds) != 0)) { + // error("failed to unlink client %zu", i); + web_client_release(w); + continue; + } + + if (unlikely(FD_ISSET(w->ifd, &refds) || FD_ISSET(w->ofd, &refds))) { + // error("no input on client %zu", i); + web_client_release(w); + continue; + } + + if (unlikely(web_client_has_wait_receive(w) && FD_ISSET(w->ifd, &rifds))) { + if (unlikely(web_client_receive(w) < 0)) { + // error("cannot read from client %zu", i); + web_client_release(w); + continue; + } + + if (w->mode != WEB_CLIENT_MODE_FILECOPY) { + debug(D_WEB_CLIENT, "%llu: Processing received data.", w->id); + web_client_process_request(w); + } + } + + if (unlikely(web_client_has_wait_send(w) && FD_ISSET(w->ofd, &rofds))) { + if (unlikely(web_client_send(w) < 0)) { + // error("cannot send data to client %zu", i); + debug(D_WEB_CLIENT, "%llu: Cannot send data to client. Closing client.", w->id); + web_client_release(w); + continue; + } + } + + if(unlikely(single_threaded_link_client(w, &ifds, &ofds, &efds, &fdmax) != 0)) { + // error("failed to link client %zu", i); + web_client_release(w); + } + } + } + else { + debug(D_WEB_CLIENT_ACCESS, "LISTENER: single threaded web server timeout."); + } + } + + netdata_thread_cleanup_pop(1); + return NULL; +} + + |