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

org.eclipse.osgi.internal.serviceregistry.ServiceUse Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/*******************************************************************************
 * Copyright (c) 2003, 2022 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Hannes Wellmann - Add deadlock-detecting and auto-closable ServiceUseLock
 *******************************************************************************/

package org.eclipse.osgi.internal.serviceregistry;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import org.eclipse.osgi.internal.debug.Debug;
import org.eclipse.osgi.internal.framework.BundleContextImpl;
import org.eclipse.osgi.internal.messages.Msg;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.ServiceException;
/**
 * This class represents the use of a service by a bundle. One is created for each
 * service acquired by a bundle.
 *
 * 

* This class manages a singleton service. * * @ThreadSafe */ public class ServiceUse { /** * Custom ServiceException type to indicate a deadlock occurred during service * registration. */ public static final int DEADLOCK = 1001; /** BundleContext associated with this service use */ final BundleContextImpl context; /** ServiceDescription of the registered service */ final ServiceRegistrationImpl registration; final Debug debug; /** bundle's use count for this service */ /* @GuardedBy("getLock()") */ private int useCount; /** * ServiceUseLock for this service use. Use the @{@link #lock()} method to lock * the lock and obtain an {@link AutoCloseable} object which is used to unlock * the lock. */ private final ServiceUseLock lock = new ServiceUseLock(); /** * Constructs a service use encapsulating the service object. * * @param context bundle getting the service * @param registration ServiceRegistration of the service */ ServiceUse(BundleContextImpl context, ServiceRegistrationImpl registration) { this.useCount = 0; this.registration = registration; this.context = context; this.debug = context.getContainer().getConfiguration().getDebug(); } /** * Get a service's service object and increment the use count. * * @return The service object. */ /* @GuardedBy("getLock()") */ S getService() { assert getLock().isHeldByCurrentThread(); if (debug.DEBUG_SERVICES) { Debug.println("[" + Thread.currentThread().getName() + "] getService[factory=" + registration.getBundle() //$NON-NLS-1$ //$NON-NLS-2$ + "](" + context.getBundleImpl() + "," + registration + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } incrementUse(); return registration.getServiceObject(); } /** * Unget a service's service object. * *

* Decrements the use count if the service was being used. * * @return true if the service was ungotten; otherwise false. */ /* @GuardedBy("getLock()") */ boolean ungetService() { assert getLock().isHeldByCurrentThread(); if (!inUse()) { return false; } if (debug.DEBUG_SERVICES) { Debug.println("[" + Thread.currentThread().getName() + "] ungetService[factory=" + registration.getBundle() //$NON-NLS-1$ //$NON-NLS-2$ + "](" + context.getBundleImpl() + "," + registration + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } decrementUse(); return true; } /** * Return the service object for this service use. * * @return The service object. */ /* @GuardedBy("getLock()") */ S getCachedService() { return registration.getServiceObject(); } /** * Get a new service object for the service. * *

* By default, this returns the result of {@link #getService()}. * * @return The service object. */ /* @GuardedBy("getLock()") */ S newServiceObject() { return getService(); } /** * Release a service object for the service. * *

* By default, this returns the result of {@link #ungetService()}. * * @param service The service object to release. * @return true if the service was released; otherwise false. * @throws IllegalArgumentException If the specified service was not * provided by this object. */ /* @GuardedBy("getLock()") */ boolean releaseServiceObject(final S service) { if ((service == null) || (service != getCachedService())) { throw new IllegalArgumentException(Msg.SERVICE_OBJECTS_UNGET_ARGUMENT_EXCEPTION); } if (debug.DEBUG_SERVICES) { Debug.println("[" + Thread.currentThread().getName() + "] releaseServiceObject[factory=" //$NON-NLS-1$ //$NON-NLS-2$ + registration.getBundle() + "](" + context.getBundleImpl() + "," + registration + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } return ungetService(); } /** * Release all uses of the service and reset the use count to zero. */ /* @GuardedBy("getLock()") */ void release() { assert getLock().isHeldByCurrentThread(); resetUse(); } /** * Is this service use using any services? * * @return true if no services are being used and this service use can be discarded. */ /* @GuardedBy("getLock()") */ boolean isEmpty() { assert getLock().isHeldByCurrentThread(); return !inUse(); } /** * Is the use count non zero? * * @return true if the use count is greater than zero. */ /* @GuardedBy("getLock()") */ boolean inUse() { return useCount > 0; } /** * Incrementing the use count. */ /* @GuardedBy("getLock()") */ void incrementUse() { if (useCount == Integer.MAX_VALUE) { throw new ServiceException(Msg.SERVICE_USE_OVERFLOW); } useCount++; } /** * Decrementing the use count. */ /* @GuardedBy("getLock()") */ void decrementUse() { assert inUse(); useCount--; } /** * Reset the use count to zero. */ /* @GuardedBy("getLock()") */ void resetUse() { useCount = 0; } /** * The ServiceUseLock for managing access to this ServiceUse. *

* Use {@link #lock()} to lock the ServiceUseLock in a try-with-resources * statement. * * @return The ServiceUseLock for this ServiceUse. */ ServiceUseLock getLock() { return lock; } /** * Acquires the {@link ServiceUseLock} of this ServiceUse. * * If this ServiceUse is locked by another thread then the current thread lies * dormant until the lock has been acquired. * * @return The unlocker, which is {@link AutoCloseable}, to unlock the * ServiceUseLock of this ServiceUse. * @throws ServiceException If a deadlock with another ServiceUseLock is * detected. */ ServiceUseLock lock() { Thread awaitingThread = null; boolean interrupted = false; try { final ServiceUseLock useLock = getLock(); // local var to avoid multiple getfields while (true) { try { if (useLock.tryLock(100_000_000L, TimeUnit.NANOSECONDS)) { // 100ms (but prevent conversion) return useLock; } awaitingThread = Thread.currentThread(); checkDeadLock(awaitingThread, useLock); } catch (InterruptedException e) { interrupted = true; // Clear interrupted status and try again to lock, just like a plain // synchronized. Re-interrupted before returning to the caller. } } } finally { if (awaitingThread != null) { registration.getAwaitedUseLocks().remove(awaitingThread); } if (interrupted) { Thread.currentThread().interrupt(); } } } private void checkDeadLock(final Thread currentThread, final ServiceUseLock currentLock) { final ConcurrentMap awaitedUseLocks = registration.getAwaitedUseLocks(); awaitedUseLocks.put(currentThread, currentLock); ServiceUseLock useLock = currentLock; // Check if current thread is in the cycle of mutually awaiting thread-lock // pairs, but prevent infinite loop if current thread awaits a dead-locked lock // but is itself not in the cycle. int maxLocks = awaitedUseLocks.size(); for (int i = 0; i < maxLocks; i++) { Thread owner = useLock.getOwner(); if (owner == currentThread) { throw new ServiceException(NLS.bind(Msg.SERVICE_USE_DEADLOCK, currentLock), DEADLOCK); } if (owner == null || (useLock = awaitedUseLocks.get(owner)) == null) { break; // lock could be released in the meantime } } // Not (yet) a dead-lock. Lock was probably regularly hold by another thread. // Race conditions are not an issue here. A deadlock is a static situation and // if we closely missed the other thread putting its awaited lock it will be // noticed in the next loop-pass. } /** * ReentrantLock subclass that allows for {@link AutoCloseable} unlocking. *

* This lock is unlocked if its {@code close()} method is invoked. * ServiceUseLock objects can therefore can be used as a resource in a * try-with-resources statement. *

* Also exposes {@link #getOwner()} and has an enhanced {@link #toString()}. * * @see ServiceUse#lock() */ static class ServiceUseLock extends ReentrantLock implements AutoCloseable { private static final long serialVersionUID = 1L; /** * Unlock this lock. *

* This method is not idempotent and should be called only once for each lock * acquisition. *

* * @see #unlock() */ @Override public void close() { unlock(); } @Override protected Thread getOwner() { return super.getOwner(); } /** * Returns a lock state description for this lock. This adds additional * information over the default implementation when the lock is held. * * @return The lock state description. */ @SuppressWarnings("nls") @Override public String toString() { Thread o = getOwner(); if (o != null) { try { ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); ThreadInfo threadInfo = threadMXBean.getThreadInfo(o.getId(), Integer.MAX_VALUE); StackTraceElement[] trace = threadInfo.getStackTrace(); StringBuilder sb = new StringBuilder(super.toString()).append(", Details:\n"); if (o.isDaemon()) { sb.append("daemon "); } sb.append("prio=").append(o.getPriority()).append(" id=").append(o.getId()).append(" ") .append(o.getState()); for (StackTraceElement traceElement : trace) { sb.append("\tat ").append(traceElement).append("\n"); } return sb.toString(); } catch (Exception e) { // do nothing and fall back to just the default, thread might be gone } } return super.toString(); } } }