Exemplo n.º 1
0
        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);
        }
Exemplo n.º 2
0
        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);
        }