Difference between revisions of "User:Kr/A Thread's Life/Library Example with SAL"
(→The Program: Adapted to <source> tag.) |
(→Library Header: Adapted to <source> tag.) |
||
Line 211: | Line 211: | ||
====Library Header==== | ====Library Header==== | ||
− | < | + | <source lang="cpp">[cpp] |
#ifndef library_hxx_included | #ifndef library_hxx_included | ||
# define library_hxx_included | # define library_hxx_included | ||
Line 238: | Line 238: | ||
#endif | #endif | ||
− | </ | + | </source> |
====Library Implementation==== | ====Library Implementation==== |
Revision as of 20:19, 10 June 2008
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"
Contents
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
#include "library.hxx" #include <sal/main.h> #include <osl/interlck.h> #include <osl/thread.h> #include <osl/module.hxx> #include <rtl/alloc.h> #include <stdio.h> #include <time.h> #ifdef WNT extern "C" void _endthread(void); #else # include <pthread.h> #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 #ifdef WNT _endthread(); #else pthread_exit(NULL); #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. */ #ifndef UNX TimeValue oneSecond_ = {1, 0}; osl_waitThread(&oneSecond_); osl_destroyThread(thread); #else (void)thread; #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; #if defined GCC # define DAEMON_CREATE "_Z13daemon_createPFvPvES_" # define DAEMONS_GETCOUNT "_Z16daemons_getCountv" # define DAEMONS_CANCELANDJOIN "_Z21daemons_cancelAndJoinv" #elif defined __SUNPRO_CC # define DAEMON_CREATE "__1cNdaemon_create6FpFpv_v0_v_" # define DAEMONS_GETCOUNT "__1cQdaemons_getCount6F_i_" # define DAEMONS_CANCELANDJOIN "__1cVdaemons_cancelAndJoin6F_v_" #elif defined _MSC_VER # define DAEMON_CREATE "?daemon_create@@YAXP6AXPAX@Z0@Z" # define DAEMONS_GETCOUNT "?daemons_getCount@@YAHXZ" # define DAEMONS_CANCELANDJOIN "?daemons_cancelAndJoin@@YAXXZ" #else # error "unknown compiler" #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] #ifndef library_hxx_included # define library_hxx_included #include <osl/thread.h> #ifdef LIBRARY_IMPL # define LIBRARY_EXPORT SAL_DLLPUBLIC_EXPORT #else # define LIBRARY_EXPORT SAL_DLLPUBLIC_IMPORT #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); #endif
Library Implementation
[cpp]
- define LIBRARY_IMPL
- include "library.hxx"
- include <rtl/alloc.h>
- include <osl/mutex.h>
- include <osl/thread.h>
- 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)
- SHL1IMPLIB := i$(SHL1TARGET)
SHL1DEF := empty.def
.INCLUDE : target.mk
The DEF File
HEAPSIZE 0 EXPORTS