I've been doing some testing where I trigger the rsync hang I talked
about in my previous email (where the redo pipe to the generator process
fills up and causes the receiver to deadlock). This bug is easy to
trigger on a local-to-local rsync copy if I change receiver.c to retry
every file during the first phase. My previous patch made it much
harder to cause a deadlock, but was not totally effective -- it was
still possible for an io_flush() in the generator (which happens prior
to the read of the f_recv pipe) to block, allowing the receiver to
deadlock trying to write to the generator.
In order to fix this it looks like we need to make the generator keep
the f_recv pipe empty while it is trying to write to the sender. The
appended patch accomplishes this by changing io.c to allow an input fd
to be registered and then monitored whenever we're writing data (through
a simple extension of the existing select() call). The data is simply
buffered up and used when generate_files() reads the ints (I changed
readfd(), which is called by read_int()).
With the appended patch I have not been able to get rsync to hang
anymore. This patch relative to the current CVS. You should do a
"make proto" after applying this patch.
..wayne..
---8<------8<------8<------8<---cut here--->8------>8------>8------>8---
Index: io.c
--- io.c 7 May 2001 06:59:37 -0000 1.85
+++ io.c 1 Jun 2001 01:17:28 -0000
@@ -261,6 +261,36 @@
return ret;
}
+static char *inbuf;
+static int inbuf_start, inbuf_end, inbuf_alloc_len;
+static int input_buffer_fd = -1;
+
+void io_set_input_buffer_fd(int fd)
+{
+ input_buffer_fd = fd;
+}
+
+static void add_to_input_buffer()
+{
+ if (inbuf_alloc_len == inbuf_end) {
+ if (inbuf_start > 0) {
+ char *f = inbuf + inbuf_start, *t = inbuf;
+ char *e = inbuf + inbuf_end;
+ while (f != e)
+ *t++ = *f++;
+ inbuf_end -= inbuf_start;
+ inbuf_start = 0;
+ } else {
+ inbuf_alloc_len += 4096;
+ inbuf = Realloc(inbuf, inbuf_alloc_len);
+ if (!inbuf) out_of_memory("add_to_input_buffer");
+ }
+ }
+ else if (inbuf_start == inbuf_end)
+ inbuf_start = inbuf_end = 0;
+ if (read(input_buffer_fd, inbuf + inbuf_end, 1) == 1)
+ inbuf_end++;
+}
/* do a buffered read from fd. don't return until all N bytes
have been read. If all N can't be read then exit with an error */
@@ -272,14 +302,20 @@
while (total < N) {
io_flush();
- ret = read_unbuffered(fd,buffer + total,N-total);
+ if (fd == input_buffer_fd
+ && (ret = inbuf_end - inbuf_start) > 0) {
+ if (ret > N-total)
+ ret = N-total;
+ memcpy(buffer + total, inbuf + inbuf_start, ret);
+ inbuf_start += ret;
+ } else
+ ret = read_unbuffered(fd,buffer + total,N-total);
total += ret;
}
stats.total_read += total;
}
-
int32 read_int(int f)
{
char b[4];
@@ -356,6 +392,11 @@
if (io_error_fd > fd_count)
fd_count = io_error_fd;
}
+ if (input_buffer_fd != -1) {
+ FD_SET(input_buffer_fd,&r_fds);
+ if (input_buffer_fd > fd_count)
+ fd_count = input_buffer_fd;
+ }
tv.tv_sec = io_timeout?io_timeout:SELECT_TIMEOUT;
tv.tv_usec = 0;
@@ -377,6 +418,9 @@
if (io_error_fd != -1 && FD_ISSET(io_error_fd, &r_fds)) {
read_error_fd();
+ }
+ if (input_buffer_fd != -1 && FD_ISSET(input_buffer_fd,&r_fds)) {
+ add_to_input_buffer();
}
if (FD_ISSET(fd, &w_fds)) {
Index: main.c
--- main.c 29 May 2001 14:37:54 -0000 1.127
+++ main.c 1 Jun 2001 01:17:30 -0000
@@ -371,9 +371,11 @@
io_set_error_fd(error_pipe[0]);
+ io_set_input_buffer_fd(recv_pipe[0]);
generate_files(f_out,flist,local_name,recv_pipe[0]);
read_int(recv_pipe[0]);
+ io_set_input_buffer_fd(-1);
close(recv_pipe[0]);
if (remote_version >= 24) {
/* send a final goodbye message */
---8<------8<------8<------8<---cut here--->8------>8------>8------>8---