You should always be looking at the m_interpreter for the correct execution context to use. The selected thread is for use in the command interpreter:
(lldb) thread select 12
Now when <command> is executed, it might cause the process to run (as is happening in your case), and the selected thread might change or get invalidated.
In this case the selected thread (exe_ctx.process->GetThreadList().GetSelectedThread()) matches the interpreter thread (m_interpreter.GetExecutionContext().thread), but this isn't always true.
So at the start of your command execution, you should remember the thread (and the frame if you need that) that was selected if that is important to you by storing it away (copy the thread shared pointer) in your CommandObject subclass. Later you might also need to verify that your thread is still alive and well by checking for the thread in the process using the
ThreadList::FindThreadByIndexID (uint32_t index_id, bool can_update);
The index id is a unique thread index that will continue to increase as new threads come and go. This guarantees that if a system can re-use thread ids (not thread index ids, but thread ids or tid), that if a thread comes and goes that we can track that we were actually trying to step on the correct thread.
Other answers below:
Is it normal for these two threads to differ?
Very normal. The interpreter's execution context is what you should always be using.
exe_ctx = m_interpreter.GetExecutionContext();
exe_ctx.thread: 0x107900610, exe_ctx.process->GetThreadList().GetSelectedThread(): 0x0
Also, if we only have one thread, shouldn't that thread always be selected?
Yes, but then your code would fail if there were more than one thread where thread 1 was selected yet somehow you tried to return from thread 2. This could be possibly done as:
(lldb) thread select 1
(lldb) thread return --thread 2 <return value>
The currently selected thread would be 1, but your command should be acting on thread 2...
Somehow the ThreadList.m_selected_thread is being set to LLDB_INVALID_THREADID right after I run my command.
Again, don't rely on the selected thread, use the interpreter's execution context as the truth, as anything that causes the program to execute can change what thread is selected.
For example if you run from thread 1 and hit a breakpoint in thread 2, the selected thread will be changed to thread 2 when the process stop event is delivered...
So the quick rules are:
1 - trust the execution context in the interpreter as truth
2 - if you run the program in any way, the execution context in m_interpreter might change
3 - save any interpreter execution context you may need in your command prior to resuming the target in any way and verify your process, thread or frame objects are still around after you stop again to make sure the process/thread didn't exit, or to make sure you didn't go higher in the stack for frames where your frame doesn't exist anymore...
Does this help?