diff options
author | Justus Winter <4winter@informatik.uni-hamburg.de> | 2013-11-23 16:12:55 +0100 |
---|---|---|
committer | Justus Winter <4winter@informatik.uni-hamburg.de> | 2015-04-07 14:23:36 +0200 |
commit | 4241dd5673566a61add85bd9eb52d4ae7db2750a (patch) | |
tree | 0c7ce8514067068a877d8181544c2870af1dac7d /libports/port-deref-deferred.c | |
parent | 785f4aea18b5705e63609001d2aa12871a774804 (diff) |
libports: use protected payloads to optimize the object lookup
* NEWS: Mention protected payloads.
* libports/Makefile (SRCS): Add `port-deref-deferred.c'.
* libports/create-internal.c (_ports_create_port_internal): Set the
protected payload to the objects address.
* libports/import-port.c (ports_import_port): Likewise.
* libports/reallocate-from-external.c (ports_reallocate_from_external):
Likewise.
* libports/reallocate-port.c (ports_reallocate_port): Likewise.
* libports/transfer-right.c (ports_transfer_right): Likewise.
* libports/manage-multithread.c (ports_manage_port_operations_multithread):
Use the protected payload for the object lookup if provided. Add
thread pool management calls.
* libports/manage-one-thread.c (ports_manage_port_operations_one_thread):
Likewise.
* libports/destroy-right.c (ports_destroy_right): Defer the
dereferencing of outstanding send rights to avoid a port_info
use-after-free.
* libports/port-deref-deferred.c: New file.
* libports/port-deref-deferred.h: Likewise.
* libports/ports.h (struct port_bucket): New field `threadpool'.
(ports_lookup_payload): Check `port_right'.
Diffstat (limited to 'libports/port-deref-deferred.c')
-rw-r--r-- | libports/port-deref-deferred.c | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/libports/port-deref-deferred.c b/libports/port-deref-deferred.c new file mode 100644 index 00000000..2580e913 --- /dev/null +++ b/libports/port-deref-deferred.c @@ -0,0 +1,161 @@ +/* Delayed deallocation of port_info objects. + + Copyright (C) 2015 Free Software Foundation, Inc. + + Written by Justus Winter <4winter@informatik.uni-hamburg.de> + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <pthread.h> +#include "ports.h" + +/* + * A threadpool has a color indicating which threads belong to the old + * generation. + */ +#define COLOR_BLACK 0 +#define COLOR_WHITE 1 +#define COLOR_INVALID ~0U + +static inline int +valid_color (unsigned int c) +{ + return c == COLOR_BLACK || c == COLOR_WHITE; +} + +static inline unsigned int +flip_color (unsigned int c) +{ + assert (valid_color (c)); + return ! c; +} + +/* Initialize the thread pool. */ +void +_ports_threadpool_init (struct ports_threadpool *pool) +{ + pthread_spin_init (&pool->lock, PTHREAD_PROCESS_PRIVATE); + pool->color = COLOR_BLACK; + pool->old_threads = 0; + pool->old_objects = NULL; + pool->young_threads = 0; + pool->young_objects = NULL; +} + +/* Turn all young objects and threads into old ones. */ +static inline void +flip_generations (struct ports_threadpool *pool) +{ + assert (pool->old_threads == 0); + pool->old_threads = pool->young_threads; + pool->old_objects = pool->young_objects; + pool->young_threads = 0; + pool->young_objects = NULL; + pool->color = flip_color (pool->color); +} + +/* Called by a thread to join a thread pool. */ +void +_ports_thread_online (struct ports_threadpool *pool, + struct ports_thread *thread) +{ + pthread_spin_lock (&pool->lock); + thread->color = flip_color (pool->color); + pool->young_threads += 1; + pthread_spin_unlock (&pool->lock); +} + +struct pi_list +{ + struct pi_list *next; + struct port_info *pi; +}; + +/* Called by a thread that enters its quiescent period. */ +void +_ports_thread_quiescent (struct ports_threadpool *pool, + struct ports_thread *thread) +{ + struct pi_list *free_list = NULL, *p; + assert (valid_color (thread->color)); + + pthread_spin_lock (&pool->lock); + if (thread->color == pool->color) + { + pool->old_threads -= 1; + pool->young_threads += 1; + thread->color = flip_color (thread->color); + + if (pool->old_threads == 0) + { + free_list = pool->old_objects; + flip_generations (pool); + } + } + pthread_spin_unlock (&pool->lock); + + for (p = free_list; p;) + { + struct pi_list *old = p; + p = p->next; + + ports_port_deref (old->pi); + free (old); + } +} + +/* Called by a thread to leave a thread pool. */ +void +_ports_thread_offline (struct ports_threadpool *pool, + struct ports_thread *thread) +{ + assert (valid_color (thread->color)); + + retry: + pthread_spin_lock (&pool->lock); + if (thread->color == pool->color) + { + pthread_spin_unlock (&pool->lock); + _ports_thread_quiescent (pool, thread); + goto retry; + } + thread->color = COLOR_INVALID; + pool->young_threads -= 1; + pthread_spin_unlock (&pool->lock); +} + +/* Schedule an object for deallocation. */ +void +_ports_port_deref_deferred (struct port_info *pi) +{ + struct ports_threadpool *pool = &pi->bucket->threadpool; + + struct pi_list *pl = malloc (sizeof *pl); + if (pl == NULL) + return; + pl->pi = pi; + + pthread_spin_lock (&pool->lock); + pl->next = pool->young_objects; + pool->young_objects = pl; + if (pool->old_threads == 0) + { + assert (pool->old_objects == NULL); + flip_generations (pool); + } + pthread_spin_unlock (&pool->lock); +} |