Skip to content

Commit 64ecf65

Browse files
committed
Improve asset management
1 parent 07be1ff commit 64ecf65

File tree

4 files changed

+208
-117
lines changed

4 files changed

+208
-117
lines changed

backends/apple/coreml/runtime/delegate/ETCoreMLAssetManager.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,17 @@ NS_ASSUME_NONNULL_BEGIN
9999
- (NSUInteger)compact:(NSUInteger)sizeInBytes error:(NSError* __autoreleasing*)error;
100100

101101

102+
/// Executes a block with a unique temporary directory.
103+
///
104+
/// A new temporary subdirectory URL is created inside the receiver’s designated
105+
/// base directory. The directory is passed to the block, which can use it to
106+
/// perform temporary file operations. After the block finishes executing,
107+
/// the directory and its contents are removed.
108+
///
109+
/// @param block A block to execute. The block receives a unique URL.
110+
- (void)withTemporaryDirectory:(void (^)(NSURL *directoryURL))block;
111+
112+
102113
/// Purges the assets storage. The assets are moved to the trash directory and are asynchronously
103114
/// deleted.
104115
///
@@ -117,6 +128,12 @@ NS_ASSUME_NONNULL_BEGIN
117128
/// contents are deleted asynchronously.
118129
@property (copy, readonly, nonatomic) NSURL* trashDirectoryURL;
119130

131+
132+
/// The staging directory URL, used to hold assets that are being prepared or processed
133+
/// before they are moved into their final location. The contents of this directory
134+
/// are temporary and may be cleared when no longer needed.
135+
@property (copy, readonly, nonatomic) NSURL* stagingDirectoryURL;
136+
120137
/// The file manager.
121138
@property (strong, readonly, nonatomic) NSFileManager* fileManager;
122139

backends/apple/coreml/runtime/delegate/ETCoreMLAssetManager.mm

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,18 @@ BOOL is_asset_alive(NSMapTable<NSString *, ETCoreMLAsset *> *assets_in_use_map,
254254

255255
return assets;
256256
}
257+
258+
NSURL * _Nullable move_to_directory(NSURL *url,
259+
NSURL *directoryURL,
260+
NSFileManager *fileManager,
261+
NSError * __autoreleasing *error) {
262+
NSURL *dstURL = [directoryURL URLByAppendingPathComponent:[NSUUID UUID].UUIDString];
263+
if (![fileManager moveItemAtURL:url toURL:dstURL error:error]) {
264+
return nil;
265+
}
266+
return dstURL;
267+
}
268+
257269
} //namespace
258270

259271
@interface ETCoreMLAssetManager () <NSFileManagerDelegate> {
@@ -299,12 +311,20 @@ - (nullable instancetype)initWithDatabase:(const std::shared_ptr<Database>&)data
299311
if (!managedAssetsDirectoryURL) {
300312
return nil;
301313
}
302-
314+
303315
NSURL *managedTrashDirectoryURL = ::create_directory_if_needed(trashDirectoryURL, @"models", fileManager, error);
304316
if (!managedTrashDirectoryURL) {
305317
return nil;
306318
}
307-
319+
320+
// Remove any existing contents in the staging directory by moving them to the trash directory,
321+
// then recreate a clean staging directory for new use.
322+
move_to_directory([assetsDirectoryURL URLByAppendingPathComponent:@"staging"], managedTrashDirectoryURL, fileManager, nil);
323+
NSURL *managedStagingDirectoryURL = ::create_directory_if_needed(assetsDirectoryURL, @"staging", fileManager, error);
324+
if (!managedStagingDirectoryURL) {
325+
return nil;
326+
}
327+
308328
// If directory is empty then purge the stores
309329
if (::is_directory_empty(managedAssetsDirectoryURL, fileManager, nil)) {
310330
assetsMetaStore.impl()->purge(ec);
@@ -315,6 +335,7 @@ - (nullable instancetype)initWithDatabase:(const std::shared_ptr<Database>&)data
315335
_assetsStore = std::move(assetsStore);
316336
_assetsMetaStore = std::move(assetsMetaStore);
317337
_assetsDirectoryURL = managedAssetsDirectoryURL;
338+
_stagingDirectoryURL = managedStagingDirectoryURL;
318339
_trashDirectoryURL = managedTrashDirectoryURL;
319340
_estimatedSizeInBytes = sizeInBytes.value();
320341
_maxAssetsSizeInBytes = maxAssetsSizeInBytes;
@@ -349,12 +370,18 @@ - (nullable instancetype)initWithDatabaseURL:(NSURL *)databaseURL
349370
- (nullable NSURL *)moveURL:(NSURL *)url
350371
toUniqueURLInDirectory:(NSURL *)directoryURL
351372
error:(NSError * __autoreleasing *)error {
352-
NSURL *dstURL = [directoryURL URLByAppendingPathComponent:[NSUUID UUID].UUIDString];
353-
if (![self.fileManager moveItemAtURL:url toURL:dstURL error:error]) {
354-
return nil;
373+
return move_to_directory(url, directoryURL, self.fileManager, error);
374+
}
375+
376+
- (void)withTemporaryDirectory:(void (^)(NSURL *directoryURL))block {
377+
NSURL *dstURL = [self.stagingDirectoryURL URLByAppendingPathComponent:[NSUUID UUID].UUIDString];
378+
block(dstURL);
379+
if (![self.fileManager fileExistsAtPath:dstURL.path]) {
380+
return;
355381
}
356-
357-
return dstURL;
382+
383+
[self moveURL:dstURL toUniqueURLInDirectory:self.trashDirectoryURL error:nil];
384+
[self cleanupTrashDirectory];
358385
}
359386

360387
- (void)cleanupAssetIfNeeded:(ETCoreMLAsset *)asset {
@@ -407,7 +434,7 @@ - (nullable ETCoreMLAsset *)_storeAssetAtURL:(NSURL *)srcURL
407434
return false;
408435
}
409436

410-
// If an asset exists move it
437+
// If an asset exists move it,
411438
[self moveURL:dstURL toUniqueURLInDirectory:self.trashDirectoryURL error:nil];
412439

413440
// Move the asset to assets directory.
@@ -433,16 +460,25 @@ - (nullable ETCoreMLAsset *)_storeAssetAtURL:(NSURL *)srcURL
433460
}
434461

435462
- (void)triggerCompaction {
436-
if (self.estimatedSizeInBytes < self.maxAssetsSizeInBytes) {
437-
return;
463+
if (self.estimatedSizeInBytes >= self.maxAssetsSizeInBytes) {
464+
__weak __typeof(self) weakSelf = self;
465+
dispatch_async(self.syncQueue, ^{
466+
NSError *localError = nil;
467+
if (![weakSelf _compact:self.maxAssetsSizeInBytes error:&localError]) {
468+
ETCoreMLLogError(localError, "Failed to compact asset store.");
469+
}
470+
});
438471
}
439-
472+
473+
// Always clean the trash directory to ensure a minimal footprint.
474+
// The `trashQueue` is serialized, so only one cleanup will run at a time.
475+
[self cleanupTrashDirectory];
476+
}
477+
478+
- (void)cleanupTrashDirectory {
440479
__weak __typeof(self) weakSelf = self;
441-
dispatch_async(self.syncQueue, ^{
442-
NSError *localError = nil;
443-
if (![weakSelf _compact:self.maxAssetsSizeInBytes error:&localError]) {
444-
ETCoreMLLogError(localError, "Failed to compact asset store.");
445-
}
480+
dispatch_async(self.trashQueue, ^{
481+
[weakSelf removeFilesInTrashDirectory];
446482
});
447483
}
448484

@@ -649,13 +685,7 @@ - (NSUInteger)_compact:(NSUInteger)sizeInBytes error:(NSError * __autoreleasing
649685
identifier);
650686
}
651687
}
652-
653-
// Trigger cleanup.
654-
__weak __typeof(self) weakSelf = self;
655-
dispatch_async(self.trashQueue, ^{
656-
[weakSelf removeFilesInTrashDirectory];
657-
});
658-
688+
659689
return _estimatedSizeInBytes;
660690
}
661691

@@ -664,7 +694,10 @@ - (NSUInteger)compact:(NSUInteger)sizeInBytes error:(NSError * __autoreleasing *
664694
dispatch_sync(self.syncQueue, ^{
665695
result = [self _compact:sizeInBytes error:error];
666696
});
667-
697+
698+
// Always clean the trash directory to ensure a minimal footprint.
699+
// The `trashQueue` is serialized, so only one cleanup will run at a time.
700+
[self cleanupTrashDirectory];
668701
return result;
669702
}
670703

@@ -724,13 +757,7 @@ - (BOOL)_purge:(NSError * __autoreleasing *)error {
724757

725758
::set_error_from_error_code(ec, error);
726759
// Trigger cleanup
727-
if (status) {
728-
__weak __typeof(self) weakSelf = self;
729-
dispatch_async(self.trashQueue, ^{
730-
[weakSelf removeFilesInTrashDirectory];
731-
});
732-
}
733-
760+
[self cleanupTrashDirectory];
734761
return static_cast<BOOL>(status);
735762
}
736763

backends/apple/coreml/runtime/delegate/ETCoreMLModelLoader.mm

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,21 +62,12 @@ + (nullable ETCoreMLModel *)loadModelWithContentsOfURL:(NSURL *)compiledModelURL
6262
if (model) {
6363
return model;
6464
}
65-
66-
if (localError) {
67-
ETCoreMLLogError(localError,
68-
"Failed to load model from compiled asset with identifier = %@",
69-
identifier);
70-
}
71-
72-
// If store failed then we will load the model from compiledURL.
73-
auto backingAsset = Asset::make(compiledModelURL, identifier, assetManager.fileManager, error);
74-
if (!backingAsset) {
75-
return nil;
65+
66+
if (error) {
67+
*error = localError;
7668
}
77-
78-
asset = [[ETCoreMLAsset alloc] initWithBackingAsset:backingAsset.value()];
79-
return ::get_model_from_asset(asset, configuration, metadata, error);
69+
70+
return nil;
8071
}
8172

8273
@end

0 commit comments

Comments
 (0)