[Macros] Send termination signal to macro plugin

When shutting down, the plugin, we send SIGTERM, then wait(2) on it. But
we observed some cases the compiler waits for the plugin process exit
for long time.

To resolve that, send an empty message to the plugin and let them exit
itself.

rdar://160820381
This commit is contained in:
Rintaro Ishizaki
2025-10-10 14:51:30 -07:00
parent 9883477c02
commit a0b9b54944
6 changed files with 16 additions and 0 deletions

View File

@@ -179,6 +179,7 @@ public:
: CompilerPlugin(ExecutablePath),
LastModificationTime(LastModificationTime),
disableSandbox(disableSandbox){};
~LoadedExecutablePlugin();
/// The last modification time of 'ExecutablePath' when this object is
/// created.

View File

@@ -172,6 +172,16 @@ PluginRegistry::loadExecutablePlugin(StringRef path, bool disableSandbox) {
return storage.get();
}
LoadedExecutablePlugin::~LoadedExecutablePlugin() {
if (Process) {
// If the process is alive, send an empty message as the termination signal.
// The plugin should exit itself when receiving it.
if (auto error = sendMessage(""))
llvm::consumeError(std::move(error));
Process.reset();
}
}
llvm::Error LoadedExecutablePlugin::spawnIfNeeded() {
if (Process) {
// NOTE: We don't check the mtime here because 'stat(2)' call is too heavy.

View File

@@ -34,6 +34,7 @@
// CHECK: <-(plugin:[[#PID]]) {"expandFreestandingMacroResult":{"diagnostics":[],"expandedSource":"\"123\"\n + \"foo \""}}
// CHECK: ->(plugin:[[#PID]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","lexicalContext":[{{.*}}],"macro":{"moduleName":"TestPlugin","name":"testStringWithError","typeName":"TestStringWithErrorMacro"},"macroRole":"expression","staticBuildConfiguration"{{.*}},"syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"{{.+}}test.swift","line":6,"offset":336},"source":"#testStringWithError(321)"}}}
// CHECK: <-(plugin:[[#PID]]) {"expandFreestandingMacroResult":{"diagnostics":[{"fixIts":[],"highlights":[],"message":"message from plugin","notes":[],"position":{"fileName":"{{.*}}test.swift","offset":336},"severity":"error"}],"expandedSource":"\"bar\""}}
// CHECK: ->(plugin:[[#PID:]]) {{$}}
//--- test.swift
@freestanding(expression) macro testString(_: Any) -> String = #externalMacro(module: "TestPlugin", type: "TestStringMacro")

View File

@@ -27,6 +27,7 @@
// CHECK-NEXT: <-(plugin:[[#PID2]]) {"getCapabilityResult":{"capability":{"protocolVersion":1}}}
// CHECK-NEXT: ->(plugin:[[#PID2]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","lexicalContext":[{{.*}}],"macro":{"moduleName":"TestPlugin","name":"fooMacro","typeName":"FooMacro"},"macroRole":"expression","staticBuildConfiguration"{{.*}},"syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"{{.+}}test.swift","line":11,"offset":[[#]]},"source":"#fooMacro(3)"}}}
// CHECK-NEXT: <-(plugin:[[#PID2:]]) {"expandFreestandingMacroResult":{"diagnostics":[],"expandedSource":"3.description"}}
// CHECK-NEXT: ->(plugin:[[#PID2:]]) {{$}}
//--- test.swift
@freestanding(expression) macro fooMacro(_: Any) -> String = #externalMacro(module: "TestPlugin", type: "FooMacro")

View File

@@ -74,6 +74,7 @@
// CHECK-NEXT: ->(plugin:[[#PID1]]) {"expandFreestandingMacro":{"discriminator":"${{.*}}","lexicalContext":[{{.*}}],"macro":{"moduleName":"MacroDefinition","name":"stringify","typeName":"StringifyMacro"},"macroRole":"expression","staticBuildConfiguration"{{.*}},"syntax":{"kind":"expression","location":{{{.+}}},"source":"#stringify(a + b)"}}}
// CHECK-NEXT: <-(plugin:[[#PID1]]) {"expandMacroResult":{"diagnostics":[],"expandedSource":"(a + b, \"a + b\")"}}
// CHECK-NEXT: ->(plugin:[[#PID1]]) {"expandFreestandingMacro":{"discriminator":"${{.*}}","lexicalContext":[{{.*}}],"macro":{"moduleName":"EvilMacros","name":"evil","typeName":"CrashingMacro"},"macroRole":"expression","staticBuildConfiguration"{{.*}},"syntax":{"kind":"expression","location":{{{.+}}},"source":"#evil(42)"}}}
// CHECK-NOT: ->(plugin:[[#PID1]]) {{$}}
// ^ This crashes the plugin server.
// CHECK: ->(plugin:[[#PID2:]]) {"getCapability":{"capability":{"protocolVersion":[[#PROTOCOL_VERSION]]}}}
@@ -89,6 +90,7 @@
// CHECK-NEXT: <-(plugin:[[#PID2]]) {"expandMacroResult":{"diagnostics":[{"fixIts":[],"highlights":[{{{.*}}}],"message":"type 'MacroDefinition.TypeDoesNotExist' could not be found in library plugin '{{.*}}MacroDefinition.{{dylib|so|dll}}'","notes":[],"position":{{{.*}}},"severity":"error"}]}}
// CHECK-NEXT: ->(plugin:[[#PID2]]) {"expandFreestandingMacro":{"discriminator":"${{.*}}","lexicalContext":[{{.*}}],"macro":{"moduleName":"MacroDefinition","name":"notMacro","typeName":"NotMacroStruct"},"macroRole":"expression","staticBuildConfiguration"{{.*}},"syntax":{"kind":"expression","location":{{({.+})}},"source":"#notMacro()"}}}
// CHECK-NEXT: <-(plugin:[[#PID2]]) {"expandMacroResult":{"diagnostics":[{"fixIts":[],"highlights":[{{{.*}}}],"message":"type 'MacroDefinition.NotMacroStruct' is not a valid macro implementation type in library plugin '{{.*}}MacroDefinition.{{dylib|so|dll}}'","notes":[],"position":{{{.*}}},"severity":"error"}]}}
// CHECK-NEXT: ->(plugin:[[#PID2]]) {{$}}
@freestanding(expression) macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: "MacroDefinition", type: "StringifyMacro")
@freestanding(expression) macro evil(_ value: Int) -> String = #externalMacro(module: "EvilMacros", type: "CrashingMacro")

View File

@@ -64,6 +64,7 @@
// CHECK-NEXT: <-(plugin:[[#PID3]]) {"loadPluginLibraryResult":{"diagnostics":[],"loaded":true}}
// CHECK-NEXT: ->(plugin:[[#PID3]]) {"expandFreestandingMacro":{"discriminator":"{{.*}}","lexicalContext":{{.*}},"macro":{"moduleName":"MacroDefinition","name":"stringify","typeName":"StringifyMacro"}{{.*}}
// CHECK-NEXT: <-(plugin:[[#PID3]]) {"expandMacroResult":{"diagnostics":[],"expandedSource":"(1, \"1\")"}}
// CHECK-NEXT: ->(plugin:[[#PID3]]) {{$}}
//--- MacroDefinition.swift
import SwiftSyntax