[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ] [ Search: ]

6.2 Threads in Crystal Space

Written by Jorrit Tyberghein and Michael Gist.

The Crystal Space project as such is not thread-safe. So when using threads in combination with Crystal Space you have to be careful. However, several parts of Crystal Space are thread-safe to some degree. This is documented in this section. However, by default you must assume that some Crystal Space plugin or module is not thread-safe unless it is mentioned here.

Threadsafe parts of Crystal Space

Thread Manager

Crystal Space provides a thread manager and macros to make multi-threading as easy as possible for the user. The thread manager is a powerful and generic system which supports safe scaling of threads and importantly allows non-main threads to safely dispatch method calls to be executed by other non-main threads. In essence, if you write a plugin which correctly uses this system it will increase in parallelism as time goes by (cpus increasingly being released with larger numbers of cores) without you having to change anything to compensate (up to some point which depends on your plugin).

Interfaces

To declare that you want a method in your interface to be run in a thread other than the main thread, you need to make use of the ‘THREADED_INTERFACE’ macro. This is rather straightforward as shown;

 
#include "iutil/threadmanager.h"

struct iThreadTest : public virtual iBase
{
  THREADED_INTERFACE(Test1);
  THREADED_INTERFACE1(Test2, bool b);
  THREADED_INTERFACE2(Test3, int i, float f);
}

This will add extra information to your method, such as a special return type.

Class and Method declaration

Inside your class, to declare a method as being threaded you need to do two things:

The ‘THREADED_CALLABLE_DECL’ macro is what kicks everything off. It creates extra functions to dispatch the method call off to the thread manager, and provides a declaration to your function. It takes multiple parameters:

Be very careful when dispatching to the main thread and waiting on it to finish. E.g. If you're holding a lock which the main thread is currently waiting on, you will cause a deadlock by calling such a method.

 
#include "csutil/threadmanager.h"

class csThreadTest :
  public ThreadedCallable<csThreadTest>,
  public scfImplementation1<csThreadTest, iThreadTest>
{
public:
  csThreadTest(iObjectRegistry* objReg);

  iObjectRegistry* GetObjectRegistry() const
  { return objReg; }

  THREADED_CALLABLE_DECL(
    csThreadTest, Test1, csThreadReturn, true, false)
  THREADED_CALLABLE_DECL1(
    csThreadTest, Test2, csThreadReturn, bool, b, true, false)
  THREADED_CALLABLE_DECL2(
    csThreadTest, Test3, csThreadReturn, int, in, float, flo,
    true, false)

private:
  iObjectRegistry* objReg;
}

Method definition

Now that you have your class set up and methods declared, you need to define them. You do this using the ‘THREADED_CALLABLE_IMPL’ macro. The first parameter is the class name. The second is the method name and the rest are your variables as normal.

The ‘return’ here is actually a provided object, as you will read more about below. This is called ‘ret’.

 
THREADED_CALLABLE_IMPL(csThreadTest, Test1)
{
}

THREADED_CALLABLE_IMPL1(csThreadTest, Test2, bool b)
{
}

THREADED_CALLABLE_IMPL2(csThreadTest, Test3, int i, float f)
{
}

Return types

As mentioned above, you specify the class of the return object in the method declaration. There a restriction here; The object must implement the ‘iThreadReturn’ interface. This is already implemented in ‘csutil/threadmanager.h’ (‘csThreadReturn’), or you may implement this yourself.

This return object basically tells/gives you three things:

You set the return data yourself using the provided ‘ret’ ‘iThreadReturn’ object interface in your method definition. The return data is in the form of a csRef<iBase> or a ‘void*’. It is up to you which you choose to use. To indicate whether or not your function has succeeded, return a boolean value (true for success). Your return object will automatically be updated when your method has finished executing to mark that this is the case.

An example of this:

 
THREADED_CALLABLE_IMPL(csClassName, MethodName2)
{
  csRef<iInterface> obj = csPtr<iInterface>(new csImpl())
  ret->SetData(csRef<iBase>(obj));
  return true;
}

csClassName::MethodName()
{
  csRef<iThreadReturn> ret = MethodName2();
  ret->Wait();
  if(ret->WasSuccessful())
  {
    csRef<iInterface> obj;
    ret->GetData(obj);
  }
}

Waiting

You may have noticed something new in the example above:

 
ret->Wait();

The majority of the time you will want your method call to return immediately so you can continue onwards. At some point you might want to check and wait for your method to finish before continuing. The thread manager provides a simple way to do this. Call Wait(), on your ‘iThreadReturn’ and it will send a message to the thread manager and then wait until your method has finished executing, at which point it will return.

Threaded Loading

One important threaded section of Crystal Space is the threaded loader. In order to ensure that the threaded loader works correctly you must make sure that you use thread-safe versions of the following modules in Crystal Space:


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

This document was generated using texi2html 1.76.