Skip to content

Commit 5e9ff15

Browse files
authored
Merge pull request swiftlang#39540 from ahoppen/pr/simulate-long-request
[SourceKit] Add option to simulate a long-running request
2 parents e4f71c8 + fa0ead5 commit 5e9ff15

File tree

10 files changed

+88
-8
lines changed

10 files changed

+88
-8
lines changed
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
// Check that we can cancel requests.
22
// We need to wait a little bit after request scheduling and cancellation to make sure we are not cancelling the request before it got scheduled.
33

4-
// RUN: not %sourcekitd-test -req=cursor -id=slow -async -pos=10:3 %s -- %s == \
4+
// RUN: not %sourcekitd-test -req=cursor -id=slow -async -pos=9:5 -simulate-long-request=5000 %s -- %s == \
55
// RUN: -shell -- sleep 1 == \
66
// RUN: -cancel=slow 2>&1 \
77
// RUN: | %FileCheck %s
88

9-
func foo(x: Invalid1, y: Invalid2) {
10-
x / y / x / y / x / y / x / y
11-
}
9+
let x = "Hello World"
1210

1311
// CHECK: error response (Request Cancelled)

tools/SourceKit/include/SourceKit/Core/Context.h

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414
#define LLVM_SOURCEKIT_CORE_CONTEXT_H
1515

1616
#include "SourceKit/Core/LLVM.h"
17-
#include "llvm/ADT/StringRef.h"
17+
#include "SourceKit/Support/CancellationToken.h"
18+
#include "SourceKit/Support/Concurrency.h"
1819
#include "llvm/ADT/STLExtras.h"
20+
#include "llvm/ADT/StringRef.h"
1921
#include "llvm/Support/Mutex.h"
22+
#include <map>
2023
#include <memory>
2124
#include <string>
2225

@@ -51,12 +54,52 @@ class GlobalConfig {
5154
Settings::CompletionOptions getCompletionOpts() const;
5255
};
5356

57+
class SlowRequestSimulator {
58+
std::map<SourceKitCancellationToken, std::shared_ptr<Semaphore>>
59+
InProgressRequests;
60+
61+
/// Mutex guarding \c InProgressRequests.
62+
llvm::sys::Mutex InProgressRequestsMutex;
63+
64+
public:
65+
/// Simulate that a request takes \p DurationMs to execute. While waiting that
66+
/// duration, the request can be cancelled using the \p CancellationToken.
67+
/// Returns \c true if the request waited the required duration and \c false
68+
/// if it was cancelled.
69+
bool simulateLongRequest(int64_t DurationMs,
70+
SourceKitCancellationToken CancellationToken) {
71+
auto Sema = std::make_shared<Semaphore>(0);
72+
{
73+
llvm::sys::ScopedLock L(InProgressRequestsMutex);
74+
InProgressRequests[CancellationToken] = Sema;
75+
}
76+
bool DidTimeOut = Sema->wait(DurationMs);
77+
{
78+
llvm::sys::ScopedLock L(InProgressRequestsMutex);
79+
InProgressRequests[CancellationToken] = nullptr;
80+
}
81+
// If we timed out, we waited the required duration. If we didn't time out,
82+
// the semaphore was cancelled.
83+
return DidTimeOut;
84+
}
85+
86+
/// Cancel a simulated long request. If the required wait duration already
87+
/// elapsed, this is a no-op.
88+
void cancel(SourceKitCancellationToken CancellationToken) {
89+
llvm::sys::ScopedLock L(InProgressRequestsMutex);
90+
if (auto InProgressSema = InProgressRequests[CancellationToken]) {
91+
InProgressSema->signal();
92+
}
93+
}
94+
};
95+
5496
class Context {
5597
std::string RuntimeLibPath;
5698
std::string DiagnosticDocumentationPath;
5799
std::unique_ptr<LangSupport> SwiftLang;
58100
std::shared_ptr<NotificationCenter> NotificationCtr;
59101
std::shared_ptr<GlobalConfig> Config;
102+
std::shared_ptr<SlowRequestSimulator> SlowRequestSim;
60103

61104
public:
62105
Context(StringRef RuntimeLibPath, StringRef DiagnosticDocumentationPath,
@@ -75,6 +118,10 @@ class Context {
75118
std::shared_ptr<NotificationCenter> getNotificationCenter() { return NotificationCtr; }
76119

77120
std::shared_ptr<GlobalConfig> getGlobalConfiguration() { return Config; }
121+
122+
std::shared_ptr<SlowRequestSimulator> getSlowRequestSimulator() {
123+
return SlowRequestSim;
124+
}
78125
};
79126

80127
} // namespace SourceKit

tools/SourceKit/include/SourceKit/Support/Concurrency.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class Semaphore {
3535
return Impl::wait(ImplObj);
3636
}
3737

38+
/// Waits for the semaphore, timing out after \p milliseconds.
39+
/// Returns \c true if waiting timed out.
3840
bool wait(long milliseconds) {
3941
return Impl::wait(ImplObj, milliseconds);
4042
}

tools/SourceKit/lib/Core/Context.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ SourceKit::Context::Context(
4444
DiagnosticDocumentationPath(DiagnosticDocumentationPath),
4545
NotificationCtr(
4646
new NotificationCenter(shouldDispatchNotificationsOnMain)),
47-
Config(new GlobalConfig()) {
47+
Config(new GlobalConfig()), SlowRequestSim(new SlowRequestSimulator()) {
4848
// Should be called last after everything is initialized.
4949
SwiftLang = LangSupportFactoryFn(*this);
5050
}

tools/SourceKit/tools/sourcekitd-test/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ def suppress_config_request : Flag<["-"], "suppress-config-request">,
157157
def module_cache_path: Separate<["-"], "module-cache-path">, HelpText<"module cache path">;
158158
def module_cache_path_EQ : Joined<["-"], "module-cache-path=">, Alias<module_cache_path>;
159159

160+
def simulate_long_request : Separate<["-"], "simulate-long-request">,
161+
HelpText<"Simulate that the request takes x ms longer to execute. The request can be cancelled while waiting this duration">;
162+
def simulate_long_request_EQ : Joined<["-"], "simulate-long-request=">, Alias<simulate_long_request>;
163+
160164
def shell: Flag<["-"], "shell">,
161165
HelpText<"Run shell command">;
162166

tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,15 @@ bool TestOptions::parseArgs(llvm::ArrayRef<const char *> Args) {
420420
ModuleCachePath = InputArg->getValue();
421421
break;
422422

423+
case OPT_simulate_long_request:
424+
unsigned SimulatedDuration;
425+
if (StringRef(InputArg->getValue()).getAsInteger(10, SimulatedDuration)) {
426+
llvm::errs() << "error: expected integer for 'simulate-long-request'\n";
427+
return true;
428+
}
429+
SimulateLongRequest = SimulatedDuration;
430+
break;
431+
423432
case OPT_shell:
424433
ShellExecution = true;
425434
break;

tools/SourceKit/tools/sourcekitd-test/TestOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ struct TestOptions {
107107
std::string RequestId;
108108
/// If not empty, all requests with this ID should be cancelled.
109109
std::string CancelRequest;
110+
/// If set, simulate that the request takes x ms longer than it actually
111+
/// does. The request can be cancelled while waiting this duration.
112+
llvm::Optional<uint64_t> SimulateLongRequest;
110113
bool CheckInterfaceIsASCII = false;
111114
bool UsedSema = false;
112115
bool PrintRequest = true;

tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,10 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) {
11371137
sourcekitd_request_dictionary_set_int64(Req, KeyCancelOnSubsequentRequest,
11381138
*Opts.CancelOnSubsequentRequest);
11391139
}
1140+
if (Opts.SimulateLongRequest.hasValue()) {
1141+
sourcekitd_request_dictionary_set_int64(Req, KeySimulateLongRequest,
1142+
*Opts.SimulateLongRequest);
1143+
}
11401144

11411145
if (!Opts.SwiftVersion.empty()) {
11421146
if (Opts.PassVersionAsString) {

tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,8 +399,8 @@ void sourcekitd::handleRequest(sourcekitd_object_t Req,
399399
}
400400

401401
void sourcekitd::cancelRequest(SourceKitCancellationToken CancellationToken) {
402-
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
403-
Lang.cancelRequest(CancellationToken);
402+
getGlobalContext().getSlowRequestSimulator()->cancel(CancellationToken);
403+
getGlobalContext().getSwiftLangSupport().cancelRequest(CancellationToken);
404404
}
405405

406406
static std::unique_ptr<llvm::MemoryBuffer> getInputBufForRequest(
@@ -472,6 +472,16 @@ void handleRequestImpl(sourcekitd_object_t ReqObj,
472472
++numRequests;
473473

474474
RequestDict Req(ReqObj);
475+
476+
if (auto SimulateLongRequestDuration =
477+
Req.getOptionalInt64(KeySimulateLongRequest)) {
478+
if (!getGlobalContext().getSlowRequestSimulator()->simulateLongRequest(
479+
*SimulateLongRequestDuration, CancellationToken)) {
480+
Rec(createErrorRequestCancelled());
481+
return;
482+
}
483+
}
484+
475485
sourcekitd_uid_t ReqUID = Req.getUID(KeyRequest);
476486
if (!ReqUID)
477487
return Rec(createErrorRequestInvalid("missing 'key.request' with UID value"));

utils/gyb_sourcekit_support/UIDs.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ def __init__(self, internal_name, external_name):
199199
KEY('EffectiveAccess', 'key.effective_access'),
200200
KEY('DeclarationLang', 'key.decl_lang'),
201201
KEY('SecondarySymbols', 'key.secondary_symbols'),
202+
# Before executing the actual request wait x ms. The request can be canceled
203+
# in this time. For cancellation testing purposes.
204+
KEY('SimulateLongRequest', 'key.simulate_long_request'),
202205
]
203206

204207

0 commit comments

Comments
 (0)