Sfoglia il codice sorgente

fixes to irc/socket.go

* fix a race condition: a call to `Write` does not spawn a writer goroutine
  if the trylock is held, so `BlockingWrite` must check for fresh data after
  releasing the trylock
* streamline some close/finalize logic
tags/v1.0.0-rc1
Shivaram Lingamneni 5 anni fa
parent
commit
c8cf0befc6
1 ha cambiato i file con 24 aggiunte e 9 eliminazioni
  1. 24
    9
      irc/socket.go

+ 24
- 9
irc/socket.go Vedi File

134
 		prospectiveLen := socket.totalLength + len(data)
134
 		prospectiveLen := socket.totalLength + len(data)
135
 		if prospectiveLen > socket.maxSendQBytes {
135
 		if prospectiveLen > socket.maxSendQBytes {
136
 			socket.sendQExceeded = true
136
 			socket.sendQExceeded = true
137
+			socket.closed = true
137
 			err = errSendQExceeded
138
 			err = errSendQExceeded
138
 		} else {
139
 		} else {
139
 			socket.buffers = append(socket.buffers, data)
140
 			socket.buffers = append(socket.buffers, data)
161
 		return
162
 		return
162
 	}
163
 	}
163
 
164
 
165
+	// after releasing the semaphore, we must check for fresh data, same as `send`
166
+	defer func() {
167
+		if socket.readyToWrite() {
168
+			socket.wakeWriter()
169
+		}
170
+	}()
171
+
164
 	// blocking acquire of the trylock
172
 	// blocking acquire of the trylock
165
 	socket.writerSemaphore.Acquire()
173
 	socket.writerSemaphore.Acquire()
166
 	defer socket.writerSemaphore.Release()
174
 	defer socket.writerSemaphore.Release()
206
 	socket.Lock()
214
 	socket.Lock()
207
 	defer socket.Unlock()
215
 	defer socket.Unlock()
208
 	// on the first time observing socket.closed, we still have to write socket.finalData
216
 	// on the first time observing socket.closed, we still have to write socket.finalData
209
-	return !socket.finalized && (socket.totalLength > 0 || socket.closed || socket.sendQExceeded)
217
+	return !socket.finalized && (socket.totalLength > 0 || socket.closed)
210
 }
218
 }
211
 
219
 
212
 // send actually writes messages to socket.Conn; it may block
220
 // send actually writes messages to socket.Conn; it may block
238
 	buffers := socket.buffers
246
 	buffers := socket.buffers
239
 	socket.buffers = nil
247
 	socket.buffers = nil
240
 	socket.totalLength = 0
248
 	socket.totalLength = 0
249
+	closed = socket.closed
241
 	socket.Unlock()
250
 	socket.Unlock()
242
 
251
 
243
-	// on Linux, the runtime will optimize this into a single writev(2) call:
244
-	_, err := (*net.Buffers)(&buffers).WriteTo(socket.conn)
245
-
246
-	socket.Lock()
247
-	shouldClose := (err != nil) || socket.closed || socket.sendQExceeded
248
-	socket.Unlock()
252
+	var err error
253
+	if !closed && len(buffers) > 0 {
254
+		// on Linux, the runtime will optimize this into a single writev(2) call:
255
+		_, err = (*net.Buffers)(&buffers).WriteTo(socket.conn)
256
+	}
249
 
257
 
250
-	if shouldClose {
258
+	closed = closed || err != nil
259
+	if closed {
251
 		socket.finalize()
260
 		socket.finalize()
252
 	}
261
 	}
253
-	return shouldClose
262
+	return
254
 }
263
 }
255
 
264
 
256
 // mark closed and send final data. you must be holding the semaphore to call this:
265
 // mark closed and send final data. you must be holding the semaphore to call this:
258
 	// mark the socket closed (if someone hasn't already), then write error lines
267
 	// mark the socket closed (if someone hasn't already), then write error lines
259
 	socket.Lock()
268
 	socket.Lock()
260
 	socket.closed = true
269
 	socket.closed = true
270
+	finalized := socket.finalized
261
 	socket.finalized = true
271
 	socket.finalized = true
262
 	finalData := socket.finalData
272
 	finalData := socket.finalData
263
 	if socket.sendQExceeded {
273
 	if socket.sendQExceeded {
264
 		finalData = "\r\nERROR :SendQ Exceeded\r\n"
274
 		finalData = "\r\nERROR :SendQ Exceeded\r\n"
265
 	}
275
 	}
266
 	socket.Unlock()
276
 	socket.Unlock()
277
+
278
+	if finalized {
279
+		return
280
+	}
281
+
267
 	if finalData != "" {
282
 	if finalData != "" {
268
 		socket.conn.Write([]byte(finalData))
283
 		socket.conn.Write([]byte(finalData))
269
 	}
284
 	}

Loading…
Annulla
Salva