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

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).

public string Name
    {
      get
      {
        return Thread.GetName_internal(this.Internal);
      }
      set
      {
        Thread.SetName_internal(this.Internal, value);
      }
    }

I managed to create a solution to call Thread.SetName_internal to set the name, but I still get an exception that:

InvalidOperationException: Thread.Name can only be set once.
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)
Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.
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)
System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at <fb001e01371b4adca20013e0ac763896>:0)
LibEternal.Helper.ThreadHelper+<>c__DisplayClass1_1.<.cctor>b__0 (System.Threading.Thread thread, System.String newName) (at <8443522375144a4485d443ee8df0a402>:0)
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?

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).

The fact that it’s write-once is documented, but why it’s write-once certainly isn’t.
6188082--678375--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.

1 Like

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.

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.

[quote=“lordofduct, post:4, topic: 803865, username:lordofduct”]
What do you need to change the name for anyways? Maybe we can help come up with a work around.
[/quote] 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.

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!

1 Like

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

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.

1 Like