diff --git a/briar-core/src/net/sf/briar/db/Database.java b/briar-core/src/net/sf/briar/db/Database.java index f735dcc7654f1e4b3f80d52024a1b3498e4be27c..9faecae8e9e114a23064196f795be65f23493d98 100644 --- a/briar-core/src/net/sf/briar/db/Database.java +++ b/briar-core/src/net/sf/briar/db/Database.java @@ -90,7 +90,7 @@ interface Database<T> { /** * Adds an endpoint to the database. * <p> - * Locking: contact read, transport read, window write. + * Locking: window write. */ void addEndpoint(T txn, Endpoint ep) throws DbException; @@ -105,7 +105,7 @@ interface Database<T> { /** * Records a received message as needing to be acknowledged. * <p> - * Locking: contact read, message write. + * Locking: message write. */ void addMessageToAck(T txn, ContactId c, MessageId m) throws DbException; @@ -113,7 +113,7 @@ interface Database<T> { * Stores the given message, or returns false if the message is already in * the database. * <p> - * Locking: contact read, message write. + * Locking: message write. */ boolean addPrivateMessage(T txn, Message m, ContactId c) throws DbException; @@ -121,7 +121,7 @@ interface Database<T> { * Stores the given temporary secrets and deletes any secrets that have * been made obsolete. * <p> - * Locking: contact read, transport read, window write. + * Locking: window write. */ void addSecrets(T txn, Collection<TemporarySecret> secrets) throws DbException; @@ -130,7 +130,7 @@ interface Database<T> { * Initialises the status (seen or unseen) of the given message with * respect to the given contact. * <p> - * Locking: contact read, message write. + * Locking: message write. */ void addStatus(T txn, ContactId c, MessageId m, boolean seen) throws DbException; @@ -147,14 +147,14 @@ interface Database<T> { * Adds a new transport to the database and returns true if the transport * was not previously in the database. * <p> - * Locking: transport write. + * Locking: transport write, window write. */ boolean addTransport(T txn, TransportId t) throws DbException; /** * Makes the given group visible to the given contact. * <p> - * Locking: contact write, subscription write. + * Locking: subscription write. */ void addVisibility(T txn, ContactId c, GroupId g) throws DbException; @@ -182,7 +182,7 @@ interface Database<T> { /** * Returns true if the database contains the given transport. * <p> - * Locking: contact read, transport read. + * Locking: transport read. */ boolean containsTransport(T txn, TransportId t) throws DbException; @@ -190,7 +190,7 @@ interface Database<T> { * Returns true if the user subscribes to the given group and the * subscription is visible to the given contact. * <p> - * Locking: contact read, subscription read. + * Locking: subscription read. */ boolean containsVisibleSubscription(T txn, ContactId c, GroupId g) throws DbException; @@ -219,7 +219,7 @@ interface Database<T> { /** * Returns all endpoints. * <p> - * Locking: contact read, transport read, window read. + * Locking: window read. */ Collection<Endpoint> getEndpoints(T txn) throws DbException; @@ -227,8 +227,6 @@ interface Database<T> { * Returns the amount of free storage space available to the database, in * bytes. This is based on the minimum of the space available on the device * where the database is stored and the database's configured size. - * <p> - * Locking: message read. */ long getFreeSpace() throws DbException; @@ -245,7 +243,7 @@ interface Database<T> { * Returns the time at which a connection to the given contact was last * made. * <p> - * Locking: contact read, window read. + * Locking: window read. */ long getLastConnected(T txn, ContactId c) throws DbException; @@ -292,7 +290,7 @@ interface Database<T> { * Returns the IDs of some messages received from the given contact that * need to be acknowledged, up to the given number of messages. * <p> - * Locking: contact read, message read. + * Locking: message read. */ Collection<MessageId> getMessagesToAck(T txn, ContactId c, int maxMessages) throws DbException; @@ -301,7 +299,7 @@ interface Database<T> { * Returns the IDs of some messages that are eligible to be sent to the * given contact, up to the given number of messages. * <p> - * Locking: contact read, message read, subscription read. + * Locking: message read, subscription read. */ Collection<MessageId> getMessagesToOffer(T txn, ContactId c, int maxMessages) throws DbException; @@ -327,7 +325,7 @@ interface Database<T> { * Returns null if the message is not present in the database or is not * sendable to the given contact. * <p> - * Locking: contact read, message read, subscription read. + * Locking: message read, subscription read. */ byte[] getRawMessageIfSendable(T txn, ContactId c, MessageId m) throws DbException; @@ -357,7 +355,7 @@ interface Database<T> { /** * Returns all remote properties for the given transport. * <p> - * Locking: contact read, transport read. + * Locking: transport read. */ Map<ContactId, TransportProperties> getRemoteProperties(T txn, TransportId t) throws DbException; @@ -365,7 +363,7 @@ interface Database<T> { /** * Returns a retention ack for the given contact, or null if no ack is due. * <p> - * Locking: contact read, retention write. + * Locking: retention write. */ RetentionAck getRetentionAck(T txn, ContactId c) throws DbException; @@ -373,7 +371,7 @@ interface Database<T> { * Returns a retention update for the given contact and updates its expiry * time using the given latency. Returns null if no update is due. * <p> - * Locking: contact read, retention write. + * Locking: message read, retention write. */ RetentionUpdate getRetentionUpdate(T txn, ContactId c, long maxLatency) throws DbException; @@ -381,7 +379,7 @@ interface Database<T> { /** * Returns all temporary secrets. * <p> - * Locking: contact read, transport read, window read. + * Locking: window read. */ Collection<TemporarySecret> getSecrets(T txn) throws DbException; @@ -397,7 +395,7 @@ interface Database<T> { * given contact, with a total length less than or equal to the given * length. * <p> - * Locking: contact read, message read, subscription read. + * Locking: message read, subscription read. */ Collection<MessageId> getSendableMessages(T txn, ContactId c, int maxLength) throws DbException; @@ -419,7 +417,7 @@ interface Database<T> { /** * Returns the groups to which the given contact subscribes. * <p> - * Locking: contact read, subscription read. + * Locking: subscription read. */ Collection<Group> getSubscriptions(T txn, ContactId c) throws DbException; @@ -427,7 +425,7 @@ interface Database<T> { * Returns a subscription ack for the given contact, or null if no ack is * due. * <p> - * Locking: contact read, subscription write. + * Locking: subscription write. */ SubscriptionAck getSubscriptionAck(T txn, ContactId c) throws DbException; @@ -435,7 +433,7 @@ interface Database<T> { * Returns a subscription update for the given contact and updates its * expiry time using the given latency. Returns null if no update is due. * <p> - * Locking: contact read, subscription write. + * Locking: subscription write. */ SubscriptionUpdate getSubscriptionUpdate(T txn, ContactId c, long maxLatency) throws DbException; @@ -444,7 +442,7 @@ interface Database<T> { * Returns the transmission count of the given message with respect to the * given contact. * <p> - * Locking: contact read, message read. + * Locking: message read. */ int getTransmissionCount(T txn, ContactId c, MessageId m) throws DbException; @@ -453,7 +451,7 @@ interface Database<T> { * Returns a collection of transport acks for the given contact, or null if * no acks are due. * <p> - * Locking: contact read, transport write. + * Locking: transport write. */ Collection<TransportAck> getTransportAcks(T txn, ContactId c) throws DbException; @@ -463,31 +461,29 @@ interface Database<T> { * updates their expiry times using the given latency. Returns null if no * updates are due. * <p> - * Locking: contact read, transport write. + * Locking: transport write. */ Collection<TransportUpdate> getTransportUpdates(T txn, ContactId c, long maxLatency) throws DbException; - /** - * Returns the version number of the /** * Returns the number of unread messages in each subscribed group. * <p> - * Locking: message read, subscription read. + * Locking: message read. */ Map<GroupId, Integer> getUnreadMessageCounts(T txn) throws DbException; /** * Returns the contacts to which the given group is visible. * <p> - * Locking: contact read, subscription read. + * Locking: subscription read. */ Collection<ContactId> getVisibility(T txn, GroupId g) throws DbException; /** * Returns the subscriptions that are visible to the given contact. * <p> - * Locking: contact read, subscription read. + * Locking: subscription read. */ Collection<GroupId> getVisibleSubscriptions(T txn, ContactId c) throws DbException; @@ -495,7 +491,7 @@ interface Database<T> { /** * Returns true if any messages are sendable to the given contact. * <p> - * Locking: contact read, message read. + * Locking: message read, subscription read. */ boolean hasSendableMessages(T txn, ContactId c) throws DbException; @@ -503,7 +499,7 @@ interface Database<T> { * Increments the outgoing connection counter for the given contact * transport in the given rotation period and returns the old value; * <p> - * Locking: contact read, transport read, window write. + * Locking: window write. */ long incrementConnectionCounter(T txn, ContactId c, TransportId t, long period) throws DbException; @@ -512,7 +508,7 @@ interface Database<T> { * Increments the retention time versions for all contacts to indicate that * the database's retention time has changed and updates should be sent. * <p> - * Locking: contact read, retention write. + * Locking: retention write. */ void incrementRetentionVersions(T txn) throws DbException; @@ -545,7 +541,7 @@ interface Database<T> { /** * Removes a message (and all associated state) from the database. * <p> - * Locking: contact read, message write. + * Locking: message write. */ void removeMessage(T txn, MessageId m) throws DbException; @@ -553,7 +549,7 @@ interface Database<T> { * Marks the given messages received from the given contact as having been * acknowledged. * <p> - * Locking: contact read, message write. + * Locking: message write. */ void removeMessagesToAck(T txn, ContactId c, Collection<MessageId> acked) throws DbException; @@ -562,7 +558,7 @@ interface Database<T> { * Marks any of the given messages that are considered outstanding with * respect to the given contact as seen by the contact. * <p> - * Locking: contact read, message write. + * Locking: message write. */ void removeOutstandingMessages(T txn, ContactId c, Collection<MessageId> acked) throws DbException; @@ -571,21 +567,21 @@ interface Database<T> { * Unsubscribes from the given group. Any messages belonging to the group * are deleted from the database. * <p> - * Locking: contact write, message write, subscription write. + * Locking: message write, subscription write. */ void removeSubscription(T txn, GroupId g) throws DbException; /** * Removes a transport (and all associated state) from the database. * <p> - * Locking: transport write. + * Locking: transport write, window write. */ void removeTransport(T txn, TransportId t) throws DbException; /** * Makes the given group invisible to the given contact. * <p> - * Locking: contact write, subscription write. + * Locking: subscription write. */ void removeVisibility(T txn, ContactId c, GroupId g) throws DbException; @@ -593,7 +589,7 @@ interface Database<T> { * Sets the connection reordering window for the given endpoint in the * given rotation period. * <p> - * Locking: contact read, transport read, window write. + * Locking: window write. */ void setConnectionWindow(T txn, ContactId c, TransportId t, long period, long centre, byte[] bitmap) throws DbException; @@ -601,7 +597,7 @@ interface Database<T> { /** * Sets the time at which a connection to the given contact was last made. * <p> - * Locking: contact read, window write. + * Locking: window write. */ void setLastConnected(T txn, ContactId c, long now) throws DbException; @@ -626,7 +622,7 @@ interface Database<T> { * with an equal or higher version number has already been received from * the contact. * <p> - * Locking: contact read, transport write. + * Locking: transport write. */ void setRemoteProperties(T txn, ContactId c, TransportId t, TransportProperties p, long version) throws DbException; @@ -636,7 +632,7 @@ interface Database<T> { * update with an equal or higher version number has already been received * from the contact. * <p> - * Locking: contact read, retention write. + * Locking: retention write. */ void setRetentionTime(T txn, ContactId c, long retention, long version) throws DbException; @@ -662,7 +658,7 @@ interface Database<T> { * that is visible to the given contact, marks the message as seen by the * contact and returns true; otherwise returns false. * <p> - * Locking: contact read, message write, subscription read. + * Locking: message write, subscription read. */ boolean setStatusSeenIfVisible(T txn, ContactId c, MessageId m) throws DbException; @@ -672,7 +668,7 @@ interface Database<T> { * update with an equal or higher version number has already been received * from the contact. * <p> - * Locking: contact read, subscription write. + * Locking: subscription write. */ void setSubscriptions(T txn, ContactId c, Collection<Group> subs, long version) throws DbException; @@ -681,7 +677,7 @@ interface Database<T> { * Records a retention ack from the given contact for the given version * unless the contact has already acked an equal or higher version. * <p> - * Locking: contact read, retention write. + * Locking: retention write. */ void setRetentionUpdateAcked(T txn, ContactId c, long version) throws DbException; @@ -690,7 +686,7 @@ interface Database<T> { * Records a subscription ack from the given contact for the given version * unless the contact has already acked an equal or higher version. * <p> - * Locking: contact read, subscription write. + * Locking: subscription write. */ void setSubscriptionUpdateAcked(T txn, ContactId c, long version) throws DbException; @@ -699,7 +695,7 @@ interface Database<T> { * Records a transport ack from the give contact for the given version * unless the contact has already acked an equal or higher version. * <p> - * Locking: contact read, transport write. + * Locking: transport write. */ void setTransportUpdateAcked(T txn, ContactId c, TransportId t, long version) throws DbException; @@ -709,7 +705,7 @@ interface Database<T> { * contact, using the given transmission counts and the latency of the * transport over which they were sent. * <p> - * Locking: contact read, message write. + * Locking: message write. */ void updateExpiryTimes(T txn, ContactId c, Map<MessageId, Integer> sent, long maxLatency) throws DbException; diff --git a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java index 7cb268aaef2d1fa4e64a978fa64e0757b670dd51..5456d379717b7461f7325c23206c788ed5f1c24d 100644 --- a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java +++ b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java @@ -254,21 +254,27 @@ DatabaseCleaner.Callback { try { messageLock.writeLock().lock(); try { - subscriptionLock.readLock().lock(); + ratingLock.readLock().lock(); try { - T txn = db.startTransaction(); + subscriptionLock.readLock().lock(); try { - // Don't store the message if the user has - // unsubscribed from the group - if(db.containsSubscription(txn, m.getGroup().getId())) - added = storeGroupMessage(txn, m, null); - db.commitTransaction(txn); - } catch(DbException e) { - db.abortTransaction(txn); - throw e; + T txn = db.startTransaction(); + try { + // Don't store the message if the user has + // unsubscribed from the group + GroupId g = m.getGroup().getId(); + if(db.containsSubscription(txn, g)) + added = storeGroupMessage(txn, m, null); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + subscriptionLock.readLock().unlock(); } } finally { - subscriptionLock.readLock().unlock(); + ratingLock.readLock().unlock(); } } finally { messageLock.writeLock().unlock(); @@ -285,7 +291,7 @@ DatabaseCleaner.Callback { * sendability of its ancestors if necessary, marks the message as seen by * the sender and unseen by all other contacts, and returns true. * <p> - * Locking: contact read, message write. + * Locking: contact read, message write, rating read. * @param sender may be null for a locally generated message. */ private boolean storeGroupMessage(T txn, Message m, ContactId sender) @@ -315,7 +321,7 @@ DatabaseCleaner.Callback { /** * Calculates and returns the sendability score of a message. * <p> - * Locking: message write. + * Locking: message read, rating read. */ private int calculateSendability(T txn, Message m) throws DbException { int sendability = 0; @@ -431,13 +437,18 @@ DatabaseCleaner.Callback { boolean added; transportLock.writeLock().lock(); try { - T txn = db.startTransaction(); + windowLock.writeLock().lock(); try { - added = db.addTransport(txn, t); - db.commitTransaction(txn); - } catch(DbException e) { - db.abortTransaction(txn); - throw e; + T txn = db.startTransaction(); + try { + added = db.addTransport(txn, t); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + windowLock.writeLock().unlock(); } } finally { transportLock.writeLock().unlock(); @@ -452,7 +463,7 @@ DatabaseCleaner.Callback { * the given contact, depending on whether the message is outgoing or * incoming, respectively. * <p> - * Locking: contact read, message write. + * Locking: message write. */ private boolean storePrivateMessage(T txn, Message m, ContactId c, boolean incoming) throws DbException { @@ -515,7 +526,7 @@ DatabaseCleaner.Callback { // Get some sendable messages from the database contactLock.readLock().lock(); try { - messageLock.writeLock().lock(); + messageLock.readLock().lock(); try { subscriptionLock.readLock().lock(); try { @@ -537,7 +548,7 @@ DatabaseCleaner.Callback { subscriptionLock.readLock().unlock(); } } finally { - messageLock.writeLock().unlock(); + messageLock.readLock().unlock(); } if(messages.isEmpty()) return null; // Record the messages as sent @@ -627,15 +638,20 @@ DatabaseCleaner.Callback { try { messageLock.readLock().lock(); try { - T txn = db.startTransaction(); + subscriptionLock.readLock().lock(); try { - if(!db.containsContact(txn, c)) - throw new NoSuchContactException(); - offered = db.getMessagesToOffer(txn, c, maxMessages); - db.commitTransaction(txn); - } catch(DbException e) { - db.abortTransaction(txn); - throw e; + T txn = db.startTransaction(); + try { + if(!db.containsContact(txn, c)) + throw new NoSuchContactException(); + offered = db.getMessagesToOffer(txn, c, maxMessages); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + subscriptionLock.readLock().unlock(); } } finally { messageLock.readLock().unlock(); @@ -674,22 +690,27 @@ DatabaseCleaner.Callback { throws DbException { contactLock.readLock().lock(); try { - retentionLock.writeLock().lock(); + messageLock.readLock().lock(); try { - T txn = db.startTransaction(); + retentionLock.writeLock().lock(); try { - if(!db.containsContact(txn, c)) - throw new NoSuchContactException(); - RetentionUpdate u = - db.getRetentionUpdate(txn, c, maxLatency); - db.commitTransaction(txn); - return u; - } catch(DbException e) { - db.abortTransaction(txn); - throw e; + T txn = db.startTransaction(); + try { + if(!db.containsContact(txn, c)) + throw new NoSuchContactException(); + RetentionUpdate u = + db.getRetentionUpdate(txn, c, maxLatency); + db.commitTransaction(txn); + return u; + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + retentionLock.writeLock().unlock(); } } finally { - retentionLock.writeLock().unlock(); + messageLock.readLock().unlock(); } } finally { contactLock.readLock().unlock(); @@ -882,17 +903,22 @@ DatabaseCleaner.Callback { throws DbException { messageLock.readLock().lock(); try { - T txn = db.startTransaction(); + subscriptionLock.readLock().lock(); try { - if(!db.containsSubscription(txn, g)) - throw new NoSuchSubscriptionException(); - Collection<GroupMessageHeader> headers = - db.getMessageHeaders(txn, g); - db.commitTransaction(txn); - return headers; - } catch(DbException e) { - db.abortTransaction(txn); - throw e; + T txn = db.startTransaction(); + try { + if(!db.containsSubscription(txn, g)) + throw new NoSuchSubscriptionException(); + Collection<GroupMessageHeader> headers = + db.getMessageHeaders(txn, g); + db.commitTransaction(txn); + return headers; + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + subscriptionLock.readLock().unlock(); } } finally { messageLock.readLock().unlock(); @@ -956,53 +982,37 @@ DatabaseCleaner.Callback { public Map<ContactId, TransportProperties> getRemoteProperties( TransportId t) throws DbException { - contactLock.readLock().lock(); + transportLock.readLock().lock(); try { - transportLock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - Map<ContactId, TransportProperties> properties = - db.getRemoteProperties(txn, t); - db.commitTransaction(txn); - return properties; - } catch(DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - transportLock.readLock().unlock(); + Map<ContactId, TransportProperties> properties = + db.getRemoteProperties(txn, t); + db.commitTransaction(txn); + return properties; + } catch(DbException e) { + db.abortTransaction(txn); + throw e; } } finally { - contactLock.readLock().unlock(); + transportLock.readLock().unlock(); } } public Collection<TemporarySecret> getSecrets() throws DbException { - contactLock.readLock().lock(); + windowLock.readLock().lock(); try { - transportLock.readLock().lock(); + T txn = db.startTransaction(); try { - windowLock.readLock().lock(); - try { - T txn = db.startTransaction(); - try { - Collection<TemporarySecret> secrets = - db.getSecrets(txn); - db.commitTransaction(txn); - return secrets; - } catch(DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - windowLock.readLock().unlock(); - } - } finally { - transportLock.readLock().unlock(); + Collection<TemporarySecret> secrets = db.getSecrets(txn); + db.commitTransaction(txn); + return secrets; + } catch(DbException e) { + db.abortTransaction(txn); + throw e; } } finally { - contactLock.readLock().unlock(); + windowLock.readLock().unlock(); } } @@ -1045,20 +1055,14 @@ DatabaseCleaner.Callback { public Map<GroupId, Integer> getUnreadMessageCounts() throws DbException { messageLock.readLock().lock(); try { - subscriptionLock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - Map<GroupId, Integer> counts = - db.getUnreadMessageCounts(txn); - db.commitTransaction(txn); - return counts; - } catch(DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - subscriptionLock.readLock().unlock(); + Map<GroupId, Integer> counts = db.getUnreadMessageCounts(txn); + db.commitTransaction(txn); + return counts; + } catch(DbException e) { + db.abortTransaction(txn); + throw e; } } finally { messageLock.readLock().unlock(); @@ -1066,26 +1070,21 @@ DatabaseCleaner.Callback { } public Collection<ContactId> getVisibility(GroupId g) throws DbException { - contactLock.readLock().lock(); + subscriptionLock.readLock().lock(); try { - subscriptionLock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if(!db.containsSubscription(txn, g)) - throw new NoSuchSubscriptionException(); - Collection<ContactId> visible = db.getVisibility(txn, g); - db.commitTransaction(txn); - return visible; - } catch(DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - subscriptionLock.readLock().unlock(); + if(!db.containsSubscription(txn, g)) + throw new NoSuchSubscriptionException(); + Collection<ContactId> visible = db.getVisibility(txn, g); + db.commitTransaction(txn); + return visible; + } catch(DbException e) { + db.abortTransaction(txn); + throw e; } } finally { - contactLock.readLock().unlock(); + subscriptionLock.readLock().unlock(); } } @@ -1250,21 +1249,26 @@ DatabaseCleaner.Callback { try { messageLock.writeLock().lock(); try { - subscriptionLock.readLock().lock(); + ratingLock.readLock().lock(); try { - T txn = db.startTransaction(); + subscriptionLock.readLock().lock(); try { - if(!db.containsContact(txn, c)) - throw new NoSuchContactException(); - added = storeMessage(txn, c, m); - db.addMessageToAck(txn, c, m.getId()); - db.commitTransaction(txn); - } catch(DbException e) { - db.abortTransaction(txn); - throw e; + T txn = db.startTransaction(); + try { + if(!db.containsContact(txn, c)) + throw new NoSuchContactException(); + added = storeMessage(txn, c, m); + db.addMessageToAck(txn, c, m.getId()); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + subscriptionLock.readLock().unlock(); } } finally { - subscriptionLock.readLock().unlock(); + ratingLock.readLock().unlock(); } } finally { messageLock.writeLock().unlock(); @@ -1280,7 +1284,7 @@ DatabaseCleaner.Callback { * Attempts to store a message received from the given contact, and returns * true if it was stored. * <p> - * Locking: contact read, message write, subscription read. + * Locking: contact read, message write, rating read, subscription read. */ private boolean storeMessage(T txn, ContactId c, Message m) throws DbException { @@ -1530,15 +1534,20 @@ DatabaseCleaner.Callback { public void removeTransport(TransportId t) throws DbException { transportLock.writeLock().lock(); try { - T txn = db.startTransaction(); + windowLock.writeLock().lock(); try { - if(!db.containsTransport(txn, t)) - throw new NoSuchTransportException(); - db.removeTransport(txn, t); - db.commitTransaction(txn); - } catch(DbException e) { - db.abortTransaction(txn); - throw e; + T txn = db.startTransaction(); + try { + if(!db.containsTransport(txn, t)) + throw new NoSuchTransportException(); + db.removeTransport(txn, t); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + windowLock.writeLock().unlock(); } } finally { transportLock.writeLock().unlock(); @@ -1705,7 +1714,7 @@ DatabaseCleaner.Callback { public void setVisibility(GroupId g, Collection<ContactId> visible) throws DbException { Collection<ContactId> affected = new ArrayList<ContactId>(); - contactLock.writeLock().lock(); + contactLock.readLock().lock(); try { subscriptionLock.writeLock().lock(); try { @@ -1739,7 +1748,7 @@ DatabaseCleaner.Callback { subscriptionLock.writeLock().unlock(); } } finally { - contactLock.writeLock().unlock(); + contactLock.readLock().unlock(); } if(!affected.isEmpty()) callListeners(new LocalSubscriptionsUpdatedEvent(affected)); @@ -1767,31 +1776,26 @@ DatabaseCleaner.Callback { public void unsubscribe(GroupId g) throws DbException { Collection<ContactId> affected; - contactLock.writeLock().lock(); + messageLock.writeLock().lock(); try { - messageLock.writeLock().lock(); + subscriptionLock.writeLock().lock(); try { - subscriptionLock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if(!db.containsSubscription(txn, g)) - throw new NoSuchSubscriptionException(); - affected = db.getVisibility(txn, g); - db.removeSubscription(txn, g); - db.commitTransaction(txn); - } catch(DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - subscriptionLock.writeLock().unlock(); + if(!db.containsSubscription(txn, g)) + throw new NoSuchSubscriptionException(); + affected = db.getVisibility(txn, g); + db.removeSubscription(txn, g); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; } } finally { - messageLock.writeLock().unlock(); + subscriptionLock.writeLock().unlock(); } } finally { - contactLock.writeLock().unlock(); + messageLock.writeLock().unlock(); } callListeners(new LocalSubscriptionsUpdatedEvent(affected)); } @@ -1817,34 +1821,28 @@ DatabaseCleaner.Callback { */ private boolean expireMessages(int size) throws DbException { boolean removed = false; - contactLock.readLock().lock(); + messageLock.writeLock().lock(); try { - messageLock.writeLock().lock(); + retentionLock.writeLock().lock(); try { - retentionLock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - Collection<MessageId> old = - db.getOldMessages(txn, size); - if(!old.isEmpty()) { - for(MessageId m : old) removeMessage(txn, m); - db.incrementRetentionVersions(txn); - removed = true; - } - db.commitTransaction(txn); - } catch(DbException e) { - db.abortTransaction(txn); - throw e; + Collection<MessageId> old = db.getOldMessages(txn, size); + if(!old.isEmpty()) { + for(MessageId m : old) removeMessage(txn, m); + db.incrementRetentionVersions(txn); + removed = true; } - } finally { - retentionLock.writeLock().unlock(); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; } } finally { - messageLock.writeLock().unlock(); + retentionLock.writeLock().unlock(); } } finally { - contactLock.readLock().unlock(); + messageLock.writeLock().unlock(); } if(removed) callListeners(new MessageExpiredEvent()); return removed; @@ -1853,7 +1851,7 @@ DatabaseCleaner.Callback { /** * Removes the given message (and all associated state) from the database. * <p> - * Locking: contact read, message write. + * Locking: message write. */ private void removeMessage(T txn, MessageId m) throws DbException { int sendability = db.getSendability(txn, m); diff --git a/briar-core/src/net/sf/briar/db/H2Database.java b/briar-core/src/net/sf/briar/db/H2Database.java index 6a396bddd0575aebb9bd5ba56a9eefd28d07a804..6c28bc77958a84b8fec70adc8e115e52136d5bba 100644 --- a/briar-core/src/net/sf/briar/db/H2Database.java +++ b/briar-core/src/net/sf/briar/db/H2Database.java @@ -65,6 +65,14 @@ class H2Database extends JdbcDatabase { } } + private long getDiskSpace(File f) { + long total = 0; + if(f.isDirectory()) { + for(File child : f.listFiles()) total += getDiskSpace(child); + return total; + } else return f.length(); + } + @Override protected Connection createConnection() throws SQLException { Properties props = new Properties(); diff --git a/briar-core/src/net/sf/briar/db/JdbcDatabase.java b/briar-core/src/net/sf/briar/db/JdbcDatabase.java index 4be4420fd19ad1c4230d0e45868dd91ce18305c9..2ea61d2da3bb5e94d8456dbc7eb7a614a9a34f63 100644 --- a/briar-core/src/net/sf/briar/db/JdbcDatabase.java +++ b/briar-core/src/net/sf/briar/db/JdbcDatabase.java @@ -61,6 +61,7 @@ import net.sf.briar.util.FileUtils; abstract class JdbcDatabase implements Database<Connection> { // Locking: contact + // Dependents: message, retention, subscription, transport, window private static final String CREATE_CONTACTS = "CREATE TABLE contacts " + " (contactId COUNTER," @@ -68,6 +69,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " PRIMARY KEY (contactId))"; // Locking: subscription + // Dependents: message private static final String CREATE_GROUPS = "CREATE TABLE groups" + " (groupId HASH NOT NULL," @@ -75,7 +77,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " key BINARY," // Null for unrestricted groups + " PRIMARY KEY (groupId))"; - // Locking: contact read, subscription + // Locking: subscription private static final String CREATE_GROUP_VISIBILITIES = "CREATE TABLE groupVisibilities" + " (contactId INT NOT NULL," @@ -87,7 +89,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " REFERENCES groups (groupId)" + " ON DELETE CASCADE)"; - // Locking: contact read, subscription + // Locking: subscription private static final String CREATE_CONTACT_GROUPS = "CREATE TABLE contactGroups" + " (contactId INT NOT NULL," @@ -99,7 +101,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; - // Locking: contact read, subscription + // Locking: subscription private static final String CREATE_GROUP_VERSIONS = "CREATE TABLE groupVersions" + " (contactId INT NOT NULL," @@ -153,17 +155,17 @@ abstract class JdbcDatabase implements Database<Connection> { private static final String INDEX_MESSAGES_BY_SENDABILITY = "CREATE INDEX messagesBySendability ON messages (sendability)"; - // Locking: contact read, message + // Locking: message private static final String CREATE_MESSAGES_TO_ACK = "CREATE TABLE messagesToAck" - + " (messageId HASH NOT NULL," + + " (messageId HASH NOT NULL," // Not a foreign key + " contactId INT NOT NULL," + " PRIMARY KEY (messageId, contactId)," + " FOREIGN KEY (contactId)" + " REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; - // Locking: contact read, message + // Locking: message private static final String CREATE_STATUSES = "CREATE TABLE statuses" + " (messageId HASH NOT NULL," @@ -192,7 +194,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " rating SMALLINT NOT NULL," + " PRIMARY KEY (authorId))"; - // Locking: contact read, retention + // Locking: retention private static final String CREATE_RETENTION_VERSIONS = "CREATE TABLE retentionVersions" + " (contactId INT NOT NULL," @@ -209,6 +211,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " ON DELETE CASCADE)"; // Locking: transport + // Dependents: window private static final String CREATE_TRANSPORTS = "CREATE TABLE transports (transportId HASH NOT NULL)"; @@ -234,7 +237,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " REFERENCES transports (transportId)" + " ON DELETE CASCADE)"; - // Locking: contact read, transport + // Locking: transport private static final String CREATE_TRANSPORT_VERSIONS = "CREATE TABLE transportVersions" + " (contactId INT NOT NULL," @@ -251,7 +254,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " REFERENCES transports (transportId)" + " ON DELETE CASCADE)"; - // Locking: contact read, transport + // Locking: transport private static final String CREATE_CONTACT_TRANSPORT_PROPS = "CREATE TABLE contactTransportProperties" + " (contactId INT NOT NULL," @@ -263,7 +266,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; - // Locking: contact read, transport + // Locking: transport private static final String CREATE_CONTACT_TRANSPORT_VERSIONS = "CREATE TABLE contactTransportVersions" + " (contactId INT NOT NULL," @@ -275,7 +278,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; - // Locking: contact read, transport read, window + // Locking: window private static final String CREATE_ENDPOINTS = "CREATE TABLE endpoints" + " (contactId INT NOT NULL," @@ -292,7 +295,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " REFERENCES transports (transportId)" + " ON DELETE CASCADE)"; - // Locking: contact read, transport read, window + // Locking: window private static final String CREATE_SECRETS = "CREATE TABLE secrets" + " (contactId INT NOT NULL," @@ -310,7 +313,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " REFERENCES transports (transportId)" + " ON DELETE CASCADE)"; - // Locking: contact read, window + // Locking: window private static final String CREATE_CONNECTION_TIMES = "CREATE TABLE connectionTimes" + " (contactId INT NOT NULL," @@ -754,7 +757,7 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT COUNT (groupId) from GROUPS"; + String sql = "SELECT COUNT (groupId) FROM groups"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); if(!rs.next()) throw new DbStateException(); @@ -1118,14 +1121,6 @@ abstract class JdbcDatabase implements Database<Connection> { } } - protected long getDiskSpace(File f) { - long total = 0; - if(f.isDirectory()) { - for(File child : f.listFiles()) total += getDiskSpace(child); - return total; - } else return f.length(); - } - public MessageId getGroupMessageParent(Connection txn, MessageId m) throws DbException { PreparedStatement ps = null;