A portion of our application transmits and receives data via InputStream and OutputStream. This is done to facilitate high speed data transmission (think prices from a stock exchange). The application gets an URLSessionStreamTask from an URLSession, resumes that task (“streamTask.resume()”), secures the connection (“streamTask.startSecureConnection()”), then captures the streams (“streamTask.captureStreams()”).
We use the captured InputStream and OutputStream objects to communicate using a proprietary protocol. This has worked well for many years - prior to URLSessionStreamTask, we used other mechanisms to set up the streams.
With the advent of iOS 11, this no longer works. The scenario is this:
When bytes become available on the InputStream, we read a predefined, small number of bytes (4) that indicates the size of the payload. Once we know the payload size, we read off the InputStream until we’ve received the entire payload.
In iOS 10 and below, this works well. In iOS 11, the reader thread appears to deadlock in CFNetwork code when reading the payload. If we modify the application to read all available bytes (using some arbitrarily large buffer), we receive both the bytes describing the payload size and the payload itself. However, we don’t want to read this arbitrarily large buffer.
LLDB indicates that the reader thread is blocked waiting for availability of a spin lock.
I filed Radar 33741310 on August 5 with a small sample application that replicates the issue. However, as best I can tell, the Radar hasn’t been looked at. This is a critical issue for us as it renders a portion of our application inoperable.
The slightly sanitized stack trace from the blocked thread is as follows:
Thread 8 Queue : Anonymous
#0 0x0000000111ee231e in __ulock_wait ()
#1 0x0000000111f07aff in _os_ulock_wait ()
#2 0x0000000111f077d0 in _os_nospin_lock_lock_slow ()
#3 0x000000010d6ecf49 in __NSCFTCPIOReadStream::_streamImpl_CanRead(__CFReadStream*) ()
#4 0x000000010dd4dbcd in CFReadStreamHasBytesAvailable ()
#5 0x000000010dd86179 in -[__NSCFInputStream hasBytesAvailable] ()
#6 0x000000010c6d2070 in FourByteHeaderMessageProcessor.readBytesWith4ByteHeader(timeoutTimeInterval:maxMessageLength:) at /...Sanitized.../FourByteHeaderMessageProcessor.swift:80
#7 0x000000010c6ca3aa in closure #1 in static URLSessionStreamTaskManager.testGetSessionIDResponseFromStream() at /...Sanitized.../URLSessionStreamTaskManager.swift:31
#8 0x000000010c6cd835 in URLSessionStreamTaskManager.urlSession(_:streamTask:didBecome:outputStream:) at /...Sanitized.../URLSessionStreamTaskManager.swift:160
#9 0x000000010c6cde9b in @objc URLSessionStreamTaskManager.urlSession(_:streamTask:didBecome:outputStream:) ()
#10 0x000000010d77797e in __88-[NSURLSession delegate_streamTask:didBecomeInputStream:outputStream:completionHandler:]_block_invoke ()
#11 0x000000010ca119b7 in __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ ()
#12 0x000000010ca1181a in -[NSBlockOperation main] ()
#13 0x000000010ca0fcd6 in -[__NSOperationInternal _start:] ()
#14 0x0000000111a5843c in _dispatch_client_callout ()
#15 0x0000000111a5daf4 in _dispatch_block_invoke_direct ()
#16 0x0000000111a5843c in _dispatch_client_callout ()
#17 0x0000000111a5daf4 in _dispatch_block_invoke_direct ()
#18 0x0000000111a5d884 in dispatch_block_perform ()
#19 0x000000010ca0bce4 in __NSOQSchedule_f ()
#20 0x0000000111a5843c in _dispatch_client_callout ()
#21 0x0000000111a5e856 in _dispatch_continuation_pop ()
#22 0x0000000111a5cc86 in _dispatch_async_redirect_invoke ()
#23 0x0000000111a641f9 in _dispatch_root_queue_drain ()
#24 0x0000000111a63e97 in _dispatch_worker_thread3 ()
#25 0x0000000111f165a2 in _pthread_wqthread ()
#26 0x0000000111f1607d in start_wqthread ()
The only other threads LLDB shows are as follows:
Thread 1 Queue : com.apple.main-thread (serial)
#0 0x0000000111eda34a in mach_msg_trap ()
#1 0x0000000111ed9797 in mach_msg ()
#2 0x000000010dcfde85 in __CFRunLoopServiceMachPort ()
#3 0x000000010dcfd3c2 in __CFRunLoopRun ()
#4 0x000000010dcfca89 in CFRunLoopRunSpecific ()
#5 0x00000001130969c6 in GSEventRunModal ()
#6 0x000000010ecd97d0 in UIApplicationMain ()
#7 0x000000010c6c8cf7 in main at /...Sanitized.../AppDelegate.swift:11
#8 0x0000000111ad4d81 in start ()
Thread 4
#0 0x0000000111ee244e in __workq_kernreturn ()
#1 0x0000000111f1648e in _pthread_wqthread ()
#2 0x0000000111f1607d in start_wqthread ()
com.apple.uikit.eventfetch-thread (5)
#0 0x0000000111eda34a in mach_msg_trap ()
#1 0x0000000111ed9797 in mach_msg ()
#2 0x000000010dcfde85 in __CFRunLoopServiceMachPort ()
#3 0x000000010dcfd3c2 in __CFRunLoopRun ()
#4 0x000000010dcfca89 in CFRunLoopRunSpecific ()
#5 0x000000010c9f6e5e in -[NSRunLoop(NSRunLoop) runMode:beforeDate:] ()
#6 0x000000010ca749b7 in -[NSRunLoop(NSRunLoop) runUntilDate:] ()
#7 0x000000010f8f81bd in -[UIEventFetcher threadMain] ()
#8 0x000000010ca048ac in __NSThread__start__ ()
#9 0x0000000111f1693b in _pthread_body ()
#10 0x0000000111f16887 in _pthread_start ()
#11 0x0000000111f1608d in thread_start ()