diff --git a/api/net/sf/briar/api/db/DatabaseComponent.java b/api/net/sf/briar/api/db/DatabaseComponent.java index 4228f410629765931753282a8b7ea7d8fcfe783b..8f54a123fe6fdf72f796a7aa5d0f2c9054323724 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 f6e679a0ac6980e6659c9d202ab5e99328d9b211..99809c23a9e331f6249a5b10caf5a2a854064418 100644 --- a/components/net/sf/briar/db/Database.java +++ b/components/net/sf/briar/db/Database.java @@ -161,7 +161,7 @@ interface Database<T> { * Returns the IDs of any batches received from the given contact that need * to be acknowledged. * <p> - * Locking: contacts read, messageStatuses write. + * Locking: contacts read, messageStatuses read. */ Collection<BatchId> 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 8b8c7d5c980028e7b05674daaaeb428d3aafe911..a0f7a3b2fc6ad55bcfbd83b17948d90e23fd1098 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<BatchId> sent = new ArrayList<BatchId>(); + messageStatusLock.readLock().lock(); try { T txn = db.startTransaction(); try { Collection<BatchId> acks = db.getBatchesToAck(txn, c); - Collection<BatchId> sent = new ArrayList<BatchId>(); - 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(); }