Thanks! I think you have answered and solved my problem. We can conclude the thread here. @eskimo
Post
Replies
Boosts
Views
Activity
@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
@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
}
@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(¤t, nil) != 0
assert(!systemTimeError, "system clock error: system time unavailable")
return Double(current.tv_sec) + Double(current.tv_usec) / 1_000_000
}
@eskimo Thanks for the detailed explanation! Since sysctlbyname is not guaranteed to be thread-safe. I have 2 questions:
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.
Is there any thread-safe alternative to get the last boot time or system up time in iOS?
@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.