-
Notifications
You must be signed in to change notification settings - Fork 10.6k
[WIP, DNM] Track how much time a task spends scheduled. #85251
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
base: main
Are you sure you want to change the base?
Changes from all commits
33c2dac
690e7d2
f665660
bc619bc
2a90b8c
50cb7b4
f4c6dee
c00e0e2
1c365bc
83336de
633094e
026bf57
1e9f823
f92dc6a
cec0464
6b0be10
9bb6b99
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -454,6 +454,18 @@ class TaskDependencyStatusRecord : public TaskStatusRecord { | |
| JobPriority oldPriority, JobPriority newPriority); | ||
| }; | ||
|
|
||
| class TimeSpentRunningStatusRecord : public TaskStatusRecord { | ||
| public: | ||
| TimeSpentRunningStatusRecord() | ||
| : TaskStatusRecord(TaskStatusRecordKind::TimeSpentRunning) {} | ||
|
|
||
| uint64_t TimeSpentRunning = 0; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My gut tells me we will want to lower the storage for this value into |
||
|
|
||
| static bool classof(const TaskStatusRecord *record) { | ||
| return record->getKind() == TaskStatusRecordKind::TimeSpentRunning; | ||
| } | ||
| }; | ||
|
|
||
| } // end namespace swift | ||
|
|
||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1072,6 +1072,20 @@ SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) | |
| void swift_task_donateThreadToGlobalExecutorUntil(bool (*condition)(void*), | ||
| void *context); | ||
|
|
||
| /// Set whether or not the concurrency library is tracking the time spent | ||
| /// running tasks. Returns the old value. | ||
| SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) | ||
| __attribute__((__cold__)) bool | ||
| _swift_task_setTimeSpentRunningTracked(bool isTracked); | ||
|
|
||
| /// Get the duration spent running the given task (so far) in nanoseconds. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: indentation
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. VS Code is not my friend, it seems. |
||
| /// | ||
| /// If `AsyncTask::isTimeSpentRunningTracked()` is `false` (the common case), | ||
| /// task duration isn't tracked and this function returns `false`. | ||
| SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) | ||
| __attribute__((__cold__)) bool | ||
| _swift_task_getTimeSpentRunning(AsyncTask *task, uint64_t *outNanoseconds); | ||
|
|
||
| enum swift_clock_id : int { | ||
| swift_clock_id_continuous = 1, | ||
| swift_clock_id_suspending = 2, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -646,6 +646,18 @@ const void *AsyncTask::getResumeFunctionForLogging(bool isStarting) { | |
| return __ptrauth_swift_runtime_function_entry_strip(result); | ||
| } | ||
|
|
||
| std::atomic<bool> AsyncTask::_isTimeSpentRunningTracked { false }; | ||
|
|
||
| uint64_t AsyncTask::getNanosecondsOnSuspendingClock(void) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is fundamentally the same as |
||
| long long seconds = 0; | ||
| long long nanoseconds = 0; | ||
| swift_get_time(&seconds, &nanoseconds, swift_clock_id_suspending); | ||
|
|
||
| uint64_t result = static_cast<uint64_t>(seconds) * UINT64_C(1'000'000'000); | ||
| result += static_cast<uint64_t>(nanoseconds); | ||
| return result; | ||
| } | ||
|
|
||
| JobPriority swift::swift_task_currentPriority(AsyncTask *task) { | ||
| // This is racey but this is to be used in an API is inherently racey anyways. | ||
| auto oldStatus = task->_private()._status().load(std::memory_order_relaxed); | ||
|
|
@@ -1191,6 +1203,10 @@ swift_task_create_commonImpl(size_t rawTaskCreateFlags, | |
| if (jobFlags.task_hasInitialTaskName()) { | ||
| task->pushInitialTaskName(taskName); | ||
| } | ||
|
|
||
| if (SWIFT_UNLIKELY(AsyncTask::isTimeSpentRunningTracked())) { | ||
| task->pushTimeSpentRunningRecord(); | ||
| } | ||
| } | ||
|
|
||
| // If we're supposed to enqueue the task, do so now. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -991,6 +991,46 @@ internal func _getCurrentTaskNameString() -> String? { | |
| } | ||
| } | ||
|
|
||
| // MARK: - Time spent running (experimental) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This all is for the POC only. I wouldn't anticipate exposing it as API (although for Swift Testing to adopt, we may still need some underscored function since it can't directly access the |
||
|
|
||
| @available(SwiftStdlib 6.3, *) | ||
| @_silgen_name("_swift_task_getTimeSpentRunning") | ||
| internal func _getTimeSpentRunning( | ||
| _ task: Builtin.NativeObject, | ||
| _ outNanoseconds: UnsafeMutablePointer<UInt64> | ||
| ) -> Bool | ||
|
|
||
| @available(SwiftStdlib 6.3, *) | ||
| @unsafe | ||
| private func _getTimeSpentRunning(_ task: Builtin.NativeObject) -> Duration? { | ||
| var result = UInt64(0) | ||
| guard unsafe _getTimeSpentRunning(task, &result) else { | ||
| return nil | ||
| } | ||
| return .nanoseconds(result) | ||
| } | ||
|
|
||
| @available(SwiftStdlib 6.3, *) | ||
| extension Task { | ||
| /// Get the time this task has spent scheduled and running. | ||
| /// | ||
| /// This interface is experimental. It may be removed in a future release. | ||
| public var _timeSpentRunning: Duration? { | ||
| unsafe _getTimeSpentRunning(_task) | ||
| } | ||
| } | ||
|
|
||
| @available(SwiftStdlib 6.3, *) | ||
| extension UnsafeCurrentTask { | ||
| /// Get the time this task has spent scheduled and running. | ||
| /// | ||
| /// This interface is experimental. It may be removed in a future release. | ||
| public var _timeSpentRunning: Duration? { | ||
| unsafe _getTimeSpentRunning(_task) | ||
| } | ||
| } | ||
|
|
||
| // MARK: - | ||
|
|
||
| #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY | ||
| @available(SwiftStdlib 5.8, *) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -826,6 +826,11 @@ struct AsyncTask::PrivateStorage { | |
| } | ||
| } | ||
|
|
||
| // If we were tracking time spent running, clear that now too. | ||
| if (SWIFT_UNLIKELY(isTimeSpentRunningTracked())) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could theoretically leak if somebody enables tracking before a task starts, then disables it before the task finishes. For this POC, I don't care. :) |
||
| task->popTimeSpentRunningRecord(); | ||
| } | ||
|
|
||
| // Drain unlock the task and remove any overrides on thread as a | ||
| // result of the task | ||
| auto oldStatus = task->_private()._status().load(std::memory_order_relaxed); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it return a sentinel value if tracking isn't enabled, rather than 0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No because the caller already does that. We could conceivably return a
std::optionalbut at this time it isn't worth it.