[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