Skip to content

iOS: RCTImageLoader is documented to be thread safe but the setUp code has a race #46115

@mfazekas

Description

@mfazekas

Description

According to the header docs loadImageWithURLRequest can be called from any thread.

https://github.com/facebook/react-native/blob/0bb085c7b65527717a1c2750fadc0783c8da6b78/packages/react-native/Libraries/Image/RCTImageLoaderProtocol.h#L68

But the one time setup code has race condition and multiple threads can enter the setUp method:
https://github.com/facebook/react-native/blob/0bb085c7b65527717a1c2750fadc0783c8da6b78/packages/react-native/Libraries/Image/RCTImageLoader.mm#L159-L161

This could potentially lead to crashes like this: #33592

Steps to reproduce

The reproducer at https://github.com/mfazekas/RCTImageLoaderTheadSafety has a native objetive-c class exectuted that hammers RCTImageLoader from many threads at the same time. Sometimes some crash can be reproduced that way. (Note that once the [setUp] has been called the race condition is not observable so app needs to restarted).

React Native Version

0.75.1

Affected Platforms

Runtime - iOS

Output of npx react-native info

% npx react-native info
error: unknown command 'info'
(Did you mean init?)
% npx react-native -v       
14.0.0


### Stacktrace or Logs

```text
One crash I was able to reproduce with reproduce is:

*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x600000c65140> was mutated while being enumerated.'
*** First throw call stack:
(
	0   CoreFoundation                      0x00000001804ac330 __exceptionPreprocess + 172
	1   libobjc.A.dylib                     0x0000000180086c58 objc_exception_throw + 72
	2   CoreFoundation                      0x00000001804ab758 +[__NSFastEnumerationEnumerator allocWithZone:] + 0
	3   ReproducerApp                       0x0000000101318938 __30-[RCTImageLoader dequeueTasks]_block_invoke + 1532
	4   libdispatch.dylib                   0x0000000103a53f40 _dispatch_call_block_and_release + 24
	5   libdispatch.dylib                   0x0000000103a55838 _dispatch_client_callout + 16
	6   libdispatch.dylib                   0x0000000103a5db2c _dispatch_lane_serial_drain + 912
	7   libdispatch.dylib                   0x0000000103a5e830 _dispatch_lane_invoke + 420
	8   libdispatch.dylib                   0x0000000103a6b240 _dispatch_root_queue_drain_deferred_wlh + 324
	9   libdispatch.dylib                   0x0000000103a6a7d4 _dispatch_workloop_worker_thread + 764
	10  libsystem_pthread.dylib             0x0000000104223814 _pthread_wqthread + 284
	11  libsystem_pthread.dylib             0x00000001042225d4 start_wqthread + 8
)

Customer is observing a different crash:

OS Version: iOS 16.2 (20C65) Report Version: 104
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Crashed Thread: 37

Application Specific Information:
release

Thread 37 Crashed:
0 libdispatch.dylib 0x365c34ab4 _os_object_release
1 Halter 0x2006f72c4 -[RCTImageLoader imageURLLoaderForURL:] (RCTImageLoader.mm:182)
2 Halter 0x2006f81b4 -[RCTImageLoader _loadImageOrDataWithURLRequest:size:scale:resizeMode:priority:attribution:progressBlock:partialLoadBlock:completionBlock:] (RCTImageLoader.mm:507)
3 Halter 0x2006fa64c -[RCTImageLoader loadImageWithURLRequest:size:scale:clipped:resizeMode:priority:attribution:progressBlock:partialLoadBlock:completionBlock:] (RCTImageLoader.mm:857)
4 Halter 0x2006f7a0c -[RCTImageLoader loadImageWithURLRequest:size:scale:clipped:resizeMode:priority:progressBlock:partialLoadBlock:completionBlock:] (RCTImageLoader.mm:388)
5 Halter 0x2006f7904 -[RCTImageLoader loadImageWithURLRequest:size:scale:clipped:resizeMode:progressBlock:partialLoadBlock:completionBlock:] (RCTImageLoader.mm:367)
6 Halter 0x20171b958 closure in RNMBXImageQueueOperation.start (RNMBXImageQueue.swift:72)
7 Halter 0x2003dfea8 thunk for closure
8 libdispatch.dylib 0x365c344b0 _dispatch_call_block_and_release
9 libdispatch.dylib 0x365c35fd8 _dispatch_client_callout
10 libdispatch.dylib 0x365c390c4 _dispatch_queue_override_invoke
11 libdispatch.dylib 0x365c47a68 _dispatch_root_queue_drain
12 libdispatch.dylib 0x365c48280 _dispatch_worker_thread2
13 libsystem_pthread.dylib 0x3f3122db8 _pthread_wqthread


Thread 29
0 libsystem_kernel.dylib 0x3d22e5aa8 __psynch_mutexwait
1 libsystem_pthread.dylib 0x3f312414c _pthread_mutex_firstfit_lock_wait
2 libsystem_pthread.dylib 0x3f312b30c _pthread_mutex_firstfit_lock_slow
3 libc++.1.dylib 0x373d209ec std::__1::mutex::lock
4 Halter 0x2006f72d8 [inlined] std::__1::unique_lock::unique_lock[abi:ue170006] (unique_lock.h:41)
5 Halter 0x2006f72d8 [inlined] std::__1::unique_lock::unique_lock[abi:ue170006] (unique_lock.h:40)
6 Halter 0x2006f72d8 -[RCTImageLoader imageURLLoaderForURL:] (RCTImageLoader.mm:186)
7 Halter 0x2006f81b4 -[RCTImageLoader _loadImageOrDataWithURLRequest:size:scale:resizeMode:priority:attribution:progressBlock:partialLoadBlock:completionBlock:] (RCTImageLoader.mm:507)
8 Halter 0x2006fa64c -[RCTImageLoader loadImageWithURLRequest:size:scale:clipped:resizeMode:priority:attribution:progressBlock:partialLoadBlock:completionBlock:] (RCTImageLoader.mm:857)
9 Halter 0x2006f7a0c -[RCTImageLoader loadImageWithURLRequest:size:scale:clipped:resizeMode:priority:progressBlock:partialLoadBlock:completionBlock:] (RCTImageLoader.mm:388)
10 Halter 0x2006f7904 -[RCTImageLoader loadImageWithURLRequest:size:scale:clipped:resizeMode:progressBlock:partialLoadBlock:completionBlock:] (RCTImageLoader.mm:367)
11 Halter 0x20171b958 closure in RNMBXImageQueueOperation.start (RNMBXImageQueue.swift:72)
12 Halter 0x2003dfea8 thunk for closure
13 libdispatch.dylib 0x365c344b0 _dispatch_call_block_and_release
14 libdispatch.dylib 0x365c35fd8 _dispatch_client_callout
15 libdispatch.dylib 0x365c390c4 _dispatch_queue_override_invoke
16 libdispatch.dylib 0x365c47a68 _dispatch_root_queue_drain
17 libdispatch.dylib 0x365c48280 _dispatch_worker_thread2
18 libsystem_pthread.dylib 0x3f3122db8 _pthread_wqthread


### Reproducer

https://github.com/mfazekas/RCTImageLoaderTheadSafety

### Screenshots and Videos

See: https://github.com/rnmapbox/maps/issues/3589

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions