Use jni implementation of ipcsocket apis on arm macs

There isn't yet a version of the jna available that works with the new
apple silicon using arm64. To workaround this, we can use the jni
implementation by default on arm64 macs. If the user wants to force the
jni implementation for any supported platform, they can opt in with the
`sbt.ipcsocket.jni` system property and/or by setting the serverUseJni
setting.
This commit is contained in:
Ethan Atkins 2020-11-23 10:17:47 -08:00
parent 8e7e3efea1
commit ab2875e837
9 changed files with 29 additions and 7 deletions

View File

@ -31,7 +31,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import net.openhft.hashing.LongHashFunction;
import org.scalasbt.ipcsocket.UnixDomainServerSocket;
import org.scalasbt.ipcsocket.UnixDomainSocket;
import org.scalasbt.ipcsocket.Win32NamedPipeServerSocket;
import org.scalasbt.ipcsocket.Win32NamedPipeSocket;
import org.scalasbt.ipcsocket.Win32SecurityLevel;
@ -337,18 +336,24 @@ public class BootServerSocket implements AutoCloseable {
static ServerSocket newSocket(final String sock) throws ServerAlreadyBootingException {
ServerSocket socket = null;
String name = socketName(sock);
boolean jni = requiresJNI() || System.getProperty("sbt.ipcsocket.jni", "false").equals("true");
try {
if (!isWindows) Files.deleteIfExists(Paths.get(sock));
socket =
isWindows
? new Win32NamedPipeServerSocket(name, false, Win32SecurityLevel.OWNER_DACL)
: new UnixDomainServerSocket(name);
? new Win32NamedPipeServerSocket(name, jni, Win32SecurityLevel.OWNER_DACL)
: new UnixDomainServerSocket(name, jni);
return socket;
} catch (final IOException e) {
throw new ServerAlreadyBootingException();
}
}
public static Boolean requiresJNI() {
final boolean isMac = System.getProperty("os.name").toLowerCase().startsWith("mac");
return isMac && !System.getProperty("os.arch", "").equals("x86_64");
}
private static String socketName(String sock) {
return isWindows ? "\\\\.\\pipe\\" + sock : sock;
}

View File

@ -83,6 +83,12 @@ object BasicKeys {
"Configures the security level of the named pipe. Values: 0 - No security; 1 - Logon user only; 2 - Process owner only",
10000
)
val serverUseJni =
AttributeKey[Boolean](
"serverUseJni",
"Toggles whether to use the jna or jni implementation in ipcsocket.",
10000
)
val serverIdleTimeout =
AttributeKey[Option[FiniteDuration]](

View File

@ -66,7 +66,7 @@ private[sbt] object Server {
addServerError(
new Win32NamedPipeServerSocket(
pipeName,
false,
connection.useJni,
connection.windowsServerSecurityLevel
)
)
@ -80,9 +80,9 @@ private[sbt] object Server {
"or define a short \"SBT_GLOBAL_SERVER_DIR\" value. " +
s"Current path: ${path}"
)
tryClient(new UnixDomainSocket(path))
tryClient(new UnixDomainSocket(path, connection.useJni))
prepareSocketfile()
addServerError(new UnixDomainServerSocket(path))
addServerError(new UnixDomainServerSocket(path, connection.useJni))
case ConnectionType.Tcp =>
tryClient(new Socket(InetAddress.getByName(host), port))
addServerError(new ServerSocket(port, 50, InetAddress.getByName(host)))
@ -238,7 +238,8 @@ private[sbt] case class ServerConnection(
socketfile: File,
pipeName: String,
appConfiguration: AppConfiguration,
windowsServerSecurityLevel: Int
windowsServerSecurityLevel: Int,
useJni: Boolean,
) {
def shortName: String = {
connectionType match {

View File

@ -383,6 +383,7 @@ object Defaults extends BuildCommon {
},
serverHandlers :== Nil,
windowsServerSecurityLevel := Win32SecurityLevel.OWNER_DACL, // allows any owner logon session to access the server
serverUseJni := BootServerSocket.requiresJNI || SysProp.serverUseJni,
fullServerHandlers := Nil,
insideCI :== sys.env.contains("BUILD_NUMBER") ||
sys.env.contains("CI") || SysProp.ci,

View File

@ -105,6 +105,7 @@ object Keys {
val serverConnectionType = SettingKey(BasicKeys.serverConnectionType)
val serverIdleTimeout = SettingKey(BasicKeys.serverIdleTimeout)
val windowsServerSecurityLevel = SettingKey(BasicKeys.windowsServerSecurityLevel)
val serverUseJni = SettingKey(BasicKeys.serverUseJni)
val fullServerHandlers = SettingKey(BasicKeys.fullServerHandlers)
val serverHandlers = settingKey[Seq[ServerHandler]]("User-defined server handlers.")

View File

@ -130,6 +130,7 @@ private[sbt] object xMain {
case _: ServerAlreadyBootingException =>
if (SysProp.forceServerStart) (None, None)
else (None, Some(Exit(2)))
case _: UnsatisfiedLinkError => (None, None)
}
}

View File

@ -27,6 +27,7 @@ import Keys.{
serverIdleTimeout,
serverLog,
serverPort,
serverUseJni,
serverAuthentication,
serverConnectionType,
fullServerHandlers,
@ -529,10 +530,12 @@ object Project extends ProjectExtra {
projectCommand
)
val winSecurityLevel = get(windowsServerSecurityLevel).getOrElse(2)
val useJni = get(serverUseJni).getOrElse(false)
val newAttrs =
s.attributes
.put(historyPath.key, history)
.put(windowsServerSecurityLevel.key, winSecurityLevel)
.put(serverUseJni.key, useJni)
.setCond(autoStartServer.key, startSvr)
.setCond(serverPort.key, port)
.setCond(serverHost.key, host)

View File

@ -189,6 +189,7 @@ private[sbt] final class CommandExchange {
lazy val connectionType = s.get(serverConnectionType).getOrElse(ConnectionType.Tcp)
lazy val handlers = s.get(fullServerHandlers).getOrElse(Nil)
lazy val win32Level = s.get(windowsServerSecurityLevel).getOrElse(2)
lazy val useJni = s.get(serverUseJni).getOrElse(false)
lazy val portfile = s.baseDir / "project" / "target" / "active.json"
def onIncomingSocket(socket: Socket, instance: ServerInstance): Unit = {
@ -223,6 +224,7 @@ private[sbt] final class CommandExchange {
pipeName,
s.configuration,
win32Level,
useJni,
)
val serverInstance = Server.start(connection, onIncomingSocket, s.log)
// don't throw exception when it times out

View File

@ -178,6 +178,8 @@ object SysProp {
}
}
def serverUseJni = getOrFalse("sbt.ipcsocket.jni")
private[this] def file(value: String): File = new File(value)
private[this] def home: File = file(sys.props("user.home"))