How to make sysctl and gettimeofday thread-safe in Swift?

I am trying to sync the ntp time from the server using Kronos library. However, I believe the code is not fully protected from multithreading access since it is using low level system code. So, does anyone know how can I ensure sysctl and gettimeofday are thread-safe when calling them? Or, is there any thread-safe alternative to get the same result?

func currentTime() -> TimeInterval {
    var current = timeval()
    let systemTimeError = gettimeofday(&current, nil) != 0
    assert(!systemTimeError, "system clock error: system time unavailable")

    return Double(current.tv_sec) + Double(current.tv_usec) / 1_000_000
}
static func systemUptime() -> TimeInterval {
        var mib = [CTL_KERN, KERN_BOOTTIME]
        var size = MemoryLayout<timeval>.stride
        var bootTime = timeval()

        let bootTimeError = sysctl(&mib, u_int(mib.count), &bootTime, &size, nil, 0) != 0
        assert(!bootTimeError, "system clock error: kernel boot time unavailable")

        let now = currentTime()
        let uptime = Double(bootTime.tv_sec) + Double(bootTime.tv_usec) / 1_000_000
        assert(now >= uptime, "inconsistent clock state: system time precedes boot time")

        return now - uptime
    }

I have thought of using NSLock but I can only protect from the getter (caller) not the setter (system)

I believe the code is not fully protected from multithreading access since it is using low level system code. So, does anyone know how can I ensure sysctl and gettimeofday are thread-safe when calling them?

I don’t understand your concern. Why exactly do you think these functions are not thread-safe?

@endecotp I couldn't find any official document as proof that they are thread safe, for example like UserDefault: https://developer.apple.com/documentation/foundation/userdefaults. So, I have to assume they are not thread-safe. If they are not thread-safe, my concern is the app might crash and cause bugs that are very hard to fix and find the root cause.

. I couldn't find any official document as proof that they are thread safe

I believe that in the case of functions like gettimeofday that are specified by POSIX, you can probably assume they are thread-safe unless they are documented in the posix spec to the contrary. But maybe someone else can confirm that?

But maybe someone else can confirm that?

That’s right.

There’s a bunch of things to unpack here:

  • The documentation for the thread-safety of our APIs is woefully lacking )-:

  • Even if that weren’t the case, these specific APIs are not covered by Apple documentation [1].

  • Even if that weren’t the case, the concept of thread safety is a muddy one:

    • Some APIs must be called on the main thread. Think NSApplication.
    • Some APIs can be used on any thread, but you must serialise access in some way. Think NSDictionary.
    • Some APIs must be confined to a single thread or queue. The one that always bites me is SecTrustEvaluateAsyncWithError.
    • Some APIs are atomic, that is, they can be called from any thread at any time but they are still subject to races. Your sysctlbyname case is a good example of this because, in the case of a read/write value, you might race with the folks setting that value.
    • Some APIs are thread safe in all meaningful ways. Your gettimeofday example illustrates this.

BSD-level APIs are generally closer to the thread-safe end of this spectrum, although there’s no single place you can go to determine whether a specific API is safe. For example, Posix defines that gettimeofday is thread safe [2], but sysctlbyname is not part of Posix

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Something I discuss in Availability of Low-Level APIs.

[2] Section 2.9.1 Threads says:

All functions defined by this volume of POSIX.1-2017 shall be thread-safe, except that the following functions need not be thread-safe.

and gettimeofday is not in that list.

@eskimo Thanks for the detailed explanation! Since sysctlbyname is not guaranteed to be thread-safe. I have 2 questions:

  1. Is there a way to get the last boot time using sysctlbyname thread-safely in iOS? Ex: Wrapping the caller in lock, serial queue, calling it in main thread only, etc.
  2. Is there any thread-safe alternative to get the last boot time or system up time in iOS?

Since sysctlbyname is not guaranteed to be thread-safe.

Huh? It’s thread-safe. (See below.)

Anyway, what are you actually trying to do? It sounds like you’re trying to get the system boot time without using the functions in the “required reasons” list. I don’t think that’s going to end well.

Quinn wrote:

Some APIs are atomic, that is, they can be called from any thread at any time but they are still subject to races. Your sysctlbyname case is a good example of this because, in the case of a read/write value, you might race with the folks setting that value.

I disagree that his sysctl case is a good example of this, because he’s trying to read the boot time, and nothing is writing that while you’re reading it, is it? Have I missed something?

endecotp wrote:

I disagree that his sysctl case is a good example of this, because he’s trying to read the boot time, and nothing is writing that while you’re reading it, is it?

Somewhat surprisingly, the kernel updates the boot time when you change the clock using settimeofday and related routines. It does this to ensure that the uptime never goes backwards.


YKP wrote:

Since sysctlbyname is not guaranteed to be thread-safe.

I recommend that you not use the words “thread safe” because, as I’ve explained above, those words mean different things in different contexts.

sysctlbyname is atomic, that is, you can call it from any thread and expect to get a correct snapshot of the value. Given that, adding locks, main thread constraints, or whatever won’t help.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

@eskimo @endecotp Sorry for the misunderstanding!

Some APIs are atomic, that is, they can be called from any thread at any time but they are still subject to races.

Is there a way that I can prevent the race condition that you mentioned here?

After some researches, I have found the Objective-C code posted in StackOverFlow that claims to prevent the race condition when getting up time. Link: https://stackoverflow.com/a/40497811

I have converted it to Swift below, is it true that the code below can prevent the race condition?

static func systemUptime() -> TimeInterval {
    var now: TimeInterval
    var beforeNow: Double
    var afterNow = bootTime()
    var i = 0
    repeat {
        assert(i < 10, "systemUpTime loop repeats more than 10 times")
        beforeNow = afterNow
        now = currentTime()
        afterNow = bootTime()
        i += 1
    } while (afterNow != beforeNow)
    
    assert(now >= beforeNow, "inconsistent clock state: system time precedes boot time")

    return now - beforeNow
}

private static func bootTime() -> Double {
    var mib = [CTL_KERN, KERN_BOOTTIME]
    var size = MemoryLayout<timeval>.stride
    var bootTime = timeval()

    let bootTimeError = sysctl(&mib, u_int(mib.count), &bootTime, &size, nil, 0) != 0
    assert(!bootTimeError, "system clock error: kernel boot time unavailable")
    
    return Double(bootTime.tv_sec) + Double(bootTime.tv_usec) / 1_000_000
}

func currentTime() -> TimeInterval {
    var current = timeval()
    let systemTimeError = gettimeofday(&current, nil) != 0
    assert(!systemTimeError, "system clock error: system time unavailable")

    return Double(current.tv_sec) + Double(current.tv_usec) / 1_000_000
}

@eskimo I have also found another code snippet that can get system uptime using another API, clock_gettime(CLOCK_MONOTONIC_RAW, &uptime). Source: https://stackoverflow.com/a/45068046/23930754

Is the code snippet below thread-safe and free from race condition? If compared this code snippet with the code snippet (repeat loop & sysctl) above, which one of them would you recommend me to use based on its reliability and time accuracy given that I am using it to sync time in iOS with offset from the ntp time server?

static func systemUptime() -> TimeInterval {
    var uptime = timespec()
    if 0 != clock_gettime(CLOCK_MONOTONIC_RAW, &uptime) {
        fatalError("Could not execute clock_gettime, errno: \(errno)")
    }
    let uptimeInNanoSeconds = UInt64(uptime.tv_sec * 1_000_000_000) + UInt64(uptime.tv_nsec)
    let uptimeInSeconds = TimeInterval(uptimeInNanoSeconds) / TimeInterval(NSEC_PER_SEC)
    return uptimeInSeconds
}

With regards the your previous post, that’s some convoluted code you’ve got going on here. I see what it’s trying to do, and it’s theoretically useful, but in practice the chances of a time change between the calls to currentTime() and bootTime() are vanishingly small.

With regards your most recent post, using CLOCK_MONOTONIC_RAW is fine for this task. Specifically, you can clock_gettime from arbitrary threads without any additional locking.

There are some things to be aware of here. The first is that your use of CLOCK_MONOTONIC_RAW is not 100% compliant with the docs. The clock_gettime man page says “tracking the time since an arbitrary point”, and there’s no guarantee that this arbitrary point is the boot time. In practice on Apple platforms it is (modulo the next point), but it’s something to keep in mind.

The second point relates to the concept of boot time. What is that time? Is it the time that the CPU starts? Or the time that the boot loader starts? Or the time that the kernel starts? And so on. This is further complicated by various edge cases, for example, our systems can do a user space restart without actually restarting the kernel.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

@eskimo

With regards the your previous post, that’s some convoluted code you’ve got going on here. I see what it’s trying to do, and it’s theoretically useful, but in practice the chances of a time change between the calls to currentTime() and bootTime() are vanishingly small.

I understand the chances of it happening is small but just trying to prevent any unexpected bugs happening later on. If there is no problem with this implementation, then I am going to use it instead of clock_gettime & CLOCK_MONOTONIC_RAW since it may not return the correct uptime since the device is rebooted.

There are some things to be aware of here. The first is that your use of CLOCK_MONOTONIC_RAW is not 100% compliant with the docs. The clock_gettime man page says “tracking the time since an arbitrary point”, and there’s no guarantee that this arbitrary point is the boot time. In practice on Apple platforms it is (modulo the next point), but it’s something to keep in mind.

To confirm my understanding, you are saying the arbitrary point in CLOCK_MONOTONIC_RAW may not be the boot time, right? Therefore, the uptime that it returns may not be accurate?

The second point relates to the concept of boot time. What is that time? Is it the time that the CPU starts? Or the time that the boot loader starts? Or the time that the kernel starts? And so on. This is further complicated by various edge cases, for example, our systems can do a user space restart without actually restarting the kernel.

What I want to get is the time when the iOS device last rebooted

What I want to get is the time when the iOS device last rebooted

To what end? What do you plan to do with that info?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

just trying to prevent any unexpected bugs happening later on.

Don't waste your time on this; I bet you have other code that is a million times more likely to fail.

Thanks! I think you have answered and solved my problem. We can conclude the thread here. @eskimo

How to make sysctl and gettimeofday thread-safe in Swift?
 
 
Q