From da4630f4c515ccee9a14d5d8f12746aa73c86039 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 26 Feb 2022 23:24:32 -0800 Subject: [PATCH] Add a metadata loader cache Promote the Plugin Controller source file to Objective-C++, and add a simple data cache that holds on to requests for up to 5 seconds after their last access, for preventing spammed requests from hitting files over and over. This is apparently really relevant to the CUESheet reader and its embedded CUESheet handling, as that tends to reread the same file over and over as it populates the playlist with tracks. The nested reader can also lead to repeated reading even on files without CUESheets embedded. Signed-off-by: Christopher Snowhill --- Audio/CogAudio.xcodeproj/project.pbxproj | 28 ++- ...PluginController.m => PluginController.mm} | 160 +++++++++++++++++- Utils/RedundantPlaylistDataStore.h | 1 + Utils/RedundantPlaylistDataStore.m | 5 + 4 files changed, 183 insertions(+), 11 deletions(-) rename Audio/{PluginController.m => PluginController.mm} (82%) diff --git a/Audio/CogAudio.xcodeproj/project.pbxproj b/Audio/CogAudio.xcodeproj/project.pbxproj index 5af87de58..211d6ad23 100644 --- a/Audio/CogAudio.xcodeproj/project.pbxproj +++ b/Audio/CogAudio.xcodeproj/project.pbxproj @@ -39,8 +39,13 @@ 17D21EBD0B8BF44000D1EBDE /* AudioPlayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 17D21EBB0B8BF44000D1EBDE /* AudioPlayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17D21EBE0B8BF44000D1EBDE /* AudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 17D21EBC0B8BF44000D1EBDE /* AudioPlayer.m */; }; 17F94DD50B8D0F7000A34E87 /* PluginController.h in Headers */ = {isa = PBXBuildFile; fileRef = 17F94DD30B8D0F7000A34E87 /* PluginController.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 17F94DD60B8D0F7000A34E87 /* PluginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 17F94DD40B8D0F7000A34E87 /* PluginController.m */; }; + 17F94DD60B8D0F7000A34E87 /* PluginController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 17F94DD40B8D0F7000A34E87 /* PluginController.mm */; }; 17F94DDD0B8D101100A34E87 /* Plugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 17F94DDC0B8D101100A34E87 /* Plugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8328995327CB511000D7F028 /* RedundantPlaylistDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 8328995127CB510F00D7F028 /* RedundantPlaylistDataStore.m */; }; + 8328995427CB511000D7F028 /* RedundantPlaylistDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 8328995227CB511000D7F028 /* RedundantPlaylistDataStore.h */; }; + 8328995727CB51B700D7F028 /* SHA256Digest.h in Headers */ = {isa = PBXBuildFile; fileRef = 8328995527CB51B700D7F028 /* SHA256Digest.h */; }; + 8328995827CB51B700D7F028 /* SHA256Digest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8328995627CB51B700D7F028 /* SHA256Digest.m */; }; + 8328995A27CB51C900D7F028 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8328995927CB51C900D7F028 /* Security.framework */; }; 8347C7412796C58800FA8A7D /* NSFileHandle+CreateFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8347C73F2796C58800FA8A7D /* NSFileHandle+CreateFile.h */; }; 8347C7422796C58800FA8A7D /* NSFileHandle+CreateFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 8347C7402796C58800FA8A7D /* NSFileHandle+CreateFile.m */; }; 834FD4EB27AF8F380063BC83 /* AudioChunk.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FD4EA27AF8F380063BC83 /* AudioChunk.h */; }; @@ -139,9 +144,14 @@ 17D21EBB0B8BF44000D1EBDE /* AudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = AudioPlayer.h; sourceTree = ""; }; 17D21EBC0B8BF44000D1EBDE /* AudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = AudioPlayer.m; sourceTree = ""; }; 17F94DD30B8D0F7000A34E87 /* PluginController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PluginController.h; sourceTree = ""; }; - 17F94DD40B8D0F7000A34E87 /* PluginController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PluginController.m; sourceTree = ""; }; + 17F94DD40B8D0F7000A34E87 /* PluginController.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = PluginController.mm; sourceTree = ""; }; 17F94DDC0B8D101100A34E87 /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Plugin.h; sourceTree = ""; }; 32DBCF5E0370ADEE00C91783 /* CogAudio_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CogAudio_Prefix.pch; sourceTree = ""; }; + 8328995127CB510F00D7F028 /* RedundantPlaylistDataStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RedundantPlaylistDataStore.m; path = ../../Utils/RedundantPlaylistDataStore.m; sourceTree = ""; }; + 8328995227CB511000D7F028 /* RedundantPlaylistDataStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RedundantPlaylistDataStore.h; path = ../../Utils/RedundantPlaylistDataStore.h; sourceTree = ""; }; + 8328995527CB51B700D7F028 /* SHA256Digest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SHA256Digest.h; path = ../../Utils/SHA256Digest.h; sourceTree = ""; }; + 8328995627CB51B700D7F028 /* SHA256Digest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SHA256Digest.m; path = ../../Utils/SHA256Digest.m; sourceTree = ""; }; + 8328995927CB51C900D7F028 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 8347C73F2796C58800FA8A7D /* NSFileHandle+CreateFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSFileHandle+CreateFile.h"; path = "../../Utils/NSFileHandle+CreateFile.h"; sourceTree = ""; }; 8347C7402796C58800FA8A7D /* NSFileHandle+CreateFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSFileHandle+CreateFile.m"; path = "../../Utils/NSFileHandle+CreateFile.m"; sourceTree = ""; }; 834FD4EA27AF8F380063BC83 /* AudioChunk.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioChunk.h; sourceTree = ""; }; @@ -190,6 +200,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8328995A27CB51C900D7F028 /* Security.framework in Frameworks */, 83725A9127AA16D50003F694 /* AVFoundation.framework in Frameworks */, 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */, 83725A9027AA16C90003F694 /* Accelerate.framework in Frameworks */, @@ -264,7 +275,7 @@ 839366651815923C006DD712 /* CogPluginMulti.h */, 839366661815923C006DD712 /* CogPluginMulti.m */, 17F94DD30B8D0F7000A34E87 /* PluginController.h */, - 17F94DD40B8D0F7000A34E87 /* PluginController.m */, + 17F94DD40B8D0F7000A34E87 /* PluginController.mm */, 17D21C750B8BE4BA00D1EBDE /* Chain */, 17D21C9B0B8BE4BA00D1EBDE /* Output */, 17D21C9E0B8BE4BA00D1EBDE /* Status.h */, @@ -349,6 +360,10 @@ 17D21CDC0B8BE5B400D1EBDE /* Utils */ = { isa = PBXGroup; children = ( + 8328995527CB51B700D7F028 /* SHA256Digest.h */, + 8328995627CB51B700D7F028 /* SHA256Digest.m */, + 8328995227CB511000D7F028 /* RedundantPlaylistDataStore.h */, + 8328995127CB510F00D7F028 /* RedundantPlaylistDataStore.m */, 835FAC5C27BCA14D00BA8562 /* BadSampleCleaner.h */, 835FAC5D27BCA14D00BA8562 /* BadSampleCleaner.m */, 8399CF2A27B5D1D4008751F1 /* NSDictionary+Merge.h */, @@ -436,6 +451,7 @@ 83725A8F27AA16C90003F694 /* Frameworks */ = { isa = PBXGroup; children = ( + 8328995927CB51C900D7F028 /* Security.framework */, ); name = Frameworks; sourceTree = ""; @@ -470,6 +486,7 @@ 17D21CA70B8BE4BA00D1EBDE /* Node.h in Headers */, 8399CF2C27B5D1D5008751F1 /* NSDictionary+Merge.h in Headers */, 17D21CA90B8BE4BA00D1EBDE /* OutputNode.h in Headers */, + 8328995427CB511000D7F028 /* RedundantPlaylistDataStore.h in Headers */, 17D21CC50B8BE4BA00D1EBDE /* OutputCoreAudio.h in Headers */, 834FD4F427AFA2150063BC83 /* Downmix.h in Headers */, 17D21CC70B8BE4BA00D1EBDE /* Status.h in Headers */, @@ -481,6 +498,7 @@ 834FD4F027AF93680063BC83 /* ChunkList.h in Headers */, 17F94DD50B8D0F7000A34E87 /* PluginController.h in Headers */, 17F94DDD0B8D101100A34E87 /* Plugin.h in Headers */, + 8328995727CB51B700D7F028 /* SHA256Digest.h in Headers */, 834FD4EB27AF8F380063BC83 /* AudioChunk.h in Headers */, 17A2D3C50B8D1D37000778C4 /* AudioDecoder.h in Headers */, 8347C7412796C58800FA8A7D /* NSFileHandle+CreateFile.h in Headers */, @@ -590,12 +608,14 @@ 17D21CF40B8BE5EF00D1EBDE /* Semaphore.m in Sources */, 8347C7422796C58800FA8A7D /* NSFileHandle+CreateFile.m in Sources */, 17D21DC80B8BE79700D1EBDE /* CoreAudioUtils.m in Sources */, + 8328995327CB511000D7F028 /* RedundantPlaylistDataStore.m in Sources */, 8377C64C27B8C51500E8BC0F /* fft_accelerate.c in Sources */, 839366681815923C006DD712 /* CogPluginMulti.m in Sources */, 835C88AA2797D4D400E28EAE /* lpc.c in Sources */, 17D21EBE0B8BF44000D1EBDE /* AudioPlayer.m in Sources */, - 17F94DD60B8D0F7000A34E87 /* PluginController.m in Sources */, + 17F94DD60B8D0F7000A34E87 /* PluginController.mm in Sources */, 17A2D3C60B8D1D37000778C4 /* AudioDecoder.m in Sources */, + 8328995827CB51B700D7F028 /* SHA256Digest.m in Sources */, 17C940240B900909008627D6 /* AudioMetadataReader.m in Sources */, 17B619310B909BC300BC003F /* AudioPropertiesReader.m in Sources */, 17ADB13D0B97926D00257CA2 /* AudioSource.m in Sources */, diff --git a/Audio/PluginController.m b/Audio/PluginController.mm similarity index 82% rename from Audio/PluginController.m rename to Audio/PluginController.mm index 89068d408..1b52df1a4 100644 --- a/Audio/PluginController.m +++ b/Audio/PluginController.mm @@ -8,6 +8,125 @@ #import "NSDictionary+Merge.h" +#import "RedundantPlaylistDataStore.h" + +#import +#import +#import +#import + +struct Cached_Metadata { + std::chrono::steady_clock::time_point time_accessed; + NSDictionary *properties; + NSDictionary *metadata; + Cached_Metadata() + : properties(nil), metadata(nil) { + } +}; + +static std::mutex Cache_Lock; + +static std::map Cache_List; + +static RedundantPlaylistDataStore *Cache_Data_Store = nil; + +static bool Cache_Running = false; + +static std::thread *Cache_Thread = NULL; + +static void cache_run(); + +static void cache_init() { + Cache_Data_Store = [[RedundantPlaylistDataStore alloc] init]; + Cache_Thread = new std::thread(cache_run); +} + +static void cache_deinit() { + Cache_Running = false; + Cache_Thread->join(); + delete Cache_Thread; + Cache_Data_Store = nil; +} + +static void cache_insert_properties(NSURL *url, NSDictionary *properties) { + std::lock_guard lock(Cache_Lock); + + std::string path = [[url absoluteString] UTF8String]; + properties = [Cache_Data_Store coalesceEntryInfo:properties]; + + Cached_Metadata &entry = Cache_List[path]; + + entry.properties = properties; + entry.time_accessed = std::chrono::steady_clock::now(); +} + +static void cache_insert_metadata(NSURL *url, NSDictionary *metadata) { + std::lock_guard lock(Cache_Lock); + + std::string path = [[url absoluteString] UTF8String]; + metadata = [Cache_Data_Store coalesceEntryInfo:metadata]; + + Cached_Metadata &entry = Cache_List[path]; + + entry.metadata = metadata; + entry.time_accessed = std::chrono::steady_clock::now(); +} + +static NSDictionary *cache_access_properties(NSURL *url) { + std::lock_guard lock(Cache_Lock); + + std::string path = [[url absoluteString] UTF8String]; + + Cached_Metadata &entry = Cache_List[path]; + + if(entry.properties) { + entry.time_accessed = std::chrono::steady_clock::now(); + return entry.properties; + } + + return nil; +} + +static NSDictionary *cache_access_metadata(NSURL *url) { + std::lock_guard lock(Cache_Lock); + + std::string path = [[url absoluteString] UTF8String]; + + Cached_Metadata &entry = Cache_List[path]; + + if(entry.metadata) { + entry.time_accessed = std::chrono::steady_clock::now(); + return entry.metadata; + } + + return nil; +} + +static void cache_run() { + std::chrono::milliseconds dura(250); + + while(Cache_Running) { + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + + { + std::lock_guard lock(Cache_Lock); + for(auto it = Cache_List.begin(); it != Cache_List.end();) { + auto elapsed = std::chrono::duration_cast(now - it->second.time_accessed); + if(elapsed.count() >= 10) { + it = Cache_List.erase(it); + continue; + } + ++it; + } + + if(Cache_List.size() == 0) + [Cache_Data_Store reset]; + } + + std::this_thread::sleep_for(dura); + } +} + @implementation PluginController @synthesize sources; @@ -49,11 +168,17 @@ static PluginController *sharedPluginController = nil; self.decodersByMimeType = [[NSMutableDictionary alloc] init]; [self setup]; + + cache_init(); } return self; } +- (void)dealloc { + cache_deinit(); +} + - (void)setup { if(self.configured == NO) { self.configured = YES; @@ -457,6 +582,9 @@ static PluginController *sharedPluginController = nil; [urlScheme isEqualToString:@"https"]) return nil; + NSDictionary *cacheData = cache_access_metadata(url); + if(cacheData) return cacheData; + NSString *ext = [url pathExtension]; NSArray *readers = [metadataReaders objectForKey:[ext lowercaseString]]; NSString *classString; @@ -470,9 +598,13 @@ static PluginController *sharedPluginController = nil; else ++i; } - return [CogMetadataReaderMulti metadataForURL:url readers:_readers]; + cacheData = [CogMetadataReaderMulti metadataForURL:url readers:_readers]; + cache_insert_metadata(url, cacheData); + return cacheData; } - return [CogMetadataReaderMulti metadataForURL:url readers:readers]; + cacheData = [CogMetadataReaderMulti metadataForURL:url readers:readers]; + cache_insert_metadata(url, cacheData); + return cacheData; } else { classString = [readers objectAtIndex:0]; } @@ -482,7 +614,9 @@ static PluginController *sharedPluginController = nil; Class metadataReader = NSClassFromString(classString); - return [metadataReader metadataForURL:url]; + cacheData = [metadataReader metadataForURL:url]; + cache_insert_metadata(url, cacheData); + return cacheData; } // If no properties reader is defined, use the decoder's properties. @@ -493,6 +627,10 @@ static PluginController *sharedPluginController = nil; return nil; NSDictionary *properties = nil; + + properties = cache_access_properties(url); + if(properties) return properties; + NSString *ext = [url pathExtension]; id source = [self audioSourceForURL:url]; @@ -504,8 +642,10 @@ static PluginController *sharedPluginController = nil; if(readers) { if([readers count] > 1) { properties = [CogPropertiesReaderMulti propertiesForSource:source readers:readers]; - if(properties != nil && [properties count]) + if(properties != nil && [properties count]) { + cache_insert_properties(url, properties); return properties; + } } else { classString = [readers objectAtIndex:0]; } @@ -514,8 +654,10 @@ static PluginController *sharedPluginController = nil; if(readers) { if([readers count] > 1) { properties = [CogPropertiesReaderMulti propertiesForSource:source readers:readers]; - if(properties != nil && [properties count]) + if(properties != nil && [properties count]) { + cache_insert_properties(url, properties); return properties; + } } else { classString = [readers objectAtIndex:0]; } @@ -526,8 +668,10 @@ static PluginController *sharedPluginController = nil; Class propertiesReader = NSClassFromString(classString); properties = [propertiesReader propertiesForSource:source]; - if(properties != nil && [properties count]) + if(properties != nil && [properties count]) { + cache_insert_properties(url, properties); return properties; + } } { @@ -541,7 +685,9 @@ static PluginController *sharedPluginController = nil; [decoder close]; - return [NSDictionary dictionaryByMerging:properties with:metadata]; + NSDictionary *cacheData = [NSDictionary dictionaryByMerging:properties with:metadata]; + cache_insert_properties(url, cacheData); + return cacheData; } } diff --git a/Utils/RedundantPlaylistDataStore.h b/Utils/RedundantPlaylistDataStore.h index 2927f7fa5..2b2899e0d 100644 --- a/Utils/RedundantPlaylistDataStore.h +++ b/Utils/RedundantPlaylistDataStore.h @@ -21,6 +21,7 @@ NS_ASSUME_NONNULL_BEGIN - (id)init; - (NSDictionary *)coalesceEntryInfo:(NSDictionary *)pe; +- (void)reset; @end diff --git a/Utils/RedundantPlaylistDataStore.m b/Utils/RedundantPlaylistDataStore.m index f8b96f0ee..19c00d82e 100644 --- a/Utils/RedundantPlaylistDataStore.m +++ b/Utils/RedundantPlaylistDataStore.m @@ -69,4 +69,9 @@ return [NSDictionary dictionaryWithDictionary:ret]; } +- (void)reset { + [stringStore removeAllObjects]; + [artStore removeAllObjects]; +} + @end