From a0b9b54944e8731fd99f596a444d77b7ed4a08dc Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 10 Oct 2025 14:51:30 -0700 Subject: [PATCH] [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 --- include/swift/AST/PluginRegistry.h | 1 + lib/AST/PluginRegistry.cpp | 10 ++++++++++ test/Macros/macro_plugin_basic.swift | 1 + test/Macros/macro_plugin_error.swift | 1 + test/Macros/macro_plugin_server.swift | 2 ++ test/Macros/macro_plugin_server_mod.swift | 1 + 6 files changed, 16 insertions(+) diff --git a/include/swift/AST/PluginRegistry.h b/include/swift/AST/PluginRegistry.h index 02af09e0f808d..a4031e2be3b37 100644 --- a/include/swift/AST/PluginRegistry.h +++ b/include/swift/AST/PluginRegistry.h @@ -179,6 +179,7 @@ class LoadedExecutablePlugin : public CompilerPlugin { : CompilerPlugin(ExecutablePath), LastModificationTime(LastModificationTime), disableSandbox(disableSandbox){}; + ~LoadedExecutablePlugin(); /// The last modification time of 'ExecutablePath' when this object is /// created. diff --git a/lib/AST/PluginRegistry.cpp b/lib/AST/PluginRegistry.cpp index c838a505be792..9495d91c4d7ae 100644 --- a/lib/AST/PluginRegistry.cpp +++ b/lib/AST/PluginRegistry.cpp @@ -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. diff --git a/test/Macros/macro_plugin_basic.swift b/test/Macros/macro_plugin_basic.swift index b85c90a7e89c8..2b381e7f2ffba 100644 --- a/test/Macros/macro_plugin_basic.swift +++ b/test/Macros/macro_plugin_basic.swift @@ -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") diff --git a/test/Macros/macro_plugin_error.swift b/test/Macros/macro_plugin_error.swift index becbcda13afcc..2dc46b51fc1ca 100644 --- a/test/Macros/macro_plugin_error.swift +++ b/test/Macros/macro_plugin_error.swift @@ -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") diff --git a/test/Macros/macro_plugin_server.swift b/test/Macros/macro_plugin_server.swift index fa9e20574aec6..aff4d73bbce3f 100644 --- a/test/Macros/macro_plugin_server.swift +++ b/test/Macros/macro_plugin_server.swift @@ -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(_ value: T) -> (T, String) = #externalMacro(module: "MacroDefinition", type: "StringifyMacro") @freestanding(expression) macro evil(_ value: Int) -> String = #externalMacro(module: "EvilMacros", type: "CrashingMacro") diff --git a/test/Macros/macro_plugin_server_mod.swift b/test/Macros/macro_plugin_server_mod.swift index c1726977b4d8d..97a96c44c6662 100644 --- a/test/Macros/macro_plugin_server_mod.swift +++ b/test/Macros/macro_plugin_server_mod.swift @@ -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