CFReadStreamRead блокируется навсегда под iOS 7

Я вижу проблему, в которой CFReadStreamRead, как часть потоковой загрузки файлов, никогда не возвращается.

Это, похоже, происходит только на iOS7 - и гораздо чаще при отладке от физического устройства, чем в симуляторе, - или, по крайней мере, это гораздо более очевидно.

У нас есть HTTP (или HTTPS, проблема возникает в любом случае, с локально размещенным или удаленным сервером) POST файла, с помощью прямого вызова с блокировкой (без события) CFNetwork. Это необходимость кода C, вызывающего этот обработчик; нет никаких условий для обратных вызовов.

Хорошо и хорошо, сетевые вызовы происходят в фоновых потоках и/или через асинхронную отправку.

Сетевой код, о котором идет речь, сводится к (устранению ошибок для краткости):

CFReadStreamRef upload = CFReadStreamCreateWithFile(
  kCFAllocatorDefault, upload_file_url);
CFRelease(upload_file_url);
CFReadStreamOpen(upload);

CFReadStreamRef myReadStream = CFReadStreamCreateForStreamedHTTPRequest(
  kCFAllocatorDefault, myRequest, upload);

CFReadStreamOpen(myReadStream);

CFIndex numBytesRead = CFReadStreamRead(myReadStream, buf, sizeof(buf));

// etc.

Сам по себе этот код хочет висеть сразу под iOS7. Если я добавлю цикл с некоторыми вызовами на usleep до него (проверка CFReadStreamHasBytesAvailable по пути), это почти всегда будет успешным. Каждые несколько сотен попыток, он все равно потерпит неудачу, никогда не вернется. Опять же, основной поток не изменяется.

Я надеялся, что GM прояснит это поведение, но оно все еще присутствует.

Добавление метода runloop/callback для наблюдения за доступными байтами событиями не влияет - когда вызов зависает, события не видны.

Любые предложения относительно того, почему это происходит, или как это можно предотвратить? Кто-нибудь еще видит поведение CFReadStream в iOS 7?

Ответ 1

Я пробовал такое неприятное обходное решение, и оно работает для меня, проблема в том, что я запрашиваю значения дельта с сервера, поэтому, если что-то пойдет не так, я просто получаю новое значение delta, в общем случае он не будет работать (в журналах я вижу, что время от времени срабатывает иногда). По крайней мере, это предотвращает форматирование постоянной блокировки потоков и дает возможность как-то справиться с этой проблемой:

NSInteger readStreamReadWorkaround(CFReadStreamRef readStrem, UInt8 *buffer, CFIndex bufferLength) {
  static dispatch_once_t onceToken;
  static BOOL isProblematicOs = YES;
  dispatch_once(&onceToken, ^{
    isProblematicOs = [[UIDevice currentDevice].systemName compare: @"7.0" options: NSNumericSearch]!=NSOrderedAscending;
  });

  NSInteger readBytesCount = -2;

  if (isProblematicOs) {
    CFStreamStatus sStatus = CFReadStreamGetStatus(readStrem);
    NSDate *date = [NSDate date];
    while (YES) {
      if(CFReadStreamHasBytesAvailable(readStrem)) {
        readBytesCount = CFReadStreamRead(readStrem, buffer, bufferLength);
        break;
      }
      sStatus = CFReadStreamGetStatus(readStrem);
      if (sStatus!=kCFStreamStatusOpen && sStatus !=kCFStreamStatusAtEnd
          || [date timeIntervalSinceNow]<-15.0) {
        break;
      }
      usleep(50000);
    }
  } else {
    readBytesCount = CFReadStreamRead(readStrem, buffer, sizeof(buffer));
  }
  return readBytesCount;
}

Мне не нравится это решение, но пока я не вижу альтернативы.