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