Uncaught Kotlin исключение: kotlin .native.IncorrectDereferenceException: недопустимая попытка доступа к незарегистрированным - PullRequest
0 голосов
/ 12 февраля 2020

Я пытаюсь использовать Kotlin MPP (Multiplatform) для разработки общей библиотеки между Android и iOS. Но я сталкиваюсь с проблемой потоков iOS. Для моего приложения в iOS я устанавливаю sh объект в основном потоке, но он, вероятно, вызывает функцию в другом потоке и выдает это исключение следующим образом:

Uncaught Kotlin exception: kotlin.native.IncorrectDereferenceException: illegal attempt to access non-shared example.api.DrivingBehaviorDetector@397cba8 from other thread
        at 0   DrivingBehaviorDetector             0x00000001037619d7 kfun:kotlin.Throwable.<init>(kotlin.String?)kotlin.Throwable + 87
        at 1   DrivingBehaviorDetector             0x000000010375bca5 kfun:kotlin.Exception.<init>(kotlin.String?)kotlin.Exception + 85
        at 2   DrivingBehaviorDetector             0x000000010375b9a5 kfun:kotlin.RuntimeException.<init>(kotlin.String?)kotlin.RuntimeException + 85
        at 3   DrivingBehaviorDetector             0x0000000103781395 kfun:kotlin.native.IncorrectDereferenceException.<init>(kotlin.String)kotlin.native.IncorrectDereferenceException + 85
        at 4   DrivingBehaviorDetector             0x0000000103782568 ThrowIllegalObjectSharingException + 744
        at 5   DrivingBehaviorDetector             0x00000001037d72bc _ZNK27BackRefFromAssociatedObject19ensureRefAccessibleEv + 76
        at 6   DrivingBehaviorDetector             0x00000001037c97c3 -[KotlinBase toKotlin:] + 35
        at 7   DrivingBehaviorDetector             0x00000001037e3ef1 Kotlin_ObjCExport_refFromObjC + 65
        at 8   DrivingBehaviorDetector             0x00000001037c4e37 objc2kotlin.125 + 167
        at 9   Test                                0x000000010340224d $sIeg_IeyB_TR + 45 (/Users/james/Documents/Projects/go/TestMPPforiOS/<compiler-generated>:<unknown>)
        at 10  libdispatch.dylib                   0x0000000103959dd4 _dispatch_call_block_and_release + 12
        at 11  libdispatch.dylib                   0x000000010395ad48 _dispatch_client_callout + 8
        at 12  libdispatch.dylib                   0x000000010396c460 _dispatch_root_queue_drain + 819
        at 13  libdispatch.dylib                   0x000000010396cb96 _dispatch_worker_thread2 + 132
        at 14  libsystem_pthread.dylib             0x00007fff5245f6b3 _pthread_wqthread + 583
        at 15  libsystem_pthread.dylib             0x00007fff5245f3fd start_wqthread + 13
(lldb) 

Я рассматриваю решение в этих статьи:

Неизменяемость в Kotlin / Native: Неизменяемость в Kotlin / Native

Сопрограммы и неизменность K / N: Сопрограммы и неизменность K / N .

Вход в мой API (разделяемая библиотека) выглядит следующим образом:

class DrivingBehaviorDetector (
    private var gravity:Vector?,
    private var front:Vector?,
    onGravityOrFrontChanged: ((newGravity: Vector?, newFront: Vector?, timestamp: Long) -> Unit)?,
    onDrivingEventDetected: ((event: DrivingEvent) -> Unit)?
) {

    private val lowPassFilter = LowPassFilter()
    private val accProcessor = AccProcessor(gravity, front, onGravityOrFrontChanged, onDrivingEventDetected)

    init {
        gravity = gravity?.toCoreUnit()
        front = front?.toCoreUnit()
    }

    fun addData(data:Acceleration) {
        val rawAcc = data.toCoreUnit()
        val filterAcc = lowPassFilter.lowPass(rawAcc)
        accProcessor.addData(filterAcc)
    }

    fun addData(data:List<Acceleration>) {
        for(acc in data) {
            addData(acc)
        }
    }
}

Мы можем заметить, что API настолько прост, что предоставляет большое количество данных и вызывает результаты некоторых расчеты. Но в iOS выведите исключение и код, как показано ниже:

let drivingBehaviorDetector = DrivingBehaviorDetector(gravity: nil, front: nil, onGravityOrFrontChanged: { (newGravity, newFront, timestamp) in
    print("newGravity = \(newGravity)")
    print("newFront = \(newFront)")
}) { (event) in
    print("event = \(event.description())")
}

let acc1 = Acceleration(vector: Vector(x: 0, y: 0, z: 0))
let acc2 = Acceleration(vector: Vector(x: 0, y: 100, z: 0))

drivingBehaviorDetector.addData(data: acc1)

DispatchQueue.global(qos: .background).async {
    drivingBehaviorDetector.addData(data: acc2)
}

Единственное решение, которое я могу себе представить, - это принудительно использовать API в том же потоке, но это кажется немного странным. Можно ли решить эту проблему путем изменения общей библиотеки. Потому что мю использовать просто как «DrivingBehaviorDetector», но это может быть вызов функции «addData» в другом потоке. Спасибо за ответы.

1 Ответ

2 голосов
/ 12 февраля 2020

С Kotlin / Native нужно явно определить правильное поведение параллелизма, чтобы избежать гонок. В этом конкретном случае есть несколько вариантов:

  • сделать drivingBehaviorDetector экземпляр замороженным (например, вызвав freeze() в конструкторе) и реализовать addData() с использованием безопасных для параллелизма механизмов, например, связанный список замороженных образцов данных или использование экземпляра коллекции Swift
  • , продумайте сходство потоков в вашем приложении и решите, кто является владельцем экземпляра drivingBehaviorDetector, создайте экземпляр и используйте этот экземпляр только в этом контексте / потоке / queue гарантирует, что диспетчерские API
  • имеют выделенные образцы для обработки работника / сопрограммы и отправляют образцы этому работнику, как это делается в проигрывателе сэмплов видео на https://github.com/JetBrains/kotlin-native/blob/8f7ebe7f511d5700cf559c854f9202bf55240e11/samples/videoplayer/src/videoPlayerMain/kotlin/DecoderWorker.kt#L384
...