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