blob: 5024aa4648a126c16506a7614ec854da4effb1ec [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.felix.scr.impl.manager;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.osgi.service.log.LogService;
/**
* EdgeInfo holds information about the service event tracking counts for creating (open) and disposing (close)
* implementation object instances per dependency manager. These need to be maintained for each implementation object instance
* because each instance (for a service factory) will have different sets of service references available. These need to be
* maintained for each dependency manager because the open/close tracking counts are obtained when the set of current
* service references is obtained, using a lock internal to the service tracker.
*
*
* The information in the open/close counts is used in the outOfRange method which determines if a service event tracking count
* occurred before the "open" event (in which case it is reflected in the open set already and does not need to be processed separately)
* or after the "close" event (in which case it is reflected in the close set already).
*
* The open latch is used to make sure that elements in the open set are completely processed before updated or unbind events
* are processed
* The close latch is used to make sure that unbind events that are out of range wait for the close to complete before returning;
* in this case the unbind is happening in the "close" thread rather than the service event thread, so we wait for the close to complete
* so that when the service event returns the unbind will actually have been completed.
*
* Related to this functionality is the missing tracking in AbstractComponentManager. This is used on close of an instance to make
* sure all service events occuring before close starts complete processing before the close takes action.
*
*/
class EdgeInfo
{
private int open = -1;
private int close = -1;
private final CountDownLatch openLatch = new CountDownLatch(1);
private final CountDownLatch closeLatch = new CountDownLatch(1);
public void setClose( int close )
{
this.close = close;
}
public CountDownLatch getOpenLatch()
{
return openLatch;
}
public void waitForOpen(AbstractComponentManager<?> m_componentManager, String componentName, String methodName)
{
CountDownLatch latch = getOpenLatch();
String latchName = "open";
waitForLatch( m_componentManager, latch, componentName, methodName, latchName );
}
public void waitForClose(AbstractComponentManager<?> m_componentManager, String componentName, String methodName)
{
CountDownLatch latch = getCloseLatch();
String latchName = "close";
waitForLatch( m_componentManager, latch, componentName, methodName, latchName );
}
private void waitForLatch(AbstractComponentManager<?> m_componentManager, CountDownLatch latch, String componentName,
String methodName, String latchName)
{
try
{
if (!latch.await( m_componentManager.getLockTimeout(), TimeUnit.MILLISECONDS ))
{
m_componentManager.getLogger().log( LogService.LOG_ERROR,
"DependencyManager : {0} : timeout on {1} latch {2}", null, methodName, latchName, componentName );
m_componentManager.dumpThreads();
}
}
catch ( InterruptedException e )
{
try
{
if (!latch.await( m_componentManager.getLockTimeout(), TimeUnit.MILLISECONDS ))
{
m_componentManager.getLogger().log( LogService.LOG_ERROR,
"DependencyManager : {0} : timeout on {1} latch {2}", null, methodName, latchName, componentName );
m_componentManager.dumpThreads();
}
}
catch ( InterruptedException e1 )
{
m_componentManager.getLogger().log( LogService.LOG_ERROR,
"DependencyManager : {0} : Interrupted twice on {1} latch {2}",
null, methodName, latchName, componentName );
Thread.currentThread().interrupt();
}
Thread.currentThread().interrupt();
}
}
public CountDownLatch getCloseLatch()
{
return closeLatch;
}
public void setOpen( int open )
{
this.open = open;
}
public void ignore()
{
open = Integer.MAX_VALUE;
close = Integer.MAX_VALUE - 1;
openLatch.countDown();
closeLatch.countDown();
}
/**
* Returns whether the tracking count is before the open count or after the close count (if set)
* This must be called from within a block synchronized on m_tracker.tracked().
* Setting open occurs in a synchronized block as well, to the tracker's current tracking count.
* Therefore if this outOfRange call finds open == -1 then open will be set to a tracking count
* at least as high as the argument tracking count.
* @param trackingCount tracking count from tracker to compare with range
* @return true if open not set, tracking count before open, or close set and tracking count after close.
*/
public boolean outOfRange( int trackingCount )
{
return open == -1
|| trackingCount < open
|| (close != -1 && trackingCount > close);
}
public boolean beforeRange( int trackingCount )
{
return open == -1 || trackingCount < open;
}
public boolean afterRange( int trackingCount )
{
return close != -1 && trackingCount > close;
}
}