From 6ed8d89e59accaae54b360ba06d7597c9a76063f Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Tue, 27 Sep 2011 17:05:51 +0100
Subject: [PATCH] Don't block when free disk space is critically low.

If it's not possible to free up any additional space, exit rather than
hanging. In future we should warn the user before exiting.
---
 .../sf/briar/db/DatabaseComponentImpl.java    | 56 ++++++-------------
 1 file changed, 16 insertions(+), 40 deletions(-)

diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java
index bc882aa453..7ef8202db8 100644
--- a/components/net/sf/briar/db/DatabaseComponentImpl.java
+++ b/components/net/sf/briar/db/DatabaseComponentImpl.java
@@ -86,10 +86,8 @@ DatabaseCleaner.Callback {
 	private final List<DatabaseListener> listeners =
 		new ArrayList<DatabaseListener>(); // Locking: self
 	private final Object spaceLock = new Object();
-	private final Object writeLock = new Object();
 	private long bytesStoredSinceLastCheck = 0L; // Locking: spaceLock
 	private long timeOfLastCheck = 0L; // Locking: spaceLock
-	private volatile boolean writesAllowed = true;
 
 	@Inject
 	DatabaseComponentImpl(Database<T> db, DatabaseCleaner cleaner) {
@@ -162,7 +160,6 @@ DatabaseCleaner.Callback {
 
 	public void addLocalGroupMessage(Message m) throws DbException {
 		boolean added = false;
-		waitForPermissionToWrite();
 		contactLock.readLock().lock();
 		try {
 			messageLock.writeLock().lock();
@@ -201,23 +198,6 @@ DatabaseCleaner.Callback {
 		if(added) callListeners(Event.MESSAGES_ADDED);
 	}
 
-	/**
-	 * Blocks until messages are allowed to be stored in the database. The
-	 * storage of messages is not allowed while the amount of free storage
-	 * space available to the database is less than CRITICAL_FREE_SPACE.
-	 */
-	private void waitForPermissionToWrite() {
-		synchronized(writeLock) {
-			while(!writesAllowed) {
-				if(LOG.isLoggable(Level.FINE))
-					LOG.fine("Waiting for permission to write");
-				try {
-					writeLock.wait();
-				} catch(InterruptedException ignored) {}
-			}
-		}
-	}
-
 	/**
 	 * If the given message is already in the database, marks it as seen by the
 	 * sender and returns false. Otherwise stores the message, updates the
@@ -307,7 +287,6 @@ DatabaseCleaner.Callback {
 	public void addLocalPrivateMessage(Message m, ContactId c)
 	throws DbException {
 		boolean added = false;
-		waitForPermissionToWrite();
 		contactLock.readLock().lock();
 		try {
 			if(!containsContact(c)) throw new NoSuchContactException();
@@ -901,7 +880,6 @@ DatabaseCleaner.Callback {
 
 	public void receiveBatch(ContactId c, Batch b) throws DbException {
 		boolean anyAdded = false;
-		waitForPermissionToWrite();
 		contactLock.readLock().lock();
 		try {
 			if(!containsContact(c)) throw new NoSuchContactException();
@@ -1356,27 +1334,25 @@ DatabaseCleaner.Callback {
 	public void checkFreeSpaceAndClean() throws DbException {
 		long freeSpace = db.getFreeSpace();
 		while(freeSpace < MIN_FREE_SPACE) {
-			// If disk space is critical, disable the storage of new messages
-			if(freeSpace < CRITICAL_FREE_SPACE) {
-				if(LOG.isLoggable(Level.FINE)) LOG.fine("Critical cleanup");
-				writesAllowed = false;
-			} else {
-				if(LOG.isLoggable(Level.FINE)) LOG.fine("Normal cleanup");
+			boolean expired = expireMessages(BYTES_PER_SWEEP);
+			if(freeSpace < CRITICAL_FREE_SPACE && !expired) {
+				// FIXME: Work out what to do here - the amount of free space
+				// is critically low and there are no messages left to expire
+				System.err.println("Disk space is critical - shutting down");
+				System.exit(1);
 			}
-			expireMessages(BYTES_PER_SWEEP);
 			Thread.yield();
 			freeSpace = db.getFreeSpace();
-			// If disk space is no longer critical, re-enable writes
-			if(freeSpace >= CRITICAL_FREE_SPACE && !writesAllowed) {
-				writesAllowed = true;
-				synchronized(writeLock) {
-					writeLock.notifyAll();
-				}
-			}
 		}
 	}
 
-	private void expireMessages(int size) throws DbException {
+	/**
+	 * Removes the oldest messages from the database, with a total size less
+	 * than or equal to the given size, and returns true if any messages were
+	 * removed.
+	 */
+	private boolean expireMessages(int size) throws DbException {
+		Collection<MessageId> old;
 		contactLock.readLock().lock();
 		try {
 			messageLock.writeLock().lock();
@@ -1385,9 +1361,8 @@ DatabaseCleaner.Callback {
 				try {
 					T txn = db.startTransaction();
 					try {
-						for(MessageId m : db.getOldMessages(txn, size)) {
-							removeMessage(txn, m);
-						}
+						old = db.getOldMessages(txn, size);
+						for(MessageId m : old) removeMessage(txn, m);
 						db.commitTransaction(txn);
 					} catch(DbException e) {
 						db.abortTransaction(txn);
@@ -1402,6 +1377,7 @@ DatabaseCleaner.Callback {
 		} finally {
 			contactLock.readLock().unlock();
 		}
+		return old.isEmpty();
 	}
 
 	/**
-- 
GitLab