thr_sleepq.c revision 297706
1216642Sdavidxu/* 2216642Sdavidxu * Copyright (c) 2010 David Xu <davidxu@freebsd.org> 3216642Sdavidxu * All rights reserved. 4216642Sdavidxu * 5216642Sdavidxu * Redistribution and use in source and binary forms, with or without 6216642Sdavidxu * modification, are permitted provided that the following conditions 7216642Sdavidxu * are met: 8216642Sdavidxu * 1. Redistributions of source code must retain the above copyright 9216642Sdavidxu * notice unmodified, this list of conditions, and the following 10216642Sdavidxu * disclaimer. 11216642Sdavidxu * 2. Redistributions in binary form must reproduce the above copyright 12216642Sdavidxu * notice, this list of conditions and the following disclaimer in the 13216642Sdavidxu * documentation and/or other materials provided with the distribution. 14216642Sdavidxu * 15216642Sdavidxu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16216642Sdavidxu * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17216642Sdavidxu * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18216642Sdavidxu * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19216642Sdavidxu * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20216642Sdavidxu * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21216642Sdavidxu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22216642Sdavidxu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23216642Sdavidxu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24216642Sdavidxu * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25216642Sdavidxu */ 26216642Sdavidxu 27297706Skib#include <sys/cdefs.h> 28297706Skib__FBSDID("$FreeBSD: head/lib/libthr/thread/thr_sleepq.c 297706 2016-04-08 11:15:26Z kib $"); 29297706Skib 30216642Sdavidxu#include <stdlib.h> 31216642Sdavidxu#include "thr_private.h" 32216642Sdavidxu 33216642Sdavidxu#define HASHSHIFT 9 34216642Sdavidxu#define HASHSIZE (1 << HASHSHIFT) 35216642Sdavidxu#define SC_HASH(wchan) ((unsigned) \ 36216642Sdavidxu ((((uintptr_t)(wchan) >> 3) \ 37216642Sdavidxu ^ ((uintptr_t)(wchan) >> (HASHSHIFT + 3))) \ 38216642Sdavidxu & (HASHSIZE - 1))) 39216642Sdavidxu#define SC_LOOKUP(wc) &sc_table[SC_HASH(wc)] 40216642Sdavidxu 41216642Sdavidxustruct sleepqueue_chain { 42216642Sdavidxu struct umutex sc_lock; 43234947Sdavidxu int sc_enqcnt; 44216642Sdavidxu LIST_HEAD(, sleepqueue) sc_queues; 45216642Sdavidxu int sc_type; 46216642Sdavidxu}; 47216642Sdavidxu 48216642Sdavidxustatic struct sleepqueue_chain sc_table[HASHSIZE]; 49216642Sdavidxu 50216642Sdavidxuvoid 51216642Sdavidxu_sleepq_init(void) 52216642Sdavidxu{ 53216642Sdavidxu int i; 54216642Sdavidxu 55216642Sdavidxu for (i = 0; i < HASHSIZE; ++i) { 56216642Sdavidxu LIST_INIT(&sc_table[i].sc_queues); 57216642Sdavidxu _thr_umutex_init(&sc_table[i].sc_lock); 58216642Sdavidxu } 59216642Sdavidxu} 60216642Sdavidxu 61216642Sdavidxustruct sleepqueue * 62216642Sdavidxu_sleepq_alloc(void) 63216642Sdavidxu{ 64216642Sdavidxu struct sleepqueue *sq; 65216642Sdavidxu 66216642Sdavidxu sq = calloc(1, sizeof(struct sleepqueue)); 67216642Sdavidxu TAILQ_INIT(&sq->sq_blocked); 68216642Sdavidxu SLIST_INIT(&sq->sq_freeq); 69216642Sdavidxu return (sq); 70216642Sdavidxu} 71216642Sdavidxu 72216642Sdavidxuvoid 73216642Sdavidxu_sleepq_free(struct sleepqueue *sq) 74216642Sdavidxu{ 75216642Sdavidxu free(sq); 76216642Sdavidxu} 77216642Sdavidxu 78216642Sdavidxuvoid 79216642Sdavidxu_sleepq_lock(void *wchan) 80216642Sdavidxu{ 81216642Sdavidxu struct pthread *curthread = _get_curthread(); 82216642Sdavidxu struct sleepqueue_chain *sc; 83216642Sdavidxu 84216642Sdavidxu sc = SC_LOOKUP(wchan); 85216642Sdavidxu THR_LOCK_ACQUIRE_SPIN(curthread, &sc->sc_lock); 86216642Sdavidxu} 87216642Sdavidxu 88216642Sdavidxuvoid 89216642Sdavidxu_sleepq_unlock(void *wchan) 90216642Sdavidxu{ 91216642Sdavidxu struct sleepqueue_chain *sc; 92216642Sdavidxu struct pthread *curthread = _get_curthread(); 93216642Sdavidxu 94216642Sdavidxu sc = SC_LOOKUP(wchan); 95216642Sdavidxu THR_LOCK_RELEASE(curthread, &sc->sc_lock); 96216642Sdavidxu} 97216642Sdavidxu 98235218Sdavidxustatic inline struct sleepqueue * 99235218Sdavidxulookup(struct sleepqueue_chain *sc, void *wchan) 100216642Sdavidxu{ 101216642Sdavidxu struct sleepqueue *sq; 102216642Sdavidxu 103216642Sdavidxu LIST_FOREACH(sq, &sc->sc_queues, sq_hash) 104216642Sdavidxu if (sq->sq_wchan == wchan) 105216642Sdavidxu return (sq); 106216642Sdavidxu return (NULL); 107216642Sdavidxu} 108216642Sdavidxu 109235218Sdavidxustruct sleepqueue * 110235218Sdavidxu_sleepq_lookup(void *wchan) 111235218Sdavidxu{ 112235218Sdavidxu return (lookup(SC_LOOKUP(wchan), wchan)); 113235218Sdavidxu} 114235218Sdavidxu 115216642Sdavidxuvoid 116216642Sdavidxu_sleepq_add(void *wchan, struct pthread *td) 117216642Sdavidxu{ 118216642Sdavidxu struct sleepqueue_chain *sc; 119216642Sdavidxu struct sleepqueue *sq; 120216642Sdavidxu 121235068Sdavidxu sc = SC_LOOKUP(wchan); 122235218Sdavidxu sq = lookup(sc, wchan); 123216642Sdavidxu if (sq != NULL) { 124216642Sdavidxu SLIST_INSERT_HEAD(&sq->sq_freeq, td->sleepqueue, sq_flink); 125216642Sdavidxu } else { 126216642Sdavidxu sq = td->sleepqueue; 127216642Sdavidxu LIST_INSERT_HEAD(&sc->sc_queues, sq, sq_hash); 128216642Sdavidxu sq->sq_wchan = wchan; 129216642Sdavidxu /* sq->sq_type = type; */ 130216642Sdavidxu } 131216642Sdavidxu td->sleepqueue = NULL; 132216642Sdavidxu td->wchan = wchan; 133234947Sdavidxu if (((++sc->sc_enqcnt << _thr_queuefifo) & 0xff) != 0) 134234947Sdavidxu TAILQ_INSERT_HEAD(&sq->sq_blocked, td, wle); 135234947Sdavidxu else 136234947Sdavidxu TAILQ_INSERT_TAIL(&sq->sq_blocked, td, wle); 137216642Sdavidxu} 138216642Sdavidxu 139216642Sdavidxuint 140216642Sdavidxu_sleepq_remove(struct sleepqueue *sq, struct pthread *td) 141216642Sdavidxu{ 142216642Sdavidxu int rc; 143216642Sdavidxu 144216642Sdavidxu TAILQ_REMOVE(&sq->sq_blocked, td, wle); 145216642Sdavidxu if (TAILQ_EMPTY(&sq->sq_blocked)) { 146216642Sdavidxu LIST_REMOVE(sq, sq_hash); 147216642Sdavidxu td->sleepqueue = sq; 148216642Sdavidxu rc = 0; 149216642Sdavidxu } else { 150216642Sdavidxu td->sleepqueue = SLIST_FIRST(&sq->sq_freeq); 151216642Sdavidxu SLIST_REMOVE_HEAD(&sq->sq_freeq, sq_flink); 152216642Sdavidxu rc = 1; 153216642Sdavidxu } 154216642Sdavidxu td->wchan = NULL; 155216642Sdavidxu return (rc); 156216642Sdavidxu} 157216642Sdavidxu 158216642Sdavidxuvoid 159216642Sdavidxu_sleepq_drop(struct sleepqueue *sq, 160216642Sdavidxu void (*cb)(struct pthread *, void *arg), void *arg) 161216642Sdavidxu{ 162216642Sdavidxu struct pthread *td; 163216642Sdavidxu struct sleepqueue *sq2; 164216642Sdavidxu 165216642Sdavidxu td = TAILQ_FIRST(&sq->sq_blocked); 166216642Sdavidxu if (td == NULL) 167216642Sdavidxu return; 168216642Sdavidxu LIST_REMOVE(sq, sq_hash); 169216642Sdavidxu TAILQ_REMOVE(&sq->sq_blocked, td, wle); 170216642Sdavidxu if (cb != NULL) 171216642Sdavidxu cb(td, arg); 172216642Sdavidxu td->sleepqueue = sq; 173216642Sdavidxu td->wchan = NULL; 174216642Sdavidxu sq2 = SLIST_FIRST(&sq->sq_freeq); 175216642Sdavidxu TAILQ_FOREACH(td, &sq->sq_blocked, wle) { 176216642Sdavidxu if (cb != NULL) 177216642Sdavidxu cb(td, arg); 178216642Sdavidxu td->sleepqueue = sq2; 179216642Sdavidxu td->wchan = NULL; 180216642Sdavidxu sq2 = SLIST_NEXT(sq2, sq_flink); 181216642Sdavidxu } 182216642Sdavidxu TAILQ_INIT(&sq->sq_blocked); 183216642Sdavidxu SLIST_INIT(&sq->sq_freeq); 184216642Sdavidxu} 185