File: Calendar_Cache.swift

package info (click to toggle)
swiftlang 6.1.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,791,532 kB
  • sloc: cpp: 9,901,743; ansic: 2,201,431; asm: 1,091,827; python: 308,252; objc: 82,166; f90: 80,126; lisp: 38,358; pascal: 25,559; sh: 20,429; ml: 5,058; perl: 4,745; makefile: 4,484; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (113 lines) | stat: -rw-r--r-- 4,636 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#if FOUNDATION_FRAMEWORK
internal import _ForSwiftFoundation
import CoreFoundation
#endif

#if FOUNDATION_FRAMEWORK && canImport(_FoundationICU)
internal func _calendarICUClass() -> _CalendarProtocol.Type? {
    _CalendarICU.self
}
#else
dynamic package func _calendarICUClass() -> _CalendarProtocol.Type? {
    nil
}
#endif

func _calendarClass(identifier: Calendar.Identifier, useGregorian: Bool) -> _CalendarProtocol.Type? {
    if useGregorian && identifier == .gregorian {
        return _CalendarGregorian.self
    } else {
        return _calendarICUClass()
    }
}

/// Singleton which listens for notifications about preference changes for Calendar and holds cached singletons for the current locale, calendar, and time zone.
struct CalendarCache : Sendable, ~Copyable {
    // MARK: - State
    
    static let cache = CalendarCache()
    
    // The values stored in these two locks do not depend upon each other, so it is safe to access them with separate locks. This helps avoids contention on a single lock.
    
    private let _current = LockedState<(any _CalendarProtocol)?>(initialState: nil)
    private let _fixed = LockedState<[Calendar.Identifier: any _CalendarProtocol]>(initialState: [:])
    
    fileprivate init() {
    }
    
    var current: any _CalendarProtocol {
        if let result = _current.withLock({ $0 }) {
            return result
        }
                        
        let id = Locale.current._calendarIdentifier
        // If we cannot create the right kind of class, we fail immediately here
        let calendarClass = _calendarClass(identifier: id, useGregorian: true)!
        let calendar = calendarClass.init(identifier: id, timeZone: nil, locale: Locale.current, firstWeekday: nil, minimumDaysInFirstWeek: nil, gregorianStartDate: nil)
        
        return _current.withLock {
            if let current = $0 {
                // Someone beat us to setting it - use the existing one
                return current
            } else {
                $0 = calendar
                return calendar
            }
        }
    }
    
    func reset() {
        // rdar://102017659
        // Don't create `currentCalendar` here to avoid deadlocking when retrieving a fixed
        // calendar. Creating the current calendar gets the current locale, decodes a plist
        // from CFPreferences, and may call +[NSDate initialize] on a separate thread. This
        // leads to a deadlock if we are also initializing a class on the current thread
        _current.withLock { $0 = nil }
        _fixed.withLock { $0 = [:] }
    }
    
    // MARK: Singletons
    
    static let autoupdatingCurrent = _CalendarAutoupdating()
    
    // MARK: -
    
    func fixed(_ id: Calendar.Identifier) -> any _CalendarProtocol {
        if let existing = _fixed.withLock({ $0[id] }) {
            return existing
        }
        
        // If we cannot create the right kind of class, we fail immediately here
        let calendarClass = _calendarClass(identifier: id, useGregorian: true)!
        let new = calendarClass.init(identifier: id, timeZone: nil, locale: nil, firstWeekday: nil, minimumDaysInFirstWeek: nil, gregorianStartDate: nil)
        
        return _fixed.withLock {
            if let existing = $0[id] {
                return existing
            } else {
                $0[id] = new
                return new
            }
        }
    }
    
    func fixed(identifier: Calendar.Identifier, locale: Locale?, timeZone: TimeZone?, firstWeekday: Int?, minimumDaysInFirstWeek: Int?, gregorianStartDate: Date?) -> any _CalendarProtocol {
        // Note: Only the ObjC NSCalendar initWithCoder supports gregorian start date values. For Swift it is always nil.
        // If we cannot create the right kind of class, we fail immediately here
        let calendarClass = _calendarClass(identifier: identifier, useGregorian: true)!
        return calendarClass.init(identifier: identifier, timeZone: timeZone, locale: locale, firstWeekday: firstWeekday, minimumDaysInFirstWeek: minimumDaysInFirstWeek, gregorianStartDate: gregorianStartDate)
    }

}