private object CallJavaMethod( bool isStatic, object classNameOrJvmObjectReference, string methodName, object[] args) { object returnValue = null; ISocketWrapper socket = null; try { // dotnet-interactive does not have a dedicated thread to process // code submissions and each code submission can be processed in different // threads. DotnetHandler uses the CLR thread id to ensure that the same // JVM thread is used to handle the request, which means that code submitted // through dotnet-interactive may be executed in different JVM threads. To // mitigate this, when running in the REPL, submit requests to the DotnetHandler // using the same thread id. This mitigation has some limitations in multithreaded // scenarios. If a JVM method is blocking and needs a JVM method call issued by a // separate thread to unblock it, then this scenario is not supported. // // ie, `StreamingQuery.AwaitTermination()` is a blocking call and requires // `StreamingQuery.Stop()` to be called to unblock it. However, the `Stop` // call will never run because DotnetHandler will assign the method call to // run on the same thread that `AwaitTermination` is running on. Thread thread = _isRunningRepl ? null : Thread.CurrentThread; int threadId = thread == null ? ThreadIdForRepl : thread.ManagedThreadId; MemoryStream payloadMemoryStream = s_payloadMemoryStream ??= new MemoryStream(); payloadMemoryStream.Position = 0; PayloadHelper.BuildPayload( payloadMemoryStream, isStatic, _processId, threadId, classNameOrJvmObjectReference, methodName, args); socket = GetConnection(); Stream outputStream = socket.OutputStream; outputStream.Write( payloadMemoryStream.GetBuffer(), 0, (int)payloadMemoryStream.Position); outputStream.Flush(); if (thread != null) { _jvmThreadPoolGC.TryAddThread(thread); } Stream inputStream = socket.InputStream; int isMethodCallFailed = SerDe.ReadInt32(inputStream); if (isMethodCallFailed != 0) { string jvmFullStackTrace = SerDe.ReadString(inputStream); string errorMessage = BuildErrorMessage( isStatic, classNameOrJvmObjectReference, methodName, args); _logger.LogError(errorMessage); _logger.LogError(jvmFullStackTrace); throw new Exception(errorMessage, new JvmException(jvmFullStackTrace)); } char typeAsChar = Convert.ToChar(inputStream.ReadByte()); switch (typeAsChar) // TODO: Add support for other types. { case 'n': break; case 'j': returnValue = new JvmObjectReference(SerDe.ReadString(inputStream), this); break; case 'c': returnValue = SerDe.ReadString(inputStream); break; case 'i': returnValue = SerDe.ReadInt32(inputStream); break; case 'g': returnValue = SerDe.ReadInt64(inputStream); break; case 'd': returnValue = SerDe.ReadDouble(inputStream); break; case 'b': returnValue = Convert.ToBoolean(inputStream.ReadByte()); break; case 'l': returnValue = ReadCollection(inputStream); break; default: // Convert typeAsChar to UInt32 because the char may be non-printable. throw new NotSupportedException( string.Format( "Identifier for type 0x{0:X} not supported", Convert.ToUInt32(typeAsChar))); } _sockets.Enqueue(socket); } catch (Exception e) { _logger.LogException(e); if (e.InnerException is JvmException) { // DotnetBackendHandler caught JVM exception and passed back to dotnet. // We can reuse this connection. _sockets.Enqueue(socket); } else { // In rare cases we may hit the Netty connection thread deadlock. // If max backend threads is 10 and we are currently using 10 active // connections (0 in the _sockets queue). When we hit this exception, // the socket?.Dispose() will not requeue this socket and we will release // the semaphore. Then in the next thread (assuming the other 9 connections // are still busy), a new connection will be made to the backend and this // connection may be scheduled on the blocked Netty thread. socket?.Dispose(); } throw; } finally { _socketSemaphore.Release(); } return(returnValue); }
private object CallJavaMethod( bool isStatic, object classNameOrJvmObjectReference, string methodName, object[] args) { object returnValue = null; ISocketWrapper socket = null; try { MemoryStream payloadMemoryStream = s_payloadMemoryStream ??= new MemoryStream(); payloadMemoryStream.Position = 0; PayloadHelper.BuildPayload( payloadMemoryStream, isStatic, classNameOrJvmObjectReference, methodName, args); socket = GetConnection(); Stream outputStream = socket.OutputStream; outputStream.Write( payloadMemoryStream.GetBuffer(), 0, (int)payloadMemoryStream.Position); outputStream.Flush(); Stream inputStream = socket.InputStream; int isMethodCallFailed = SerDe.ReadInt32(inputStream); if (isMethodCallFailed != 0) { string jvmFullStackTrace = SerDe.ReadString(inputStream); string errorMessage = BuildErrorMessage( isStatic, classNameOrJvmObjectReference, methodName, args); _logger.LogError(errorMessage); _logger.LogError(jvmFullStackTrace); throw new Exception(errorMessage, new JvmException(jvmFullStackTrace)); } char typeAsChar = Convert.ToChar(inputStream.ReadByte()); switch (typeAsChar) // TODO: Add support for other types. { case 'n': break; case 'j': returnValue = new JvmObjectReference(SerDe.ReadString(inputStream), this); break; case 'c': returnValue = SerDe.ReadString(inputStream); break; case 'i': returnValue = SerDe.ReadInt32(inputStream); break; case 'g': returnValue = SerDe.ReadInt64(inputStream); break; case 'd': returnValue = SerDe.ReadDouble(inputStream); break; case 'b': returnValue = Convert.ToBoolean(inputStream.ReadByte()); break; case 'l': returnValue = ReadCollection(inputStream); break; default: // Convert typeAsChar to UInt32 because the char may be non-printable. throw new NotSupportedException( string.Format( "Identifier for type 0x{0:X} not supported", Convert.ToUInt32(typeAsChar))); } _sockets.Enqueue(socket); } catch (Exception e) { _logger.LogException(e); socket?.Dispose(); throw; } return(returnValue); }
private object CallJavaMethod( bool isStatic, object classNameOrJvmObjectReference, string methodName, object[] args) { object returnValue = null; ISocketWrapper socket = null; try { Thread thread = Thread.CurrentThread; MemoryStream payloadMemoryStream = s_payloadMemoryStream ??= new MemoryStream(); payloadMemoryStream.Position = 0; PayloadHelper.BuildPayload( payloadMemoryStream, isStatic, thread.ManagedThreadId, classNameOrJvmObjectReference, methodName, args); socket = GetConnection(); Stream outputStream = socket.OutputStream; outputStream.Write( payloadMemoryStream.GetBuffer(), 0, (int)payloadMemoryStream.Position); outputStream.Flush(); _jvmThreadPoolGC.TryAddThread(thread); Stream inputStream = socket.InputStream; int isMethodCallFailed = SerDe.ReadInt32(inputStream); if (isMethodCallFailed != 0) { string jvmFullStackTrace = SerDe.ReadString(inputStream); string errorMessage = BuildErrorMessage( isStatic, classNameOrJvmObjectReference, methodName, args); _logger.LogError(errorMessage); _logger.LogError(jvmFullStackTrace); throw new Exception(errorMessage, new JvmException(jvmFullStackTrace)); } char typeAsChar = Convert.ToChar(inputStream.ReadByte()); switch (typeAsChar) // TODO: Add support for other types. { case 'n': break; case 'j': returnValue = new JvmObjectReference(SerDe.ReadString(inputStream), this); break; case 'c': returnValue = SerDe.ReadString(inputStream); break; case 'i': returnValue = SerDe.ReadInt32(inputStream); break; case 'g': returnValue = SerDe.ReadInt64(inputStream); break; case 'd': returnValue = SerDe.ReadDouble(inputStream); break; case 'b': returnValue = Convert.ToBoolean(inputStream.ReadByte()); break; case 'l': returnValue = ReadCollection(inputStream); break; default: // Convert typeAsChar to UInt32 because the char may be non-printable. throw new NotSupportedException( string.Format( "Identifier for type 0x{0:X} not supported", Convert.ToUInt32(typeAsChar))); } _sockets.Enqueue(socket); } catch (Exception e) { _logger.LogException(e); if (e.InnerException is JvmException) { // DotnetBackendHandler caught JVM exception and passed back to dotnet. // We can reuse this connection. _sockets.Enqueue(socket); } else { // In rare cases we may hit the Netty connection thread deadlock. // If max backend threads is 10 and we are currently using 10 active // connections (0 in the _sockets queue). When we hit this exception, // the socket?.Dispose() will not requeue this socket and we will release // the semaphore. Then in the next thread (assuming the other 9 connections // are still busy), a new connection will be made to the backend and this // connection may be scheduled on the blocked Netty thread. socket?.Dispose(); } throw; } finally { _socketSemaphore.Release(); } return(returnValue); }