org.openide.filesystems.EventControl Maven / Gradle / Ivy
/*
* 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.filesystems;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;
/**
* @author rmatous
*/
class EventControl {
/** number of requests posted and not processed. to
* know what to do in sync.
*/
private int requests;
/** number of priority requests (requested from priority atomic block) posted
* and not processed. to
* know what to do in sync.
*/
private int priorityRequests;
/** Holds current propagation ID and link to previous*/
private AtomicActionLink currentAtomAction;
/** List of requests
* @GuardedBy("this")
*/
private LinkedList requestsQueue;
/**
* Method that can fire events directly, postpone them, fire them in
* standalone thread or in RequestProcessor
*/
void dispatchEvent(FileSystem.EventDispatcher dispatcher) {
if (postponeFiring(dispatcher)) {
return;
}
dispatcher.run();
}
/**
* Begin of priority atomic actions. Atomic actions from inside of org.openide.FileSystems
* are considered as priority atomic actions. From last priority atomic actions
* are fired events regardless if nested in any normal atomic action.
*
* Begin of block, that should be performed without firing events.
* Firing of events is postponed after end of block .
* There is strong necessity to use always both methods: beginAtomicAction
* and finishAtomicAction. It is recommended use it in try - finally block.
* @see FileSystemt#beginAtomicAction
* @param run Events fired from this atomic action will be marked as events
* that were fired from this run.
*/
void beginAtomicAction(FileSystem.AtomicAction run) {
enterAtomicAction(run, true);
}
/**
* End of priority atomic actions. Atomic actions from inside of org.openide.FileSystems
* are considered as priority atomic actions. From last priority atomic actions
* are fired events regardless if nested in any normal atomic action.
*
* End of block, that should be performed without firing events.
* Firing of events is postponed after end of block .
* There is strong necessity to use always both methods: beginAtomicAction
* and finishAtomicAction. It is recommended use it in try - finally block.
* @see FileSystemt#finishAtomicAction
*/
void finishAtomicAction() {
exitAtomicAction(true);
}
/** Executes atomic action. The atomic action represents a set of
* operations constituting one logical unit. It is guaranteed that during
* execution of such an action no events about changes in the filesystem
* will be fired.*/
void runAtomicAction(final FileSystem.AtomicAction run)
throws IOException {
try {
enterAtomicAction(run, false);
run.run();
} finally {
exitAtomicAction(false);
}
}
/** Enters atomic action.
*/
private synchronized void enterAtomicAction(Object propID, boolean priority) {
AtomicActionLink nextPropID = new AtomicActionLink(propID);
nextPropID.setPreviousLink(currentAtomAction);
currentAtomAction = nextPropID;
if (priority) {
priorityRequests++;
}
if (requests++ == 0) {
setRequestsQueue(new LinkedList());
}
}
/** Exits atomic action.
*/
private void exitAtomicAction(boolean priority) {
boolean fireAll = false;
boolean firePriority = false;
LinkedList reqQueueCopy;
synchronized (this) {
currentAtomAction = currentAtomAction.getPreviousLink();
requests--;
if (priority) {
priorityRequests--;
}
if (requests == 0) {
fireAll = true;
}
if (!fireAll && priority && (priorityRequests == 0)) {
firePriority = true;
}
if (fireAll || firePriority) {
reqQueueCopy = getRequestsQueue();
setRequestsQueue(null);
priorityRequests = 0;
} else {
return;
}
if (firePriority && !fireAll) {
setRequestsQueue(new LinkedList());
}
}
/** firing events outside synchronized block*/
if (fireAll) {
invokeDispatchers(false, reqQueueCopy);
return;
}
if (firePriority) {
LinkedList newReqQueue = invokeDispatchers(true, reqQueueCopy);
synchronized (this) {
while ((getRequestsQueue() != null) && !getRequestsQueue().isEmpty()) {
FileSystem.EventDispatcher r = getRequestsQueue().removeFirst();
newReqQueue.add(r);
}
setRequestsQueue(newReqQueue);
}
}
}
private LinkedList invokeDispatchers(boolean priority, LinkedList reqQueueCopy) {
LinkedList newEnum = new LinkedList();
Set postNotify = new LinkedHashSet();
while ((reqQueueCopy != null) && !reqQueueCopy.isEmpty()) {
FileSystem.EventDispatcher r = reqQueueCopy.removeFirst();
r.dispatch(priority, postNotify);
if (priority) {
newEnum.add(r);
}
}
for (Runnable r : postNotify) {
r.run();
}
return newEnum;
}
/* Adds dispatcher to queue.*/
private synchronized boolean postponeFiring(FileSystem.EventDispatcher disp) {
if (priorityRequests == 0) {
disp.setAtomicActionLink(currentAtomAction);
disp.dispatch(true, null);
}
if (getRequestsQueue() != null) {
// run later
disp.setAtomicActionLink(currentAtomAction);
getRequestsQueue().add(disp);
return true;
}
return false;
}
private LinkedList getRequestsQueue() {
assert Thread.holdsLock(this);
return requestsQueue;
}
private void setRequestsQueue(LinkedList requestsQueue) {
assert Thread.holdsLock(this);
this.requestsQueue = requestsQueue;
}
/** Container that holds hierarchy of propagation IDs related to atomic actions
* Implemented as linked list
*/
static final class AtomicActionLink {
private AtomicActionLink upper;
private Object propagationID;
AtomicActionLink(Object propagationID) {
this.propagationID = propagationID;
}
Object getAtomicAction() {
return propagationID;
}
void setPreviousLink(AtomicActionLink upper) {
this.upper = upper;
}
AtomicActionLink getPreviousLink() {
return upper;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy