1 /*
2  * Copyright 2022 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 androidx.hardware
18 
19 import android.os.Build
20 import androidx.annotation.RequiresApi
21 import androidx.graphics.utils.JniVisible
22 import java.util.concurrent.TimeUnit
23 import java.util.concurrent.locks.ReentrantLock
24 import kotlin.concurrent.withLock
25 
26 /**
27  * A SyncFence represents a synchronization primitive which signals when hardware buffers have
28  * completed work on a particular resource.
29  *
30  * For example, GPU rendering to a frame buffer may generate a synchronization fence which signals
31  * when rendering has completed.
32  *
33  * When the fence signals, then the backing storage for the framebuffer may be safely read from,
34  * such as for display or media encoding.
35  */
36 @JniVisible
37 internal class SyncFenceV19(private var fd: Int) : AutoCloseable, SyncFenceImpl {
38 
39     private val fenceLock = ReentrantLock()
40 
41     /**
42      * Checks if the SyncFence object is valid.
43      *
44      * @return `true` if it is valid, `false` otherwise
45      */
<lambda>null46     override fun isValid(): Boolean = fenceLock.withLock { fd != -1 }
47 
48     /**
49      * Returns the time that the fence signaled in the [CLOCK_MONOTONIC] time domain. This returns
50      * [SyncFenceCompat.SIGNAL_TIME_INVALID] if the SyncFence is invalid.
51      */
52     // Relies on NDK APIs sync_file_info/sync_file_info_free which were introduced in API level 26
53     @RequiresApi(Build.VERSION_CODES.O)
getSignalTimeNanosnull54     override fun getSignalTimeNanos(): Long =
55         fenceLock.withLock {
56             if (isValid()) {
57                 SyncFenceBindings.nGetSignalTime(fd)
58             } else {
59                 SyncFenceCompat.SIGNAL_TIME_INVALID
60             }
61         }
62 
63     // Accessed through JNI to obtain the dup'ed file descriptor in a thread safe manner
64     @JniVisible
dupeFileDescriptornull65     private fun dupeFileDescriptor(): Int =
66         fenceLock.withLock {
67             return if (isValid()) {
68                 nDup(fd)
69             } else {
70                 -1
71             }
72         }
73 
74     /**
75      * Waits for a SyncFence to signal for up to the [timeoutNanos] duration. An invalid SyncFence,
76      * that is if [isValid] is `false`, is treated equivalently to a SyncFence that has already
77      * signaled. That is, wait() will immediately return `true`.
78      *
79      * @param timeoutNanos Timeout duration in nanoseconds. Providing a negative value will wait
80      *   indefinitely until the fence is signaled
81      * @return `true` if the fence signaled or is not valid, `false` otherwise
82      */
awaitnull83     override fun await(timeoutNanos: Long): Boolean {
84         fenceLock.withLock {
85             if (isValid()) {
86                 val timeout: Int
87                 if (timeoutNanos < 0) {
88                     timeout = -1
89                 } else {
90                     timeout = TimeUnit.NANOSECONDS.toMillis(timeoutNanos).toInt()
91                 }
92                 return nWait(fd, timeout)
93             } else {
94                 // invalid file descriptors will always return true
95                 return true
96             }
97         }
98     }
99 
100     /**
101      * Waits forever for a SyncFence to signal. An invalid SyncFence is treated equivalently to a
102      * SyncFence that has already signaled. That is, wait() will immediately return `true`.
103      *
104      * @return `true` if the fence signaled or isn't valid, `false` otherwise
105      */
awaitForevernull106     override fun awaitForever(): Boolean = await(-1)
107 
108     /**
109      * Close the SyncFence instance. After this method is invoked the fence is invalid. That is
110      * subsequent calls to [isValid] will return `false`
111      */
112     override fun close() {
113         fenceLock.withLock {
114             if (isValid()) {
115                 nClose(fd)
116                 fd = -1
117             }
118         }
119     }
120 
finalizenull121     protected fun finalize() {
122         close()
123     }
124 
125     // SyncFence in the framework implements timeoutNanos as a long but
126     // it is casted down to an int within native code and eventually calls into
127     // the poll API which consumes a timeout in nanoseconds as an int.
nWaitnull128     @JniVisible private external fun nWait(fd: Int, timeoutMillis: Int): Boolean
129 
130     @JniVisible private external fun nClose(fd: Int)
131 
132     /**
133      * Dup the provided file descriptor, this method requires the caller to acquire the
134      * corresponding [fenceLock] before invoking
135      */
136     @JniVisible private external fun nDup(fd: Int): Int
137 
138     companion object {
139 
140         init {
141             System.loadLibrary("graphics-core")
142         }
143     }
144 }
145