Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Any way to bypass a thread's name being write-once

Discussion in 'Scripting' started by EternalClickbait, Aug 10, 2020.

  1. EternalClickbait

    EternalClickbait

    Joined:
    May 5, 2019
    Posts:
    22
    I would like to be able to rename threads as I go, which normally I would do simply by setting the
    Thread.Name
    property, but this is write-once, so I have to resort to reflection. The standard solution to this is to set the backing field
    m_Name
    or sometimes
    _name
    to null, and then set it via the property. However, unity doesn't use backing fields (because why would you).
    Code (CSharp):
    1. public string Name
    2.     {
    3.       get
    4.       {
    5.         return Thread.GetName_internal(this.Internal);
    6.       }
    7.       set
    8.       {
    9.         Thread.SetName_internal(this.Internal, value);
    10.       }
    11.     }
    I managed to create a solution to call
    Thread.SetName_internal
    to set the name, but I still get an exception that:
    Code (csharp):
    1. InvalidOperationException: Thread.Name can only be set once.
    2. System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <fb001e01371b4adca20013e0ac763896>:0)
    3. Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.
    4. System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <fb001e01371b4adca20013e0ac763896>:0)
    5. System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at <fb001e01371b4adca20013e0ac763896>:0)
    6. LibEternal.Helper.ThreadHelper+<>c__DisplayClass1_1.<.cctor>b__0 (System.Threading.Thread thread, System.String newName) (at <8443522375144a4485d443ee8df0a402>:0)
    7. LibEternal.Helper.ThreadHelper.Rename (System.Threading.Thread thread, System.String newName) (at <8443522375144a4485d443ee8df0a402>:0)
    Honestly, why is this the case? Why can't I just set it multiple times? What's the reasoning behind making it a write-once property?

    </rant>
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,722
    I don't know why this is, but it is certainly documented in the C# docs.

    A workaround could be to "have your own party" by simply creating a Dictionary<int, string> that contains your own personal naming scheme for threads (thread managed id -> name).
     
  3. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,741
    The fact that it's write-once is documented, but why it's write-once certainly isn't.
    upload_2020-8-10_11-37-22.png

    That's all the C# docs have to say about it. After a few minutes on Google there doesn't appear to be anyone who actually knows/can answer why this property is write-once.
     
    PraetorBlue likes this.
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    I would avoid using reflection to do this. Though I will say that from what I can tell, there is a backing field:
    https://referencesource.microsoft.com/#mscorlib/system/threading/thread.cs,1496

    Even looking at the decompiled source of mscorlib.dll found in my install of Unity shows 'm_Name' being the backing field for it.

    Still though, I would NOT go about modifying it this way. This hooks into the runtime via the InformThreadNameChange and if Microsoft decided it to be write once, they probably have a reason.

    As for what that reason is. Welp... so the documentation gods do not grant us the knowledge of. Often times things are left undocumented for various reasons from "forgot to" to "to allow changes to be made in the future without breaking compatibility. Basically... if something isn't documented, don't go around the documentation, as it could change in the future (or at least understand it could change and you may have to fix your code in due time).

    I too tried googling around for a reason and couldn't find anything.

    But I will say this... since this is not a Unity specific thing, but rather a .Net framework specific thing... in researching/asking about. The Unity context is not where I'd begin. They don't necessarily have control over this.

    What do you need to change the name for anyways? Maybe we can help come up with a work around.
     
  5. EternalClickbait

    EternalClickbait

    Joined:
    May 5, 2019
    Posts:
    22
    Actually, it is a unity specific thing. I tried using 'm_Name', which is default in .net framework. That was my main way of doing it, but in unity it doesn't actually work. If you look at my question, you can see the decompiled code, which I also put here https://github.com/EternalClickbait/LibEternal/issues/1. Unity uses a slightly modified version of mscorlib. I initially noticed because using 'm_Name' didn't work, and listing all the private instance fields wasn't showing it. Rider automatically shows me the default microsoft one, but when I used a
    Debug.Log
    on
    typeof(Thread).Assembly.Location
    , it showed a different file to the one Rider used. When I decompiled the different assembly, I saw there was no backing field, and instaed it was using
    SetName_internal
    (again, see my question). Unity changed it on purpose, I want to know why.

    I need it so I can track the threads. Mainly for logging, but also for debugging with an IDE/Debug.Log. I can create my own dictionary workaround, but it seems like so much work to create a workaround for a stupid design choice.
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,713
    I infer from this it could be any combination of the following:

    - unity implemented something that relied on enforcing this

    - unity might still require it, or might not, probably hard for them to actually tell for sure

    - unity had a bug when people changed thread names based on the above

    If you're already writing code to "track" your threads, you already have to put them in an object of some kind... make that object have an extra field called
    m_EternalClickbaits_Name
    and off you go!
     
    lordofduct likes this.
  7. EternalClickbait

    EternalClickbait

    Joined:
    May 5, 2019
    Posts:
    22
    I'm not tracking the threads. All I do is
    Thread.CurrentThread.Name
    , or let Serilog do it automatically. I would have to change a lot of code to make this change. I would love to hear from a unity dev, if anyone can @ them
     
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    When I say "it's not a Unity thing", I mean that the 'Thread' class is not written by Unity.

    Now Unity may have altered the underlying fields in Thread. Which really adds to my argument of reflecting this out is a bad idea. You really shouldn't be doing that. You may think it's "stupid" that you can't rename it, but there may be a very good reason, that reason is just not documented for you.
     
    Kurt-Dekker likes this.