Browse code

- fix: partial writes were not taken into account

Andrei Pelinescu-Onciul authored on 22/12/2007 17:49:03
Showing 1 changed files
... ...
@@ -638,6 +638,59 @@ error:
638 638
 
639 639
 
640 640
 
641
+/* unsafe version, call while holding the connection write lock
642
+ * inserts data at the beginning, it ignores the max queue size checks and
643
+ * the timeout (use sparingly)
644
+ * Note: it should never be called on a write buffer after wbufq_run() */
645
+inline static int _wbufq_insert(struct  tcp_connection* c, char* data, 
646
+							unsigned int size)
647
+{
648
+	struct tcp_wbuffer_queue* q;
649
+	struct tcp_wbuffer* wb;
650
+	
651
+	q=&c->wbuf_q;
652
+	if (likely(q->first==0)) /* if empty, use wbufq_add */
653
+		return _wbufq_add(c, data, size);
654
+	
655
+	if (unlikely((*tcp_total_wq+size)>tcp_options.tcp_wq_max)){
656
+		LOG(L_ERR, "ERROR: wbufq_insert(%d bytes): write queue full or timeout"
657
+					" (%d, total %d, last write %d s ago)\n",
658
+					size, q->queued, *tcp_total_wq,
659
+					TICKS_TO_S(get_ticks_raw()-q->wr_timeout-
660
+										tcp_options.tcp_wq_timeout));
661
+		goto error;
662
+	}
663
+	if (unlikely(q->offset)){
664
+		LOG(L_CRIT, "BUG: wbufq_insert: non-null offset %d (bad call, should"
665
+				"never be called after the wbufq_run())\n", q->offset);
666
+		goto error;
667
+	}
668
+	if ((q->first==q->last) && ((q->last->b_size-q->last_used)>=size)){
669
+		/* one block with enough space in it for size bytes */
670
+		memmove(q->first->buf+size, q->first->buf, size);
671
+		memcpy(q->first->buf, data, size);
672
+		q->last_used+=size;
673
+	}else{
674
+		/* create a size bytes block directly */
675
+		wb=shm_malloc(sizeof(*wb)+size-1);
676
+		if (unlikely(wb==0))
677
+			goto error;
678
+		wb->b_size=size;
679
+		/* insert it */
680
+		wb->next=q->first;
681
+		q->first=wb;
682
+		memcpy(wb->buf, data, size);
683
+	}
684
+	
685
+	q->queued+=size;
686
+	atomic_add_int((int*)tcp_total_wq, size);
687
+	return 0;
688
+error:
689
+	return -1;
690
+}
691
+
692
+
693
+
641 694
 /* unsafe version, call while holding the connection write lock */
642 695
 inline static void _wbufq_destroy( struct  tcp_wbuffer_queue* q)
643 696
 {
... ...
@@ -1543,13 +1596,14 @@ no_id:
1543 1543
 				 * desired either lock before the write or use 
1544 1544
 				 * _wbufq_insert(...) */
1545 1545
 				n=_tcpconn_write_nb(fd, c, buf, len);
1546
-				if (unlikely(n<0)){
1547
-					if (errno==EAGAIN|| errno==EWOULDBLOCK){
1548
-						DBG("tcp_send: pending write on new connection (%p)\n",
1549
-								c);
1546
+				if (unlikely(n<len)){
1547
+					if ((n>=0) || errno==EAGAIN || errno==EWOULDBLOCK){
1548
+						DBG("tcp_send: pending write on new connection %p "
1549
+								" (%d/%d bytes written)\n", c, n, len);
1550
+						if (n<0) n=0;
1550 1551
 						/* add to the write queue */
1551 1552
 						lock_get(&c->write_lock);
1552
-							if (unlikely(_wbufq_add(c, buf, len)<0)){
1553
+							if (unlikely(_wbufq_insert(c, buf+n, len-n)<0)){
1553 1554
 								lock_release(&c->write_lock);
1554 1555
 								n=-1;
1555 1556
 								LOG(L_ERR, "ERROR: tcp_send: EAGAIN and"
... ...
@@ -1735,12 +1789,13 @@ send_it:
1735 1735
 	
1736 1736
 	DBG("tcp_send: after real write: c= %p n=%d fd=%d\n",c, n, fd);
1737 1737
 	DBG("tcp_send: buf=\n%.*s\n", (int)len, buf);
1738
-	if (unlikely(n<0)){
1738
+	if (unlikely(n<len)){
1739 1739
 #ifdef TCP_BUF_WRITE
1740 1740
 		if (tcp_options.tcp_buf_write && 
1741
-				(errno==EAGAIN || errno==EWOULDBLOCK)){
1741
+				((n>=0) || errno==EAGAIN || errno==EWOULDBLOCK)){
1742 1742
 			enable_write_watch=_wbufq_empty(c);
1743
-			if (unlikely(_wbufq_add(c, buf, len)<0)){
1743
+			if (n<0) n=0;
1744
+			if (unlikely(_wbufq_add(c, buf+n, len-n)<0)){
1744 1745
 				lock_release(&c->write_lock);
1745 1746
 				n=-1;
1746 1747
 				goto error;
... ...
@@ -2086,14 +2141,15 @@ inline static int tcpconn_put_destroy(struct tcp_connection* tcpconn)
2086 2086
 		tcpconn_destroy(tcpconn);
2087 2087
 		return 1;
2088 2088
 	}else{
2089
+		tcpconn->state=S_CONN_BAD;
2090
+		/* in case it's still in a reader timer */
2091
+		tcpconn->timeout=get_ticks_raw();
2092
+		/* fast close: close fds now */
2089 2093
 		if (likely(!(tcpconn->flags & F_CONN_FD_CLOSED))){
2090 2094
 			tcpconn_close_main_fd(tcpconn);
2091 2095
 			tcpconn->flags|=F_CONN_FD_CLOSED;
2092 2096
 			(*tcp_connections_no)--;
2093 2097
 		}
2094
-		tcpconn->state=S_CONN_BAD;
2095
-		/* in case it's still in a reader timer */
2096
-		tcpconn->timeout=get_ticks_raw();
2097 2098
 	}
2098 2099
 	return 0;
2099 2100
 }