1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.printspooler.model;
18 
19 import android.util.Log;
20 
21 import java.io.File;
22 import java.io.IOException;
23 
24 /**
25  * This class provides a shared file to several threads. Only one thread
26  * at a time can use the file. To acquire the file a thread has to
27  * request it in a blocking call to {@link #acquireFile(OnReleaseRequestCallback)}.
28  * The provided callback is optional and is used to notify the owning thread
29  * when another one wants to acquire the file. In case a release is requested
30  * the thread owning the file must release it as soon as possible. If no
31  * callback is provided a thread that acquires the file must release it
32  * as soon as possible, i.e. even if callback was provided the thread cannot
33  * have the file for less time.
34  */
35 public final class MutexFileProvider {
36     private static final String LOG_TAG = "MutexFileProvider";
37 
38     private static final boolean DEBUG = true;
39 
40     private final Object mLock = new Object();
41 
42     private final File mFile;
43 
44     private Thread mOwnerThread;
45 
46     private OnReleaseRequestCallback mOnReleaseRequestCallback;
47 
48     public interface OnReleaseRequestCallback {
onReleaseRequested(File file)49         public void onReleaseRequested(File file);
50     }
51 
MutexFileProvider(File file)52     public MutexFileProvider(File file) throws IOException {
53         mFile = file;
54         if (file.exists()) {
55             file.delete();
56         }
57         file.createNewFile();
58     }
59 
acquireFile(OnReleaseRequestCallback callback)60     public File acquireFile(OnReleaseRequestCallback callback) {
61         synchronized (mLock) {
62             // If this thread has the file, nothing to do.
63             if (mOwnerThread == Thread.currentThread()) {
64                 return mFile;
65             }
66 
67             // Another thread wants file ask for a release.
68             if (mOwnerThread != null && mOnReleaseRequestCallback != null) {
69                 mOnReleaseRequestCallback.onReleaseRequested(mFile);
70             }
71 
72             // Wait until the file is released.
73             while (mOwnerThread != null) {
74                 try {
75                     mLock.wait();
76                 } catch (InterruptedException ie) {
77                     /* ignore */
78                 }
79             }
80 
81             // Update the owner and the callback.
82             mOwnerThread = Thread.currentThread();
83             mOnReleaseRequestCallback = callback;
84 
85             if (DEBUG) {
86                 Log.i(LOG_TAG, "Acquired file: " + mFile + " by thread: " + mOwnerThread);
87             }
88 
89             return mFile;
90         }
91     }
92 
releaseFile()93     public void releaseFile() {
94         synchronized (mLock) {
95             if (mOwnerThread != Thread.currentThread()) {
96                 return;
97             }
98 
99             if (DEBUG) {
100                 Log.i(LOG_TAG, "Released file: " + mFile + " from thread: " + mOwnerThread);
101             }
102 
103             // Update the owner and the callback.
104             mOwnerThread = null;
105             mOnReleaseRequestCallback = null;
106 
107             mLock.notifyAll();
108         }
109     }
110 }
111