/* 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 . */ #include #include #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); }