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)
}
}
|