diff --git a/api/net/sf/briar/api/db/DatabaseComponent.java b/api/net/sf/briar/api/db/DatabaseComponent.java index 4228f4106..8f54a123f 100644 --- a/api/net/sf/briar/api/db/DatabaseComponent.java +++ b/api/net/sf/briar/api/db/DatabaseComponent.java @@ -69,8 +69,11 @@ public interface DatabaseComponent { /** Adds a locally generated private message to the database. */ void addLocalPrivateMessage(Message m, ContactId c) throws DbException; - /** Generates an acknowledgement for the given contact. */ - void generateAck(ContactId c, AckWriter a) throws DbException, + /** + * Generates an acknowledgement for the given contact. + * @return True if any batch IDs were added to the acknowledgement. + */ + boolean generateAck(ContactId c, AckWriter a) throws DbException, IOException; /** diff --git a/components/net/sf/briar/db/Database.java b/components/net/sf/briar/db/Database.java index f6e679a0a..99809c23a 100644 --- a/components/net/sf/briar/db/Database.java +++ b/components/net/sf/briar/db/Database.java @@ -161,7 +161,7 @@ interface Database { * Returns the IDs of any batches received from the given contact that need * to be acknowledged. *

- * Locking: contacts read, messageStatuses write. + * Locking: contacts read, messageStatuses read. */ Collection getBatchesToAck(T txn, ContactId c) throws DbException; diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java index 8b8c7d5c9..a0f7a3b2f 100644 --- a/components/net/sf/briar/db/DatabaseComponentImpl.java +++ b/components/net/sf/briar/db/DatabaseComponentImpl.java @@ -377,20 +377,21 @@ DatabaseCleaner.Callback { } } - public void generateAck(ContactId c, AckWriter a) throws DbException, + public boolean generateAck(ContactId c, AckWriter a) throws DbException, IOException { contactLock.readLock().lock(); try { if(!containsContact(c)) throw new NoSuchContactException(); - messageStatusLock.writeLock().lock(); + Collection sent = new ArrayList(); + messageStatusLock.readLock().lock(); try { T txn = db.startTransaction(); try { Collection acks = db.getBatchesToAck(txn, c); - Collection sent = new ArrayList(); - for(BatchId b : acks) if(a.writeBatchId(b)) sent.add(b); - a.finish(); - db.removeBatchesToAck(txn, c, sent); + for(BatchId b : acks) { + if(!a.writeBatchId(b)) break; + sent.add(b); + } if(LOG.isLoggable(Level.FINE)) LOG.fine("Added " + acks.size() + " acks"); db.commitTransaction(txn); @@ -401,6 +402,23 @@ DatabaseCleaner.Callback { db.abortTransaction(txn); throw e; } + } finally { + messageStatusLock.readLock().unlock(); + } + // Record the contents of the ack, unless it's empty + if(sent.isEmpty()) return false; + a.finish(); + messageStatusLock.writeLock().lock(); + try { + T txn = db.startTransaction(); + try { + db.removeBatchesToAck(txn, c, sent); + db.commitTransaction(txn); + return true; + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } } finally { messageStatusLock.writeLock().unlock(); }