blob: 5164a4d65bf6c2c74e6536c47169521d97421dcd [file] [log] [blame]
/*******************************************************************************
* 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;
}
}