1 /*
2  * Copyright 2024 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.navigation.internal
18 
19 import kotlin.experimental.ExperimentalNativeApi
20 import kotlin.native.ref.createCleaner
21 import kotlinx.cinterop.Arena
22 import kotlinx.cinterop.ExperimentalForeignApi
23 import kotlinx.cinterop.alloc
24 import kotlinx.cinterop.ptr
25 import platform.posix.pthread_mutex_destroy
26 import platform.posix.pthread_mutex_init
27 import platform.posix.pthread_mutex_lock
28 import platform.posix.pthread_mutex_t
29 import platform.posix.pthread_mutex_unlock
30 import platform.posix.pthread_mutexattr_destroy
31 import platform.posix.pthread_mutexattr_init
32 import platform.posix.pthread_mutexattr_settype
33 import platform.posix.pthread_mutexattr_t
34 
35 /**
36  * Wrapper for platform.posix.PTHREAD_MUTEX_RECURSIVE which is represented as kotlin.Int on darwin
37  * platforms and kotlin.UInt on linuxX64 See: // https://youtrack.jetbrains.com/issue/KT-41509
38  */
39 internal expect val PTHREAD_MUTEX_RECURSIVE: Int
40 
41 internal actual class SynchronizedObject actual constructor() {
42 
43     private val resource = Resource()
44 
45     @Suppress("unused") // The returned Cleaner must be assigned to a property
46     @OptIn(ExperimentalNativeApi::class)
47     private val cleaner = createCleaner(resource, Resource::dispose)
48 
locknull49     fun lock() {
50         resource.lock()
51     }
52 
unlocknull53     fun unlock() {
54         resource.unlock()
55     }
56 
57     @OptIn(ExperimentalForeignApi::class)
58     private class Resource {
59         private val arena: Arena = Arena()
60         private val attr: pthread_mutexattr_t = arena.alloc()
61         private val mutex: pthread_mutex_t = arena.alloc()
62 
63         init {
64             pthread_mutexattr_init(attr.ptr)
65             pthread_mutexattr_settype(attr.ptr, PTHREAD_MUTEX_RECURSIVE)
66             pthread_mutex_init(mutex.ptr, attr.ptr)
67         }
68 
locknull69         fun lock(): Int = pthread_mutex_lock(mutex.ptr)
70 
71         fun unlock(): Int = pthread_mutex_unlock(mutex.ptr)
72 
73         fun dispose() {
74             pthread_mutex_destroy(mutex.ptr)
75             pthread_mutexattr_destroy(attr.ptr)
76             arena.clear()
77         }
78     }
79 }
80 
synchronizedImplnull81 internal actual inline fun <T> synchronizedImpl(
82     lock: SynchronizedObject,
83     crossinline action: () -> T
84 ): T {
85     lock.lock()
86     return try {
87         action()
88     } finally {
89         lock.unlock()
90     }
91 }
92