# This patch file was generated by NetBeans IDE # This patch can be applied using context Tools: Apply Diff Patch action on respective folder. # It uses platform neutral UTF-8 encoding. # Above lines and this line are ignored by the patching process. Index: openide/util/apichanges.xml --- openide/util/apichanges.xml Base (1.27) +++ openide/util/apichanges.xml Locally Modified (Based On 1.27) @@ -49,6 +49,24 @@ Actions API + + + Mutex made pluggable + + + + + +

+ Added new constructor + Mutex(Priviledged, Executor) + that allows creators of the mutex to intercept and wrap posted actions + with custom code. +

+
+ + +
Obsolete method Utilities.isLargeFrameIcons deprecated. Index: openide/util/src/org/openide/util/Mutex.java --- openide/util/src/org/openide/util/Mutex.java Base (1.19) +++ openide/util/src/org/openide/util/Mutex.java Locally Modified (Based On 1.19) @@ -48,6 +48,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.concurrent.Executor; import java.util.logging.Level; import java.util.logging.Logger; @@ -176,6 +177,9 @@ /** protects internal data structures */ private /*final*/ Object LOCK; + /** wrapper, if any */ + private final Executor wrapper; + /** threads that - owns or waits for this mutex */ private /*final*/ Map registeredThreads; @@ -200,12 +204,14 @@ */ public Mutex(Object lock) { init(lock); + this.wrapper = null; } /** Default constructor. */ public Mutex() { init(new InternalLock()); + this.wrapper = null; } /** @param privileged can enter privileged states of this Mutex @@ -218,8 +224,28 @@ init(new InternalLock()); privileged.setParent(this); } + this.wrapper = null; } + /** Constructor for those who wish to do some custom additional tasks + * whenever an action or runnable is executed in the {@link Mutex}. This + * may be useful for wrapping all the actions with custom {@link ThreadLocal} + * value, etc. Just implement the {@link Executor}'s execute(Runnable) + * method and do pre and post initialization tasks before running the runnable. + *

+ * The {@link Executor#execute} method shall return only when the passed in + * {@link Runnable} is finished, otherwise methods like {@link Mutex#readAccess(Action)} and co. + * might not return proper result. + * + * @param privileged can enter privileged states of this Mutex + * @param executor allows to wrap the work of the mutex with a custom code + * @since 7.12 + */ + public Mutex(Privileged privileged, Executor executor) { + LOCK = new Mutex(privileged); + this.wrapper = executor; + } + /** Initiates this Mutex */ private void init(Object lock) { this.LOCK = lock; @@ -236,7 +262,7 @@ * @param action the action to perform * @return the object returned from {@link Mutex.Action#run} */ - public T readAccess(Action action) { + public T readAccess(final Action action) { if (this == EVENT) { try { return doEventAccess(action); @@ -244,6 +270,13 @@ throw (InternalError) new InternalError("Exception from non-Exception Action").initCause(e.getException()); // NOI18N } } + if (wrapper != null) { + try { + return doWrapperAccess(action, null, true); + } catch (MutexException e) { + throw (InternalError) new InternalError("Exception from non-Exception Action").initCause(e.getException()); // NOI18N + } + } Thread t = Thread.currentThread(); readEnter(t); @@ -279,10 +312,13 @@ * @exception RuntimeException if any runtime exception is thrown from the run method * @see #readAccess(Mutex.Action) */ - public T readAccess(ExceptionAction action) throws MutexException { + public T readAccess(final ExceptionAction action) throws MutexException { if (this == EVENT) { return doEventAccess(action); } + if (wrapper != null) { + return doWrapperAccess(action, null, true); + } Thread t = Thread.currentThread(); readEnter(t); @@ -310,6 +346,14 @@ return; } + if (wrapper != null) { + try { + doWrapperAccess(null, action, true); + return; + } catch (MutexException ex) { + throw (IllegalStateException)new IllegalStateException().initCause(ex); + } + } Thread t = Thread.currentThread(); readEnter(t); @@ -335,6 +379,13 @@ throw (InternalError) new InternalError("Exception from non-Exception Action").initCause(e.getException()); // NOI18N } } + if (wrapper != null) { + try { + return doWrapperAccess(action, null, false); + } catch (MutexException e) { + throw (InternalError) new InternalError("Exception from non-Exception Action").initCause(e.getException()); // NOI18N + } + } Thread t = Thread.currentThread(); writeEnter(t); @@ -371,6 +422,9 @@ if (this == EVENT) { return doEventAccess(action); } + if (wrapper != null) { + return doWrapperAccess(action, null, false); + } Thread t = Thread.currentThread(); writeEnter(t); @@ -399,6 +453,14 @@ return; } + if (wrapper != null) { + try { + doWrapperAccess(null, action, false); + } catch (MutexException ex) { + throw (IllegalStateException)new IllegalStateException().initCause(ex); + } + return; + } Thread t = Thread.currentThread(); writeEnter(t); @@ -431,6 +493,10 @@ if (this == EVENT) { return javax.swing.SwingUtilities.isEventDispatchThread(); } + if (wrapper != null) { + Mutex m = (Mutex)LOCK; + return m.isReadAccess(); + } Thread t = Thread.currentThread(); ThreadInfo info; @@ -460,6 +526,10 @@ if (this == EVENT) { return javax.swing.SwingUtilities.isEventDispatchThread(); } + if (wrapper != null) { + Mutex m = (Mutex)LOCK; + return m.isWriteAccess(); + } Thread t = Thread.currentThread(); ThreadInfo info; @@ -492,7 +562,7 @@ * @param run runnable to run */ public void postReadRequest(final Runnable run) { - postRequest(S, run); + postRequest(S, run, null); } /** Posts a write request. This request runs immediately iff @@ -509,7 +579,7 @@ * @param run runnable to run */ public void postWriteRequest(Runnable run) { - postRequest(X, run); + postRequest(X, run, null); } /** toString */ @@ -1128,14 +1198,19 @@ * @param mutexMode mutex mode for which the action is rquested * @param run the action */ - private void postRequest(int mutexMode, Runnable run) { + private void postRequest(final int mutexMode, final Runnable run, Executor exec) { if (this == EVENT) { doEventRequest(run); return; } + if (wrapper != null) { + Mutex m = (Mutex)LOCK; + m.postRequest(mutexMode, run, wrapper); + return; + } - Thread t = Thread.currentThread(); + final Thread t = Thread.currentThread(); ThreadInfo info; synchronized (LOCK) { @@ -1156,8 +1231,22 @@ // this mutex is not held if (info == null) { + if (exec != null) { + class Exec implements Runnable { + public void run() { enter(mutexMode, t, true); + try { + run.run(); + } finally { + leave(t); + } + } + } + exec.execute(new Exec()); + return; + } + enter(mutexMode, t, true); try { run.run(); } finally { @@ -1193,6 +1282,49 @@ return (threadGranted == S) && (requested == X) && (readersNo == 1); } + // -------------------------------- WRAPPERS -------------------------------- + + private T doWrapperAccess( + final ExceptionAction action, final Runnable runnable, final boolean readOnly + ) throws MutexException { + class R implements Runnable { + T ret; + MutexException e; + + public void run() { + Mutex m = (Mutex)LOCK; + try { + if (readOnly) { + if (action != null) { + ret = m.readAccess(action); + } else { + m.readAccess(runnable); + } + } else { + if (action != null) { + ret = m.writeAccess(action); + } else { + m.writeAccess(runnable); + } + } + } catch (MutexException ex) { + this.e = ex; + } + } + } + R run = new R(); + Mutex m = (Mutex)LOCK; + if (m.isWriteAccess() || m.isReadAccess()) { + run.run(); + } else { + wrapper.execute(run); + } + if (run.e != null) { + throw run.e; + } + return run.ret; + } + // ------------------------------- EVENT METHODS ---------------------------- /** Runs the runnable in event queue, either immediatelly, Index: openide/util/test/unit/src/org/openide/util/MutexWrapTest.java --- openide/util/test/unit/src/org/openide/util/MutexWrapTest.java No Base Revision +++ openide/util/test/unit/src/org/openide/util/MutexWrapTest.java Locally New @@ -0,0 +1,225 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ + +package org.openide.util; + +import java.util.concurrent.Executor; +import junit.framework.Test; +import org.netbeans.junit.NbTestCase; +import org.netbeans.junit.NbTestSuite; +import org.openide.util.Mutex.Action; +import org.openide.util.Mutex.ExceptionAction; + + +public class MutexWrapTest extends NbTestCase implements Executor { + Mutex.Privileged p; + Mutex m; + ThreadLocal IN = new ThreadLocal(); + + public MutexWrapTest(java.lang.String testName) { + super(testName); + } + + public static Test suite() { +// return new MutexWrapTest("testPostRead"); + return new NbTestSuite(MutexWrapTest.class); + } + + /** Sets up the test. + */ + @Override + protected void setUp () { + p = new Mutex.Privileged (); + m = new Mutex (p, this); + } + + public void testRead() throws Exception { + A arg = new A(); + Object ret = m.readAccess(arg); + assertEquals("Return type is ok", arg, ret); + } + public void testWrite() throws Exception { + A arg = new A(); + Object ret = m.writeAccess(arg); + assertEquals("Return type is ok", arg, ret); + } + public void testExRead() throws Exception { + E arg = new E(); + Object ret = m.readAccess(arg); + assertEquals("Return type is ok", arg, ret); + } + public void testExWrite() throws Exception { + E arg = new E(); + Object ret = m.writeAccess(arg); + assertEquals("Return type is ok", arg, ret); + } + public void testRunRead() throws Exception { + R arg = new R(); + m.readAccess(arg); + assertTrue("Executed", arg.exec); + } + public void testRunWrite() throws Exception { + R arg = new R(); + m.writeAccess(arg); + assertTrue("Executed", arg.exec); + } + public void testPostRead() throws Exception { + R arg = new R(); + m.postReadRequest(arg); + assertTrue("Executed", arg.exec); + } + public void testPostReadFromWrite() throws Exception { + R arg = new R(); + Del del = new Del(); + del.read = arg; + m.writeAccess(del); + assertTrue("Executed", arg.exec); + } + public void testPostReadFromRead() throws Exception { + R arg = new R(); + Del del = new Del(); + del.read = arg; + m.readAccess(del); + assertTrue("Executed", arg.exec); + } + public void testPostWrite() throws Exception { + R arg = new R(); + m.postWriteRequest(arg); + assertTrue("Executed", arg.exec); + } + public void testPostWriteFromRead() throws Exception { + R arg = new R(); + Del del = new Del(); + del.write = arg; + m.readAccess(del); + assertTrue("Executed", arg.exec); + } + public void testPostWriteFromWrite() throws Exception { + R arg = new R(); + Del del = new Del(); + del.write = arg; + m.writeAccess(del); + assertTrue("Executed", arg.exec); + } + public void testReadAndRead() throws Exception { + R arg = new R(); + Del del = new Del(); + del.readNow = arg; + m.readAccess(del); + assertTrue("Executed", arg.exec); + } + public void testWriteAndRead() throws Exception { + R arg = new R(); + Del del = new Del(); + del.readNow = arg; + m.writeAccess(del); + assertTrue("Executed", arg.exec); + } + public void testWriteAndWrite() throws Exception { + R arg = new R(); + Del del = new Del(); + del.writeNow = arg; + m.writeAccess(del); + assertTrue("Executed", arg.exec); + } + + + private class A implements Mutex.Action { + boolean exec; + + public Object run() { + exec = true; + assertEquals("I am wrapped", MutexWrapTest.this, IN.get()); + return this; + } + } + private class E implements Mutex.ExceptionAction { + boolean exec; + + public Object run() { + exec = true; + assertEquals("I am wrapped", MutexWrapTest.this, IN.get()); + return this; + } + } + private class R implements Runnable { + boolean exec; + + public void run() { + exec = true; + assertEquals("I am wrapped", MutexWrapTest.this, IN.get()); + } + } + private class Del implements Runnable { + Runnable write; + Runnable read; + Runnable writeNow; + Runnable readNow; + + public void run() { + if (write != null) { + m.postWriteRequest(write); + } + if (read != null) { + m.postReadRequest(read); + } + if (writeNow != null) { + m.writeAccess(writeNow); + } + if (readNow != null) { + m.readAccess(readNow); + } + } + } + public void execute(final Runnable run) { + Object prev = IN.get(); + assertEquals("No previous value set", null, prev); + IN.set(MutexWrapTest.this); + try { + run.run(); + } finally { + IN.set(prev); + } + assertFalse("No read", m.isReadAccess()); + assertFalse("No write", m.isWriteAccess()); + } + +}