blob: 992b16ed52cb5a1a2ea87a64c666fefd18278b81 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.openide.text;
import java.util.Date;
import java.beans.*;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import javax.swing.text.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.Position;
import javax.swing.text.StyledDocument;
import junit.textui.TestRunner;
import org.netbeans.junit.NbTestCase;
import org.netbeans.junit.NbTestSuite;
import org.openide.text.CloneableEditorSupport;
import org.openide.text.NbDocument;
/**
* Tries closing a document while holding a read lock and adding
* a position reference from other thread.
*
* This test fails if switched to writeLock instead of readLock
*
* @author Petr Nejedly
*/
public class Deadlock49178Test extends NbTestCase implements CloneableEditorSupport.Env {
static {
System.setProperty("org.openide.windows.DummyWindowManager.VISIBLE", "false");
}
boolean inWait = false;
boolean shouldWait = true;
Object waitLock = new Object();
/** the support to work with */
private CES support;
// Env variables
private String content = "Hello";
private boolean valid = true;
private boolean modified = false;
private Date date = new Date ();
private transient PropertyChangeSupport prop = new PropertyChangeSupport(this);
private transient VetoableChangeSupport veto = new VetoableChangeSupport(this);
private Exception exception;
boolean processingDone = false;
boolean closingDone = false;
public Deadlock49178Test(String s) {
super(s);
}
public static void main(String[] args) {
TestRunner.run(new NbTestSuite(Deadlock49178Test.class));
}
protected void setUp () {
support = new CES (this, org.openide.util.Lookup.EMPTY);
}
public void testDeadlock49178() throws Exception {
// open the document
final StyledDocument docu = support.openDocument();
// Perform a modification so that notifyModify() gets called as supposed by the test
// Otherwise notifyUnmodified() would not be called (there's no reason to call it in such case).
docu.insertString(0, "a", null);
// start closing it
Thread closing = new Thread(new Runnable() { public void run() {
support.close(false); // will block in notifyUnmodified()
closingDone = true;
}});
closing.start();
Thread processing = new Thread(new Runnable() {
boolean second = false;
public void run() {
if (!second) {
second = true;
docu.render(this);
// NbDocument.runAtomic(docu, this);
} else { // inside readLock
support.createPositionRef(0, Position.Bias.Forward);
processingDone = true;
}
}
});
synchronized(waitLock) {
while (!inWait) waitLock.wait();
}
processing.start();
Thread.sleep(1000);
synchronized(waitLock) {
shouldWait = false;
waitLock.notifyAll();
}
closing.join(10000);
processing.join(10000);
assertNull("No exception thrown", exception);
assertTrue("Closing thread finished", closingDone);
assertTrue("Processing thread finished", processingDone);
}
//
// Implementation of the CloneableEditorSupport.Env
//
public void addPropertyChangeListener(PropertyChangeListener l) {
prop.addPropertyChangeListener (l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
prop.removePropertyChangeListener (l);
}
public void addVetoableChangeListener(VetoableChangeListener l) {
veto.addVetoableChangeListener (l);
}
public void removeVetoableChangeListener(VetoableChangeListener l) {
veto.removeVetoableChangeListener (l);
}
public org.openide.windows.CloneableOpenSupport findCloneableOpenSupport() {
return support;
}
public String getMimeType() {
return "text/plain";
}
public java.util.Date getTime() {
return date;
}
public java.io.InputStream inputStream() throws java.io.IOException {
return new ByteArrayInputStream(content.getBytes());
}
public java.io.OutputStream outputStream() throws java.io.IOException {
return new ByteArrayOutputStream();
}
public boolean isValid() {
return valid;
}
public boolean isModified() {
return modified;
}
public void markModified() throws java.io.IOException {
modified = true;
}
public void unmarkModified() {
modified = false;
}
/** Implementation of the CES */
private final class CES extends CloneableEditorSupport {
public CES (Env env, org.openide.util.Lookup l) {
super (env, l);
}
protected String messageName() {
return "Name";
}
protected String messageOpened() {
return "Opened";
}
protected String messageOpening() {
return "Opening";
}
protected String messageSave() {
return "Save";
}
protected String messageToolTip() {
return "ToolTip";
}
protected void notifyUnmodified () {
super.notifyUnmodified();
synchronized(waitLock) {
inWait = true;
waitLock.notifyAll();
try {
while (shouldWait) waitLock.wait();
} catch (InterruptedException e) {
exception = e;
}
}
}
protected StyledDocument createStyledDocument (EditorKit kit) {
return new Doc(); // Why the FilterDocument doesn't support WriteLockable?
}
class Doc extends DefaultStyledDocument implements NbDocument.WriteLockable {
public void runAtomic (Runnable r) {
writeLock();
try {
r.run();
} finally {
writeUnlock();
}
}
public void runAtomicAsUser (Runnable r) throws BadLocationException {
runAtomic(r);
}
}
} // end of CES
}