blob: 3a8293fca61ed52c66a4d8c3329aad27ed0afbe2 [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.apache.ode.bpel.engine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* An N state synchronization latch useful for implementing hydration/dehydration. The
* latch functions as follows. At any time, the latch is in one of N states and has a
* count. Clients can "latch" and "release" the latch, which increments/decrements the
* count; however, when latching, a state must be specified. If the state does not match
* the current state, the latch blocks until the count is zero. Essentially, the latch
* can change state only when the count is zero. Every time the latch changes state an
* optional {@link Runnable} corresponding to the new state is executed.
*
* @author Maciej Szefler ( m s z e f l e r @ g m a i l . c o m )
*/
public class NStateLatch {
static final Logger __log = LoggerFactory.getLogger(NStateLatch.class);
/** Current state. */
private int _state = -1;
/** Current depth (i.e. number of enter() calls) */
private int _depth = 0;
/** Action for state transition ?-->i */
protected Runnable _transitions[];
/** Synchronization lock .*/
private Lock _lock;
/** _depth == 0 condition. */
private Condition _depth0;
private boolean _transitioning = false;
/**
* Constructor, the array of {@link Runnable}s defines the number of states and the transition
* actions.
* @param transitions action to perform when entering state x.
*/
public NStateLatch(Runnable [] transitions) {
_transitions = transitions;
_lock = new ReentrantLock();
_depth0 = _lock.newCondition();
}
public void latch(int state) {
if (state >= _transitions.length || state < 0)
throw new IllegalArgumentException("Invalid state.");
_lock.lock();
try {
if (_transitioning )
throw new IllegalStateException("Manipulating latch from transition. ");
if (_state != state) {
// wait for the depth to become 0
while (_depth != 0)
_depth0.awaitUninterruptibly();
if (_state != state) {
if (_transitions[state] != null)
try {
_transitioning = true;
_transitions[state].run();
} finally {
_transitioning = false;
}
_state = state;
}
}
} finally {
_depth ++;
_lock.unlock();
}
}
public void release(int state) {
_lock.lock();
try {
if (_transitioning )
throw new IllegalStateException("Manipulating latch from transition. ");
if (_state != state)
__log.error("Latch error, was releasing for state " + state + " but actually in " + _state);
if (_depth <= 0)
throw new IllegalStateException("Too many release() calls.");
_depth --;
if (_depth == 0)
_depth0.signal();
} finally {
_lock.unlock();
}
}
public int getDepth(int state) {
return (_state == state ? _depth : 0);
}
}