6

All articles in Internet says that using Thread.Abort is evil (because synchronization primitives belongs to operating system, and process doesn't terminate yet, and primitives might remain locked after thread aborting). Developers advises to terminate whole processes instead, because operation system will free synchronization primitives when process stops.

  1. Will AppDomain unloading help, If one will use Slim synchronization primitives? (With .net 4.0 several new classes have been added relating to threading: ManualResetEventSlim, SemaphoreSlim, ReaderWriterLockSlim).

    Documentation says that these primitives can't be used beween different processes, because implementation code of primitives is fully managed. But I don't understand - will these primitives work through AppDomain border. (see Can't set synchronization context when using appdomains)

  2. If yes, how they do that? If no, then why documentation omit this limitation?

UPD: All my code is trusted by me, including the code inside the domain which i unload. I don't want to leave the thread working, when the time comes to terminate it. I want to terminate (abort) thread instead of "setting flag" and "making nice architecture". If it is neccessary to create additional thread first (i think this is necessary at start of background processing in separate domain to create one more thread), i will do. I don't want to use "setting flag" approach, because it requires me to instrument background algorithm with flag checks, i shouldn't, it's the runtime or compiler should automate that instrumentation for me. Right now there is no such instrumentation, that is why I am trying to apply the approach with domain unloading.

Adding checks between each instruction (or in deep nested cycles) will slow the code significantly. Adding check in random locations will not give guaranties of prompt termination. If difficulties of writing abort-safe code can be solved, why not to try to abort threads and unload domain?

Community
  • 1
  • 1
R2D4
  • 133
  • 1
  • 4
  • 2
    How would you get them through the AppDomain boundary? They are not serializable, nor do they inherit from `MarshalByRefObject`. – Mike Zboray Jun 01 '15 at 15:33
  • 3
    AppDomain solves this problem. But using synchronization primitives across appdomains cannot work, you have to be able to afford to throw away *everything*. – Hans Passant Jun 02 '15 at 11:35
  • There's more issues with `Thread.Abort` than those you mentioned (in fact, some synchronization primitives are released just fine with `Thread.Abort`; provided the thread actually dies of course). The question is, why are you trying to use aggressive abortion instead of coöperative cancellation? Just send a signal and let the thread decide when it's safe to abort. Writing abort-safe code is much more difficult than mere thread-safe code - for both managed and native resources. – Luaan Jun 03 '15 at 06:46

1 Answers1

3

You misunderstands the AppDomain, Process and Thread definitions - AppDomains always lives in one process, but one process can be a master for different AppDomains *:

The ability to run multiple applications within a single process dramatically increases server scalability.

  • this is similar for the Thread, just for mention.

So, the answer for your question is Yes, the slim synchronization primitives will work with various AppDomains, as there are no cross-process calls in such architecture, only a logical distinction for various processes.

As for the linked question, there are no problem with synchronization primitives there, only with SynchronizationContext, which is Thread-specific, but not AppDomain-specific.

You can find a great answer about the difference between AppDomain, Thread and Process here: Difference between AppDomain, Assembly, Process, and a Thread

As for the unloading the AppDomain, I think that you can refactor your code as you can start a worker Thread into your AppDomain with limited access to the system resources, and simply wait for it to be finished, something like it mentioned here:

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        var ad = AppDomain.CreateDomain("WhereTheWorkHappens");

        Task<string> t = DoWorkInOtherDomain(ad);
        Console.WriteLine("waiting...");
        Console.WriteLine(t.Result);

        Console.ReadLine();
    }

    static Task<string> DoWorkInOtherDomain(AppDomain ad)
    {
        var ch = new MarshaledResultSetter<string>();

        Worker worker = (Worker)ad.CreateInstanceAndUnwrap(typeof(Worker).Assembly.FullName, typeof(Worker).FullName);
        worker.DoWork(ch);

        return ch.Task;
    }

    class Worker : MarshalByRefObject
    {
        public void DoWork(MarshaledResultSetter<string> callback)
        {
            ThreadPool.QueueUserWorkItem(delegate
            {
                Thread.SpinWait(500000000);
                callback.SetResult(AppDomain.CurrentDomain.FriendlyName);
            });
        }
    }

    class MarshaledResultSetter<T> : MarshalByRefObject
    {
        private TaskCompletionSource<T> m_tcs = new TaskCompletionSource<T>();
        public void SetResult(T result) { m_tcs.SetResult(result); }
        public Task<T> Task { get { return m_tcs.Task; } }
    }
}

As an additional idea for you, you can read about Sandboxing your code with TPL, which, as I think, is better approach as you don't manually manage the system resources and has less chances for being hacked by your non-trusted code.

Also, you can find a GitHub project with Cross-AppDomain Marshaling friendly TPL wrappers for APM

Community
  • 1
  • 1
VMAtm
  • 27,943
  • 17
  • 79
  • 125
  • I do understand, that AppDomain belongs to only one process and AppDomain doesn't span several processes. I do understand that SynchronizationContext (for ContextBoundObjects) doesn't span several AppDomains. Why do you decide, that I don't? Looks like you was fighting your own old misunderstandings, assuming that I have the same. Mine's are different! I don't understood, how to syncronize between AppDomains (without using OS-level synchronization primitives). I still don't understand what happens to thread when it crosses AppDomain boundary. – R2D4 Jun 03 '15 at 06:22
  • Look at this question - http://stackoverflow.com/questions/21493944/synchronizing-between-appdomains-using-named-mutex I think the question is worded improperly. We synchronize Threads, not AppDomain's. But Threads belongs to Processes, not to AppDomains. – R2D4 Jun 03 '15 at 06:51
  • I've answered your question: Yes, slim versions will work across various `AppDomain` as long as various `Threads` as they still are in **the same `Process`**. – VMAtm Jun 03 '15 at 08:46
  • your answer contradicts to the comment to question, that slim-classes are not serializable and can't be marshalled – R2D4 Jun 03 '15 at 14:40
  • Why are you talking about marshalling? Marshalling is not related to the `AppDomains`, only for managed-unmanaged processing. And slim versions are exists in managed code only! – VMAtm Jun 03 '15 at 19:14
  • "Marshalling is not related to the AppDomains". No, it does. The word "Unwrap" in the name of CreateInstanceAndUnwrap function says us, that proxy is created. So the MBR-mechanism is used, and cross-appdomain calls shoud eighter use MBR-objects or serializable objects. – R2D4 Jun 03 '15 at 19:25