All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openide.util.Mutex Maven / Gradle / Ivy

There is a newer version: RELEASE230
Show newest version
/*
 * 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.util;

import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import org.openide.util.MutexException;
import org.netbeans.modules.openide.util.DefaultMutexImplementation;
import org.netbeans.modules.openide.util.LazyMutexImplementation;
import org.openide.util.spi.MutexEventProvider;
import org.openide.util.spi.MutexImplementation;

/** Read-many/write-one lock.
* Allows control over resources that
* can be read by several readers at once but only written by one writer.
* 

* It is guaranteed that if you are a writer you can also enter the * mutex as a reader. Conversely, if you are the only reader you * can enter the mutex as a writer, but you'll be warned because it is very * deadlock prone (two readers trying to get write access concurently). *

* If the mutex is used only by one thread, the thread can repeatedly * enter it as a writer or reader. So one thread can never deadlock itself, * whichever order operations are performed in. *

* There is no strategy to prevent starvation. * Even if there is a writer waiting to enter, another reader might enter * the section instead. *

* Examples of use: * *

* Mutex m = new Mutex();
*
* // Grant write access, compute an integer and return it:
* return m.writeAccess(new Mutex.Action<Integer>(){
*     public Integer run() {
*         return 1;
*     }
* });
*
* // Obtain read access, do some computation,
* // possibly throw an IOException:
* try {
*     m.readAccess(new Mutex.ExceptionAction<Void>() {
*         public Void run() throws IOException {
*             if (...) throw new IOException();
*             return null;
*         }
*     });
* } catch (MutexException ex) {
*     throw (IOException) ex.getException();
* }
*
* // check whether you are already in read access
* if (m.isReadAccess()) {
*     // do your work
* }
* 
* * @author Ales Novak */ public final class Mutex { /** * The actual delegate, which performs the work */ private final MutexImplementation impl; /** logger for things that happen in mutex */ private static final Logger LOG = Logger.getLogger(Mutex.class.getName()); /** Mutex that allows code to be synchronized with the AWT event dispatch thread. *

* When the Mutex methods are invoked on this mutex, the methods' semantics * change as follows: *

    *
  • The {@link #isReadAccess} and {@link #isWriteAccess} methods * return true if the current thread is the event dispatch thread * and false otherwise. *
  • The {@link #postReadRequest} and {@link #postWriteRequest} methods * asynchronously execute the {@link java.lang.Runnable} passed in their * run parameter on the event dispatch thead. *
  • The {@link #readAccess(java.lang.Runnable)} and * {@link #writeAccess(java.lang.Runnable)} methods asynchronously execute the * {@link java.lang.Runnable} passed in their run parameter * on the event dispatch thread, unless the current thread is * the event dispatch thread, in which case * run.run() is immediately executed. *
  • The {@link #readAccess(Mutex.Action)}, * {@link #readAccess(Mutex.ExceptionAction action)}, * {@link #writeAccess(Mutex.Action action)} and * {@link #writeAccess(Mutex.ExceptionAction action)} * methods synchronously execute the {@link Mutex.ExceptionAction} * passed in their action parameter on the event dispatch thread, * unless the current thread is the event dispatch thread, in which case * action.run() is immediately executed. *
*/ public static final Mutex EVENT; static { final Callable c = new Callable() { @Override public MutexImplementation call() throws Exception { final MutexEventProvider provider = Lookup.getDefault().lookup(MutexEventProvider.class); if (provider == null) { throw new IllegalStateException("No MutexEventProvider found in default Lookup."); //NOI18N } final MutexImplementation mutexImpl = provider.createMutex(); if (mutexImpl == null) { throw new IllegalStateException(String.format( "Null value from %s.createMutex()", //NOI18N provider.getClass())); } return mutexImpl; } }; EVENT = new Mutex(new LazyMutexImplementation(c)); } // lock mode constants /** * Creates {@link Mutex} with given SPI. * @param impl the {@link Mutex} SPI. * @since 9.1 */ public Mutex(MutexImplementation impl) { Parameters.notNull("impl", impl); //NOI18N this.impl = impl; } public Mutex(Object lock) { this(DefaultMutexImplementation.usingLock(lock)); } /** Default constructor. */ public Mutex() { this(DefaultMutexImplementation.create()); } /** @param privileged can enter privileged states of this Mutex * This helps avoid creating of custom Runnables. */ public Mutex(Privileged privileged) { this.impl = DefaultMutexImplementation.controlledBy(privileged.delegate); } /** 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 * @see SimpleMutex#SimpleMutex(org.openide.util.ReadWriteAccess.Privileged, java.util.concurrent.Executor) */ public Mutex(Privileged privileged, Executor executor) { this.impl = DefaultMutexImplementation.controlledBy(privileged.delegate, executor); } /** Run an action only with read access. * See class description re. entering for write access within the dynamic scope. * @param action the action to perform * @return the object returned from {@link Mutex.Action#run} */ public T readAccess(final Action action) { try { return impl.readAccess(action); } catch (MutexException ex) { throw (InternalError) new InternalError("Exception from non-Exception Action").initCause(ex.getException()); // NOI18N } } /** Run an action with read access and possibly throw a checked exception. * The exception if thrown is then encapsulated * in a MutexException and thrown from this method. One is encouraged * to catch MutexException, obtain the inner exception, and rethrow it. * Here is an example: *

    * try {
    *   mutex.readAccess (new ExceptionAction () {
    *     public void run () throws IOException {
    *       throw new IOException ();
    *     }
    *   });
    *  } catch (MutexException ex) {
    *    throw (IOException) ex.getException ();
    *  }
    * 
* Note that runtime exceptions are always passed through, and neither * require this invocation style, nor are encapsulated. * @param action the action to execute * @return the object returned from {@link Mutex.ExceptionAction#run} * @exception MutexException encapsulates a user exception * @exception RuntimeException if any runtime exception is thrown from the run method * @see #readAccess(Mutex.Action) */ public T readAccess(final ExceptionAction action) throws MutexException { return impl.readAccess(action); } /** Run an action with read access, returning no result. * It may be run asynchronously. * * @param action the action to perform * @see #readAccess(Mutex.Action) */ public void readAccess(final Runnable action) { impl.readAccess(action); } /** Run an action with write access. * The same thread may meanwhile reenter the mutex; see the class description for details. * * @param action the action to perform * @return the result of {@link Mutex.Action#run} */ public T writeAccess(Action action) { try { return impl.writeAccess(action); } catch (MutexException ex) { throw (InternalError) new InternalError("Exception from non-Exception Action").initCause(ex.getException()); // NOI18N } } /** Run an action with write access and possibly throw an exception. * Here is an example: *

    * try {
    *   mutex.writeAccess (new ExceptionAction () {
    *     public void run () throws IOException {
    *       throw new IOException ();
    *     }
    *   });
    *  } catch (MutexException ex) {
    *    throw (IOException) ex.getException ();
    *  }
    * 
* * @param action the action to execute * @return the result of {@link Mutex.ExceptionAction#run} * @exception MutexException an encapsulated checked exception, if any * @exception RuntimeException if a runtime exception is thrown in the action * @see #writeAccess(Mutex.Action) * @see #readAccess(Mutex.ExceptionAction) */ public T writeAccess(ExceptionAction action) throws MutexException { return impl.writeAccess(action); } /** Run an action with write access and return no result. * It may be run asynchronously. * * @param action the action to perform * @see #writeAccess(Mutex.Action) * @see #readAccess(Runnable) */ public void writeAccess(final Runnable action) { impl.writeAccess(action); } /** Tests whether this thread has already entered the mutex in read access. * If it returns true, calling readAccess * will be executed immediatelly * without any blocking. * Calling postWriteAccess will delay the execution * of its Runnable until a readAccess section is over * and calling writeAccess is strongly prohibited and will * result in a warning as a deadlock prone behaviour. *

Warning: since a thread with write access automatically * has effective read access as well (whether or not explicitly requested), if * you want to check whether a thread can read some data, you should check for * either kind of access, e.g.: *

assert myMutex.isReadAccess() || myMutex.isWriteAccess();
* * @return true if the thread is in read access section * @since 4.48 */ public boolean isReadAccess() { return impl.isReadAccess(); } /** Tests whether this thread has already entered the mutex in write access. * If it returns true, calling writeAccess will be executed * immediatelly without any other blocking. postReadAccess * will be delayed until a write access runnable is over. * * @return true if the thread is in write access section * @since 4.48 */ public boolean isWriteAccess() { return impl.isWriteAccess(); } /** toString */ @Override public String toString() { return String.format( "Mutex[%s]", //NOI18N impl.toString()); } // priv methods ----------------------------------------- /** Posts a read request. This request runs immediately iff * this SimpleMutex is in the shared mode or this SimpleMutex is not contended * at all. * * This request is delayed if this SimpleMutex is in the exclusive * mode and is held by this thread, until the exclusive is left. * * Finally, this request blocks, if this SimpleMutex is in the exclusive * mode and is held by another thread. * *

Warning: this method blocks.

* * @param run runnable to run */ public void postReadRequest(final Runnable run) { impl.postReadRequest(run); } /** Posts a write request. This request runs immediately iff * this SimpleMutex is in the "pure" exclusive mode, i.e. this SimpleMutex * is not reentered in shared mode after the exclusive mode * was acquired. Otherwise it is delayed until all read requests * are executed. * * This request runs immediately if this SimpleMutex is not contended at all. * * This request blocks if this SimpleMutex is in the shared mode. * *

Warning: this method blocks.

* @param run runnable to run */ public void postWriteRequest(Runnable run) { impl.postWriteRequest(run); } /** Action to be executed in a mutex without throwing any checked exceptions. * Unchecked exceptions will be propagated to calling code. * @param T the type of object to return */ @SuppressWarnings("PublicInnerClass") public interface Action extends ExceptionAction { /** Execute the action. * @return any object, then returned from {@link Mutex#readAccess(Mutex.Action)} or {@link Mutex#writeAccess(Mutex.Action)} */ @Override T run(); } /** Action to be executed in a mutex, possibly throwing checked exceptions. * May throw a checked exception, in which case calling * code should catch the encapsulating exception and rethrow the * real one. * Unchecked exceptions will be propagated to calling code without encapsulation. * @param T the type of object to return */ @SuppressWarnings("PublicInnerClass") public interface ExceptionAction { /** Execute the action. * Can throw an exception. * @return any object, then returned from {@link Mutex#readAccess(Mutex.ExceptionAction)} or {@link Mutex#writeAccess(Mutex.ExceptionAction)} * @exception Exception any exception the body needs to throw */ T run() throws Exception; } /** Provides access to Mutex's internal methods. * * This class can be used when one wants to avoid creating a * bunch of Runnables. Instead, *
     * try {
     *     enterXAccess ();
     *     yourCustomMethod ();
     * } finally {
     *     exitXAccess ();
     * }
     * 
* can be used. * * You must, however, control the related Mutex, i.e. you must be creator of * the Mutex. * * @since 1.17 */ public static final class Privileged { private final DefaultMutexImplementation.Privileged delegate; public Privileged() { this.delegate = new DefaultMutexImplementation.Privileged(); } public void enterReadAccess() { delegate.enterReadAccess(); } /** Tries to obtain read access. If the access cannot by * gained by given milliseconds, the method returns without gaining * it. * * @param timeout amount of milliseconds to wait before giving up. * 0 means to wait indefinitely. * -1 means to not wait at all and immediately exit * @return true if the access has been granted, * false otherwise * @since 8.37 */ public boolean tryReadAccess(long timeout) { return delegate.tryReadAccess(timeout); } public void enterWriteAccess() { delegate.enterWriteAccess(); } /** * Tries to obtain write access. If the access cannot by gained by given * milliseconds, the method returns without gaining it. * * @param timeout amount of milliseconds to wait before giving up. * 0 means to wait indefinitely. * -1 means to not wait at all and immediately exit * @return true if the access has been granted, * false otherwise * @since 8.37 */ public boolean tryWriteAccess(long timeout) { return delegate.tryWriteAccess(timeout); } public void exitReadAccess() { delegate.exitReadAccess(); } public void exitWriteAccess() { delegate.exitWriteAccess(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy