diff options
author | Jeremy Allison <jra@samba.org> | 2015-03-16 12:17:40 -0700 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2015-03-17 16:48:07 +0100 |
commit | 61e0a67c552d28c7544399fe07f381eab673e726 (patch) | |
tree | cf7e71b6a8b95b8f0d0d8a5d4ae294d88e9b9621 /lib/talloc | |
parent | 9139caa57a4247ecaa71d6f86db2f79ec20f8140 (diff) | |
download | samba-61e0a67c552d28c7544399fe07f381eab673e726.tar.gz |
lib: docs: talloc: Add a threads tutorial and samples showing how to use talloc with threads.
Signed-off-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Simo <simo@samba.org>
Diffstat (limited to 'lib/talloc')
-rw-r--r-- | lib/talloc/doc/mainpage.dox | 3 | ||||
-rw-r--r-- | lib/talloc/doc/tutorial_introduction.dox | 4 | ||||
-rw-r--r-- | lib/talloc/doc/tutorial_threads.dox | 203 | ||||
-rw-r--r-- | lib/talloc/talloc_guide.txt | 3 |
4 files changed, 210 insertions, 3 deletions
diff --git a/lib/talloc/doc/mainpage.dox b/lib/talloc/doc/mainpage.dox index 3b56898a087..ece6ccb8f0c 100644 --- a/lib/talloc/doc/mainpage.dox +++ b/lib/talloc/doc/mainpage.dox @@ -102,7 +102,8 @@ * - when using talloc_enable_leak_report(), giving directly NULL as a parent * context implicitly refers to a hidden "null context" global variable, so * this should not be used in a multi-threaded environment without proper - * synchronization. + * synchronization. In threaded code turn off null tracking using + * talloc_disable_null_tracking(). * - the context returned by talloc_autofree_context() is also global so * shouldn't be used by several threads simultaneously without * synchronization. diff --git a/lib/talloc/doc/tutorial_introduction.dox b/lib/talloc/doc/tutorial_introduction.dox index 02777b9f774..418c38b5a18 100644 --- a/lib/talloc/doc/tutorial_introduction.dox +++ b/lib/talloc/doc/tutorial_introduction.dox @@ -40,4 +40,6 @@ recursively frees all of its descendants as well. @subpage libtalloc_bestpractices -*/
\ No newline at end of file +@subpage libtalloc_threads + +*/ diff --git a/lib/talloc/doc/tutorial_threads.dox b/lib/talloc/doc/tutorial_threads.dox new file mode 100644 index 00000000000..111bbf587aa --- /dev/null +++ b/lib/talloc/doc/tutorial_threads.dox @@ -0,0 +1,203 @@ +/** +@page libtalloc_threads Chapter 8: Using threads with talloc + +@section Talloc and thread safety + +The talloc library is not internally thread-safe, in that accesses +to variables on a talloc context are not controlled by mutexes or +other thread-safe primitives. + +However, so long as talloc_disable_null_tracking() is called from +the main thread to disable global variable access within talloc, +then each thread can safely use its own top level talloc context +allocated off the NULL context. + +For example: + +@code +static void *thread_fn(void *arg) +{ + const char *ctx_name = (const char *)arg; + /* + * Create a new top level talloc hierarchy in + * this thread. + */ + void *top_ctx = talloc_named_const(NULL, 0, "top"); + if (top_ctx == NULL) { + return NULL; + } + sub_ctx = talloc_named_const(top_ctx, 100, ctx_name); + if (sub_ctx == NULL) { + return NULL; + } + + /* + * Do more processing/talloc calls on top_ctx + * and its children. + */ + ...... + + talloc_free(top_ctx); + return value; +} +@endcode + +is a perfectly safe use of talloc within a thread. + +The problem comes when one thread wishes to move some +memory allocated on its local top level talloc context +to another thread. Care must be taken to add data access +exclusion to prevent memory corruption. One method would +be to lock a mutex before any talloc call on each thread, +but this would push the burden of total talloc thread-safety +on the poor user of the library. + +A much easier way to transfer talloced memory between +threads is by the use of an intermediate, mutex locked, +intermediate variable. + +An example of this is below - taken from test code inside +the talloc testsuite. + +The main thread creates 1000 sub-threads, and then accepts +the transfer of some thread-talloc'ed memory onto its top +level context from each thread in turn. + +A pthread mutex and condition variable are used to +synchronize the transfer via the intermediate_ptr +variable. + +@code +/* Required sync variables. */ +static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t condvar = PTHREAD_COND_INITIALIZER; + +/* Intermediate talloc pointer for transfer. */ +static void *intermediate_ptr; + +/* Subthread. */ +static void *thread_fn(void *arg) +{ + int ret; + const char *ctx_name = (const char *)arg; + void *sub_ctx = NULL; + /* + * Do stuff that creates a new talloc hierarchy in + * this thread. + */ + void *top_ctx = talloc_named_const(NULL, 0, "top"); + if (top_ctx == NULL) { + return NULL; + } + sub_ctx = talloc_named_const(top_ctx, 100, ctx_name); + if (sub_ctx == NULL) { + return NULL; + } + + /* + * Now transfer a pointer from our hierarchy + * onto the intermediate ptr. + */ + ret = pthread_mutex_lock(&mtx); + if (ret != 0) { + talloc_free(top_ctx); + return NULL; + } + + /* Wait for intermediate_ptr to be free. */ + while (intermediate_ptr != NULL) { + ret = pthread_cond_wait(&condvar, &mtx); + if (ret != 0) { + talloc_free(top_ctx); + return NULL; + } + } + + /* and move our memory onto it from our toplevel hierarchy. */ + intermediate_ptr = talloc_move(NULL, &sub_ctx); + + /* Tell the main thread it's ready for pickup. */ + pthread_cond_broadcast(&condvar); + pthread_mutex_unlock(&mtx); + + talloc_free(top_ctx); + return NULL; +} + +/* Main thread. */ + +#define NUM_THREADS 1000 + +static bool test_pthread_talloc_passing(void) +{ + int i; + int ret; + char str_array[NUM_THREADS][20]; + pthread_t thread_id; + void *mem_ctx; + + /* + * Important ! Null tracking breaks threaded talloc. + * It *must* be turned off. + */ + talloc_disable_null_tracking(); + + /* Main thread toplevel context. */ + mem_ctx = talloc_named_const(NULL, 0, "toplevel"); + if (mem_ctx == NULL) { + return false; + } + + /* + * Spin off NUM_THREADS threads. + * They will use their own toplevel contexts. + */ + for (i = 0; i < NUM_THREADS; i++) { + (void)snprintf(str_array[i], + 20, + "thread:%d", + i); + if (str_array[i] == NULL) { + return false; + } + ret = pthread_create(&thread_id, + NULL, + thread_fn, + str_array[i]); + if (ret != 0) { + return false; + } + } + + /* Now wait for NUM_THREADS transfers of the talloc'ed memory. */ + for (i = 0; i < NUM_THREADS; i++) { + ret = pthread_mutex_lock(&mtx); + if (ret != 0) { + talloc_free(mem_ctx); + return false; + } + + /* Wait for intermediate_ptr to have our data. */ + while (intermediate_ptr == NULL) { + ret = pthread_cond_wait(&condvar, &mtx); + if (ret != 0) { + talloc_free(mem_ctx); + return false; + } + } + + /* and move it onto our toplevel hierarchy. */ + (void)talloc_move(mem_ctx, &intermediate_ptr); + + /* Tell the sub-threads we're ready for another. */ + pthread_cond_broadcast(&condvar); + pthread_mutex_unlock(&mtx); + } + + /* Dump the hierarchy. */ + talloc_report(mem_ctx, stdout); + talloc_free(mem_ctx); + return true; +} +@endcode +*/ diff --git a/lib/talloc/talloc_guide.txt b/lib/talloc/talloc_guide.txt index 16afc9b6b8a..95f7f295e8c 100644 --- a/lib/talloc/talloc_guide.txt +++ b/lib/talloc/talloc_guide.txt @@ -69,7 +69,8 @@ order to be safe. In particular: - when using talloc_enable_leak_report(), giving directly NULL as a parent context implicitly refers to a hidden "null context" global variable, so this should not be used in a multi-threaded environment -without proper synchronization ; +without proper synchronization. In threaded code turn off null tracking using +talloc_disable_null_tracking(). ; - the context returned by talloc_autofree_context() is also global so shouldn't be used by several threads simultaneously without synchronization. |