User:Kr/A Thread's Life/Library Example with SAL

From Apache OpenOffice Wiki
< User:Kr‎ | A Thread's Life
Revision as of 11:38, 31 October 2007 by Kr (Talk | contribs)

Jump to: navigation, search

SAL Thread Issues:

  • Different semantic of UNIX and Win32 implementations
  • Problematic "pthread_detach" in UNIX usage, likely leading to races if trying to join a thread.
  • Problematic "CloseHandle" in Win32 usage, only easily possible to create "detached" (daemon) threads if calling "osl_destroyThread" from within the new thread.

RFE:

  • Reliable thread cancellation mechanism ...
  • Result setting function. Needed in multi-thread keeping process alive scenario.
  • Passing of the "oslThread" object to the worker function, otherwise it must be explicitly passed with help of conditions etc.
  • Better named "osl_scheduleThread".
  • A way to get this threads "oslThread".
  • Missing pendant to "pthread_exit" / "ExitThread" and therefore also missing clean-up handlers etc.
  • Less capable cancellation mechanism compared to "pthread_cancel"

How-to Build

Get an OOo build environment
mkdir salexample
mkdir salexample/source
mkdir salexample/prj
touch salexample/prj/d.lst
cd salexample/source
dmake

The Program

[cpp]

  1. include "library.hxx"
  1. include <sal/main.h>
  2. include <osl/interlck.h>
  3. include <osl/thread.h>
  4. include <osl/module.hxx>
  5. include <rtl/alloc.h>
  1. include <stdio.h>
  2. include <time.h>
  1. ifdef WNT

extern "C" void _endthread(void);

  1. else
  2. include <pthread.h>
  1. endif


static oslInterlockedCount activities_ = 1; /* The "main" thread is the first "activity". */

static void activity_terminate_(void) {

   int use_exit = 0;
   
   use_exit = osl_decrementInterlockedCount(&activities_) == 0;
   /* Terminate the process, if this was the last "activity"! */
   if (use_exit)
       exit(0);
   else
  1. ifdef WNT
       _endthread();
  1. else
       pthread_exit(NULL);
  1. endif

}

typedef void activity_worker_t(void);

static void activity_func_(activity_worker_t ** ffunc) {

   activity_worker_t * func = *ffunc;
   rtl_freeMemory(ffunc);
   func();
   activity_terminate_();

}

static void activity_create_(activity_worker_t * func) {

   activity_worker_t ** ffunc = (activity_worker_t **)rtl_allocateMemory(sizeof(activity_worker_t *));
   *ffunc = func;
   oslThread thread = osl_createThread((oslWorkerFunction)activity_func_, ffunc);
   /* Activities need to be detached. */
  1. ifndef UNX
   TimeValue oneSecond_ = {1, 0};
   osl_waitThread(&oneSecond_); 
   
   osl_destroyThread(thread);
  1. else
   (void)thread;
  1. endif
   osl_incrementInterlockedCount(&activities_);

}


/* Some daemon functions. */ static daemon_create_t * pdaemon_create_; static daemons_getCount_t * pdaemons_getCount_;


extern "C" void log_threads(void) {

   fprintf(stderr, "activities: %u  daemons: %u\n", activities_, pdaemons_getCount_ ? pdaemons_getCount_() : 0);

}

static TimeValue oneSecond_ = {1, 0};

static void log_daemon_(oslThread thread) {

   fprintf(stderr, "\tlog daemon starting ...\n");
   while (osl_scheduleThread(thread)) {
       log_threads();
       osl_waitThread(&oneSecond_); 
   }
   fprintf(stderr, "\tlog daemon finishing ...\n");

}

static void some_daemon_(oslThread thread) /*throw (int)*/ {

   fprintf(stderr, "\tsome daemon starting ...\n");
   int n = 10;
   while (osl_scheduleThread(thread) && n > 0) {
       osl_waitThread(&oneSecond_); 
       if (rand() < (RAND_MAX / (pdaemons_getCount_() + 1)))
           pdaemon_create_(some_daemon_, thread);
       -- n;
   }
   fprintf(stderr, "\tsome daemon finishing ...\n");

}

/* Some activity functions. */ static void some_activity_(void) {

   fprintf(stderr, "\tsome activitiy starting ...\n");
   srand(time (0));
   int n = 3;
   while (n) {
       osl_waitThread(&oneSecond_); 
       
       if (rand() < (RAND_MAX / (activities_ + 1)))
           activity_create_(some_activity_);
       if (rand() < (RAND_MAX / (pdaemons_getCount_() + 1)))
           pdaemon_create_(some_daemon_, NULL);
       -- n;
   }
   fprintf(stderr, "\tsome activitiy dying ...\n");

}

static osl::Module library;

  1. if defined GCC
  2. define DAEMON_CREATE "_Z13daemon_createPFvPvES_"
  3. define DAEMONS_GETCOUNT "_Z16daemons_getCountv"
  4. define DAEMONS_CANCELANDJOIN "_Z21daemons_cancelAndJoinv"
  1. elif defined __SUNPRO_CC
  2. define DAEMON_CREATE "__1cNdaemon_create6FpFpv_v0_v_"
  3. define DAEMONS_GETCOUNT "__1cQdaemons_getCount6F_i_"
  4. define DAEMONS_CANCELANDJOIN "__1cVdaemons_cancelAndJoin6F_v_"
  1. elif defined _MSC_VER
  2. define DAEMON_CREATE "?daemon_create@@YAXP6AXPAX@Z0@Z"
  3. define DAEMONS_GETCOUNT "?daemons_getCount@@YAHXZ"
  4. define DAEMONS_CANCELANDJOIN "?daemons_cancelAndJoin@@YAXXZ"
  1. else
  2. error "unknown compiler"
  1. endif

extern "C" void close_library(void) {

   daemons_cancelAndJoin_t * pdaemons_cancelAndJoin = 
        (daemons_cancelAndJoin_t *)library.getFunctionSymbol(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DAEMONS_CANCELANDJOIN)));
   pdaemons_cancelAndJoin();
   library.unload();
   pdaemon_create_ = NULL;
   pdaemons_getCount_ = NULL;

}

SAL_IMPLEMENT_MAIN() {

   atexit(log_threads);
   atexit(close_library);
   library.load(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("library" SAL_DLLEXTENSION)));
   pdaemon_create_    = (daemon_create_t *)library.getFunctionSymbol(
       rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DAEMON_CREATE)));
   pdaemons_getCount_ = (daemons_getCount_t *)library.getFunctionSymbol(
       rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DAEMONS_GETCOUNT)));
   pdaemon_create_(log_daemon_, NULL);
   some_activity_();
   activity_terminate_();
   return 0;

}

Library Header

[cpp]

  1. ifndef library_hxx_included
  2. define library_hxx_included
  1. include <osl/thread.h>
  1. ifdef LIBRARY_IMPL
  2. define LIBRARY_EXPORT SAL_DLLPUBLIC_EXPORT
  1. else
  2. define LIBRARY_EXPORT SAL_DLLPUBLIC_IMPORT
  1. endif


typedef void daemon_worker_t(oslThread thread);

typedef void daemon_create_t (daemon_worker_t * func, oslThread thread); typedef int daemons_getCount_t (void); typedef void daemons_cancelAndJoin_t(void);

LIBRARY_EXPORT void daemon_create (daemon_worker_t * func, oslThread thread) /*throw (int)*/; LIBRARY_EXPORT int daemons_getCount (void); LIBRARY_EXPORT void daemons_cancelAndJoin(void);


  1. endif

Library Implementation

[cpp]

  1. define LIBRARY_IMPL
  1. include "library.hxx"
  1. include <rtl/alloc.h>
  2. include <osl/mutex.h>
  3. include <osl/thread.h>
  1. include <stdio.h>


typedef struct Daemon Daemon;

struct Daemon {

   Daemon * previous_;
   Daemon * next_;
   daemon_worker_t * func_;
   oslThread         hd_;
   int               finished_;

};

static oslMutex daemons_mutex_; struct DaemonConstruct {

   DaemonConstruct() {
       daemons_mutex_ = osl_createMutex();
   }
   ~DaemonConstruct() {
       osl_destroyMutex(daemons_mutex_);
   }        

};

static DaemonConstruct daemonConstruct;

static Daemon * firstDaemon_ = NULL; static unsigned int daemons_ = 0;

int daemons_getCount(void) {

   return daemons_;

}

/* Joins all or only "finished" daemons. */ static void daemons_join_(int finished) {

   osl_acquireMutex(daemons_mutex_);
   Daemon * daemon = firstDaemon_;
   while (daemon) {
       Daemon * next = daemon->next_;
       if (!finished || daemon->finished_) {
           -- daemons_;
           
           if (daemon->next_)
               daemon->next_->previous_ = daemon->previous_;
           
           if (daemon->previous_)
               daemon->previous_->next_ = daemon->next_;
           
           if (firstDaemon_ == daemon)
               firstDaemon_ = daemon->next_;
           osl_releaseMutex(daemons_mutex_);
           osl_joinWithThread(daemon->hd_);
           osl_acquireMutex(daemons_mutex_);
           osl_destroyThread(daemon->hd_);
           rtl_freeMemory(daemon);
           daemon = firstDaemon_;
       }
       else
           daemon = next;
   }
   osl_releaseMutex(daemons_mutex_);

}

/* Daemon finish function. */ static void daemon_finish_(Daemon * daemon) {

   daemons_join_(1);
   osl_acquireMutex(daemons_mutex_);
   daemon->finished_ = 1;
   osl_releaseMutex(daemons_mutex_);

}

static void daemon_func_(Daemon * daemon) {

   try {
       daemon->func_(daemon->hd_);
   }
   catch (...) {
       fprintf(stderr, " caught something ...\n");
   }
   daemon_finish_(daemon);

}

/* Create a "Daemon" struct and register it. Start the "Daemon". */ LIBRARY_EXPORT void daemon_create(daemon_worker_t * func, oslThread thread) /*throw (int)*/ {

   Daemon * daemon   = (Daemon *)rtl_allocateMemory(sizeof(Daemon));
   daemon->func_     = func;
   daemon->finished_ = 0;
   osl_acquireMutex(daemons_mutex_);
   // add a cancelation point
   if (thread && !osl_scheduleThread(thread)) {
       osl_releaseMutex(daemons_mutex_);
       rtl_freeMemory(daemon);
       throw int();
   }
   daemon->previous_ = NULL;
   daemon->next_ = firstDaemon_;
   
   if (firstDaemon_) 
       firstDaemon_->previous_ = daemon;
   
   firstDaemon_ = daemon;
   ++ daemons_;
       
   daemon->hd_ = osl_createSuspendedThread((oslWorkerFunction)daemon_func_, daemon);
   osl_resumeThread(daemon->hd_);
   osl_releaseMutex(daemons_mutex_);

}

/* Ask all registered daemons for cancellation and join them. */ /* Calling pthread_canel from within _fini leads to deadlocks.*/ /* static void __attribute__((destructor)) daemons_cancelAndJoin_(void) {*/

void daemons_cancelAndJoin(void) {

   fprintf(stderr, "Cancelling daemons ... ");
   osl_acquireMutex(daemons_mutex_);
   Daemon * daemon = firstDaemon_;
   while (daemon) {
       osl_terminateThread(daemon->hd_);
       /* respectively "osl_killThread" :-) */
       fprintf(stderr, "# ");
       daemon = daemon->next_;
   }
   osl_releaseMutex(daemons_mutex_);
   fprintf(stderr, "done\n");


   fprintf(stderr, "Joining daemons ... ");
   daemons_join_(0);
   fprintf(stderr, "done\n");

}

struct LibExit {

   LibExit() {
       fprintf(stderr, "initializing library ...\n");
   }
   ~LibExit() {
       fprintf(stderr, "de-initializing library ...\n");

// daemons_cancelAndJoin();

   }

};

static LibExit libExit;

Makefile

[cpp] PRJ  := .. PRJNAME := SALTHREADS TARGET  := main USE_DEFFILE  := TRUE

ENABLE_EXCEPTIONS  := TRUE

.INCLUDE : settings.mk


.IF "$(OS)"=="SOLARIS" DIRECT := .ENDIF

APP1TARGET  := main APP1OBJS  := $(OBJ)$/main.obj .IF "$(OS)"=="SOLARIS" APP1STDLIBS := -lrary $(SALLIB) .ELSE APP1STDLIBS := $(SALLIB) .ENDIF

SHL1DLLPRE  := SHL1TARGET  := library SHL1OBJS  := $(SLO)$/library.obj SHL1STDLIBS := $(SALHELPERLIB) $(SALLIB)

  1. SHL1IMPLIB  := i$(SHL1TARGET)

SHL1DEF  := empty.def


.INCLUDE : target.mk

The DEF File

HEAPSIZE	  0
EXPORTS
Personal tools