internal void SubmitSnippet(SnippetInfo snippetInfo, TaskCompletionSource <SnippetResult> completion) { // check if we need to re-introduce some domains in the pool if (Interlocked.Read(ref numberOfThreadsInPool) < NumberOfDomainsInPool) { // A sort of "double-check optimization": we enter here only if there is a shortage of threads/domains, but // we actually create one only if there is a slot int emptySlotIdx = -1; lock (poolLock) { // find the empty slot for (int i = 0; i < NumberOfDomainsInPool; ++i) { if (poolDomains[i] == null) { poolDomains[i] = new PooledDomainData(); emptySlotIdx = i; } } } if (emptySlotIdx >= 0) { var thread = CreateDomainThread(emptySlotIdx); thread.Start(); } } // Store the continuation _before_ adding the snippet info the the processing queue if (!String.IsNullOrEmpty(snippetInfo.submissionId) && completion != null) { submissionMap[snippetInfo.submissionId] = completion; } snippetsQueue.Add(snippetInfo); }
private void InitializeDomainPool() { watchdogThread = new Thread(WatchdogThreadFunc); for (int i = 0; i < NumberOfDomainsInPool; ++i) { poolDomains[i] = new PooledDomainData(); var thread = CreateDomainThread(i); thread.Start(); } watchdogThread.Priority = ThreadPriority.AboveNormal; watchdogThread.Start(); }
internal void ResetContextFor(PooledDomainData domainData) { domainData.isAborting = 0; hostContext.ResetCountersForAppDomain(domainData.domainId); }
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 } } }