3131import java .io .IOException ;
3232import java .lang .ref .ReferenceQueue ;
3333import java .lang .ref .WeakReference ;
34+ import java .nio .channels .FileLock ;
3435import java .nio .file .Path ;
3536import java .util .concurrent .ConcurrentHashMap ;
3637
@@ -79,25 +80,26 @@ private void cleanUp()
7980 }
8081 }
8182
82- private KeyLockState keyLockState (final Path path ) {
83+ private KeyLockState keyLockState (final Path path , final IoPolicy policy ) throws IOException {
8384
8485 final String key = path .toAbsolutePath ().toString ();
8586
8687 cleanUp ();
8788
8889 final WeakValue existingRef = locks .get (key );
8990 KeyLockState state = existingRef == null ? null : existingRef .get ();
90- if (state != null ) {
91- return state ;
91+ if (state == null ) {
92+ final KeyLockState newState = new KeyLockState (path , policy );
93+ while (state == null ) {
94+ final WeakValue ref = locks .compute (key ,
95+ (k , v ) -> (v != null && v .get () != null )
96+ ? v
97+ : new WeakValue (k , newState , refQueue ));
98+ state = ref .get ();
99+ }
92100 }
93-
94- final KeyLockState newState = new KeyLockState (path );
95- while (state == null ) {
96- final WeakValue ref = locks .compute (key ,
97- (k , v ) -> (v != null && v .get () != null )
98- ? v
99- : new WeakValue (k , newState , refQueue ));
100- state = ref .get ();
101+ if (state .policy () != policy ) {
102+ throw new IOException ("Trying to lock \" " + path + "\" with policy " + policy + ", but it is already used with " + state .policy ());
101103 }
102104 return state ;
103105 }
@@ -108,31 +110,73 @@ private KeyLockState keyLockState(final Path path) {
108110 * <p>
109111 * The first reader will acquire a shared file lock. Subsequent readers
110112 * only acquire the thread-level lock.
113+ * <p>
114+ * The given locking {@link IoPolicy policy} applies to OS-level locking.
115+ * For both the {@code STRICT} and {@code PERMISSIVE} policy, a {@link
116+ * FileLock} is obtained. If this fails, {@code STRICT} will throw an {@code
117+ * IOException}. {@code PERMISSIVE} will proceed without locking. {@code
118+ * UNSAFE} will not attempt OS-level locking, however will still manage
119+ * mutual exclusion of readers and writers in the same JVM. Trying to lock
120+ * the same path with different locking policies will throw an {@code
121+ * IOException}.
111122 *
112123 * @param path
113- * the key (file path) to lock for reading
124+ * the key (file path) to lock for reading
125+ * @param policy
126+ * the locking policy
127+ *
114128 * @return a {@link LockedChannel} that must be closed when done
129+ *
115130 * @throws IOException
116- * if acquiring the file lock fails
131+ * if acquiring the file lock fails
132+ */
133+ public LockedFileChannel lockForReading (final Path path , final IoPolicy policy ) throws IOException {
134+
135+ return keyLockState (path , policy ).acquireRead ();
136+ }
137+
138+ /**
139+ * Acquires a read lock for the specified key with the {@link IoPolicy#STRICT} locking policy.
117140 */
118141 public LockedFileChannel lockForReading (final Path path ) throws IOException {
119142
120- return keyLockState (path ). acquireRead ( );
143+ return lockForReading (path , IoPolicy . STRICT );
121144 }
122145
123146 /**
124147 * Acquires a write lock for the specified key. Only one thread can hold a
125148 * write lock for a key at a time, and no readers can hold locks.
149+ * <p>
150+ * The given locking {@link IoPolicy policy} applies to OS-level locking.
151+ * For both the {@code STRICT} and {@code PERMISSIVE} policy, a {@link
152+ * FileLock} is obtained. If this fails, {@code STRICT} will throw an {@code
153+ * IOException}. {@code PERMISSIVE} will proceed without locking. {@code
154+ * UNSAFE} will not attempt OS-level locking, however will still manage
155+ * mutual exclusion of readers and writers in the same JVM. Trying to lock
156+ * the same path with different locking policies will throw an {@code
157+ * IOException}.
126158 *
127159 * @param path
128- * the file path to lock for writing
160+ * the file path to lock for writing
161+ * @param policy
162+ * the locking policy
163+ *
129164 * @return a {@link LockedChannel} that must be closed when done
165+ *
130166 * @throws IOException
131- * if acquiring the file lock fails
167+ * if acquiring the file lock fails
168+ */
169+ public LockedFileChannel lockForWriting (final Path path , final IoPolicy policy ) throws IOException {
170+
171+ return keyLockState (path , policy ).acquireWrite ();
172+ }
173+
174+ /**
175+ * Acquires a write lock for the specified key with the {@link IoPolicy#STRICT} locking policy.
132176 */
133177 public LockedFileChannel lockForWriting (final Path path ) throws IOException {
134178
135- return keyLockState (path ). acquireWrite ( );
179+ return lockForWriting (path , IoPolicy . STRICT );
136180 }
137181
138182 /**
@@ -144,14 +188,4 @@ int size() {
144188
145189 return locks .size ();
146190 }
147-
148- /**
149- * Removes the lock state for a key if no locks are held.
150- *
151- * @param key the key whose lock state should be removed
152- * @return true if removed, false if currently in use or not found
153- */
154- public boolean removeLockIfUnused (final String key ) {
155- throw new UnsupportedOperationException ("TODO. REMOVE" );
156- }
157191}
0 commit comments