1/*
2 * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.lwawt.macosx;
27
28import java.util.concurrent.locks.Lock;
29import java.util.concurrent.locks.ReadWriteLock;
30import java.util.concurrent.locks.ReentrantReadWriteLock;
31
32/**
33 * Safely holds and disposes of native AppKit resources, using the
34 * correct AppKit threading and Objective-C GC semantics.
35 */
36public class CFRetainedResource {
37    private static native void nativeCFRelease(final long ptr, final boolean disposeOnAppKitThread);
38
39    private final boolean disposeOnAppKitThread;
40    // TODO this pointer should be private and accessed via CFNativeAction class
41    protected volatile long ptr;
42
43    private final ReadWriteLock lock = new ReentrantReadWriteLock();
44    private final Lock writeLock = lock.writeLock();
45    private final Lock readLock = lock.readLock();
46
47    /**
48     * @param ptr CFRetained native object pointer
49     * @param disposeOnAppKitThread is the object needs to be CFReleased on the main thread
50     */
51    protected CFRetainedResource(final long ptr, final boolean disposeOnAppKitThread) {
52        this.disposeOnAppKitThread = disposeOnAppKitThread;
53        this.ptr = ptr;
54    }
55
56    /**
57     * CFReleases previous resource and assigns new pre-retained resource.
58     * @param ptr CFRetained native object pointer
59     */
60    protected void setPtr(final long ptr) {
61        writeLock.lock();
62        try {
63            if (this.ptr != 0) {
64                dispose();
65            }
66            this.ptr = ptr;
67        } finally {
68            writeLock.unlock();
69        }
70    }
71
72    /**
73     * Manually CFReleases the native resource.
74     */
75    protected void dispose() {
76        long oldPtr = 0L;
77        writeLock.lock();
78        try {
79            if (ptr == 0) {
80                return;
81            }
82            oldPtr = ptr;
83            ptr = 0;
84        } finally {
85            writeLock.unlock();
86        }
87
88        nativeCFRelease(oldPtr, disposeOnAppKitThread); // perform outside of the synchronized block
89    }
90
91    /**
92     * The interface which allows to execute some native operations with
93     * assumption that the native pointer will be valid till the end.
94     */
95    public interface CFNativeAction {
96
97        /**
98         * The native operation should be called from this method.
99         *
100         * @param  ptr the pointer to the native data
101         */
102        void run(long ptr);
103    }
104
105    /**
106     * The interface which allows to execute some native operations and get a
107     * result with assumption that the native pointer will be valid till the
108     * end.
109     */
110    interface CFNativeActionGet {
111
112        /**
113         * The native operation should be called from this method.
114         *
115         * @param  ptr the pointer to the native data
116         * @return result of the native operation
117         */
118        long run(long ptr);
119    }
120
121    /**
122     * This is utility method which should be used instead of the direct access
123     * to the {@link #ptr}, because this method guaranteed that the pointer will
124     * not be zero and will be valid till the end of the operation.It is highly
125     * recomended to not use any external lock in action. If the current
126     * {@link #ptr} is {@code 0} then action will be ignored.
127     *
128     * @param  action The native operation
129     */
130    public final void execute(final CFNativeAction action) {
131        readLock.lock();
132        try {
133            if (ptr != 0) {
134                action.run(ptr);
135            }
136        } finally {
137            readLock.unlock();
138        }
139    }
140
141    /**
142     * This is utility method which should be used instead of the direct access
143     * to the {@link #ptr}, because this method guaranteed that the pointer will
144     * not be zero and will be valid till the end of the operation. It is highly
145     * recomended to not use any external lock in action. If the current
146     * {@link #ptr} is {@code 0} then action will be ignored and {@code} is
147     * returned.
148     *
149     * @param  action the native operation
150     * @return result of the native operation, usually the native pointer to
151     *         some other data
152     */
153    final long executeGet(final CFNativeActionGet action) {
154        readLock.lock();
155        try {
156            if (ptr != 0) {
157                return action.run(ptr);
158            }
159        } finally {
160            readLock.unlock();
161        }
162        return 0;
163    }
164
165    @Override
166    @SuppressWarnings("deprecation")
167    protected final void finalize() throws Throwable {
168        dispose();
169    }
170}
171