Skip to main content
summaryrefslogtreecommitdiffstats
blob: 921fb887ccdcec67b1f8cc7cf632c9baa25d9792 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*******************************************************************************
 * Copyright (c) 2009, 2015 IBM Corporation 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: 
 *     IBM Corporation - initial API and implementation
 *     Red Hat Inc. - Bug 460967
 ******************************************************************************/
package org.eclipse.equinox.internal.p2.engine;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
import org.eclipse.osgi.service.datalocation.Location;
import org.eclipse.osgi.util.NLS;

/**
 * The purpose of this class is to enable cross process locking.
 * See 257654 for more details.
 */
public class ProfileLock {
	private static final String LOCK_FILENAME = ".lock"; //$NON-NLS-1$

	private final Location location;
	private final Object lock;
	private Thread lockHolder;
	private int waiting;

	public ProfileLock(Object lock, File profileDirectory) {
		this.lock = lock;
		location = createLockLocation(profileDirectory);
	}

	private static Location createLockLocation(File parent) {
		Location anyLoc = ServiceHelper.getService(EngineActivator.getContext(), Location.class);
		try {
			final URL url = parent.toURL();
			Location location = anyLoc.createLocation(null, url, false);
			location.set(url, false, LOCK_FILENAME);
			return location;
		} catch (MalformedURLException e) {
			throw new IllegalArgumentException(NLS.bind(Messages.SimpleProfileRegistry_Bad_profile_location, e.getLocalizedMessage()), e);
		} catch (IllegalStateException e) {
			throw e;
		} catch (IOException e) {
			throw new IllegalStateException(e.getLocalizedMessage(), e);
		}
	}

	/**
	 * Asserts that this thread currently holds the profile lock.
	 * @throws IllegalStateException If this thread does not currently hold the profile lock
	 */
	public void checkLocked() {
		synchronized (lock) {
			if (lockHolder == null)
				throw new IllegalStateException(Messages.SimpleProfileRegistry_Profile_not_locked);

			Thread current = Thread.currentThread();
			if (lockHolder != current)
				throw new IllegalStateException(Messages.thread_not_owner);
		}
	}

	/**
	 * Attempts to obtain an exclusive write lock on a profile. The profile lock must be
	 * owned by any process and thread that wants to modify a profile. If the lock
	 * is currently held by another thread in this process, this method will  block until
	 * the lock becomes available. If the lock is currently held by another process,
	 * this method returns <code>false</code>. Re-entrant attempts to acquire the
	 * same profile lock multiple times in the same thread is not allowed.
	 * 
	 * @return <code>true</code> if the lock was successfully obtained by this thread,
	 * and <code>false</code> if another process is currently holding the lock.
	 */
	public boolean lock() {
		synchronized (lock) {
			Thread current = Thread.currentThread();
			if (lockHolder == current)
				throw new IllegalStateException(Messages.profile_lock_not_reentrant);

			boolean locationLocked = (waiting != 0);
			while (lockHolder != null) {
				locationLocked = true;
				waiting++;
				boolean interrupted = false;
				try {
					lock.wait();
				} catch (InterruptedException e) {
					interrupted = true;
				} finally {
					waiting--;
					// if interrupted restore interrupt to thread state
					if (interrupted)
						current.interrupt();
				}
			}
			try {
				if (!locationLocked && !location.lock())
					return false;

				lockHolder = current;
			} catch (IOException e) {
				throw new IllegalStateException(NLS.bind(Messages.SimpleProfileRegistry_Profile_not_locked_due_to_exception, e.getLocalizedMessage()), e);
			}
			return true;
		}
	}

	/**
	 * Releases the exclusive write lock on a profile. This method must only be called
	 * by a thread that currently owns the lock.
	 */
	public void unlock() {
		synchronized (lock) {
			if (lockHolder == null)
				throw new IllegalStateException(Messages.SimpleProfileRegistry_Profile_not_locked);

			Thread current = Thread.currentThread();
			if (lockHolder != current)
				throw new IllegalStateException(Messages.thread_not_owner);

			lockHolder = null;
			if (waiting == 0)
				location.release();
			else
				lock.notify();
		}
	}

	/**
	 * Returns whether a thread in this process currently holds the profile lock.
	 * 
	 * @return <code>true</code> if a thread in this process owns the profile lock,
	 * and <code>false</code> otherwise
	 */
	public boolean processHoldsLock() {
		synchronized (lock) {
			return lockHolder != null;
		}
	}
}

Back to the top