Skip to content

Commit

Permalink
fix(android): fix concurrency issue in timeoutmap(#419) (#560)
Browse files Browse the repository at this point in the history
* fix(android): fix concurrency issue in timeoutmap(#419)

* fix(android): rename into TimeoutHandler, cleanup

* fix(android): replace removeIf

---------

Co-authored-by: Anders Hausding <[email protected]>
  • Loading branch information
Andy3189 and Anders Hausding authored Aug 14, 2023
1 parent 948c887 commit 31fa734
Showing 1 changed file with 27 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,33 @@ import android.os.Handler
import android.os.Looper
import com.getcapacitor.Logger
import java.util.*
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicReference

class CallbackResponse(
val success: Boolean,
val value: String,
)

class TimeoutHandler(
val key: String,
val handler: Handler
)

fun <T> ConcurrentLinkedQueue<T>.popFirstMatch(predicate: (T) -> Boolean): T? {
synchronized(this) {
val iterator = this.iterator()
while (iterator.hasNext()) {
val nextItem = iterator.next()
if (predicate(nextItem)) {
iterator.remove()
return nextItem
}
}
return null
}
}

class Device(
private val context: Context,
bluetoothAdapter: BluetoothAdapter,
Expand All @@ -35,7 +56,7 @@ class Device(
private var device: BluetoothDevice = bluetoothAdapter.getRemoteDevice(address)
private var bluetoothGatt: BluetoothGatt? = null
private var callbackMap = HashMap<String, ((CallbackResponse) -> Unit)>()
private var timeoutMap = HashMap<String, Handler>()
private val timeoutQueue = ConcurrentLinkedQueue<TimeoutHandler>()
private var bondStateReceiver: BroadcastReceiver? = null
private var currentMtu = -1

Expand Down Expand Up @@ -510,28 +531,27 @@ class Device(
private fun resolve(key: String, value: String) {
if (callbackMap.containsKey(key)) {
Logger.debug(TAG, "resolve: $key $value")

timeoutQueue.popFirstMatch { it.key == key }?.handler?.removeCallbacksAndMessages(null)
callbackMap[key]?.invoke(CallbackResponse(true, value))
callbackMap.remove(key)
timeoutMap[key]?.removeCallbacksAndMessages(null)
timeoutMap.remove(key)
}
}

private fun reject(key: String, value: String) {
if (callbackMap.containsKey(key)) {
Logger.debug(TAG, "reject: $key $value")
timeoutQueue.popFirstMatch { it.key == key }?.handler?.removeCallbacksAndMessages(null)
callbackMap[key]?.invoke(CallbackResponse(false, value))
callbackMap.remove(key)
timeoutMap[key]?.removeCallbacksAndMessages(null)
timeoutMap.remove(key)
}
}

private fun setTimeout(
key: String, message: String, timeout: Long
) {
val handler = Handler(Looper.getMainLooper())
timeoutMap[key] = handler
timeoutQueue.add(TimeoutHandler(key, handler))
handler.postDelayed({
reject(key, message)
}, timeout)
Expand All @@ -544,7 +564,7 @@ class Device(
timeout: Long,
) {
val handler = Handler(Looper.getMainLooper())
timeoutMap[key] = handler
timeoutQueue.add(TimeoutHandler(key, handler))
handler.postDelayed({
connectionState = STATE_DISCONNECTED
gatt?.disconnect()
Expand Down

0 comments on commit 31fa734

Please sign in to comment.