Skip to content

Commit e675b31

Browse files
phauslerlorentey
andauthored
[SE-0329] Clock/Instant/Duration (#40609)
* [WIP] Initial draft at v2 Clock/Instant/Duration * Ensure the literal types for _DoubleWide are able to be at least 64 bits on 32 bit platforms * static cast timespec members to long * Remove runtime exports from clock functions * Export clock functions in implementations as they are in headers * Clean up internal properties by adding leading underscores, refine availability to a TBD marker macro, and break at 80 lines to match style * Shift operators to concrete Instant types to avoid complexity in solver resolution * Adjust diagnostic note and error expectation of ambiguities to reflect new potential solver (perhaps incorrect) solutions * Update stdlib/public/Concurrency/TaskSleep.swift Co-authored-by: Karoy Lorentey <klorentey@apple.com> * [stdlib][NFC] Remove trailing whitespace * [stdlib] Remove _DoubleWidth from stdlib's ABI * [stdlib] Strip downd _DoubleWidth to _[U]Int128 * Additional adjustments to diagnostic notes and errors expectation of ambiguities to reflect new potential solver (perhaps incorrect) solutions * Disable type checker performance validation for operator overload inferences (rdar://33958047) * Decorate Duration, DurationProtocol, Instant and clocks with @available(SwiftStdlib 9999, *) * Restore diagnostic ambiguity test assertion (due to availability) * Add a rough attempt at implementing time accessors on win32 * Remove unused clock id, rename SPI for swift clock ids and correct a few more missing availabilities * remove obsolete case of realtime clock for dispatch after callout * Use the default implementation of ~ for Int128 and UInt128 * Ensure diagnostic ambiguitiy applies evenly to all platforms and their resolved types * Restore the simd vector build modifications (merge damage) * Update to latest naming results for Instant.Duration * Updates to latest proposal initializers and accessors and adjust encoding/decoding to string based serialization * Update availability for Clock/Instant/Duration methods and types to be 5.7 * Correct *Clock.now to report via the correct runtime API * Ensure the hashing of Duration is based upon the attoseconds hashing * Avoid string based encoding and resort back to high and low bit encoding/decoding but as unkeyed * Adjust naming of component initializer to use suffixes on parameters * Duration decoding should use a mutable container for decoding * fix up components initializer and decode access * Add platform base initializers for timespec and tiemval to and from Duration * Add some first draft documentation for standard library types Duration, DurationProtocol and InstantProtocol * Another round of documentation prose and some drive-by availability fixes * InstantProtocol availability should be 5.7 * Correct linux timeval creation to be Int and not Int32 Co-authored-by: Karoy Lorentey <klorentey@apple.com>
1 parent e9d2752 commit e675b31

22 files changed

+2128
-8
lines changed

include/swift/Runtime/Concurrency.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,10 @@ using JobDelay = unsigned long long;
665665
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
666666
void swift_task_enqueueGlobalWithDelay(JobDelay delay, Job *job);
667667

668+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
669+
void swift_task_enqueueGlobalWithDeadline(long long sec, long long nsec,
670+
long long tsec, long long tnsec, int clock, Job *job);
671+
668672
/// Enqueue the given job on the main executor.
669673
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
670674
void swift_task_enqueueMainExecutor(Job *job);
@@ -691,6 +695,21 @@ SWIFT_CC(swift) void (*swift_task_enqueueGlobalWithDelay_hook)(
691695
unsigned long long delay, Job *job,
692696
swift_task_enqueueGlobalWithDelay_original original);
693697

698+
typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobalWithDeadline_original)(
699+
long long sec,
700+
long long nsec,
701+
long long tsec,
702+
long long tnsec,
703+
int clock, Job *job);
704+
SWIFT_EXPORT_FROM(swift_Concurrency)
705+
SWIFT_CC(swift) void (*swift_task_enqueueGlobalWithDeadline_hook)(
706+
long long sec,
707+
long long nsec,
708+
long long tsec,
709+
long long tnsec,
710+
int clock, Job *job,
711+
swift_task_enqueueGlobalWithDeadline_original original);
712+
694713
/// A hook to take over main executor enqueueing.
695714
typedef SWIFT_CC(swift) void (*swift_task_enqueueMainExecutor_original)(
696715
Job *job);
@@ -825,6 +844,21 @@ void swift_task_donateThreadToGlobalExecutorUntil(bool (*condition)(void*),
825844

826845
#endif
827846

847+
enum swift_clock_id : int {
848+
swift_clock_id_continuous = 1,
849+
swift_clock_id_suspending = 2
850+
};
851+
852+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
853+
void swift_get_time(long long *seconds,
854+
long long *nanoseconds,
855+
swift_clock_id clock_id);
856+
857+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
858+
void swift_get_clock_res(long long *seconds,
859+
long long *nanoseconds,
860+
swift_clock_id clock_id);
861+
828862
#ifdef __APPLE__
829863
/// A magic symbol whose address is the mask to apply to a frame pointer to
830864
/// signal that it is an async frame. Do not try to read the actual value of

stdlib/public/Concurrency/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I
113113
AsyncThrowingStream.swift
114114
AsyncStream.cpp
115115
Deque.swift
116+
Clock.cpp
117+
Clock.swift
118+
ContinuousClock.swift
119+
SuspendingClock.swift
116120
${swift_concurrency_extra_sources}
117121
linker-support/magic-symbols-for-install-name.c
118122

stdlib/public/Concurrency/Clock.cpp

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//===--- Clock.cpp - Time and clock resolution ----------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "swift/Runtime/Concurrency.h"
14+
15+
#if __has_include(<time.h>)
16+
#define HAS_TIME 1
17+
#include <time.h>
18+
#endif
19+
#if defined(_WIN32)
20+
#define WIN32_LEAN_AND_MEAN
21+
#define NOMINMAX
22+
#include <Windows.h>
23+
#endif
24+
25+
using namespace swift;
26+
27+
SWIFT_EXPORT_FROM(swift_Concurrency)
28+
SWIFT_CC(swift)
29+
void swift_get_time(
30+
long long *seconds,
31+
long long *nanoseconds,
32+
swift_clock_id clock_id) {
33+
switch (clock_id) {
34+
case swift_clock_id_continuous: {
35+
#if defined(__linux__) && HAS_TIME
36+
struct timespec continuous;
37+
clock_gettime(CLOCK_BOOTTIME, &continuous);
38+
*seconds = continuous.tv_sec;
39+
*nanoseconds = continuous.tv_nsec;
40+
#elif defined(__APPLE__) && HAS_TIME
41+
struct timespec continuous;
42+
clock_gettime(CLOCK_MONOTONIC, &continuous);
43+
*seconds = continuous.tv_sec;
44+
*nanoseconds = continuous.tv_nsec;
45+
#elif defined(_WIN32)
46+
LARGE_INTEGER freq;
47+
QueryPerformanceFrequency(&freq);
48+
LARGE_INTEGER count;
49+
QueryPerformanceCounter(&count);
50+
*seconds = count.QuadPart / freq.QuadPart;
51+
if (freq.QuadPart < 1000000000) {
52+
*nanoseconds =
53+
((count.QuadPart % freq.QuadPart) * 1000000000) / freq.QuadPart;
54+
} else {
55+
*nanoseconds =
56+
(count.QuadPart % freq.QuadPart) * (1000000000.0 / freq.QuadPart);
57+
}
58+
#else
59+
#error Missing platform continuous time definition
60+
#endif
61+
break;
62+
}
63+
case swift_clock_id_suspending: {
64+
#if defined(__linux__) && HAS_TIME
65+
struct timespec suspending;
66+
clock_gettime(CLOCK_MONOTONIC_RAW, &suspending);
67+
*seconds = suspending.tv_sec;
68+
*nanoseconds = suspending.tv_nsec;
69+
#elif defined(__APPLE__) && HAS_TIME
70+
struct timespec suspending;
71+
clock_gettime(CLOCK_UPTIME_RAW, &suspending);
72+
*seconds = suspending.tv_sec;
73+
*nanoseconds = suspending.tv_nsec;
74+
#elif defined(_WIN32)
75+
LARGE_INTEGER freq;
76+
QueryPerformanceFrequency(&freq);
77+
LARGE_INTEGER count;
78+
QueryPerformanceCounter(&count);
79+
*seconds = count.QuadPart / freq.QuadPart;
80+
if (freq.QuadPart < 1000000000) {
81+
*nanoseconds =
82+
((count.QuadPart % freq.QuadPart) * 1000000000) / freq.QuadPart;
83+
} else {
84+
*nanoseconds =
85+
(count.QuadPart % freq.QuadPart) * (1000000000.0 / freq.QuadPart);
86+
}
87+
#else
88+
#error Missing platform suspending time definition
89+
#endif
90+
break;
91+
}
92+
}
93+
}
94+
95+
SWIFT_EXPORT_FROM(swift_Concurrency)
96+
SWIFT_CC(swift)
97+
void swift_get_clock_res(
98+
long long *seconds,
99+
long long *nanoseconds,
100+
swift_clock_id clock_id) {
101+
switch (clock_id) {
102+
case swift_clock_id_continuous: {
103+
#if defined(__linux__) && HAS_TIME
104+
struct timespec continuous;
105+
clock_getres(CLOCK_BOOTTIME, &continuous);
106+
*seconds = continuous.tv_sec;
107+
*nanoseconds = continuous.tv_nsec;
108+
#elif defined(__APPLE__) && HAS_TIME
109+
struct timespec continuous;
110+
clock_getres(CLOCK_MONOTONIC, &continuous);
111+
*seconds = continuous.tv_sec;
112+
*nanoseconds = continuous.tv_nsec;
113+
#elif defined(_WIN32)
114+
*seconds = 0;
115+
*nanoseconds = 1000;
116+
#else
117+
#error Missing platform continuous time definition
118+
#endif
119+
break;
120+
}
121+
case swift_clock_id_suspending: {
122+
struct timespec suspending;
123+
#if defined(__linux__) && HAS_TIME
124+
clock_getres(CLOCK_MONOTONIC_RAW, &suspending);
125+
*seconds = suspending.tv_sec;
126+
*nanoseconds = suspending.tv_nsec;
127+
#elif defined(__APPLE__) && HAS_TIME
128+
clock_gettime(CLOCK_UPTIME_RAW, &suspending);
129+
*seconds = suspending.tv_sec;
130+
*nanoseconds = suspending.tv_nsec;
131+
#elif defined(_WIN32)
132+
*seconds = 0;
133+
*nanoseconds = 1000;
134+
#else
135+
#error Missing platform suspending time definition
136+
#endif
137+
break;
138+
}
139+
}
140+
}

stdlib/public/Concurrency/Clock.swift

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
import Swift
13+
14+
/// A mechanism in which to measure time, and delay work until a given point
15+
/// in time.
16+
///
17+
/// Types that conform to the `Clock` protocol define a concept of "now" which
18+
/// is the specific instant in time that property is accessed. Any pair of calls
19+
/// to the `now` property may have a minimum duration between them - this
20+
/// minimum resolution is exposed by the `minimumResolution` property to inform
21+
/// any user of the type the expected granularity of accuracy.
22+
///
23+
/// One of the primary uses for clocks is to schedule task sleeping. This method
24+
/// resumes the calling task after a given deadline has been met or passed with
25+
/// a given tolerance value. The tolerance is expected as a leeway around the
26+
/// deadline. The clock may reschedule tasks within the tolerance to ensure
27+
/// efficient execution of resumptions by reducing potential operating system
28+
/// wake-ups. If no tolerance is specified (i.e. nil is passed in) the sleep
29+
/// function is expected to schedule with a default tolerance strategy.
30+
///
31+
/// For more information about specific clocks see `ContinuousClock` and
32+
/// `SuspendingClock`.
33+
@available(SwiftStdlib 5.7, *)
34+
public protocol Clock: Sendable {
35+
associatedtype Instant: InstantProtocol
36+
37+
var now: Instant { get }
38+
var minimumResolution: Instant.Duration { get }
39+
40+
func sleep(until deadline: Instant, tolerance: Instant.Duration?) async throws
41+
}
42+
43+
44+
@available(SwiftStdlib 5.7, *)
45+
extension Clock {
46+
/// Measure the elapsed time to execute a closure.
47+
///
48+
/// let clock = ContinuousClock()
49+
/// let elapsed = clock.measure {
50+
/// someWork()
51+
/// }
52+
@available(SwiftStdlib 5.7, *)
53+
public func measure(_ work: () throws -> Void) rethrows -> Instant.Duration {
54+
let start = now
55+
try work()
56+
let end = now
57+
return start.duration(to: end)
58+
}
59+
60+
/// Measure the elapsed time to execute an asynchronous closure.
61+
///
62+
/// let clock = ContinuousClock()
63+
/// let elapsed = await clock.measure {
64+
/// await someWork()
65+
/// }
66+
@available(SwiftStdlib 5.7, *)
67+
public func measure(
68+
_ work: () async throws -> Void
69+
) async rethrows -> Instant.Duration {
70+
let start = now
71+
try await work()
72+
let end = now
73+
return start.duration(to: end)
74+
}
75+
}
76+
77+
@available(SwiftStdlib 5.7, *)
78+
@usableFromInline
79+
enum _ClockID: Int32 {
80+
case continuous = 1
81+
case suspending = 2
82+
}
83+
84+
@available(SwiftStdlib 5.7, *)
85+
@_silgen_name("swift_get_time")
86+
@usableFromInline
87+
internal func _getTime(
88+
seconds: UnsafeMutablePointer<Int64>,
89+
nanoseconds: UnsafeMutablePointer<Int64>,
90+
clock: _ClockID)
91+
92+
@available(SwiftStdlib 5.7, *)
93+
@_silgen_name("swift_get_clock_res")
94+
@usableFromInline
95+
internal func _getClockRes(
96+
seconds: UnsafeMutablePointer<Int64>,
97+
nanoseconds: UnsafeMutablePointer<Int64>,
98+
clock: _ClockID)

0 commit comments

Comments
 (0)