need idea on thread_exit()

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
bluemoon
Member
Member
Posts: 1761
Joined: Wed Dec 01, 2010 3:41 am
Location: Hong Kong

need idea on thread_exit()

Post by bluemoon »

First, some description on what I wanted to do:

I create a process or kthread, with a thread control block that has an initial construction on stack so that it will "resume" to thread's entry and upon return it will call thread_exit(TCB*)

On 32-bit system it looks like this on the initial stack:
[TCB* tcb]
[TCB* tcb]
[thread_exit]
[thread_func]

So, upon context switch, it will "return" to thread_func, and thread_func will return to thread_exit, the interface will be:

Code: Select all

void thread_func(TCB* tcb) {
   // do some stuff
}
void thread_exit(TCB* tcb) {
//...
}
It all work fine.

However, on x86_64 the AMD ABI uses register to pass parameter, and it's not preserved across function calls.
I then come up with ugly hack that:
stack construction:
[TCB* tcb]
[thread_exit]
[thread_func]
RDI := tcb

Once context switch it successfully "return" to thread_func, and thread_func returns to thread_exit,
but to retrieve TCB* I now doing this:

Code: Select all

void kthread_test(PROCESS* process) {
    for (int i=0; i<(int)process->pid; i++) {
        kprintf ("KMAIN : thread(%d): %d\n", process->pid, i);
        scheduler_sleep(1000);
    }
}
void process_exit (void) { // PROCESS *process ) {
    PROCESS *process;
    __asm volatile ("pop %0" : "=A"(process) );
    if ( process == NULL ) return;
    process->flags |= PROCESS_FLAG_ZOMBIE;
    for(;;) {
        scheduler_sleep(0); // yield
    }
} 
It works, but the assembly pop it is ugly and make this code architecture specific. I wonder if there is more elegant way to do this?

PS. for simplicity I current test with PROCESS* which contain only single thread.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: need idea on thread_exit()

Post by gerryg400 »

I'm not sure I understand. The problem is that you can't pass the TCB* parameter to thread_exit ?
If a trainstation is where trains stop, what is a workstation ?
User avatar
bluemoon
Member
Member
Posts: 1761
Joined: Wed Dec 01, 2010 3:41 am
Location: Hong Kong

Re: need idea on thread_exit()

Post by bluemoon »

yes.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: need idea on thread_exit()

Post by gerryg400 »

When my threads are created, the kernel puts a return address on the stack but I use a small assembly function that moves the value to the correct reg like this. Note that in my case the parameter to thread_exit is the value return by the thread function. Yours will be slightly different.

Code: Select all

.code64
.text
        .globl    __pthread_return_func

__pthread_return_func:

        /* This function is 'called' when a thread returns. On x86 a pointer
         * to it is on the stack of every thread and is popped off when the
         * thread entry function returns.
         *
         * We will call pthread_exit.
         *
         * The only other thing we need to do is move the value that would
         * have been returned, from the return register (eax on i386,
         * rax on x86_64) to wherever the first function parameter would be
         * (stack on i386, rdi on x86_64)
         */

        movq    %rax, %rdi
        callq   pthread_exit
In your case you would probably use a popq then a movq.
If a trainstation is where trains stop, what is a workstation ?
Rudster816
Member
Member
Posts: 141
Joined: Thu Jun 17, 2010 2:36 am

Re: need idea on thread_exit()

Post by Rudster816 »

Have you thought of just storing a pointer to the PROCESS* structure for the active thread on a particular processor in global variable? Any kernel routine could access it to check process privileges, make changes, etc, without having to access it the pointer from the stack.

e.g.

Code: Select all

active_process[THIS_CPU]->flags |= PROCESS_FLAG_ZOMBIE;

You'll completely get rid of any ABI assumptions, because many other ABI's (not just AMD64 SystemV) use registers to pass parameters.
User avatar
bluemoon
Member
Member
Posts: 1761
Joined: Wed Dec 01, 2010 3:41 am
Location: Hong Kong

Re: need idea on thread_exit()

Post by bluemoon »

@Rudster816: sounds good. I have such global variable for the scheduler anyway. It solve the ABI issue.

Code: Select all

void process_exit (void) {
    PROCESS* process = scheduler_current();
    process->flags |= PROCESS_FLAG_ZOMBIE;
    for(;;) {
        scheduler_sleep(0); // yield
    }
}
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: need idea on thread_exit()

Post by Combuster »

The alternative would be to set up the frame for just the kernel thread, and have the thread explicitly call exit. Exit would then terminate the thread as a whole so that the return into nothingness doesn't get executed. The resulting application would end up like the following:

Code: Select all

void thread_func(TCB* tcb) {
   
   // do some stuff

   thread_exit(tcb);
}
void thread_exit(TCB* tcb) {
   //...
}
It's not possible to write a thread creation routine without ABI specifics, because processor state must be explicitly manipulated in all cases. Globals allow for only one kthread, and fixing that with TLS requires much more ABI-specific support code. Killing the kthread is similarly ABI-specific plus it shouldn't significantly care about what stack state it happens in.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
AndrewAPrice
Member
Member
Posts: 2303
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Re: need idea on thread_exit()

Post by AndrewAPrice »

My instinct would tell me to make a single thread entry point function that calls their thread function, and upon returning exits the thread:

Code: Select all

typedef void (*ThreadFunc)  (void *userData);
void ThreadEntryPoint(ThreadFunc threadFunc, void *userData)
{
   threadFunc(userData);
   ThreadExit();
}
Then the user could do something like:

Code: Select all

void work(void *userData) {
    printf("Hello from thread %i\n", threadNo);
    // do work
    printf("I'm going to disappear when I return!\n");
}

// somewhere later
int threadNo;
for(threadNo = 0; threadNo < 10; threadNo++)
    syscall_CreateThread(&work, threadNo);
My OS is Perception.
rdos
Member
Member
Posts: 3307
Joined: Wed Oct 01, 2008 1:55 pm

Re: need idea on thread_exit()

Post by rdos »

I really dislike the idea of keeping scheduler-related information (TCB) on the stack for a thread to thrash. Kernel thread or not.
User avatar
xenos
Member
Member
Posts: 1121
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: need idea on thread_exit()

Post by xenos »

My approach is very similar to Rudster816's suggestion. Whenever a thread calls ExitThread (or ExitProcess), it doesn't need to pass its thread ID or even a pointer to the TCB as a parameter, since the scheduler simply knows which thread is currently running and requesting to be terminated. My threads / processes need to call ExitThread / ExitProcess explicitly, because the kernel does no a priori know whether only one thread or the whole process (which may contain more than one thread) should be terminated when the thread function returns, so it cannot decide whether to push ExitThread or ExitProcess as a return address onto the stack.
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
Post Reply