]> PatchyGit - patchyircd.git/commitdiff
sendClient: never block
authorEgor <egor@patchysicp.xyz>
Wed, 18 Oct 2023 20:00:17 +0000 (23:00 +0300)
committerEgor <egor@patchysicp.xyz>
Wed, 18 Oct 2023 20:00:17 +0000 (23:00 +0300)
common.h
main.c
net.c

index 3c5243b..6681546 100644 (file)
--- a/common.h
+++ b/common.h
@@ -43,6 +43,8 @@ struct client_data {
        unsigned short peerport;
        char   buf[2048];
        size_t bufLen;
+       char   wbuf[8192];
+       size_t wbufLen;
        char   umode;
        char   regstate;
        struct trie_node *channels;
@@ -51,7 +53,9 @@ struct client_data {
        time_t signon;
        time_t lastactivity;
        unsigned int caps;
-       char   want_write_for_read;
+       char   want_write_for_read :1;
+       char   want_read_for_write :1;
+       char   want_write_for_write :1;
 };
 struct channel {
        char *key;
diff --git a/main.c b/main.c
index b7f1743..c00c1ac 100644 (file)
--- a/main.c
+++ b/main.c
@@ -321,7 +321,7 @@ int main(int argc, char *argv[])
 #endif
                for (struct client_data *cl = clients; cl; cl = cl->next) {
                        FD_SET(cl->FD, &readfds);
-                       if (cl->want_write_for_read)
+                       if (cl->want_write_for_read || cl->want_write_for_write)
                                FD_SET(cl->FD, &writefds);
                }
 
@@ -360,8 +360,12 @@ int main(int argc, char *argv[])
                // Respond to already connected clients
                for (struct client_data *cl = clients; cl;) {
                        struct client_data *next = cl->next;
-                       if (FD_ISSET(cl->FD, &readfds) || (cl->want_write_for_read && FD_ISSET(cl->FD, &writefds)))
+                       int isr = FD_ISSET(cl->FD, &readfds);
+                       int isw = FD_ISSET(cl->FD, &writefds);
+                       if (isr || (cl->want_write_for_read && isw))
                                recvProc(cl);
+                       if ((cl->want_read_for_write && isr) || (cl->want_write_for_write && isw))
+                               sendClient(cl, 0, 0);
                        cl = next;
                }
        }
diff --git a/net.c b/net.c
index 76c91e6..90d4c3c 100644 (file)
--- a/net.c
+++ b/net.c
@@ -12,6 +12,26 @@ struct client_data *allocClient(){
        return NewClient;
 };
 
+#ifdef HAS_TLS
+static void block(struct client_data *cl, int out)
+{
+#ifdef _WIN32
+       fd_set fds;
+       FD_ZERO(&fds);
+       FD_SET(cl->FD, &fds);
+       if (out)
+               select(cl->FD+1, 0, &fds, 0, 0);
+       else
+               select(cl->FD+1, &fds, 0, 0, 0);
+#else
+       struct pollfd pfd;
+       pfd.fd = cl->FD;
+       pfd.events = out ? POLLOUT : POLLIN;
+       poll(&pfd, 1, 0);
+#endif
+}
+#endif
+
 void killConnection(struct client_data *cl, bool byChoice) {
        if (!cl) return;
        for (struct client_data **next = &clients; *next; next = &(*next)->next) {
@@ -34,9 +54,13 @@ void killConnection(struct client_data *cl, bool byChoice) {
                if (cl->cctx) {
                        // FIXME: this blocks
                        int rv;
-                       do {
+                       for (;;) {
                                rv = tls_close(cl->cctx);
-                       } while (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT);
+                               if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT)
+                                       block(cl, rv == TLS_WANT_POLLOUT);
+                               else
+                                       break;
+                       }
                        if (rv == -1) {
                                fprintf(stderr, "tls_close (%"PRISocket"): %s\n", cl->FD, tls_error(cl->cctx));
                        }
@@ -78,24 +102,6 @@ void killConnection(struct client_data *cl, bool byChoice) {
        free(cl);
 }
 
-static void block(struct client_data *cl, int out)
-{
-#ifdef _WIN32
-       fd_set fds;
-       FD_ZERO(&fds);
-       FD_SET(cl->FD, &fds);
-       if (out)
-               select(cl->FD+1, 0, &fds, 0, 0);
-       else
-               select(cl->FD+1, &fds, 0, 0, 0);
-#else
-       struct pollfd pfd;
-       pfd.fd = cl->FD;
-       pfd.events = out ? POLLOUT : POLLIN;
-       poll(&pfd, 1, 0);
-#endif
-}
-
 void recvProc(struct client_data *cl) {
        ssize_t bytesRead;
        char *x;
@@ -157,24 +163,27 @@ void recvProc(struct client_data *cl) {
        }
 }
 
-void sendClient(struct client_data *cl, const char *buf, size_t len)
+/* returns the count of bytes which were not sent */
+static int sendClientRaw(struct client_data *cl, const char *buf, size_t len)
 {
+       cl->want_read_for_write = 0;
+       cl->want_write_for_write = 0;
        if (cl->FD == (SOCKET)INVALID_SOCKET)
-               return;
+               return 0;
        fprintf(stderr, "SERVER %"PRISocket": %.*s", cl->FD, (int)len, (char*)buf);
-       // TODO: error handling
-       // FIXME: blocks, probably the reason why server is slow
+       /* FIXME: error handling */
 #ifdef HAS_TLS
        if (cl->cctx) {
                while (len) {
                        int rv = tls_write(cl->cctx, buf, len);
-                       if (!rv)
-                               break;
-                       if (rv == -1)
+                       if (rv == 0 || rv == -1) {
                                break;
-                       if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT) {
-                               block(cl, rv == TLS_WANT_POLLOUT);
-                               continue;
+                       } else if (rv == TLS_WANT_POLLIN) {
+                               cl->want_read_for_write = 1;
+                               return len;
+                       } else if (rv == TLS_WANT_POLLOUT) {
+                               cl->want_write_for_write = 1;
+                               return len;
                        }
                        buf += rv;
                        len -= rv;
@@ -188,8 +197,8 @@ void sendClient(struct client_data *cl, const char *buf, size_t len)
                                break;
                        if (rv == -1) {
                                if (net_wouldblock()) {
-                                       block(cl, 1);
-                                       continue;
+                                       cl->want_write_for_write = 1;
+                                       return len;
                                } else {
                                        break;
                                }
@@ -198,4 +207,30 @@ void sendClient(struct client_data *cl, const char *buf, size_t len)
                        len -= rv;
                }
        }
+       return len;
+}
+
+void sendClient(struct client_data *cl, const char *buf, size_t len)
+{
+       if (cl->wbufLen + len > sizeof(cl->wbuf)) {
+               /* Buffer overrun */
+               /* FIXME: probably should terminate the connection at this point */
+               len = 0;
+               /* we don't return here, because we still want to push old data if possible */
+       }
+       if (!cl->wbufLen) {
+               if (len) {
+                       int left = sendClientRaw(cl, buf, len);
+                       memcpy(cl->wbuf, buf+len-left, left);
+                       cl->wbufLen = left;
+               }
+               return;
+       }
+       if (len) {
+               memcpy(cl->wbuf + cl->wbufLen, buf, len);
+               cl->wbufLen += len;
+       }
+       int left = sendClientRaw(cl, cl->wbuf, cl->wbufLen);
+       memmove(cl->wbuf, cl->wbuf+cl->wbufLen-left, left);
+       cl->wbufLen = left;
 }