Skip to content

Commit

Permalink
fix: fix the race condition when calling readAsDataURL after new Blob…
Browse files Browse the repository at this point in the history
…(blobs) (#34096)

Summary:
```js
async () => {
  let blobs = [];
  for (let i = 0; i < 4; i++) {
    const res = await fetch();
    blobs = [...blobs, await res.blob()]
  }
  const blob = new Blob(blobs); // <<<<<<<<<<<<<<< a
  return await new Promise((resolve, reject) => {
    const fileReader = new FileReader();
    fileReader.onload = async () => {
      await RNFS.writeFile(destPath, (fileReader.result as string).split(',')[1], 'base64');
      resolve(destPath);
    };
    fileReader.onabort = () => {
      reject('');
    };
    fileReader.onerror = (event) => {
      reject('');
    };
    fileReader.readAsDataURL(blob); // <<<<<<<<<<<<<<< b
  });
}
```

Sometime `fileReader.readAsDataURL` is unable to get blob from the dictionary after `new Blob(blobs)` and then reject with `Unable to resolve data for blob: blobId` in iOS or `The specified blob is invalid` in android. Because line `a` and `b` can be run in different thread. `new Blob([])` is in progress and `fileReader.readAsDataURL` accesses the blob dictionary ahead of the blob creation.

The expected behaviour is it should finish new Blob([]) first and then readAsDataURL(blob)

To fix that, there should be a lock inside the method `createFromParts`. For iOS, It needs to be a recursive_mutex to allow same thread to acquire lock

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[iOS] [Fixed] - fix the race condition when calling readAsDataURL after new Blob(blobs)

Pull Request resolved: #34096

Reviewed By: cipolleschi

Differential Revision: D37514981

Pulled By: javache

fbshipit-source-id: 4bf84ece99871276ecaa5aa1849b9145ff44dbf4
  • Loading branch information
wood1986 authored and kelset committed Jul 12, 2022
1 parent 4f72a02 commit 112d678
Showing 1 changed file with 31 additions and 27 deletions.
58 changes: 31 additions & 27 deletions Libraries/Blob/RCTFileReaderModule.mm
Original file line number Diff line number Diff line change
Expand Up @@ -31,44 +31,48 @@ @implementation RCTFileReaderModule
reject:(RCTPromiseRejectBlock)reject)
{
RCTBlobManager *blobManager = [_moduleRegistry moduleForName:"BlobModule"];
NSData *data = [blobManager resolve:blob];
dispatch_async(blobManager.methodQueue, ^{
NSData *data = [blobManager resolve:blob];

if (data == nil) {
reject(RCTErrorUnspecified,
[NSString stringWithFormat:@"Unable to resolve data for blob: %@", [RCTConvert NSString:blob[@"blobId"]]], nil);
} else {
NSStringEncoding stringEncoding;

if (encoding == nil) {
stringEncoding = NSUTF8StringEncoding;
if (data == nil) {
reject(RCTErrorUnspecified,
[NSString stringWithFormat:@"Unable to resolve data for blob: %@", [RCTConvert NSString:blob[@"blobId"]]], nil);
} else {
stringEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef) encoding));
}
NSStringEncoding stringEncoding;

NSString *text = [[NSString alloc] initWithData:data encoding:stringEncoding];
if (encoding == nil) {
stringEncoding = NSUTF8StringEncoding;
} else {
stringEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef) encoding));
}

resolve(text);
}
NSString *text = [[NSString alloc] initWithData:data encoding:stringEncoding];

resolve(text);
}
});
}

RCT_EXPORT_METHOD(readAsDataURL:(NSDictionary<NSString *, id> *)blob
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
{
RCTBlobManager *blobManager = [_moduleRegistry moduleForName:"BlobModule"];
NSData *data = [blobManager resolve:blob];

if (data == nil) {
reject(RCTErrorUnspecified,
[NSString stringWithFormat:@"Unable to resolve data for blob: %@", [RCTConvert NSString:blob[@"blobId"]]], nil);
} else {
NSString *type = [RCTConvert NSString:blob[@"type"]];
NSString *text = [NSString stringWithFormat:@"data:%@;base64,%@",
type != nil && [type length] > 0 ? type : @"application/octet-stream",
[data base64EncodedStringWithOptions:0]];

resolve(text);
}
dispatch_async(blobManager.methodQueue, ^{
NSData *data = [blobManager resolve:blob];

if (data == nil) {
reject(RCTErrorUnspecified,
[NSString stringWithFormat:@"Unable to resolve data for blob: %@", [RCTConvert NSString:blob[@"blobId"]]], nil);
} else {
NSString *type = [RCTConvert NSString:blob[@"type"]];
NSString *text = [NSString stringWithFormat:@"data:%@;base64,%@",
type != nil && [type length] > 0 ? type : @"application/octet-stream",
[data base64EncodedStringWithOptions:0]];

resolve(text);
}
});
}

- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params
Expand Down

0 comments on commit 112d678

Please sign in to comment.