Skip to content

Calendar's CoW is broken. #4630

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
sidepelican opened this issue Sep 1, 2022 · 4 comments
Closed

Calendar's CoW is broken. #4630

sidepelican opened this issue Sep 1, 2022 · 4 comments
Assignees

Comments

@sidepelican
Copy link

Problem

Calendar's some functions is not guarded with CoW.

Calendar wraps NSCalendar and it uses _applyMutation for any mutating actions.
but next two functions on NSCalendar mutates itself in the function. This behavior slips through the CoW.

https://github.com/apple/swift-corelibs-foundation/blob/d248649df1fd102269c0ba312220a1922b4f8bec/Sources/Foundation/NSCalendar.swift#L490-L509

https://github.com/apple/swift-corelibs-foundation/blob/d248649df1fd102269c0ba312220a1922b4f8bec/Sources/Foundation/NSCalendar.swift#L756-L762

When we use these functions in another thread using copied Calendar, may cause unexpected behavior.

How to reproduce

Run this code in Linux environemnt.
Sometimes prints "bug!" or sometimes crashes.

import Foundation

func main() {
    var calendar = Calendar(identifier: .gregorian)
    calendar.timeZone = TimeZone(secondsFromGMT: 9 * 3600)!

    let now = Date()
    let expected: Int = calendar.component(.hour, from: now)

    f(calendar) // Copy the calendar here and use it in backgrount thread.

    for _ in 0...100_000 {
        let v = calendar.component(.hour, from: now)
        if v != expected {
            print("bug! expected=\(expected), actual=\(v)")
        }
    }
    print("end")
}

func f(_ calendar: Calendar) {
    let comps = DateComponents(
        timeZone: TimeZone(secondsFromGMT: 0)!,
        year: 2022, month: 9, day: 1
    )

    DispatchQueue.global().async {
        for _ in 0...100_000 {
            _ = calendar.date(from: comps)
        }
    }
}

main()

Expected

Calendar's CoW works right.
I know Calendar is not thread safe unlike the Apple platform. This issue focuses abount CoW.

@parkera
Copy link
Contributor

parkera commented Sep 1, 2022

You're right about this. Part of the issue is that ICU's ucal is itself not thread safe, so it needs to be wrapped with a lock at a higher level.

@parkera parkera self-assigned this Sep 1, 2022
@parkera
Copy link
Contributor

parkera commented Jul 10, 2024

This will be resolved with the entirely new Calendar implementations in #5001

@sidepelican
Copy link
Author

I have confirmed that the new implementation does not cause this problems (Swift 6.0.3)

@parkera
Copy link
Contributor

parkera commented Dec 18, 2024

Thank you for checking!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants