FIXED - task 559: Deadlock between autosave thread and history code

http://code.google.com/p/google-refine/issues/detail?id=559

git-svn-id: http://google-refine.googlecode.com/svn/trunk@2538 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
Tom Morris 2012-08-30 16:22:28 +00:00
parent ba89daec1c
commit 9b54a8f29e
2 changed files with 32 additions and 13 deletions

View File

@ -127,8 +127,13 @@ public class History implements Jsonizable {
* Adding a new entry clears all currently held future histories * Adding a new entry clears all currently held future histories
* @param entry * @param entry
*/ */
synchronized public void addEntry(HistoryEntry entry) { public void addEntry(HistoryEntry entry) {
entry.apply(ProjectManager.singleton.getProject(_projectID)); Project project = ProjectManager.singleton.getProject(_projectID);
synchronized (project) {
// NOTE: project lock must be acquired *first* to prevent deadlocks, so we use a
// synchronized block instead of synchronizing the entire method.
synchronized (this) {
entry.apply(project);
_pastEntries.add(entry); _pastEntries.add(entry);
setModified(); setModified();
@ -146,6 +151,8 @@ public class History implements Jsonizable {
} }
} }
} }
}
}
protected void setModified() { protected void setModified() {
ProjectManager.singleton.getProjectMetadata(_projectID).updateModified(); ProjectManager.singleton.getProjectMetadata(_projectID).updateModified();
@ -273,6 +280,12 @@ public class History implements Jsonizable {
writer.endObject(); writer.endObject();
} }
/*
* NOTE: this method is called from the autosave thread with the Project
* lock already held, so no other synchronized method here can aquire that
* Project lock or a deadlock will result.be careful of thread synchronization to avoid
* deadlocks.
*/
synchronized public void save(Writer writer, Properties options) throws IOException { synchronized public void save(Writer writer, Properties options) throws IOException {
writer.write("pastEntryCount="); writer.write(Integer.toString(_pastEntries.size())); writer.write('\n'); writer.write("pastEntryCount="); writer.write(Integer.toString(_pastEntries.size())); writer.write('\n');
for (HistoryEntry entry : _pastEntries) { for (HistoryEntry entry : _pastEntries) {

View File

@ -119,6 +119,12 @@ public class HistoryEntry implements Jsonizable {
_manager.save(this, writer, options); _manager.save(this, writer, options);
} }
/**
* Apply a change to a project. In most cases you should already hold the Project lock
* before calling this method to prevent deadlocks.
*
* @param project the project the change should be applied to
*/
public void apply(Project project) { public void apply(Project project) {
if (getChange() == null) { if (getChange() == null) {
ProjectManager.singleton.getHistoryEntryManager().loadChange(this); ProjectManager.singleton.getHistoryEntryManager().loadChange(this);