diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java index 7ccc8277ec20591319668275f6b9fb9489d26aaa..ccb162b80cd9ed90e0ca4be8cc6e7ac026e3938f 100644 --- a/components/net/sf/briar/db/DatabaseComponentImpl.java +++ b/components/net/sf/briar/db/DatabaseComponentImpl.java @@ -21,13 +21,13 @@ import net.sf.briar.api.protocol.MessageId; * Abstract superclass containing code shared by ReadWriteLockDatabaseComponent * and SynchronizedDatabaseComponent. */ -abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent, +abstract class DatabaseComponentImpl<T> implements DatabaseComponent, DatabaseCleaner.Callback { private static final Logger LOG = Logger.getLogger(DatabaseComponentImpl.class.getName()); - protected final Database<Txn> db; + protected final Database<T> db; protected final DatabaseCleaner cleaner; private final List<DatabaseListener> listeners = @@ -38,7 +38,7 @@ DatabaseCleaner.Callback { private long timeOfLastCheck = 0L; // Locking: spaceLock private volatile boolean writesAllowed = true; - DatabaseComponentImpl(Database<Txn> db, DatabaseCleaner cleaner) { + DatabaseComponentImpl(Database<T> db, DatabaseCleaner cleaner) { this.db = db; this.cleaner = cleaner; } @@ -71,7 +71,7 @@ DatabaseCleaner.Callback { * <p> * Locking: messages write. */ - private int calculateSendability(Txn txn, Message m) throws DbException { + private int calculateSendability(T txn, Message m) throws DbException { int sendability = 0; // One point for a good rating if(db.getRating(txn, m.getAuthor()) == Rating.GOOD) sendability++; @@ -121,7 +121,7 @@ DatabaseCleaner.Callback { * Locking: contacts read. */ protected boolean containsContact(ContactId c) throws DbException { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { boolean contains = db.containsContact(txn, c); db.commitTransaction(txn); @@ -137,7 +137,7 @@ DatabaseCleaner.Callback { * <p> * Locking: contacts read, messages write, messageStatuses write. */ - protected void removeMessage(Txn txn, MessageId id) throws DbException { + protected void removeMessage(T txn, MessageId id) throws DbException { Integer sendability = db.getSendability(txn, id); assert sendability != null; // If the message is sendable, deleting it may affect its ancestors' @@ -176,7 +176,7 @@ DatabaseCleaner.Callback { * <p> * Locking: contacts read, messages write, messageStatuses write. */ - protected boolean storeGroupMessage(Txn txn, Message m, ContactId sender) + protected boolean storeGroupMessage(T txn, Message m, ContactId sender) throws DbException { if(m.getGroup() == null) throw new IllegalArgumentException(); boolean stored = db.addGroupMessage(txn, m); @@ -204,7 +204,7 @@ DatabaseCleaner.Callback { * Attempts to store the given messages, received from the given contact, * and returns true if any were stored. */ - protected boolean storeMessages(Txn txn, ContactId c, + protected boolean storeMessages(T txn, ContactId c, Collection<Message> messages) throws DbException { boolean anyStored = false; for(Message m : messages) { @@ -226,7 +226,7 @@ DatabaseCleaner.Callback { * <p> * Locking: contacts read, messages write, messageStatuses write. */ - protected boolean storePrivateMessage(Txn txn, Message m, ContactId c, + protected boolean storePrivateMessage(T txn, Message m, ContactId c, boolean incoming) throws DbException { if(m.getGroup() != null) throw new IllegalArgumentException(); if(m.getAuthor() != null) throw new IllegalArgumentException(); @@ -250,7 +250,7 @@ DatabaseCleaner.Callback { * @param increment True if the message's sendability has changed from 0 to * greater than 0, or false if it has changed from greater than 0 to 0. */ - private int updateAncestorSendability(Txn txn, MessageId m, + private int updateAncestorSendability(T txn, MessageId m, boolean increment) throws DbException { int affected = 0; boolean changed = true; @@ -285,7 +285,7 @@ DatabaseCleaner.Callback { * @param increment True if the user's rating for the author has changed * from not good to good, or false if it has changed from good to not good. */ - protected void updateAuthorSendability(Txn txn, AuthorId a, + protected void updateAuthorSendability(T txn, AuthorId a, boolean increment) throws DbException { int direct = 0, indirect = 0; for(MessageId id : db.getMessagesByAuthor(txn, a)) { diff --git a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java index f7c50d561935120326e6bca2044fdf38ee9d03ea..2800184cda340de0487c3f5ab6d9e402275f446e 100644 --- a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java +++ b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java @@ -38,9 +38,11 @@ import com.google.inject.Inject; /** * An implementation of DatabaseComponent using reentrant read-write locks. - * This implementation can allow writers to starve. + * Depending on the JVM's read-write lock implementation, this implementation + * may allow writers to starve. LockFairnessTest can be used to test whether + * this implementation is safe on a given JVM. */ -class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { +class ReadWriteLockDatabaseComponent<T> extends DatabaseComponentImpl<T> { private static final Logger LOG = Logger.getLogger(ReadWriteLockDatabaseComponent.class.getName()); @@ -66,7 +68,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { new ReentrantReadWriteLock(true); @Inject - ReadWriteLockDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner) { + ReadWriteLockDatabaseComponent(Database<T> db, DatabaseCleaner cleaner) { super(db, cleaner); } @@ -77,7 +79,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { messageStatusLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { for(MessageId m : db.getOldMessages(txn, size)) { removeMessage(txn, m); @@ -111,7 +113,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { transportLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { c = db.addContact(txn, transports, secret); db.commitTransaction(txn); @@ -143,7 +145,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { subscriptionLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { // Don't store the message if the user has // unsubscribed from the group or the message @@ -184,7 +186,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { messageStatusLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { added = storePrivateMessage(txn, m, c, false); db.commitTransaction(txn); @@ -215,7 +217,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { messageStatusLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { lost = db.getLostBatches(txn, c); db.commitTransaction(txn); @@ -240,7 +242,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { messageStatusLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { if(LOG.isLoggable(Level.FINE)) LOG.fine("Removing lost batch"); @@ -269,7 +271,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { if(!containsContact(c)) throw new NoSuchContactException(); messageStatusLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Collection<BatchId> acks = db.getBatchesToAck(txn, c); Collection<BatchId> sent = new ArrayList<BatchId>(); @@ -307,7 +309,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { subscriptionLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { sent = new ArrayList<MessageId>(); int capacity = b.getCapacity(); @@ -338,7 +340,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { BatchId id = b.finish(); messageStatusLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { db.addOutstandingBatch(txn, c, id, sent); db.commitTransaction(txn); @@ -369,7 +371,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try{ subscriptionLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { sent = new ArrayList<MessageId>(); considered = new ArrayList<MessageId>(); @@ -406,7 +408,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { BatchId id = b.finish(); messageStatusLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { db.addOutstandingBatch(txn, c, id, sent); db.commitTransaction(txn); @@ -435,7 +437,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { messageStatusLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Collection<MessageId> sendable = db.getSendableMessages(txn, c, Integer.MAX_VALUE); @@ -474,7 +476,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { if(!containsContact(c)) throw new NoSuchContactException(); subscriptionLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Map<Group, Long> subs = db.getVisibleSubscriptions(txn, c); s.writeSubscriptions(subs, System.currentTimeMillis()); @@ -503,7 +505,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { if(!containsContact(c)) throw new NoSuchContactException(); transportLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Map<String, Map<String, String>> transports = db.getTransports(txn); @@ -533,7 +535,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { if(!containsContact(c)) throw new NoSuchContactException(); windowLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { ConnectionWindow w = db.getConnectionWindow(txn, c, transportId); @@ -554,7 +556,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { public Collection<ContactId> getContacts() throws DbException { contactLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Collection<ContactId> contacts = db.getContacts(txn); db.commitTransaction(txn); @@ -571,7 +573,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { public Rating getRating(AuthorId a) throws DbException { ratingLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Rating r = db.getRating(txn, a); db.commitTransaction(txn); @@ -589,7 +591,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { contactLock.readLock().lock(); try { if(!containsContact(c)) throw new NoSuchContactException(); - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { byte[] secret = db.getSharedSecret(txn, c); db.commitTransaction(txn); @@ -606,7 +608,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { public Collection<Group> getSubscriptions() throws DbException { subscriptionLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Collection<Group> subs = db.getSubscriptions(txn); db.commitTransaction(txn); @@ -624,7 +626,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { throws DbException { transportLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Map<String, String> config = db.getTransportConfig(txn, name); db.commitTransaction(txn); @@ -641,7 +643,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { public Map<String, Map<String, String>> getTransports() throws DbException { transportLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Map<String, Map<String, String>> transports = db.getTransports(txn); @@ -663,7 +665,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { if(!containsContact(c)) throw new NoSuchContactException(); transportLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Map<String, Map<String, String>> transports = db.getTransports(txn, c); @@ -686,7 +688,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { subscriptionLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Collection<ContactId> visible = db.getVisibility(txn, g); db.commitTransaction(txn); @@ -713,7 +715,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { subscriptionLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { boolean has = db.hasSendableMessages(txn, c); db.commitTransaction(txn); @@ -747,7 +749,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { Collection<BatchId> acks = a.getBatchIds(); for(BatchId ack : acks) { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { db.removeAckedBatch(txn, c, ack); db.commitTransaction(txn); @@ -781,7 +783,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { subscriptionLock.readLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { anyAdded = storeMessages(txn, c, b.getMessages()); db.addBatchToAck(txn, c, b.getId()); @@ -819,7 +821,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { Collection<MessageId> offered = o.getMessageIds(); BitSet request = new BitSet(offered.size()); - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Iterator<MessageId> it = offered.iterator(); for(int i = 0; it.hasNext(); i++) { @@ -857,7 +859,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { if(!containsContact(c)) throw new NoSuchContactException(); subscriptionLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Map<Group, Long> subs = s.getSubscriptions(); db.setSubscriptions(txn, c, subs, s.getTimestamp()); @@ -884,7 +886,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { if(!containsContact(c)) throw new NoSuchContactException(); transportLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Map<String, Map<String, String>> transports = t.getTransports(); @@ -917,7 +919,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { transportLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { db.removeContact(txn, c); db.commitTransaction(txn); @@ -951,7 +953,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { if(!containsContact(c)) throw new NoSuchContactException(); windowLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { db.setConnectionWindow(txn, c, transportId, w); db.commitTransaction(txn); @@ -971,7 +973,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { ratingLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Rating old = db.setRating(txn, a, r); // Update the sendability of the author's messages @@ -997,7 +999,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { boolean changed = false; transportLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Map<String, String> old = db.getTransportConfig(txn, name); if(!config.equals(old)) { @@ -1021,7 +1023,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { boolean changed = false; transportLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { Map<String, String> old = db.getTransports(txn).get(name); if(!properties.equals(old)) { @@ -1046,7 +1048,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { subscriptionLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { // Remove any ex-contacts from the set Collection<ContactId> present = @@ -1073,7 +1075,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { boolean added = false; subscriptionLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { if(db.containsSubscription(txn, g.getId())) { db.addSubscription(txn, g); @@ -1102,7 +1104,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { subscriptionLock.writeLock().lock(); try { - Txn txn = db.startTransaction(); + T txn = db.startTransaction(); try { if(db.containsSubscription(txn, g)) { db.removeSubscription(txn, g);