Skip to content

Commit be63e10

Browse files
authored
Fix for a race condition in the usage of URLCache (#4821)
1 parent ad5b05d commit be63e10

4 files changed

Lines changed: 59 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010

1111
* Added `RouteControllerNotificationUserInfoKey.shouldPlayRerouteSoundKey` to the user info dictionary of `Notification.Name.routeControllerDidReroute` notification. ([#4822](https://github.com/mapbox/mapbox-navigation-ios/pull/4822))
1212
* Fixed a bug with excessive `VisualInstructionDelegate.label(_:willPresent:as:)` delegate method call during initialization.
13+
* Fixed a randomly occuring race condition related to the usage of `URLCache` that could cause a crash.
1314

1415
## v2.20.4
1516

1617
### Other changes
1718

1819
* Reroute notification sound now plays only after a new route is successfully retrieved.
1920
* Fixed a bug with excessive `VisualInstructionDelegate.label(_:willPresent:as:)` delegate method call during initialization.
21+
* Fixed a randomly occuring race condition related to the usage of `URLCache` that could cause a crash.
2022

2123
## v2.20.3
2224

Sources/MapboxNavigation/Cache.swift

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,29 @@ protocol URLCaching {
3838
A general purpose URLCache used by `SpriteRepository` implementations.
3939
*/
4040
internal class URLDataCache: URLCaching {
41-
let defaultDiskCacheURL: URL = {
41+
private static var defaultDiskCacheURL: URL {
4242
let fileManager = FileManager.default
4343
let basePath = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
4444
let identifier = Bundle.mapboxNavigation.bundleIdentifier!
4545
return basePath.appendingPathComponent(identifier).appendingPathComponent("URLDataCache")
46-
}()
46+
}
47+
48+
private let urlCache: URLCache
49+
private static let defaultCapacity = 5 * 1024 * 1024
50+
private let cacheLock = NSLock()
51+
52+
var currentMemoryUsage: Int {
53+
urlCache.currentMemoryUsage
54+
}
4755

48-
let urlCache: URLCache
49-
let defaultCapacity = 5 * 1024 * 1024
56+
var currentDiskUsage: Int {
57+
urlCache.currentDiskUsage
58+
}
5059

5160
init(memoryCapacity: Int? = nil, diskCapacity: Int? = nil, diskCacheURL: URL? = nil) {
52-
let memoryCapacity = memoryCapacity ?? defaultCapacity
53-
let diskCapacity = diskCapacity ?? defaultCapacity
54-
let diskCacheURL = diskCacheURL ?? defaultDiskCacheURL
61+
let memoryCapacity = memoryCapacity ?? Self.defaultCapacity
62+
let diskCapacity = diskCapacity ?? Self.defaultCapacity
63+
let diskCacheURL = diskCacheURL ?? Self.defaultDiskCacheURL
5564
if #available(iOS 13.0, *) {
5665
urlCache = URLCache(memoryCapacity: memoryCapacity, diskCapacity: diskCapacity, directory: diskCacheURL)
5766
} else {
@@ -60,18 +69,34 @@ internal class URLDataCache: URLCaching {
6069
}
6170

6271
func store(_ cachedResponse: CachedURLResponse, for url: URL) {
72+
cacheLock.lock()
73+
defer {
74+
cacheLock.unlock()
75+
}
6376
urlCache.storeCachedResponse(cachedResponse, for: URLRequest(url))
6477
}
6578

6679
func response(for url: URL) -> CachedURLResponse? {
80+
cacheLock.lock()
81+
defer {
82+
cacheLock.unlock()
83+
}
6784
return urlCache.cachedResponse(for: URLRequest(url))
6885
}
6986

7087
func clearCache() {
88+
cacheLock.lock()
89+
defer {
90+
cacheLock.unlock()
91+
}
7192
urlCache.removeAllCachedResponses()
7293
}
7394

7495
func removeCache(for url: URL) {
96+
cacheLock.lock()
97+
defer {
98+
cacheLock.unlock()
99+
}
75100
urlCache.removeCachedResponse(for: URLRequest(url))
76101
}
77102
}

Sources/MapboxNavigation/NavigationViewController.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -867,10 +867,6 @@ open class NavigationViewController: UIViewController, NavigationStatusPresenter
867867
styleManager = StyleManager()
868868
styleManager.delegate = self
869869
styleManager.styles = navigationOptions?.styles ?? [DayStyle(), NightStyle()]
870-
871-
if let currentStyle = styleManager.currentStyle {
872-
updateMapStyle(currentStyle)
873-
}
874870
}
875871

876872
var currentStatusBarStyle: UIStatusBarStyle = .default

Tests/MapboxNavigationTests/URLDataCacheTests.swift

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,25 @@ import TestHelper
55
class URLDataCacheTest: TestCase {
66
let url = ShieldImage.i280.baseURL
77
var cache: URLDataCache!
8+
9+
private static var cacheURL: URL {
10+
let fileManager = FileManager.default
11+
let basePath = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
12+
let identifier = Bundle.main.bundleIdentifier!
13+
return basePath.appendingPathComponent(identifier).appendingPathComponent("TestURLDataCache")
14+
}
815

916
override func setUp() {
1017
super.setUp()
1118

1219
self.continueAfterFailure = false
13-
cache = URLDataCache()
14-
cache.urlCache.diskCapacity = 0
20+
cache = URLDataCache(diskCapacity: 0, diskCacheURL: Self.cacheURL)
21+
cache.clearCache()
22+
}
23+
24+
override func tearDown() {
25+
cache.clearCache()
26+
super.tearDown()
1527
}
1628

1729
private func exampleResponse(with storagePolicy: URLCache.StoragePolicy) -> CachedURLResponse {
@@ -39,7 +51,7 @@ class URLDataCacheTest: TestCase {
3951

4052
cache.clearCache()
4153
XCTAssertNil(cache.response(for: url)?.data)
42-
XCTAssertEqual(cache.urlCache.currentMemoryUsage, 0)
54+
XCTAssertEqual(cache.currentMemoryUsage, 0)
4355
}
4456

4557
func testRemoveRequestCache() {
@@ -59,7 +71,7 @@ class URLDataCacheTest: TestCase {
5971

6072
let cachedResponse = cache.response(for: url)
6173
XCTAssertEqual(cachedResponse, response)
62-
XCTAssertEqual(cache.urlCache.currentMemoryUsage, response.data.count)
74+
XCTAssertEqual(cache.currentMemoryUsage, response.data.count)
6375
}
6476

6577
func testStoreCacheWithMemoryWarning() {
@@ -76,15 +88,19 @@ class URLDataCacheTest: TestCase {
7688
let response = exampleResponse(with: .allowedInMemoryOnly)
7789

7890
let limitCapacity = 1
79-
let limitCache = URLDataCache(memoryCapacity: limitCapacity, diskCapacity: limitCapacity)
8091
XCTAssertTrue(response.data.count > limitCapacity)
8192

93+
let limitCache = URLDataCache(
94+
memoryCapacity: limitCapacity,
95+
diskCapacity: limitCapacity,
96+
diskCacheURL: Self.cacheURL
97+
)
98+
limitCache.clearCache()
99+
82100
limitCache.store(response, for: url)
83101
XCTAssertNil(cache.response(for: url))
84-
XCTAssertEqual(limitCache.urlCache.currentMemoryUsage, 0)
85-
86-
limitCache.urlCache.diskCapacity = 0
87-
limitCache.clearCache()
102+
XCTAssertEqual(limitCache.currentMemoryUsage, 0)
103+
// not checking disk usage as it is always non-zero
88104
}
89105
}
90106

0 commit comments

Comments
 (0)