[Bf-committers] Blender Command Port (does anybody know if pthread mutexes and condition variables work under windows?)
Dietrich Bollmann
diresu at web.de
Mon Apr 14 10:36:41 CEST 2008
Hi Stephen :)
On Mon, 2008-04-14 at 01:14 -0400, Stephen Swaney wrote:
> On Mon, Apr 14, 2008 at 12:09:46PM +0900, Dietrich Bollmann wrote:
>
> > Unfortunately I can only test my patch on Linux, as I do not have the
> > money to pay for a working MS development environment. Hope the
> > MS port is not too difficult though :)
>
> Adding -Wdeclaration-after-statement to your compiler warning flags
> will go a long way towards helping you to remember you are trying
> to write standard C.
Thanks, I am using the following compile options:
gcc
-o /home/dietrich/blendev/working/branches/bcp/build/linux2/source/blender/commandport/blender/src/bcp_blender.o -c -pipe -fPIC -funsigned-char -fno-strict-aliasing -O2 -Wall -Wno-char-subscripts -Wdeclaration-after-statement -DXP_UNIX
...snip...
Do you think I should add anything more?
The more important doubt I have is about the mutex and condition
variable statements I use.
I found the following:
- "Pthread Support in Microsoft Windows Services for UNIX Version 3.5"
http://technet.microsoft.com/en-us/library/bb463209.aspx
Abstract
The Microsoft® Windows® Services for UNIX (SFU) 3.5 product is a
collection of software packages for UNIX users and administrators who
need to work with or on Windows platforms. It includes cross-platform
network services that allow you to integrate your Windows® and
UNIX-based environments together. It also includes a complete UNIX
system environment called Interix that installs and runs on Windows,
co-existing with the Windows subsystem. This environment comes with
hundreds of UNIX utilities, such as ksh, csh, awk and telnet, and a
complete C and C++ programming development environment for UNIX
applications. With the release of SFU 3.5, this development
environment now includes support for POSIX threads (Pthreads) and
the POSIX semaphore functions.
with paragraphs about how to use mutexes and condition variables under
windows.
Here is still another document:
- POSIX Threads for Windows – REFERENCE - Pthreads-w32
http://sourceware.org/pthreads-win32/manual/pthread_mutex_init.html
Do you know if this means that the code works as it is (or more or less
as it is) under windows?
Here for what I use mutexes and pthreads in my code - and what hopefully
also works under windows?:
/* mutex to protect struct against
simultaneous access by BCP and Blender threads */
pthread_mutex_t mutex;
/* thread condition variable to implement a master/servant model:
- The BCP thread waits on the condition for the result of his
request
- The Blender thread fills in the result slot
and signals the BCP that his run request has been finished
and the result can be read.
*/
pthread_cond_t condition;
Unfortunately I can't test neither the code nor the scons build files
for windows myself :(
Thanks, Dietrich
Here the relevant files (shortened):
--------------------------------------------------------------------------------
~/blendev/working/branches/bcp/blender/source/blender/src/mainqueue.c
Making the blender main event queue thread save:
--------------------------------------------------------------------------------
/*
* $Id: mainqueue.c 12931 2007-12-17 18:20:48Z theeth $
...snip...
* ***** END GPL/BL DUAL LICENSE BLOCK *****
*
* Just the functions to maintain a central event
* queue.
*/
#include <stdlib.h>
#include <string.h>
#ifdef WITH_MULTI_THREADING_SUPPORT
#include <pthread.h>
#endif
#include <stdio.h>
#include "BIF_mainqueue.h"
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WITH_MULTI_THREADING_SUPPORT
/**
Using a mutex to protect the mainqueue against simultaneous access
by different threads:
*/
pthread_mutex_t mainqueue_mutex = PTHREAD_MUTEX_INITIALIZER;
/**
Locking the mutex
protecting the mainqueue against access by multiple threads.
*/
void lock_mainqueue_mutex()
{
if ( pthread_mutex_lock( &mainqueue_mutex ) ) {
perror( "Error: couldn't lock the mainqueue mutex!" );
exit( 1 );
}
}
/**
Unlocking the mutex
protecting the mainqueue against access by multiple threads.
*/
void unlock_mainqueue_mutex()
{
if ( pthread_mutex_unlock( &mainqueue_mutex ) ) {
perror( "Error: couldn't unlock the mainqueue mutex!" );
exit( 1 );
}
}
#endif
typedef struct {
unsigned short event;
short val;
char ascii;
#ifdef WITH_COMMAND_PORT
void* args;
#endif
} QEvent;
static QEvent mainqueue[MAXQUEUE];
static unsigned int nevents= 0;
unsigned short mainqread(short *val, char *ascii)
{
void* dummy;
return mainqread_args(val, ascii
#ifdef WITH_COMMAND_PORT
, &dummy
#endif
);
}
unsigned short mainqread_args(short *val, char *ascii
#ifdef WITH_COMMAND_PORT
, void** args
#endif
)
{
unsigned short event = 0;
#ifdef WITH_MULTI_THREADING_SUPPORT
lock_mainqueue_mutex();
/* begin of critical section */
#endif
if (nevents) {
nevents--;
*val = mainqueue[nevents].val;
*ascii = mainqueue[nevents].ascii;
#ifdef WITH_COMMAND_PORT
*args = mainqueue[nevents].args;
#endif
if((*ascii<32)||(*ascii==127)) *ascii=0;
event = mainqueue[nevents].event;
}
#ifdef WITH_MULTI_THREADING_SUPPORT
/* end of critical section */
unlock_mainqueue_mutex();
#endif
return event;
}
void mainqenter(unsigned short event, short val)
{
mainqenter_args(event, val, 0
#ifdef WITH_COMMAND_PORT
, NULL
#endif
);
}
void mainqenter_ext(unsigned short event, short val, char ascii)
{
mainqenter_args(event, val, ascii
#ifdef WITH_COMMAND_PORT
, NULL
#endif
);
}
void mainqenter_args(unsigned short event, short val, char ascii
#ifdef WITH_COMMAND_PORT
, void* args
#endif
)
{
if (!event)
return;
#ifdef WITH_MULTI_THREADING_SUPPORT
lock_mainqueue_mutex();
/* begin of critical section */
#endif
if (nevents<MAXQUEUE) {
memmove(mainqueue+1, mainqueue, sizeof(*mainqueue)*nevents);
mainqueue[0].event = event;
mainqueue[0].val = val;
mainqueue[0].ascii = ascii;
#ifdef WITH_COMMAND_PORT
mainqueue[0].args = args;
#endif
nevents++;
}
#ifdef WITH_MULTI_THREADING_SUPPORT
/* end of critical section */
unlock_mainqueue_mutex();
#endif
}
void mainqpushback(unsigned short event, short val, char ascii)
{
mainqpushback_args(event, val, ascii
#ifdef WITH_COMMAND_PORT
, NULL
#endif
);
}
void mainqpushback_args(unsigned short event, short val, char ascii
#ifdef WITH_COMMAND_PORT
, void* args
#endif
)
{
#ifdef WITH_MULTI_THREADING_SUPPORT
lock_mainqueue_mutex();
/* begin of critical section */
#endif
if (nevents<MAXQUEUE) {
mainqueue[nevents].event = event;
mainqueue[nevents].val = val;
mainqueue[nevents].ascii = ascii;
#ifdef WITH_COMMAND_PORT
mainqueue[nevents].args = args;
#endif
nevents++;
}
#ifdef WITH_MULTI_THREADING_SUPPORT
/* end of critical section */
unlock_mainqueue_mutex();
#endif
}
unsigned short mainqtest()
{
unsigned short event = 0;
#ifdef WITH_MULTI_THREADING_SUPPORT
lock_mainqueue_mutex();
/* begin of critical section */
#endif
if (nevents)
event = mainqueue[nevents-1].event;
#ifdef WITH_MULTI_THREADING_SUPPORT
/* end of critical section */
unlock_mainqueue_mutex();
#endif
return event;
}
/* fin */
--------------------------------------------------------------------------------
and:
--------------------------------------------------------------------------------
~/blendev/working/branches/bcp/blender/source/blender/commandport/blender/src/bcp_blender.c
The client thread Waits for the result of the evaluation of the python
code inserted into the blender event main queue on a condition variable
which is unlocked after the code has been processed by the server thread
(blender main thread):
--------------------------------------------------------------------------------
/* a structure to pass arguments between
command port and blender thread
and to synchronize them.
*/
typedef struct bcp_blender_handler_struct {
...snip...
/* mutex to protect struct against
simultaneous access by BCP and Blender threads */
pthread_mutex_t mutex;
...snip...
} bcp_blender_handler_struct;
...snip...
/**/
bcp_blender_handler bcp_blender_handler_new() {
...snip...
pthread_mutex_init( &handler->mutex, NULL );
pthread_cond_init( &handler->condition, NULL );
return handler;
}
...snip...
/**/
void bcp_blender_handler_delete(bcp_blender_handler handler)
{
...snip...
pthread_mutex_destroy( &handler->mutex );
pthread_cond_destroy( &handler->condition );
...snip...
}
...snip...
/**/
char* bcp_blender_handle_input_allocate(bcp_blender_handler handler,
char* command)
{
...snip...
/* get the condition variable and the mutex
to synchronize command port and blender thread
*/
pthread_mutex_t* mutex = &handler->mutex;
pthread_cond_t* condition = &handler->condition;
...snip...
/* wait on the condition variable for the command being executed and
the result being returned
*/
if (pthread_mutex_lock(mutex)) {
fprintf(stderr, "[BCP] ERROR: BCP thread couldn't lock the result
mutex!\n");
exit(ERROR_SYNCHRONIZATION);
}
if (pthread_cond_wait(condition, mutex)) {
fprintf(stderr, "[BCP] ERROR: BCP thread couldn't wait on the result
condition!\n");
exit(ERROR_SYNCHRONIZATION);
}
/* get the result */
char* result = handler->result;
if (pthread_mutex_unlock(mutex)) {
fprintf(stderr, "[BCP] ERROR: BCP thread couldn't unlock the result
mutex!\n");
exit(ERROR_SYNCHRONIZATION);
}
...snip...
void bcp_blender_queue_handle_input_allocate(void* args)
{
...snip...
pthread_mutex_t* mutex = &handler_args->mutex;
pthread_cond_t* condition =
&handler_args->condition;
...snip...
/* signal the BCP thread that the result is available */
if (pthread_mutex_lock(mutex)) {
fprintf(stderr, "[BCP] ERROR: Blender thread couldn't lock the result
mutex!\n");
exit(ERROR_SYNCHRONIZATION);
}
/* set the result pointer */
handler_args->result = result;
/* the result has been calculated - awake the BCP thread to continue */
if (pthread_cond_signal(condition)) {
fprintf(stderr, "[BCP] ERROR: Blender thread couldn't signal the
result condition!\n");
exit(ERROR_SYNCHRONIZATION);
}
if (pthread_mutex_unlock(mutex)) {
fprintf(stderr, "[BCP] ERROR: Blender thread couldn't unlock the
result mutex!\n");
exit(ERROR_SYNCHRONIZATION);
}
}
--------------------------------------------------------------------------------
More information about the Bf-committers
mailing list