Как исправить это состояние гонки в задаче Objective C / async? 'неограниченный индекс 1 для пустого массива'

Привет, у меня проблема с этой библиотекой: LTSupportAutomotive

Я быстрый программист, поэтому не очень хорошо разбираюсь в задачах c.

как исправить это состояние гонки?

Я надеюсь, что кто-то может мне помочь.

'*** - [__ NSArrayM insertObject: atIndex:]: индекс 1 за пределами границ для пустого массива'

-(void)asyncEnqueueInternalCommand:(LTOBD2AdapterInternalCommand*)internalCommand
{
    @synchronized(self) {
        [_commandQueue addObject:internalCommand];
    }
}

Исходный код:

https://github.com/mickeyl/LTSupportAutomotive/blob/f51b962421f211ee6af5c733f79190117d7cac5e/LTSupportAutomotive/LTOBD2Adapter.m

ОБНОВЛЕНИЕ 1:

Создана ветка с первыми исправлениями: https://github.com/Skyb0rg/LTSupportAutomotive/tree/BugfixMemoryManagement

После добавления дополнительной commandQueue у меня появились новые ошибки:

в LTBTLEWriteCharacteristicStream.m

-(void)characteristicDidWriteValue
{
    [self.delegate stream:self handleEvent:NSStreamEventHasSpaceAvailable];
}

эта функция дает сбой: имя селектора, найденное в текущих регистрах аргументов: делегат

Поток 6 разбился: 0 libobjc.A.dylib 0x00000001bcc19430 objc_retain + 16

1 LTSupportAutomotive 0x00000001093c2a34 - [LTBTLEWriteCharacteristicStream characterDidWriteValue] (LTBTLEWriteCharacteristicStream.m: 39)

2 LTSupportAutomotive 0x00000001093c2714 - [Периферийное устройство LTBTLESerialTransporter: didWriteValueForCharacteristic: error:] (LTBTLESerialTransporter.m: 311)

3 CoreBluetooth 0x00000001c35e6ce0 - [CBPeripheral handleAttributeEvent: args: attributeSelector: delegateSelector: delegateFlag:] + 236

4 CoreBluetooth 0x00000001c35e6e40 - [CBPeripheral handleCharacteristicEvent: featureSelector: delegateSelector: delegateFlag:] + 128

5 CoreBluetooth 0x00000001c35e24f0 - [CBPeripheral handleMsg: args:] + 352

6 CoreBluetooth 0x00000001c35dcbfc - [CBCentralManager handleMsg: args:] + 200

7 CoreBluetooth 0x00000001c35eb770 __30- [CBXpcConnection _handleMsg:] _ block_invoke + 56

8 libdispatch.dylib 0x00000001bd4696c8 _dispatch_call_block_and_release + 20

9 libdispatch.dylib 0x00000001bd46a484 _dispatch_client_callout + 12

10 libdispatch.dylib 0x00000001bd411bd0 _dispatch_lane_serial_drain $ VARIANT $ mp + 588

11 libdispatch.dylib 0x00000001bd41274c _dispatch_lane_invoke $ ВАРИАНТ $ mp + 480

12 libdispatch.dylib 0x00000001bd411a9c _dispatch_lane_serial_drain $ VARIANT $ mp + 280

13 libdispatch.dylib 0x00000001bd412718 _dispatch_lane_invoke $ VARIANT $ mp + 428

14 libdispatch.dylib 0x00000001bd41aeb8 _dispatch_workloop_worker_thread + 596

15 libsystem_pthread.dylib 0x00000001bd64d0dc _pthread_wqthread + 308

16 libsystem_pthread.dylib 0x00000001bd64fcec start_wqthread + 0

и еще один сбой в: LTBTLEReadCharacteristicStream.m

-(void)characteristicDidUpdateValue
{
        NSData* value = _characteristic.value;
        [_buffer appendData:value];
        [self.delegate stream:self handleEvent:NSStreamEventHasBytesAvailable];
}

*** Завершение работы приложения из-за неперехваченного исключения «NSInvalidArgumentException», причина: «- [OS_dispatch_data stream: handleEvent:]: нераспознанный селектор отправлен в экземпляр 0x281be1b90»

Поток 9 разбился: 0 libsystem_kernel.dylib 0x00000001bd5c7104 __pthread_kill + 8

1 libsystem_pthread.dylib 0x00000001bd643020 pthread_kill $ VARIANT $ mp + 376

2 libsystem_c.dylib 0x00000001bd51ed78 отмена + 136

3 Приложение для участников VW-R-CLUB 0x00000001045603ac uncaught_exception_handler + 68

4 CoreFoundation 0x00000001bda321e0 __handleUncaughtException + 688

5 libobjc.A.dylib 0x00000001bcc01e4c _objc_terminate () + 108

6 Приложение VW-R-CLUB 0x0000000104555c4c BITCrashUncaughtCXXTerminateHandler () (BITCrashCXXExceptionHandler.mm:183)

7 libc ++ abi.dylib 0x00000001bcbf50fc std :: __ terminate (void (*) ()) + 12

8 libc ++ abi.dylib 0x00000001bcbf5188 std :: terminate () + 80

9 libdispatch.dylib 0x00000001bd46a498 _dispatch_client_callout + 32

10 libdispatch.dylib 0x00000001bd411bd0 _dispatch_lane_serial_drain $ VARIANT $ mp + 588

11 libdispatch.dylib 0x00000001bd41274c _dispatch_lane_invoke $ ВАРИАНТ $ mp + 480

12 libdispatch.dylib 0x00000001bd411a9c _dispatch_lane_serial_drain $ VARIANT $ mp + 280

13 libdispatch.dylib 0x00000001bd412718 _dispatch_lane_invoke $ VARIANT $ mp + 428

14 libdispatch.dylib 0x00000001bd41aeb8 _dispatch_workloop_worker_thread + 596

15 libsystem_pthread.dylib 0x00000001bd64d0dc _pthread_wqthread + 308

16 libsystem_pthread.dylib 0x00000001bd64fcec start_wqthread + 0


person Skyborg    schedule 11.02.2019    source источник


Ответы (1)


Во-первых, код, который вы показываете, не тот, который можно найти в github. Вы синхронизируете все использования _commandQueue или только это? Если только этот, то почему остальные не синхронизируются?

Затем в github я нахожу несколько случаев использования _commandQueue, некоторые из них в методах с именем _3 _..., но также некоторые не async, например cancelPendingCommands или responseCompleted. Вам нужно выяснить, что означает async в имени метода и почему такие методы, как cancelPendingCommands, каким-то образом не являются async.


Обновлять

Таким образом, основная идея, кажется, защитить все _commandQueue доступы внутри последовательного _dispatchQueue. Это уже имеет место в методах _12 _...: они вызываются из этой очереди:

dispatch_async( _dispatchQueue, ^{
    [self asyncEnqueueInternalCommand:internalCommand];
});

Поэтому вам необходимо убедиться, что каждый доступ к _commandQueue помещается в эту очередь, например измените cancelPendingCommands на что-то вроде

-(void)cancelPendingCommands
{
    dispatch_async( _dispatchQueue, ^{
        // This cancels all but the first command in order to prevent sending a new command while
        // the response to an active command is still pending. OBD2 adapters usually can't cope with
        // that and emit a 'STOPPED' response in that case.
        if ( _hasPendingAnswer )
        {
            NSRange allButTheFirst = NSMakeRange( 1, _commandQueue.count - 1 );
            [_commandQueue removeObjectsInRange:allButTheFirst];
        }
        else
        {
            [_commandQueue removeAllObjects];
        }
    });
}

(или создайте специальную asyncCancelPendingCommands функцию, которая будет вызываться из cancelPendingCommands из блока отправки. И так далее.

P.S. Если вам нужно синхронное выполнение, вы также можете использовать dispatch_sync вместо dispatch_async, но тогда вы должны убедиться, что вы не создаете тупиковых ситуаций.

person Andreas Oetjen    schedule 11.02.2019
comment
Привет, Андреас, это был всего лишь тест с @synchronized. Я не уверен, какое решение будет правильным. У меня есть 2 других метода с последовательной очередью. но я не знаю, как решить сбои. Мастер - гитхаб. каковы первые шаги к решению? В быстром темпе мне было бы легче, но в цели я совершенно заблудился. - person Skyborg; 12.02.2019
comment
Я думаю, что _dispatchQueue - это то, что вам нужно; пожалуйста, проверьте мой обновленный ответ - person Andreas Oetjen; 13.02.2019
comment
OK добавил _dispatchQueue в cancelPendingCommands и в responseCompleted. Также удален @synchronized. Я протестирую это в ближайшие дни. - person Skyborg; 15.02.2019
comment
OK проверил его сегодня и получил новую ошибку: [_commandQueue removeObjectsInRange: allButTheFirst]; - ›Результатов: *** Завершение работы приложения из-за неперехваченного исключения« NSRangeException », причина:« *** - [__ NSArrayM removeObjectsInRange:]: диапазон {0, 1} выходит за пределы для пустого массива » - person Skyborg; 18.02.2019
comment
И вы абсолютно уверены, что _commandQueue не пуста, когда вы вызываете removeObjectsInRange? Потому что сообщение об ошибке звучит так ... - person Andreas Oetjen; 18.02.2019
comment
нет, я не был уверен. Просто добавил следующий код: if (self - ›_ commandQueue.count› 0) {NSRange allButTheFirst = NSMakeRange (1, self - ›_ commandQueue.count - 1); [само- ›_ commandQueue removeObjectsInRange: allButTheFirst]; } - person Skyborg; 18.02.2019
comment
завтра еще раз опробую на трассе 180км. - person Skyborg; 19.02.2019
comment
Нет новых результатов ... исправлены дополнительные ошибки. Но первой ошибки больше не было. Иногда ошибка возникает только один раз из 100 000 ответов. Будет обновлено здесь на следующей неделе. - person Skyborg; 20.02.2019
comment
У меня сегодня новые сбои в небольших перекодировках OBD. Может быть, ты сможешь мне помочь. Я обновил вопрос. - person Skyborg; 21.02.2019
comment
создал новую ветку для ошибок памяти: github.com/Skyb0rg/LTSupportAutomotive/tree/ - person Skyborg; 21.02.2019
comment
Похоже, некоторые проблемы с памятью были вызваны вызовами GCD (например, память делегата уже освобождена перед доступом). Может, тебе стоит попробовать dispatch_sync вместо dispatch_async. - person Andreas Oetjen; 21.02.2019
comment
Хорошо, я попробую. Спасибо. - person Skyborg; 22.02.2019
comment
Возможно ли, что два конвейера последовательных очередей и адаптер, созданные по отдельности, должны быть одинаковыми? теперь они выполняются одновременно. - person Skyborg; 23.02.2019
comment
Если вы обращаетесь к одному и тому же ресурсу, вам нужно использовать один и только один объект мьютекса (например, очередь отправки). Возможно, будет полезно создать (и загрузить) полностью автономный тестовый проект (для которого не требуется никакого оборудования и т. Д.), Который может воспроизводить проблемы. - person Andreas Oetjen; 23.02.2019
comment
Провел несколько тестов, и единая глобальная очередь последовательной отправки, похоже, работает. Не уверен на 100%, но за последние 2 дня у меня больше не было сбоев. Автономный тестовый проект без оборудования невозможен. Вам нужна машина и электронный ключ, а также множество разных обстоятельств, данные никогда не будут одинаковыми. показания похожи на старую последовательную шину с шестнадцатеричными битовыми блоками. Чтобы подражать этому, потребовалось бы много месяцев. Я скажу вам, если что-то изменится. Спасибо за помощь. - person Skyborg; 27.02.2019
comment
Сегодня очередной сбой в тех же функциях. Но теперь это все реже. Мне нужно взглянуть на файл SerialTransporter.m. Последовательная очередь там используется только для менеджера. - person Skyborg; 28.02.2019
comment
Ага. Похоже, вам нужно много исследовать, чтобы инкапсулировать все многопоточные использования в мьютексы. Интересно, почему и как раньше запускалась программа? Вы недавно внедрили многопоточность? - person Andreas Oetjen; 01.03.2019
comment
Это общедоступный api, который я установил с cocoapod и не создал мной. Я использовал его с 3 месяцев в своем приложении (бета), и у меня возникло несколько проблем. Но теперь я использую его постоянно, раньше только для проверки текущих значений. Многопоточность была представлена ​​создателем доктором Майклом Лауэром. - person Skyborg; 01.03.2019
comment
Теперь это работает :) Делегат был создан с неслабым __unsafe_unrehibited. Создал запрос на перенос по адресу: github.com/mickeyl/LTSupportAutomotive/pull/26 Спасибо Ваш за помощь !!! - person Skyborg; 24.03.2019