/* #Specification: uithread / principles
uithread means "User Interface Thread". They are less generic threads
and potentially less problematic ones as well. They exist so multiple
dialog may interact asynchronoulsly. These dialogs are using a very
simple programming model called modal programming. uithread allows
one to keep this simple model and yet produce fancy GUI software as well.
Uithreads are created using the uithread function. This function
accept a function pointer and optionally, a void pointer. In the first
case, the function pointer is of type
void (*fct)();
while in the other case, it is of type
void (*fct)(void *)
A thread may be in 3 states: Starting, running, and waiting. The following
restriction also exists:
- Only one thread may be running at once. All the others are either
in the starting mode or waiting.
- Waiting threads are always waiting at the same place. They are
waiting in diagui_wait().
- A thread can't be preempted.
So uithreads are simpler to use then ordinary threads. They are less
flexible for sure.
*/
#include
#include
#include
#include
#include "internal.h"
#include "dialog.h"
#include "dialog.m"
static const int MAX_UITR=100; // Maximum number of uithreads
static jmp_buf tbjmp[MAX_UITR];
static void (*tbfct[MAX_UITR])(void *);
static void *tbdata[MAX_UITR];
static LINUXCONF_CONTEXT tbctx[MAX_UITR];
static short tbparent[MAX_UITR];
static bool tbactif[MAX_UITR];
static int nbid = 0;
static jmp_buf reeditjmp[MAX_UITR];
static bool tbyield[MAX_UITR];
static unsigned *tbcheck[MAX_UITR]; // Used to detect stack overrun
// (One thread has over-used its stack)
int uithread_id = 0;
#ifdef TEST
static int uithread_editfct()
{
printf ("Active thread : ");
for (int i=0; i<=nbid; i++){
if (tbactif[i]) printf ("%d ",i);
}
printf ("\n");
int ret;
while (1){
char buf[10];
printf ("Entrez a thread number : ");
fgets (buf,sizeof(buf)-1,stdin);
int ctx = atoi(buf);
if (ctx <= nbid && tbactif[ctx]){
ret = ctx;
break;
}
}
return ret;
}
#endif
/*
Start one new thread if any.
A thread is always in one of 3 states
1-Not used: tbfct[i] == NULL
2-Used, but inactive (not started yet)
In this case, we start it by switching to it. This function
will never regain control as the current thread will regain
control at a different place
3-active, waiting for an input
*/
static void uithread_startnew ()
{
for (int i=1; i<=nbid; i++){
if (!tbactif[i] && tbfct[i] != NULL){
uithread_id = i;
longjmp (tbjmp[i],1);
break;
}
}
}
/*
Wait for all User interface thread to meet here
and then call the input function editfct().
The editfct must return the ID of the thread which must resume.
*/
void uithread_sync (int (*editfct)())
{
static jmp_buf retjmp[MAX_UITR];
tbctx[uithread_id].set (ui_context);
if (!setjmp(retjmp[uithread_id])){
//int ctx = uithread_id;
//printf ("edit thread %d %p\n",uithread_id,&ctx);
tbactif[uithread_id] = true;
if (!setjmp(reeditjmp[uithread_id])){
uithread_startnew ();
}
uithread_id = -1;
for (int i=0; i 0
&& uithread_id <= nbid
&& tbfct[uithread_id] != NULL){
break;
}
}
}
ui_context.set (tbctx[uithread_id]);
longjmp (retjmp[uithread_id],1);
}
}
/*
Reserve a stack area for a new thread.
This simply allocate a large array on the stack. Nothing else.
No fancy assembly tricks.
This function precreate all the thread stack area.
This function is called only once at the begin and it will recurse
until all the stack have been created.
*/
static void uithread_setup (int nbtr)
{
/* #Specification: uithread / stack size
All uithread's are created at startup time (using uithread_init
itself called by diagui_init...). A fixed amount is created (20).
Each thread gets a 100000 bytes stack. Real memory is not really
allocated though. This 100000 is arbitrary. A much larger value
could probably used I guess (few megs).
Allocation thread stack as needed is probably doable, although
not as easily I guess (less portable). Input welcome.
*/
static int notr = 1;
char stack[100000];
tbactif[notr] = false;
tbfct[notr] = NULL;
tbcheck[notr] = (unsigned*)(stack);
*tbcheck[notr] = 0xdeadbeef;
if (setjmp(tbjmp[notr])){
int curid = uithread_id;
ui_context.set (tbctx[curid]);
(*tbfct[curid])(tbdata[curid]);
help_context_delmark (curid);
//fprintf (stderr,"Thread %d termine\n",curid);
tbactif[curid] = false;
tbfct[curid] = NULL;
uithread_startnew();
// No new thread. Give control to one of the thread in edit mode
for (int i=0; i<=nbid; i++){
if (tbactif[i]) longjmp (reeditjmp[i],1);
}
fprintf (stderr,"No more thread!!!!\n");
exit (-1);
// When this come back, we kill the last thread
}else if (notr < nbtr){
notr++;
uithread_setup (nbtr);
}
}
/*
Initialise the user interface thread with the amount of thread
which will be used. For now it can't grow.
It is believed that an application with more than say 20 user interface
thread (no necessarily windows) is over complicate for anyone to manage.
*/
EXPORT void uithread_init (int nbtr)
{
static bool done = false;
if (!done){
nbid=nbtr;
uithread_setup (nbtr);
done = true;
}
}
EXPORT bool uithread_check()
{
bool ret = true;
for (int i=1; i 0 && id < MAX_UITR){
ret = tbparent[id];
}
return ret;
}
/*
Return the parent thread ID of the current thread
*/
int uithread_getparent()
{
return uithread_getparent(uithread_id);
}
/*
Register a new thread.
The fct function will start the processing for the thread.
It return the ID number of the new thread.
*/
EXPORT int uithread (void (*fct)(void *), void *data)
{
int ret = -1;
/* #Specification: uithread / GUI mode
Uithreads only work in GUI mode. In the other modes (HTML, Text)
the uithread function simply call the thread function and wait
until it complete. At some point, uithreads could be implement
in text mode, but we would need a way for the user to switch between
threads, maybe using a task bar at the top of the screen.
*/
if (dialog_mode == DIALOG_GUI
&& !uithread_modal
&& (ui_context.treejump_level == 0
|| ui_context.treejump_level < ui_context.treemenu_level)){
ret = uithread_ok (fct,data);
}else{
LINUXCONF_CONTEXT tmp (ui_context);
(*fct)(data);
ui_context.set(tmp);
ret = 0;
}
return ret;
}
EXPORT int uithread (void (*fct)())
{
return uithread ((void (*)(void*))fct,NULL);
}
/*
Let all the other threads run. This function will return as soon
as all the other thread reach the focus point (the UI event loop
generally)
*/
EXPORT void uithread_yield()
{
if (dialog_mode == DIALOG_GUI){
tbyield[uithread_id] = true;
// fprintf (stderr,"thread yielding %d\n",uithread_id);
uithread_sync (NULL);
// fprintf (stderr,"thread yield continue %d\n",uithread_id);
}
}
#ifdef TEST
static void test0(const char *nom)
{
int nb = 0;
while (1){
uithread_sync (uithread_editfct);
printf ("edit %s %d\n",nom,nb++);
if (nb == 3) break;
}
}
static void test1(void *)
{
test0 ("test1");
}
static void test2(void *)
{
test0 ("test2");
}
static void test3(void *)
{
test0 ("test3");
}
int main (int, char *[])
{
uithread_init(20);
uithread(test1,"");
uithread(test2,"");
uithread(test3,"");
test0 ("main 0");
return 0;
}
#endif