File: Calendar_Cache.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (143 lines) | stat: -rw-r--r-- 5,804 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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//===----------------------------------------------------------------------===//
//
// 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 {
    // MARK: - State
    
    struct State : Sendable {
        // If nil, the calendar has been invalidated and will be created next time State.current() is called
        private var currentCalendar: (any _CalendarProtocol)?
        private var autoupdatingCurrentCalendar: _CalendarAutoupdating?
        private var fixedCalendars: [Calendar.Identifier: any _CalendarProtocol] = [:]

        private var noteCount = -1
        private var wasResetManually = false
                
        mutating func check() {
#if FOUNDATION_FRAMEWORK
            // On Darwin we listen for certain distributed notifications to reset the current Calendar.
            let newNoteCount = _CFLocaleGetNoteCount() + _CFTimeZoneGetNoteCount() + Int(_CFCalendarGetMidnightNoteCount())
#else
            let newNoteCount = 1
#endif
            if newNoteCount != noteCount || wasResetManually {
                // 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
                currentCalendar = nil
                fixedCalendars = [:]

                noteCount = newNoteCount
                wasResetManually = false
            }
        }

        mutating func current() -> any _CalendarProtocol {
            check()
            if let currentCalendar {
                return currentCalendar
            } else {
                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)
                currentCalendar = calendar
                return calendar
            }
        }
        
        mutating func autoupdatingCurrent() -> any _CalendarProtocol {
            if let autoupdatingCurrentCalendar {
                return autoupdatingCurrentCalendar
            } else {
                let calendar = _CalendarAutoupdating()
                autoupdatingCurrentCalendar = calendar
                return calendar
            }
        }

        mutating func fixed(_ id: Calendar.Identifier) -> any _CalendarProtocol {
            check()
            if let cached = fixedCalendars[id] {
                return cached
            } else {
                // 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)
                fixedCalendars[id] = new
                return new
            }
        }

        mutating func reset() {
            wasResetManually = true
        }
    }

    let lock: LockedState<State>

    static let cache = CalendarCache()

    fileprivate init() {
        lock = LockedState(initialState: State())
    }

    func reset() {
        lock.withLock { $0.reset() }
    }

    var current: any _CalendarProtocol {
        lock.withLock { $0.current() }
    }
    
    var autoupdatingCurrent: any _CalendarProtocol {
        lock.withLock { $0.autoupdatingCurrent() }
    }
    
    func fixed(_ id: Calendar.Identifier) -> any _CalendarProtocol {
        lock.withLock { $0.fixed(id) }
    }
    
    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)
    }

}