[ECOS] "Threads on ecos" confirmation needed...
Ivan Horvat
ihorvat@xylon.hr
Mon Feb 2 19:09:00 GMT 2004
Nick,
Thanks for the extensive reply.
>It is a basic design principle of eCos that the kernel should not get
>involved in memory allocation issues. Thus all kernel functions need
>the memory that they are going to use to be supplied by the
>application, or some higher level library.
>
>Different applications have very different needs as far as memory use
>is concerned, it would be wrong for the kernel to pre-judge that. It
>makes much more sense that memory usage decisions are made at a higher
>level. This also means that there are a whole set of error conditions
>and synchronization issues that the kernel does not have to deal with,
>keeping it simple.
>
>
I agree with this. However in ecos, the borderline between the "user"
and "kernel"
context is a quite "fuzzy", not as sharp as on general purpose OS-es.
Sometimes, this
creates confusion and misunderstanding.
>There is no limit at all on the number of threads allowed in eCos, so
>long as you have the memory for the thread objects and their
>stacks. There is a configurable limit on the number of POSIX threads
>that may be in existence at any one time, but you can increase this to
>any value.
>
>
Yes, I realized that also from some previous posts. I obviously confused
it with
the number of posix threads, or non-default scheduler...
>You can easily do all of this outside the kernel. For example:
>
>
I assume "outside the kernel" means at the level of cyg_thread_*()
functions?
>The one thing that you cannot do within a thread is to have it call
>free() to release its thread object or stack. You would then end up
>with a thread running in unallocated memory, which might be
>reallocated before it successfully exits. Also, the last thing that
>exit() does is context switch to another thread, for which we have to
>pass through the scheduler. Going through the scheduler with an
>invalid current thread would require all sorts of nasty special cases
>to be added. This is why we have a two-phase thread shutdown
>mechanism: exit() puts the thread to sleep and detaches it from the
>scheduler data structures; the final destruction of the thread and the
>freeing of its resources must then be done from another thread after
>the exit() has succeeded.
>
>
Actually, we are speaking about the same things, for the most of the
part. The
my_new_thread(...) is actually replacement for cyg_thread_create(), as I
suggested.
I made a mistake saying "...Cyg_Thread::exit() should be also
modified...", as it
is not necessary. All that is needed is modification to cyg_thread_exit().
So, I would slightly change Your implementation of my_new_thread():
Cyg_Thread thr_slots[MAX_THREADS];
void *thr_stacks[MAX_THREADS]; // CHANGED
cyg_uint32 thr_map = 0;
int thread; // MOVED HERE, NOW GLOBAL
Cyg_Thread *my_new_thread(...)
{
Cyg_Scheduler::lock();
HAL_LSBIT_INDEX( thread, thr_map );
thr_map &= 1<<thread;
Cyg_Scheduler::unlock();
thr_stacks[thread] = malloc(STACKSIZE); // ADDED
Cyg_Thread *t = new((void *)&thr_slots[thread]) Cyg_Thread
(... , thr_stacks[thread], ...);
return t;
}
This being, of course, exemplary skeleton.
Besides, isn't this limited to 32 threads, the number of bits available
in "cyg_uint32 thr_map",
so that the MAX_THREAD can be at most 32?
>Linux and Solaris are full kernel/user context operating systems. But
>they must still get out of the context of the exiting thread to clean
>it up. To do this they go into kernel mode. We don't have this
>kernel/user dichotomy, and the only other contexts we have available
>are other threads.
>
That's exactly my goal: providing another context for freeing the stack
but not
the other therads' context...
>What you are suggesting here would be very architecture-specific and
>incredibly fragile -- any change in the compiler might wreck it. While
>it might work on register-rich architectures like MIPS or PowerPC, it
>would have difficulties on smaller machines like ARM or SH4, and would
>be impossible on IA32.
>
>
>Again, this seems like a very fragile mechanism. Another design
>feature of eCos is that all code (except the initial HAL startup) runs
>in the context of a valid thread. Executing code in this proposed
>twilight zone would raise all sorts of problems throughout the rest of
>the kernel and other subsystems.
>
>In any case we already have other stacks available to us for this
>purpose, they belong to other threads, so why not use them?
>
>
It would definitely be platform specific, but this suits my needs as I'm
interested
only in SPARC, PPC and especially MIPS platform. However, I don't agree
that this
would be a very fragile mechanism. Inline "asm" macros and "noreturn"
attribute are *well*
supported by the gcc (and they will remain to be supported bu future
versions).
For example, on MIPS, I implemented a mechanism that enables the
remote-debugged application to
pass the control back to the redboot while redboots' stabs notify gdb
about application exit
so the gdb reports "Program exited normally.". That required small
amount of code in
generic-stub.c, adding one HAL virtual vector entry which is used by the
application to tell
redboots' stabs that it wants to exit, followed by "asm volatile
("\tbreak 0x5\n");", which is the trigger.
After that, redboot prompt (console), or the same gdb can be *reused* to
load the same or new
program (which avoids power-cycling) allowing comfortable work with GUI
IDE's built
using Eclipse, etc.
The MIPS my_exit() can be implement like this (skeleton again):
int exit_stack[STACKSIZE/sizeof(int)]; // ADDED
void (*exit_fn)(void) __attribute__((noreturn)); // ADDED
__attribute__((noreturn)) void my_exit() // MODIFIED
{
Cyg_Scheduler::lock(); // REORDERED WITH THE FOLOWING LINE
thread = Cyg_Thread::self()-&thr_slots[0]; // REORDERED WITH THE PREVIOUS LINE
thr_map |= 1<<thread;
exit_fn = thread->exit;
// load pre-exit stack
asm volatile ("\tmove $29, %0\n": : "r"(exit_stack): "$29");
/* Now, we are running on alternate stack... */
// free the threads' stack
free(thr_stacks[thread]);
(*exit_fn)();
}
Now, "thread" and exit method are in global variables, locking scheduler
ensures that no
other exiting thread would get the chance to enter the "critical
section" of setting the new stack
and messing it up. After switching to alternate stack, normal stack can
be freed. Then the
"exit" method would do its job and the scheduler lock will be removed
after calling
Cyg_Scheduler::reschedule(); at the end of Cyg_Thread::exit(). Maybe
there are some
"twilight zone" elements, but the HAL_THREAD_SWITCH_CONTEXT() and its friend
HAL_THREAD_LOAD_CONTEXT() certainly qualify for the Twilight Zone on
*any* platform,
anyway. :-)
But then again, maybe I have some serious flaw(s)?
>One reason for not doing anything in the idle thread is that the
>scheduler relies on the idle thread to always be executable. If the
>idle thread is doing anything that would require it to synchronize
>with other threads, then it might have to sleep. This would seriously
>complicate the scheduler.
>
Thanks for this information, I was unaware of that requirement for the
idle thread.
Regards,
Ivan.
--
Before posting, please read the FAQ: http://sources.redhat.com/fom/ecos
and search the list archive: http://sources.redhat.com/ml/ecos-discuss
More information about the Ecos-discuss
mailing list