]> git.michaelhowe.org Git - packages/b/bup.git/commitdiff
bup.client: fix freeze when suggest-index after finishing a full pack.
authorAvery Pennarun <apenwarr@gmail.com>
Sun, 14 Mar 2010 07:50:05 +0000 (03:50 -0400)
committerAvery Pennarun <apenwarr@gmail.com>
Sun, 14 Mar 2010 09:05:25 +0000 (05:05 -0400)
It was just rare enough to be hard to find: if you write an entire pack full
of stuff (1GB or more) and *then* trigger a suggest-index, the client would
freeze because it would send a send-index command without actually
suspending the receive-pack first.

The whole Client/PackWriter separation is pretty gross, so it's not terribly
surprising this would happen.

Add a unit test to detect this case if it ever happens in the future, for
what it's worth.

lib/bup/client.py
t/tclient.py [new file with mode: 0644]

index e236c0636f5b3c35566965a5c071de00f29ab9f9..cb8e9a439d7650658bd60b83d8e450fe6c7a73a4 100644 (file)
@@ -98,6 +98,10 @@ class Client:
         if self._busy:
             raise ClientError('already busy with command %r' % self._busy)
         
+    def ensure_busy(self):
+        if not self._busy:
+            raise ClientError('expected to be busy, but not busy?!')
+        
     def _not_busy(self):
         self._busy = None
 
@@ -125,6 +129,7 @@ class Client:
 
     def sync_index(self, name):
         #log('requesting %r\n' % name)
+        self.check_busy()
         mkdirp(self.cachedir)
         self.conn.write('send-index %s\n' % name)
         n = struct.unpack('!I', self.conn.read(4))[0]
@@ -154,21 +159,25 @@ class Client:
         ob = self._busy
         if ob:
             assert(ob == 'receive-objects')
-            self._busy = None
             self.conn.write('\xff\xff\xff\xff')  # suspend receive-objects
+            self._busy = None
             self.conn.drain_and_check_ok()
         self.sync_index(indexname)
         if ob:
-            self.conn.write('receive-objects\n')
             self._busy = ob
+            self.conn.write('receive-objects\n')
 
     def new_packwriter(self):
         self.check_busy()
-        self._busy = 'receive-objects'
+        def _set_busy():
+            self._busy = 'receive-objects'
+            self.conn.write('receive-objects\n')
         return PackWriter_Remote(self.conn,
                                  objcache_maker = self._make_objcache,
                                  suggest_pack = self._suggest_pack,
-                                 onclose = self._not_busy)
+                                 onopen = _set_busy,
+                                 onclose = self._not_busy,
+                                 ensure_busy = self.ensure_busy)
 
     def read_ref(self, refname):
         self.check_busy()
@@ -203,18 +212,23 @@ class Client:
 
 
 class PackWriter_Remote(git.PackWriter):
-    def __init__(self, conn, objcache_maker, suggest_pack, onclose):
+    def __init__(self, conn, objcache_maker, suggest_pack,
+                 onopen, onclose,
+                 ensure_busy):
         git.PackWriter.__init__(self, objcache_maker)
         self.file = conn
         self.filename = 'remote socket'
         self.suggest_pack = suggest_pack
+        self.onopen = onopen
         self.onclose = onclose
+        self.ensure_busy = ensure_busy
         self._packopen = False
 
     def _open(self):
         if not self._packopen:
             self._make_objcache()
-            self.file.write('receive-objects\n')
+            if self.onopen:
+                self.onopen()
             self._packopen = True
 
     def _end(self):
@@ -248,6 +262,8 @@ class PackWriter_Remote(git.PackWriter):
         assert(self.file)
         if not self._packopen:
             self._open()
+        if self.ensure_busy:
+            self.ensure_busy()
         data = ''.join(datalist)
         assert(len(data))
         self.file.write(struct.pack('!I', len(data)) + data)
diff --git a/t/tclient.py b/t/tclient.py
new file mode 100644 (file)
index 0000000..6e7dd9a
--- /dev/null
@@ -0,0 +1,30 @@
+import os, time, random
+from bup import client, git, hashsplit
+from wvtest import *
+
+def randbytes(sz):
+    s = ''
+    for i in xrange(sz):
+        s += chr(random.randrange(0,256))
+    return s
+
+@wvtest
+def test_server_split_with_indexes():
+    os.environ['BUP_MAIN_EXE'] = './bup'
+    os.environ['BUP_DIR'] = bupdir = 'buptest_tclient.tmp'
+    git.init_repo()
+    git.check_repo_or_die()
+    lw = git.PackWriter()
+    c = client.Client(bupdir, create=True)
+    rw = c.new_packwriter()
+
+    s1 = randbytes(10000)
+    s2 = randbytes(10000)
+    
+    lw.new_blob(s1)
+    lw.close()
+
+    rw.new_blob(s2)
+    rw.breakpoint()
+    rw.new_blob(s1)
+