Sometime ago a was trying to put some directfb code in the code of the wine
emulator. I wasn't working because of incompatibility of wine threads with
pthreads. So i modified DirectFB in order to use no additional thread (but
not thread safe).
I have just finished now a very experimental patch that should make directfb
slave apps require no additional thread, keeping IDirectFBEventBuffer calls
thread-safe (for calls to indipendent IDirectFBEventBuffer objects it should
work, for asyncronus calls to the same IDirectFBEventBuffer object i am not
very sure, but i do not even know which should be the correct behaviour...).
Beware: the master input handling stops working, but running the master with
the unmodified library and clients with the patched library is ok, so it
should not be difficult to fix this...
The patch works this way (for WaitEvent):
The first caller become a sort of master sets a mutex and "selects"
/dev/fusion, the others do "pthread_cond_wait" like before, with the
exception that if the "master" and the WaitEvent call a slave is awaken to
become a new master.
Of course in this complicated implementation there is an amazing
possiblity of adding new bugs :-)
But i tested it a bit and seems to work (a test program is attached).
It could be also possible for non-threaded apps to get the fusion file
descriptor and poll directly, so for instance toolkits like qt & gtk will no
more have to do the ugly poll/HasEvent/sleep/poll/etc
Regards
Maurizio Monge
diff -uNr DirectFB-0.9.19/src/core/fusion/fusion.c DirectFB-0.9.19-nothreads/src/core/fusion/fusion.c
--- DirectFB-0.9.19/src/core/fusion/fusion.c 2003-07-09 17:38:53.000000000 +0200
+++ DirectFB-0.9.19-nothreads/src/core/fusion/fusion.c 2003-07-29 02:29:30.000000000 +0200
@@ -57,8 +57,6 @@
#include "shmalloc/shmalloc_internal.h"
-static void *fusion_read_loop( CoreThread *thread, void *arg );
-
/**************************
* Fusion internal data *
**************************/
@@ -152,7 +150,7 @@
else
_fusion_shared = __shmalloc_get_root();
- read_loop = dfb_thread_create( CTT_MESSAGING, fusion_read_loop, NULL );
+ //read_loop = dfb_thread_create( CTT_MESSAGING, fusion_read_loop, NULL );
if (world_ret)
*world_ret = world;
@@ -252,63 +250,97 @@
(tv.tv_usec - _fusion_shared->start_time.tv_usec) / 1000;
}
-/*****************************
- * File internal functions *
- *****************************/
-static void *
-fusion_read_loop( CoreThread *thread, void *arg )
+void
+fusion_read_pending( )
{
int len = 0, result;
char buf[1024];
+ char *buf_p = buf;
+
+ len = read (_fusion_fd, buf, 1024);
+
+ buf_p = buf;
+ while (buf_p < buf + len)
+ {
+ FusionReadMessage *header = (FusionReadMessage*) buf_p;
+ void *data = buf_p + sizeof(FusionReadMessage);
+
+ switch (header->msg_type) {
+ case FMT_SEND:
+ if (!fusion_refs)
+ return;
+ break;
+ case FMT_CALL:
+ _fusion_call_process( header->msg_id, data );
+ break;
+ case FMT_REACTOR:
+ _fusion_reactor_process_message( header->msg_id, data );
+ break;
+ default:
+ FDEBUG( "discarding message of unknown type '%d'\n",
+ header->msg_type );
+ break;
+ }
+
+ buf_p = data + header->msg_size;
+ }
+ return;
+}
+
+
+void
+fusion_read_iteration( )
+{
+ int result;
fd_set set;
FD_ZERO(&set);
FD_SET(_fusion_fd,&set);
-
- FDEBUG( "entering loop...\n" );
- while ((result = select (_fusion_fd+1, &set, NULL, NULL, NULL)) >= 0 ||
+ if((result = select (_fusion_fd+1, &set, NULL, NULL, NULL)) >= 0 ||
errno == EINTR)
{
- char *buf_p = buf;
-
- FD_ZERO(&set);
- FD_SET(_fusion_fd,&set);
-
if (result <= 0)
- continue;
-
- len = read (_fusion_fd, buf, 1024);
-
- while (buf_p < buf + len) {
- FusionReadMessage *header = (FusionReadMessage*) buf_p;
- void *data = buf_p + sizeof(FusionReadMessage);
-
- switch (header->msg_type) {
- case FMT_SEND:
- if (!fusion_refs)
- return NULL;
- break;
- case FMT_CALL:
- _fusion_call_process( header->msg_id, data );
- break;
- case FMT_REACTOR:
- _fusion_reactor_process_message( header->msg_id, data );
- break;
- default:
- FDEBUG( "discarding message of unknown type '%d'\n",
- header->msg_type );
- break;
- }
+ FPERROR( "reading from fusion device failed\n" ); //error
+ fusion_read_pending( );
+ }
+ else
+ FPERROR( "reading from fusion device failed\n" );
- buf_p = data + header->msg_size;
- }
+ return;
+}
+
+int
+fusion_read_iteration_timeout( struct timeval tv )
+{
+ int result;
+ fd_set set;
+ struct timeval now;
+
+ FD_ZERO(&set);
+ FD_SET(_fusion_fd,&set);
+
+ gettimeofday( &now, NULL );
+ tv.tv_sec -= now.tv_sec;
+ tv.tv_usec -= now.tv_usec;
+ if(tv.tv_usec<0)
+ {
+ tv.tv_usec += 1000000;
+ tv.tv_sec -= 1;
}
- FPERROR( "reading from fusion device failed\n" );
+ if((result = select (_fusion_fd+1, &set, NULL, NULL, &tv)) >= 0 ||
+ errno == EINTR)
+ {
+ if (result <= 0)
+ FPERROR( "reading from fusion device failed\n" ); //error
+ fusion_read_pending( );
+ }
+ else
+ FPERROR( "reading from fusion device failed\n" );
- return NULL;
+ return 0;
}
#else
diff -uNr DirectFB-0.9.19/src/input/idirectfbinputbuffer.c DirectFB-0.9.19-nothreads/src/input/idirectfbinputbuffer.c
--- DirectFB-0.9.19/src/input/idirectfbinputbuffer.c 2003-07-02 05:08:32.000000000 +0200
+++ DirectFB-0.9.19-nothreads/src/input/idirectfbinputbuffer.c 2003-07-29 02:29:56.000000000 +0200
@@ -95,6 +95,18 @@
wait in WaitForEvent() */
} IDirectFBEventBuffer_data;
+typedef struct _wakeme
+{
+ struct _wakeme *next;
+ pthread_cond_t *cond;
+}wakeme;
+
+static wakeme *wakemelist = NULL;
+static pthread_mutex_t fusion_lock;
+static pthread_mutex_t wmlist_lock;
+static int mutexes_init = 0;
+
+
/*
* adds an event to the event queue
*/
@@ -106,9 +118,7 @@
static ReactionResult IDirectFBEventBuffer_WindowReact( const void *msg_data,
void *ctx );
-
-
-
+
static void
IDirectFBEventBuffer_Destruct( IDirectFBEventBuffer *thiz )
{
@@ -190,18 +200,113 @@
return DFB_OK;
}
+void fusion_read_pending( );
+void fusion_read_iteration( );
+int fusion_read_iteration_timeout( struct timeval );
+
static DFBResult
IDirectFBEventBuffer_WaitForEvent( IDirectFBEventBuffer *thiz )
{
- INTERFACE_GET_DATA(IDirectFBEventBuffer)
+ INTERFACE_GET_DATA(IDirectFBEventBuffer)
pthread_mutex_lock( &data->events_mutex );
-
- if (!data->events)
- pthread_cond_wait( &data->wait_condition, &data->events_mutex );
-
+ if(data->events)
+ {
+ pthread_mutex_unlock( &data->events_mutex );
+ return DFB_OK;
+ }
pthread_mutex_unlock( &data->events_mutex );
-
+
+ if(pthread_mutex_trylock( &fusion_lock )!=0) //someone else is the master
+ {
+ wakeme me;
+ wakeme **tmp;
+
+ //please let me know if you go away...
+ pthread_mutex_lock( &wmlist_lock );
+ tmp = &wakemelist;
+ me.cond = &data->wait_condition;
+ me.next = wakemelist;
+ wakemelist = &me;
+ pthread_mutex_unlock( &wmlist_lock );
+
+ do
+ {
+ //let me know if you go away *OR* there is something for me.
+ pthread_mutex_lock( &data->events_mutex );
+ pthread_cond_wait( &data->wait_condition, &data->events_mutex );
+ pthread_mutex_unlock( &data->events_mutex );
+
+ //cannot lock, only a message for me...
+ if(pthread_mutex_trylock( &fusion_lock )!=0)
+ {
+ pthread_mutex_lock( &data->events_mutex );
+
+ //there is really something for me!
+ if(data->events)
+ {
+ pthread_mutex_unlock( &data->events_mutex );
+
+ //remove signal condition
+ pthread_mutex_lock( &wmlist_lock );
+ while(*tmp)
+ {
+ if(*tmp == &me)
+ {
+ *tmp = (*tmp)->next;
+ break;
+ }
+ tmp = &((*tmp)->next);
+ }
+ pthread_mutex_unlock( &wmlist_lock );
+
+ return DFB_OK;
+ }
+ //d'oh, i was awaken and a brother quicker then me
+ // got the mutex ... -> continue waiting
+ pthread_mutex_unlock( &data->events_mutex );
+ }
+ else //got mutex, i must go to read...
+ break;
+ }
+ while(1);
+
+ //remove signal condition
+ pthread_mutex_lock( &wmlist_lock );
+ while(*tmp)
+ {
+ if(*tmp == &me)
+ {
+ *tmp = (*tmp)->next;
+ break;
+ }
+ tmp = &((*tmp)->next);
+ }
+ pthread_mutex_unlock( &wmlist_lock );
+ }
+
+ //select main loop
+ do
+ {
+ pthread_mutex_lock( &data->events_mutex );
+ if(data->events)
+ {
+ pthread_mutex_unlock( &data->events_mutex );
+
+ //i go away, someone else has to be awaken...
+ pthread_mutex_lock( &wmlist_lock );
+ pthread_mutex_unlock( &fusion_lock );
+ if(wakemelist)
+ pthread_cond_broadcast( wakemelist->cond );
+ pthread_mutex_unlock( &wmlist_lock );
+ return DFB_OK;
+ }
+ pthread_mutex_unlock( &data->events_mutex );
+ fusion_read_iteration( );
+ }
+ while (1);
+
+ //unreachable
return DFB_OK;
}
@@ -210,43 +315,136 @@
unsigned int seconds,
unsigned int milli_seconds )
{
- struct timeval now;
- struct timespec timeout;
- DFBResult ret = DFB_OK;
- int locked = 0;
- long int nano_seconds = milli_seconds * 1000000;
-
+ struct timeval tuout;
+ struct timespec tnout;
INTERFACE_GET_DATA(IDirectFBEventBuffer)
-
- if (pthread_mutex_trylock( &data->events_mutex ) == 0) {
- if (data->events) {
- pthread_mutex_unlock ( &data->events_mutex );
- return ret;
+
+ gettimeofday( &tuout, NULL );
+ tuout.tv_usec += milli_seconds*1000;
+ tuout.tv_sec += seconds + tuout.tv_usec/1000000;
+ tuout.tv_usec %= 1000000;
+ tnout.tv_sec = tuout.tv_sec;
+ tnout.tv_nsec = tuout.tv_usec*1000;
+
+ pthread_mutex_lock( &data->events_mutex );
+ if(data->events)
+ {
+ pthread_mutex_unlock( &data->events_mutex );
+ return DFB_OK;
+ }
+ pthread_mutex_unlock( &data->events_mutex );
+
+ if(pthread_mutex_trylock( &fusion_lock )!=0) //someone else is the master
+ {
+ wakeme me;
+ wakeme **tmp;
+
+ //please let me know if you go away...
+ pthread_mutex_lock( &wmlist_lock );
+ tmp = &wakemelist;
+ me.cond = &data->wait_condition;
+ me.next = wakemelist;
+ wakemelist = &me;
+ pthread_mutex_unlock( &wmlist_lock );
+
+ do
+ {
+ //let me know if you go away *OR* there is something for me.
+ pthread_mutex_lock( &data->events_mutex );
+ if( pthread_cond_timedwait( &data->wait_condition,
+ &data->events_mutex, &tnout ) )
+ {
+ pthread_mutex_unlock( &data->events_mutex );
+
+ //remove signal condition
+ pthread_mutex_lock( &wmlist_lock );
+ while(*tmp)
+ {
+ if(*tmp == &me)
+ {
+ *tmp = (*tmp)->next;
+ break;
+ }
+ tmp = &((*tmp)->next);
+ }
+ pthread_mutex_unlock( &wmlist_lock );
+
+ return DFB_TIMEOUT;
+ }
+ pthread_mutex_unlock( &data->events_mutex );
+
+ //cannot lock, only a message for me...
+ if(pthread_mutex_trylock( &fusion_lock )!=0)
+ {
+ pthread_mutex_lock( &data->events_mutex );
+
+ //there is really something for me!
+ if(data->events)
+ {
+ pthread_mutex_unlock( &data->events_mutex );
+
+ //remove signal condition
+ pthread_mutex_lock( &wmlist_lock );
+ while(*tmp)
+ {
+ if(*tmp == &me)
+ {
+ *tmp = (*tmp)->next;
+ break;
+ }
+ tmp = &((*tmp)->next);
+ }
+ pthread_mutex_unlock( &wmlist_lock );
+
+ return DFB_OK;
+ }
+ //d'oh, i was awaken and a brother quicker then me
+ // got the mutex ... -> continue waiting
+ pthread_mutex_unlock( &data->events_mutex );
+ }
+ else //got mutex, i must go to read...
+ break;
}
- locked = 1;
+ while(1);
+
+ //remove signal condition
+ pthread_mutex_lock( &wmlist_lock );
+ while(*tmp)
+ {
+ if(*tmp == &me)
+ {
+ *tmp = (*tmp)->next;
+ break;
+ }
+ tmp = &((*tmp)->next);
+ }
+ pthread_mutex_unlock( &wmlist_lock );
}
-
- gettimeofday( &now, NULL );
-
- timeout.tv_sec = now.tv_sec + seconds;
- timeout.tv_nsec = (now.tv_usec * 1000) + nano_seconds;
-
- timeout.tv_sec += timeout.tv_nsec / 1000000000;
- timeout.tv_nsec %= 1000000000;
-
- if (!locked)
+
+ //select main loop
+ do
+ {
pthread_mutex_lock( &data->events_mutex );
-
- if (!data->events) {
- if (pthread_cond_timedwait( &data->wait_condition,
- &data->events_mutex,
- &timeout ) == ETIMEDOUT)
- ret = DFB_TIMEOUT;
+ if(data->events)
+ {
+ pthread_mutex_unlock( &data->events_mutex );
+
+ //i go away, someone else has to be awaken...
+ pthread_mutex_lock( &wmlist_lock );
+ pthread_mutex_unlock( &fusion_lock );
+ if(wakemelist)
+ pthread_cond_broadcast( wakemelist->cond );
+ pthread_mutex_unlock( &wmlist_lock );
+ return DFB_OK;
+ }
+ pthread_mutex_unlock( &data->events_mutex );
+ if(fusion_read_iteration_timeout( tuout))
+ return DFB_TIMEOUT;
}
-
- pthread_mutex_unlock( &data->events_mutex );
-
- return ret;
+ while (1);
+
+ //unreachable
+ return DFB_OK;
}
static DFBResult
@@ -331,6 +529,13 @@
{
INTERFACE_GET_DATA(IDirectFBEventBuffer)
+ //noone else is reading for us...
+ if(pthread_mutex_trylock( &fusion_lock )==0)
+ {
+ fusion_read_pending();
+ pthread_mutex_unlock( &fusion_lock );
+ }
+
return (data->events ? DFB_OK : DFB_BUFFEREMPTY);
}
@@ -366,6 +571,11 @@
data->filter = filter;
data->filter_ctx = filter_ctx;
+ if(mutexes_init==0)
+ {
+ pthread_mutex_init( &fusion_lock, NULL );
+ pthread_mutex_init( &wmlist_lock, NULL );
+ }
pthread_mutex_init( &data->events_mutex, NULL );
pthread_cond_init( &data->wait_condition, NULL );
@@ -430,7 +640,6 @@
DFBFREE( item );
return;
}
-
pthread_mutex_lock( &data->events_mutex );
if (!data->events) {
@@ -474,7 +683,6 @@
const DFBWindowEvent *evt = msg_data;
IDirectFBEventBuffer_data *data = ctx;
IDirectFBEventBuffer_item *item;
-
item = (IDirectFBEventBuffer_item*)
DFBCALLOC( 1, sizeof(IDirectFBEventBuffer_item) );
#include <directfb.h>
#include <pthread.h>
IDirectFB *d;
IDirectFBDisplayLayer *l;
IDirectFBWindow *w1;
IDirectFBWindow *w2;
IDirectFBSurface *s1;
IDirectFBSurface *s2;
IDirectFBEventBuffer *e1;
IDirectFBEventBuffer *e2;
void * async_shit(void *a)
{
while(2)
{
DFBWindowEvent ev;
e2->WaitForEvent( e2 );
e2->GetEvent( e2, DFB_EVENT(&ev) );
if(ev.type == DWET_MOTION)
{
DFBRegion r = {ev.x-5,ev.y-5,ev.x+5,ev.y+5};
s2->FillRectangle(s2,ev.x-5,ev.y-5,10,10);
s2->Flip(s2,&r,0);
}
}
};
int main(int argc,char *argv[])
{
int i;
DirectFBInit(&argc,&argv);
DirectFBCreate(&d);
d->GetDisplayLayer(d, DLID_PRIMARY, &l );
{
DFBWindowDescription desc;
desc.flags= (DFBWindowDescriptionFlags)
(DWDESC_WIDTH|DWDESC_HEIGHT|//DWDESC_CAPS|
DWDESC_POSX|DWDESC_POSY|DWDESC_PIXELFORMAT);
desc.caps = DWCAPS_ALPHACHANNEL;
desc.pixelformat = DSPF_ARGB;
desc.posx = 40;
desc.posy = 40;
desc.height = 400;
desc.width = 400;
l->CreateWindow( l, &desc, &w1 );
desc.posx = 300;
desc.posy = 300;
l->CreateWindow( l, &desc, &w2 );
}
w1->SetOpacity( w1, 0xff );
w1->GetSurface( w1, &s1 );
w1->CreateEventBuffer( w1, &e1 );
s1->Clear(s1,0xff,0xff,0xff,0);
s1->SetColor(s1,0,0,0,0);
s1->Flip(s1,0,0);
w2->SetOpacity( w2, 0xff );
w2->GetSurface( w2, &s2 );
w2->CreateEventBuffer( w2, &e2 );
s2->Clear(s2,0,0xff,0xff,0);
s2->SetColor(s2,0xff,0,0,0);
s2->Flip(s2,0,0);
{
pthread_t dummy;
pthread_create(&dummy,NULL,async_shit,NULL);
}
while(1)
{
DFBWindowEvent ev;
e1->WaitForEvent( e1 );
e1->GetEvent( e1, DFB_EVENT(&ev) );
if(ev.type == DWET_MOTION)
{
DFBRegion r = {ev.x-5,ev.y-5,ev.x+5,ev.y+5};
s1->FillRectangle(s1,ev.x-5,ev.y-5,10,10);
s1->Flip(s1,&r,0);
}
}
return 0;
}