Exemplo n.º 1
0
        private async Task SubmitSnippetPostFeedback(string snippetId, string connectionId, long startTimestamp)
        {
            SnippetResult result;

            try {
                result = await hostConnector.RunSnippetAsync(snippetId);
            }
            catch (Exception ex) {
                result = new SnippetResult()
                {
                    status = SnippetStatus.InitializationError, exception = ex.Message
                };
            }

            var hubContext = GlobalHost.ConnectionManager.GetHubContext <ResultHub>();

            result.totalTime = StopwatchExtensions.GetTimestampMillis() - startTimestamp;
            hubContext.Clients.Client(connectionId).SendResult(connectionId, snippetId, result, result.status.ToHealth().ToColor());
        }
Exemplo n.º 2
0
        public async Task <ActionResult> SubmitRequest(string snippetId, string connectionId)
        {
            long startTimestamp = StopwatchExtensions.GetTimestampMillis();

            if (String.IsNullOrEmpty(snippetId))
            {
                return(new HttpStatusCodeResult(HttpStatusCode.BadRequest));
            }

            if (connectionId != null)
            {
                Task.Run(() => SubmitSnippetPostFeedback(snippetId, connectionId, startTimestamp)).FireAndForget();
                return(new HttpStatusCodeResult(HttpStatusCode.Accepted));
            }
            else
            {
                SnippetResult result = await hostConnector.RunSnippetAsync(snippetId);

                result.totalTime    = StopwatchExtensions.GetTimestampMillis() - startTimestamp;
                Response.StatusCode = (int)HttpStatusCode.OK;
                return(Json(new { connectionId = connectionId, message = result, newStatus = result.status.ToHealth().ToColor() }));
            }
        }
Exemplo n.º 3
0
        private Thread CreateDomainThread(int threadIndex)
        {
            System.Diagnostics.Debug.WriteLine("CreateDomainThread: " + threadIndex);

            var myPoolDomain = poolDomains[threadIndex];

            var thread = new Thread(() => {
                Interlocked.Increment(ref numberOfThreadsInPool);
                // Here we enforce the "one domain, one thread" relationship
                try {
                    var appDomain = AppDomainHelpers.CreateSandbox("Host Sandbox");
                    var manager   = (SimpleHostAppDomainManager)appDomain.DomainManager;

                    lock (poolLock) {
                        myPoolDomain.domainId = appDomain.Id;
                    }

                    while (!snippetsQueue.IsCompleted)
                    {
                        defaultDomainManager.ResetContextFor(myPoolDomain);

                        // And here is were we "rent" one AppDomain and use it to run a snippet
                        SnippetInfo snippetToRun = null;
                        try {
                            snippetToRun = snippetsQueue.Take();
                        }
                        catch (InvalidOperationException) {
                            // Someone called "complete". No more snippets in this process.
                            // We want to exit the pool.
                            return;
                        }

                        if (snippetToRun != null)
                        {
                            System.Diagnostics.Debug.WriteLine("Starting snippet '" + snippetToRun.methodName + "' in domain " + myPoolDomain.domainId);

                            SnippetResult result = new SnippetResult();
                            bool recycleDomain   = false;

                            try {
                                Interlocked.Increment(ref myPoolDomain.numberOfUsages);
                                // Record when we started
                                long startTimestamp = StopwatchExtensions.GetTimestampMillis();
                                System.Diagnostics.Debug.WriteLine("Starting execution at " + startTimestamp);
                                Thread.VolatileWrite(ref myPoolDomain.timeOfSubmission, startTimestamp);

                                // Thread transitions into the AppDomain
                                // This function DOES NOT throw
                                result = manager.InternalRun(appDomain, snippetToRun.assemblyFile, snippetToRun.mainTypeName, snippetToRun.methodName, true);


                                // ...back to the main AppDomain
                                Debug.Assert(AppDomain.CurrentDomain.IsDefaultAppDomain());
                                long currentTime     = StopwatchExtensions.GetTimestampMillis();
                                result.executionTime = currentTime - Thread.VolatileRead(ref myPoolDomain.timeOfSubmission);
                                // Flag it as "not executing"
                                Thread.VolatileWrite(ref myPoolDomain.timeOfSubmission, 0);
                            }
                            catch (ThreadAbortException ex) {
                                // Someone called abort on us.
                                result.exception = ex.Message;

                                // It may be possible to use this domain again, otherwise we will recycle
                                if (Object.Equals(ex.ExceptionState, timeoutAbortToken))
                                {
                                    // The abort was issued by us because we timed out
                                    System.Diagnostics.Debug.WriteLine("Thread Abort due to timeout");
                                    result.status = SnippetStatus.Timeout;
                                }
                                else if (Object.Equals(ex.ExceptionState, threadsExaustedAbortToken))
                                {
                                    System.Diagnostics.Debug.WriteLine("Thread Abort due to thread exaustion");
                                    result.status = SnippetStatus.ResourceError;
                                }
                                else
                                {
                                    // If it wasn't us, give us time to record the result; we will recycle (unload) the domain
                                    System.Diagnostics.Debug.WriteLine("Thread Abort due to external factors");
                                    result.status = SnippetStatus.CriticalError;
                                    recycleDomain = true;
                                }

                                // Give us time to record the result; if needed, we will recycle (unload) the domain later
                                Thread.ResetAbort();
                            }
                            catch (Exception ex) {
                                result.exception = ex.Message;
                                // Check if someone is misbehaving, throwing something else to "mask" the TAE
                                if (Thread.CurrentThread.ThreadState == System.Threading.ThreadState.AbortRequested)
                                {
                                    result.status = SnippetStatus.CriticalError;
                                    recycleDomain = true;

                                    // Give us time to record the result; we will recycle (unload) the domain
                                    Thread.ResetAbort();
                                }
                                else
                                {
                                    result.status = SnippetStatus.ExecutionError;
                                }
                            }
                            // "Although C# only allows you to throw objects of type Exception and types deriving from it,
                            // other languages don’t have any such restriction."
                            // http://weblogs.asp.net/kennykerr/introduction-to-msil-part-5-exception-handling
                            // It turns out this is no longer necessary
                            // http://blogs.msdn.com/b/jmanning/archive/2005/09/16/469091.aspx
                            // catch { }

                            // No need to catch StackOverflowException; the Host will escalate to a (rude) domain unload
                            // for us
                            // TODO: check that AppDomain.DomainUnload is called anyway!

                            // Before looping, check if we are OK; we reuse the domain only if we are not leaking
                            int threadsInDomain = defaultDomainManager.GetThreadCount(appDomain.Id);
                            int memoryUsage     = defaultDomainManager.GetMemoryUsage(appDomain.Id);

                            System.Diagnostics.Debug.WriteLine("============= AppDomain {0} =============", appDomain.Id);
                            System.Diagnostics.Debug.WriteLine("Finished in: {0}", result.executionTime);
                            System.Diagnostics.Debug.WriteLine("Status: {0}", result.status);
                            if (result.exception != null)
                            {
                                System.Diagnostics.Debug.WriteLine("Exception: " + result.exception);
                            }
                            System.Diagnostics.Debug.WriteLine("Threads: {0}", threadsInDomain);
                            System.Diagnostics.Debug.WriteLine("Memory: {0}", memoryUsage);
                            System.Diagnostics.Debug.WriteLine("========================================");

                            if (threadsInDomain > 1)
                            {
                                // The snippet is leaking threads

                                // Flag snippet as "leaking"
                                result.status = SnippetStatus.CriticalError;
                                System.Diagnostics.Debug.WriteLine("Leaking snippet");

                                recycleDomain = true;
                            }
                            else if (MaxReuse > 0 && myPoolDomain.numberOfUsages >= MaxReuse)
                            {
                                System.Diagnostics.Debug.WriteLine("Domain too old");

                                // Same if the domain is too old
                                recycleDomain = true;
                            }

                            // Return the result to the caller
                            if (!String.IsNullOrEmpty(snippetToRun.submissionId))
                            {
                                var completion = submissionMap[snippetToRun.submissionId];
                                // TCS is thread-safe (can be called cross-thread)
                                completion.TrySetResult(result);
                            }

                            if (recycleDomain)
                            {
                                System.Diagnostics.Debug.WriteLine("Recycling domain...");
                                RecycleDomain(threadIndex);
                            }
                            else
                            {
                                // Otherwise, ensure that what was allocated by the snippet is freed
                                GC.Collect();
                                System.Diagnostics.Debug.WriteLine("MonitoringSurvivedMemorySize {0}", appDomain.MonitoringSurvivedMemorySize);
                                System.Diagnostics.Debug.WriteLine("MonitoringTotalAllocatedMemorySize {0}", appDomain.MonitoringTotalAllocatedMemorySize);
                                System.Diagnostics.Debug.WriteLine("MonitoringTotalProcessorTime {0}", appDomain.MonitoringTotalProcessorTime);
                            }
                        }
                    }
                }
                catch (Exception ex) {
                    System.Diagnostics.Debug.WriteLine("Exception caught:\n" + ex.ToString());
                    RecycleDomain(threadIndex);
                }
            });

            thread.Name             = "DomainPool thread " + threadIndex;
            myPoolDomain.mainThread = thread;
            thread.Priority         = ThreadPriority.BelowNormal;
            return(thread);
        }
Exemplo n.º 4
0
        private void WatchdogThreadFunc()
        {
            while (!isExiting)
            {
                // This is a watchdog; it does not need to be precise.
                // It can be improved by recomputing the next check time (i.e. when a snippet expires)
                // at each submission, but for now we are good.

                // We sleep for a while, than check
                try {
                    //Thread.Sleep(1000);
                    HostEvent hostEvent;
                    if (defaultDomainManager.GetHostMessage(1000, out hostEvent))
                    {
                        System.Diagnostics.Debug.WriteLine("Watchdog: message from host for AppDomain " + hostEvent.appDomainId);
                        // Process event
                        switch ((HostEventType)hostEvent.eventType)
                        {
                        case HostEventType.OutOfTasks: {
                            PooledDomainData poolDomain = FindByAppDomainId(hostEvent.appDomainId);
                            if (poolDomain != null)
                            {
                                if (Interlocked.CompareExchange(ref poolDomain.isAborting, 1, 0) == 0)
                                {
                                    poolDomain.mainThread.Abort(threadsExaustedAbortToken);
                                }
                            }
                        }
                        break;
                        }
                    }
                    else
                    {
                        long currentTime = StopwatchExtensions.GetTimestampMillis();
                        //System.Diagnostics.Debug.WriteLine("Watchdog: check at " + currentTime);

                        for (int i = 0; i < NumberOfDomainsInPool; ++i)
                        {
                            // Something is running?
                            PooledDomainData poolDomain = null;
                            lock (poolLock) {
                                poolDomain = poolDomains[i];
                            }

                            if (poolDomain != null)
                            {
                                long snippetStartTime = Thread.VolatileRead(ref poolDomain.timeOfSubmission);
                                if (snippetStartTime > 0)
                                {
                                    // For too long?
                                    long millisRunning = currentTime - snippetStartTime;
                                    if (millisRunning > runningThreshold)
                                    {
                                        // Abort Thread i / Unload domain i
                                        // Calling abort here is fine: the host will escalate it for us if it times
                                        // out. Otherwise, we can still catch it and reuse the AppDomain
                                        System.Diagnostics.Debug.WriteLine("Timeout: aborting thread #{0} in domain {1} ({2} ms)", i, poolDomains[i].domainId, millisRunning);

                                        // Avoid to call Abort twice
                                        Thread.VolatileWrite(ref poolDomains[i].timeOfSubmission, 0);
                                        poolDomains[i].mainThread.Abort(timeoutAbortToken);
                                    }
                                    // Check also for appDomain.MonitoringTotalProcessorTime
                                    //else if (poolDomains[i].appDomain.MonitoringTotalProcessorTime > MaxCpuTime)
                                    //   //TODO
                                    //}
                                }

                                // If our thread was aborted rudely, we need to create a new one
                                if (poolDomain.mainThread == null || poolDomain.mainThread.ThreadState == System.Threading.ThreadState.Aborted)
                                {
                                    defaultDomainManager.HostUnloadDomain(poolDomain.domainId);
                                    poolDomains[i] = new PooledDomainData();
                                    var thread = CreateDomainThread(i);
                                    thread.Start();
                                }
                            }
                        }
                    }

                    // Ensure there is at least a thread in the pool
                    if (numberOfThreadsInPool < 1)
                    {
                        poolDomains[0] = new PooledDomainData();
                        var thread = CreateDomainThread(0);
                        thread.Start();
                    }
                }
                catch (ThreadInterruptedException) {
                    // Do we need to exit? Simply cycle back to check exit status
                }
            }
        }