TODO cleanup, rename sendFrameWrite to startFrameWrite
diff --git a/server.go b/server.go
index f194fdf..9be322b 100644
--- a/server.go
+++ b/server.go
@@ -67,20 +67,17 @@
 // TODO: send PING frames to idle clients and disconnect them if no
 // reply
 
-// TODO: don't keep the writeFrames goroutine active. turn it off when no frames
-// are enqueued.
-
-// TODO: for bonus points: turn off the serve goroutine also when
-// idle, so an idle conn only has the readFrames goroutine
-// active. (which could also be optimized probably to pin less memory
-// in crypto/tls). This would involve tracking when the serve
-// goroutine is active (atomic int32 read/CAS probably?) and starting
-// it up when frames arrive, and shutting it down when all handlers
-// exit. the occasional PING packets could use time.AfterFunc to call
-// sc.wakeStartServeLoop() (which is a no-op if already running) and
-// then queue the PING write as normal. The serve loop would then exit
-// in most cases (if no Handlers running) and not be woken up again
-// until the PING packet returns.
+// TODO: for bonus points: turn off the serve goroutine when idle, so
+// an idle conn only has the readFrames goroutine active. (which could
+// also be optimized probably to pin less memory in crypto/tls). This
+// would involve tracking when the serve goroutine is active (atomic
+// int32 read/CAS probably?) and starting it up when frames arrive,
+// and shutting it down when all handlers exit. the occasional PING
+// packets could use time.AfterFunc to call sc.wakeStartServeLoop()
+// (which is a no-op if already running) and then queue the PING write
+// as normal. The serve loop would then exit in most cases (if no
+// Handlers running) and not be woken up again until the PING packet
+// returns.
 
 // Server is an HTTP/2 server.
 type Server struct {
@@ -164,7 +161,7 @@
 		readFrameCh:       make(chan frameAndGate),
 		readFrameErrCh:    make(chan error, 1), // must be buffered for 1
 		wantWriteFrameCh:  make(chan frameWriteMsg, 8),
-		wroteFrameCh:      make(chan struct{}, 1), // TODO: consider 0. will deadlock currently in sendFrameWrite in sentReset case
+		wroteFrameCh:      make(chan struct{}, 1), // TODO: consider 0. will deadlock currently in startFrameWrite in sentReset case
 		flow:              newFlow(initialWindowSize),
 		doneServing:       make(chan struct{}),
 		advMaxStreams:     srv.maxConcurrentStreams(),
@@ -210,14 +207,12 @@
 	readFrameCh      chan frameAndGate // written by serverConn.readFrames
 	readFrameErrCh   chan error
 	wantWriteFrameCh chan frameWriteMsg // from handlers -> serve
-	wroteFrameCh     chan struct{}      // from writeFrames -> serve, tickles more frame writes
+	wroteFrameCh     chan struct{}      // from writeFrameAsync -> serve, tickles more frame writes
 	testHookCh       chan func()        // code to run on the serve loop
-
-	serveG goroutineLock // used to verify funcs are on serve()
-	writeG goroutineLock // used to verify things running on writeLoop
-	flow   *flow         // connection-wide (not stream-specific) flow control
+	flow             *flow              // connection-wide (not stream-specific) flow control
 
 	// Everything following is owned by the serve loop; use serveG.check():
+	serveG                goroutineLock // used to verify funcs are on serve()
 	pushEnabled           bool
 	sawFirstSettings      bool // got the initial SETTINGS frame after the preface
 	needToSendSettingsAck bool
@@ -241,7 +236,8 @@
 	shutdownTimerCh       <-chan time.Time // nil until used
 	shutdownTimer         *time.Timer      // nil until used
 
-	// Owned by the writeFrames goroutine; use writeG.check():
+	// Owned by the writeFrameAsync goroutine; use writeG.check():
+	writeG         goroutineLock // used to verify things running on writeFrameAsync
 	headerWriteBuf bytes.Buffer
 	hpackEncoder   *hpack.Encoder
 }
@@ -594,20 +590,19 @@
 	sc.serveG.check()
 	// Fast path for common case:
 	if !sc.writingFrame {
-		sc.sendFrameWrite(wm)
+		sc.startFrameWrite(wm)
 		return
 	}
 	sc.writeQueue = append(sc.writeQueue, wm) // TODO: proper scheduler
 }
 
-// sendFrameWrite sends a frame to the writeFrames goroutine.
-// Only one frame can be in-flight at a time.
-// sendFrameWrite also updates stream state right before the frame is
-// sent to be written.
-func (sc *serverConn) sendFrameWrite(wm frameWriteMsg) {
+// startFrameWrite starts a goroutine to write wm (in a separate
+// goroutine since that might block on the network), and updates the
+// serve goroutine's state about the world, updated from info in wm.
+func (sc *serverConn) startFrameWrite(wm frameWriteMsg) {
 	sc.serveG.check()
 	if sc.writingFrame {
-		panic("invariant")
+		panic("internal error: can only be writing one frame at a time")
 	}
 
 	st := wm.stream
@@ -660,7 +655,7 @@
 	}
 	if sc.needToSendGoAway {
 		sc.needToSendGoAway = false
-		sc.sendFrameWrite(frameWriteMsg{
+		sc.startFrameWrite(frameWriteMsg{
 			write: (*serverConn).writeGoAwayFrame,
 			v: &goAwayParams{
 				maxStreamID: sc.maxStreamID,
@@ -670,8 +665,8 @@
 		return
 	}
 	if len(sc.writeQueue) == 0 && sc.needsFrameFlush {
-		sc.sendFrameWrite(frameWriteMsg{write: (*serverConn).flushFrameWriter})
-		sc.needsFrameFlush = false // after sendFrameWrite, since it sets this true
+		sc.startFrameWrite(frameWriteMsg{write: (*serverConn).flushFrameWriter})
+		sc.needsFrameFlush = false // after startFrameWrite, since it sets this true
 		return
 	}
 	if sc.inGoAway {
@@ -680,7 +675,7 @@
 	}
 	if sc.needToSendSettingsAck {
 		sc.needToSendSettingsAck = false
-		sc.sendFrameWrite(frameWriteMsg{write: (*serverConn).writeSettingsAck})
+		sc.startFrameWrite(frameWriteMsg{write: (*serverConn).writeSettingsAck})
 		return
 	}
 	if len(sc.writeQueue) == 0 {
@@ -716,7 +711,7 @@
 	// (because a SETTINGS frame changed our max frame size while
 	// a stream was open and writing) and cut it up into smaller
 	// bits.
-	sc.sendFrameWrite(wm)
+	sc.startFrameWrite(wm)
 }
 
 func (sc *serverConn) goAway(code ErrCode) {
@@ -1277,7 +1272,7 @@
 }
 
 type frameWriteMsg struct {
-	// write runs on the writeFrames goroutine.
+	// write runs on the writeFrameAsync goroutine.
 	write func(sc *serverConn, streamID uint32, v interface{}) error
 
 	v         interface{} // passed to write