/******************************************************************************* * Copyright (c) 2013, 2014 Tasktop Technologies and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Tasktop Technologies - initial API and implementation *******************************************************************************/ package org.eclipse.mylyn.internal.commons.repositories.ui; import java.lang.reflect.Field; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.ILock; import org.eclipse.equinox.internal.security.storage.SecurePreferencesRoot; import org.eclipse.equinox.security.storage.ISecurePreferences; import org.eclipse.mylyn.commons.core.StatusHandler; import org.eclipse.mylyn.internal.commons.repositories.core.SecureCredentialsStore; import org.eclipse.swt.widgets.Display; /** * Attempts to detect the deadlock that can occur when opening the secure storage (bug 440918) and fails preemptively. * * @author Sam Davis */ public class UiSecureCredentialsStore extends SecureCredentialsStore { private static AtomicBoolean loggedDeadlockDetectionFailure = new AtomicBoolean(); public UiSecureCredentialsStore(String id) { super(id); } @Override protected ISecurePreferences getSecurePreferences() { boolean acquiredLock = false; ILock lock = getSecurePreferencesRootLock(); try { if (lock != null && lock.getDepth() > 0) { // wait and try one more time in case another thread was retrieving the master password from the cache sleep(200); if (lock.getDepth() > 0) { acquiredLock = acquire(lock); // if we acquired the lock, either the thread that alreadly held it is the current thread, or it was released // in either case, we can safely proceed if (!acquiredLock) { throw new RuntimeException("Aborting request to prevent deadlock accessing secure storage"); //$NON-NLS-1$ } } } return super.getSecurePreferences(); } finally { if (lock != null && acquiredLock) { lock.release(); } } } private void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { StatusHandler.log(new Status(IStatus.ERROR, RepositoriesUiPlugin.ID_PLUGIN, e.getMessage(), e)); } } /** * Check whether the current thread already holds the lock. This can only be true if we're on the main thread. */ private boolean acquire(ILock lock) { if (Display.getCurrent() != null) { try { return lock.acquire(1); } catch (InterruptedException e) { StatusHandler.log(new Status(IStatus.ERROR, RepositoriesUiPlugin.ID_PLUGIN, e.getMessage(), e)); } } return false; } protected static ILock getSecurePreferencesRootLock() { try { @SuppressWarnings("restriction") Field lockField = SecurePreferencesRoot.class.getDeclaredField("lock"); //$NON-NLS-1$ lockField.setAccessible(true); return (ILock) lockField.get(null); } catch (Exception e) { if (!loggedDeadlockDetectionFailure.getAndSet(true)) {// log only once per session StatusHandler.log(new Status(IStatus.ERROR, RepositoriesUiPlugin.ID_PLUGIN, "Deadlock detection failed", e)); //$NON-NLS-1$ } } return null; } }