From c8fc56dca7d42025fbe897402335df4870bf8831 Mon Sep 17 00:00:00 2001
From: John Kirkham <jakirkham@gmail.com>
Date: Wed, 8 Sep 2021 15:31:57 -0700
Subject: [PATCH 1/5] Queue write only after processing all buffers

Allows `writelines` to leverage vectorized IO within uvloop to send
multiple buffers in one `sendmsg` call.
---
 uvloop/handles/stream.pyx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/uvloop/handles/stream.pyx b/uvloop/handles/stream.pyx
index 4757ce7a..67e5e98b 100644
--- a/uvloop/handles/stream.pyx
+++ b/uvloop/handles/stream.pyx
@@ -446,7 +446,6 @@ cdef class UVStream(UVBaseTransport):
             return
 
         self._maybe_pause_protocol()
-        self._loop._queue_write(self)
 
     cdef inline _exec_write(self):
         cdef:
@@ -680,6 +679,7 @@ cdef class UVStream(UVBaseTransport):
             self._conn_lost += 1
             return
         self._write(buf)
+        self._loop._queue_write(self)
 
     def writelines(self, bufs):
         self._ensure_alive()
@@ -691,6 +691,7 @@ cdef class UVStream(UVBaseTransport):
             return
         for buf in bufs:
             self._write(buf)
+        self._loop._queue_write(self)
 
     def write_eof(self):
         self._ensure_alive()

From a12214acec8f3f9dc99968e3a820265f4861f304 Mon Sep 17 00:00:00 2001
From: John Kirkham <jakirkham@gmail.com>
Date: Fri, 17 Sep 2021 13:18:55 -0700
Subject: [PATCH 2/5] Add copyright header

---
 uvloop/handles/stream.pyx | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/uvloop/handles/stream.pyx b/uvloop/handles/stream.pyx
index 67e5e98b..6aa0fa0d 100644
--- a/uvloop/handles/stream.pyx
+++ b/uvloop/handles/stream.pyx
@@ -1,3 +1,8 @@
+# MIT and Apache 2.0
+#
+# Copyright (c) 2021, NVIDIA CORPORATION.
+
+
 DEF __PREALLOCED_BUFS = 4
 
 

From 4cd84a31e1df924d413a04082b5a04e4476d1c12 Mon Sep 17 00:00:00 2001
From: John Kirkham <jakirkham@gmail.com>
Date: Tue, 5 Oct 2021 11:38:50 -0700
Subject: [PATCH 3/5] Drop file copyright and update license copyright

---
 LICENSE-APACHE            | 2 +-
 LICENSE-MIT               | 2 +-
 uvloop/handles/stream.pyx | 5 -----
 3 files changed, 2 insertions(+), 7 deletions(-)

diff --git a/LICENSE-APACHE b/LICENSE-APACHE
index a1de698c..5f66d4ee 100644
--- a/LICENSE-APACHE
+++ b/LICENSE-APACHE
@@ -1,4 +1,4 @@
-Copyright (c) 2015-present MagicStack Inc.  http://magic.io
+Copyright (C) 2016-present the uvloop authors and contributors.
 
                                  Apache License
                            Version 2.0, January 2004
diff --git a/LICENSE-MIT b/LICENSE-MIT
index e6b42c6f..40fd0230 100644
--- a/LICENSE-MIT
+++ b/LICENSE-MIT
@@ -1,6 +1,6 @@
 The MIT License
 
-Copyright (c) 2015-present MagicStack Inc.  http://magic.io
+Copyright (C) 2016-present the uvloop authors and contributors.
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/uvloop/handles/stream.pyx b/uvloop/handles/stream.pyx
index 6aa0fa0d..67e5e98b 100644
--- a/uvloop/handles/stream.pyx
+++ b/uvloop/handles/stream.pyx
@@ -1,8 +1,3 @@
-# MIT and Apache 2.0
-#
-# Copyright (c) 2021, NVIDIA CORPORATION.
-
-
 DEF __PREALLOCED_BUFS = 4
 
 

From 3e2016f6a2009463127a88bd5a5cfdc846ede737 Mon Sep 17 00:00:00 2001
From: Fantix King <fantix.king@gmail.com>
Date: Fri, 9 Sep 2022 13:32:34 -0400
Subject: [PATCH 4/5] Buffer all data first before send in writelines()

---
 uvloop/handles/stream.pxd |  3 ++-
 uvloop/handles/stream.pyx | 18 ++++++++++--------
 2 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/uvloop/handles/stream.pxd b/uvloop/handles/stream.pxd
index 21ac6279..7c0582fa 100644
--- a/uvloop/handles/stream.pxd
+++ b/uvloop/handles/stream.pxd
@@ -31,7 +31,8 @@ cdef class UVStream(UVBaseTransport):
     cdef inline __reading_started(self)
     cdef inline __reading_stopped(self)
 
-    cdef inline _write(self, object data)
+    cdef inline _buffer_write(self, object data)
+    cdef inline _write(self)
     cdef inline _try_write(self, object data)
 
     cdef _close(self)
diff --git a/uvloop/handles/stream.pyx b/uvloop/handles/stream.pyx
index 67e5e98b..13dd9af1 100644
--- a/uvloop/handles/stream.pyx
+++ b/uvloop/handles/stream.pyx
@@ -162,7 +162,7 @@ cdef class _StreamWriteContext:
                     PyObject_GetBuffer(
                         buf, &p_pybufs[py_bufs_len], PyBUF_SIMPLE)
                 except Exception:
-                    # This shouldn't ever happen, as `UVStream._write`
+                    # This shouldn't ever happen, as `UVStream._buffer_write`
                     # casts non-bytes objects to `memoryviews`.
                     ctx.py_bufs_len = py_bufs_len
                     ctx.free_bufs()
@@ -407,7 +407,7 @@ cdef class UVStream(UVBaseTransport):
 
         return written
 
-    cdef inline _write(self, object data):
+    cdef inline _buffer_write(self, object data):
         cdef int dlen
 
         if not PyBytes_CheckExact(data):
@@ -420,6 +420,7 @@ cdef class UVStream(UVBaseTransport):
         self._buffer_size += dlen
         self._buffer.append(data)
 
+    cdef inline _write(self):
         if (not self._protocol_paused and
                 (<uv.uv_stream_t*>self._handle).write_queue_size == 0 and
                 self._buffer_size > self._high_water):
@@ -443,9 +444,10 @@ cdef class UVStream(UVBaseTransport):
                 # If not all of the data was sent successfully,
                 # we might need to pause the protocol.
                 self._maybe_pause_protocol()
-            return
 
-        self._maybe_pause_protocol()
+        elif self._buffer_size > 0:
+            self._maybe_pause_protocol()
+            self._loop._queue_write(self)
 
     cdef inline _exec_write(self):
         cdef:
@@ -678,8 +680,8 @@ cdef class UVStream(UVBaseTransport):
         if self._conn_lost:
             self._conn_lost += 1
             return
-        self._write(buf)
-        self._loop._queue_write(self)
+        self._buffer_write(buf)
+        self._write()
 
     def writelines(self, bufs):
         self._ensure_alive()
@@ -690,8 +692,8 @@ cdef class UVStream(UVBaseTransport):
             self._conn_lost += 1
             return
         for buf in bufs:
-            self._write(buf)
-        self._loop._queue_write(self)
+            self._buffer_write(buf)
+        self._write()
 
     def write_eof(self):
         self._ensure_alive()

From bfe9b8000432b6d1b3834cd4c56e57071bc762a1 Mon Sep 17 00:00:00 2001
From: Fantix King <fantix.king@gmail.com>
Date: Fri, 9 Sep 2022 16:44:16 -0400
Subject: [PATCH 5/5] CRF: rename and add comments

---
 uvloop/handles/stream.pxd | 10 ++++++++--
 uvloop/handles/stream.pyx |  6 +++---
 2 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/uvloop/handles/stream.pxd b/uvloop/handles/stream.pxd
index 7c0582fa..da6b8f67 100644
--- a/uvloop/handles/stream.pxd
+++ b/uvloop/handles/stream.pxd
@@ -21,7 +21,6 @@ cdef class UVStream(UVBaseTransport):
     cdef inline _init(self, Loop loop, object protocol, Server server,
                       object waiter, object context)
 
-    cdef inline _exec_write(self)
 
     cdef inline _shutdown(self)
     cdef inline _accept(self, UVStream server)
@@ -31,8 +30,15 @@ cdef class UVStream(UVBaseTransport):
     cdef inline __reading_started(self)
     cdef inline __reading_stopped(self)
 
+    # The user API firstly calls _buffer_write() to buffer up user data chunks,
+    # potentially multiple times in writelines(), and then call _start_write()
+    # to start writing either immediately or in the next iteration.
     cdef inline _buffer_write(self, object data)
-    cdef inline _write(self)
+    cdef inline _start_write(self)
+
+    # _exec_write() is the method that does the actual send, and _try_write()
+    # is a fast-path used in _exec_write() to send a single chunk.
+    cdef inline _exec_write(self)
     cdef inline _try_write(self, object data)
 
     cdef _close(self)
diff --git a/uvloop/handles/stream.pyx b/uvloop/handles/stream.pyx
index 13dd9af1..d1bb4ced 100644
--- a/uvloop/handles/stream.pyx
+++ b/uvloop/handles/stream.pyx
@@ -420,7 +420,7 @@ cdef class UVStream(UVBaseTransport):
         self._buffer_size += dlen
         self._buffer.append(data)
 
-    cdef inline _write(self):
+    cdef inline _start_write(self):
         if (not self._protocol_paused and
                 (<uv.uv_stream_t*>self._handle).write_queue_size == 0 and
                 self._buffer_size > self._high_water):
@@ -681,7 +681,7 @@ cdef class UVStream(UVBaseTransport):
             self._conn_lost += 1
             return
         self._buffer_write(buf)
-        self._write()
+        self._start_write()
 
     def writelines(self, bufs):
         self._ensure_alive()
@@ -693,7 +693,7 @@ cdef class UVStream(UVBaseTransport):
             return
         for buf in bufs:
             self._buffer_write(buf)
-        self._write()
+        self._start_write()
 
     def write_eof(self):
         self._ensure_alive()