135509Sjb/*
235509Sjb * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au>.
335509Sjb * All rights reserved.
435509Sjb *
535509Sjb * Redistribution and use in source and binary forms, with or without
635509Sjb * modification, are permitted provided that the following conditions
735509Sjb * are met:
835509Sjb * 1. Redistributions of source code must retain the above copyright
935509Sjb *    notice, this list of conditions and the following disclaimer.
1035509Sjb * 2. Redistributions in binary form must reproduce the above copyright
1135509Sjb *    notice, this list of conditions and the following disclaimer in the
1235509Sjb *    documentation and/or other materials provided with the distribution.
13165967Simp * 3. Neither the name of the author nor the names of any co-contributors
1435509Sjb *    may be used to endorse or promote products derived from this software
1535509Sjb *    without specific prior written permission.
1635509Sjb *
1735509Sjb * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
1835509Sjb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1935509Sjb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2049439Sdeischen * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2135509Sjb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2235509Sjb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2335509Sjb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2435509Sjb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2535509Sjb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2635509Sjb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2735509Sjb * SUCH DAMAGE.
2835509Sjb *
2950476Speter * $FreeBSD$
3035509Sjb *
3135509Sjb */
3235509Sjb
33174112Sdeischen#include "namespace.h"
34114187Sdeischen#include <sys/types.h>
35114187Sdeischen#include <machine/atomic.h>
36174112Sdeischen#include <pthread.h>
3793399Smarkm#include <libc_private.h>
38174112Sdeischen#include "un-namespace.h"
39113658Sdeischen#include "spinlock.h"
40103388Smini#include "thr_private.h"
4135509Sjb
42154288Sjasone#define	MAX_SPINLOCKS	72
43115381Sdeischen
44115381Sdeischenstruct spinlock_extra {
45122073Sdeischen	spinlock_t	*owner;
46123314Sdavidxu	pthread_mutex_t	lock;
47115381Sdeischen};
48115381Sdeischen
49174112Sdeischenstruct nv_spinlock {
50174112Sdeischen	long   access_lock;
51174112Sdeischen	long   lock_owner;
52174112Sdeischen	struct spinlock_extra *extra;	/* overlays fname in spinlock_t */
53174112Sdeischen	int    lineno;
54174112Sdeischen};
55174112Sdeischentypedef struct nv_spinlock nv_spinlock_t;
56174112Sdeischen
57115381Sdeischenstatic void	init_spinlock(spinlock_t *lck);
58115381Sdeischen
59139023Sdeischenstatic struct pthread_mutex_attr static_mutex_attr =
60139023Sdeischen    PTHREAD_MUTEXATTR_STATIC_INITIALIZER;
61139023Sdeischenstatic pthread_mutexattr_t	static_mattr = &static_mutex_attr;
62139023Sdeischen
63123314Sdavidxustatic pthread_mutex_t		spinlock_static_lock;
64115381Sdeischenstatic struct spinlock_extra	extra[MAX_SPINLOCKS];
65115381Sdeischenstatic int			spinlock_count = 0;
66115381Sdeischenstatic int			initialized = 0;
67115381Sdeischen
68113658Sdeischen/*
69113658Sdeischen * These are for compatability only.  Spinlocks of this type
70113658Sdeischen * are deprecated.
71113658Sdeischen */
72113658Sdeischen
73112665Sjeffvoid
74112665Sjeff_spinunlock(spinlock_t *lck)
75112665Sjeff{
76174112Sdeischen	struct spinlock_extra *sl_extra;
77114187Sdeischen
78174112Sdeischen	sl_extra = ((nv_spinlock_t *)lck)->extra;
79174112Sdeischen	_pthread_mutex_unlock(&sl_extra->lock);
80112665Sjeff}
81112665Sjeff
8235509Sjb/*
8335509Sjb * Lock a location for the running thread. Yield to allow other
8435509Sjb * threads to run if this thread is blocked because the lock is
8535509Sjb * not available. Note that this function does not sleep. It
8635509Sjb * assumes that the lock will be available very soon.
8735509Sjb */
8835509Sjbvoid
8936828Sjb_spinlock(spinlock_t *lck)
9035509Sjb{
91174112Sdeischen	struct spinlock_extra *sl_extra;
92114187Sdeischen
93123314Sdavidxu	if (!__isthreaded)
94123314Sdavidxu		PANIC("Spinlock called when not threaded.");
95123314Sdavidxu	if (!initialized)
96123314Sdavidxu		PANIC("Spinlocks not initialized.");
9736828Sjb	/*
9836828Sjb	 * Try to grab the lock and loop if another thread grabs
9936828Sjb	 * it before we do.
10036828Sjb	 */
101115381Sdeischen	if (lck->fname == NULL)
102115381Sdeischen		init_spinlock(lck);
103174112Sdeischen	sl_extra = ((nv_spinlock_t *)lck)->extra;
104174112Sdeischen	_pthread_mutex_lock(&sl_extra->lock);
10536828Sjb}
10636828Sjb
10736828Sjb/*
10836828Sjb * Lock a location for the running thread. Yield to allow other
10936828Sjb * threads to run if this thread is blocked because the lock is
11036828Sjb * not available. Note that this function does not sleep. It
11136828Sjb * assumes that the lock will be available very soon.
11236828Sjb *
11336828Sjb * This function checks if the running thread has already locked the
11436828Sjb * location, warns if this occurs and creates a thread dump before
11536828Sjb * returning.
11636828Sjb */
11736828Sjbvoid
118174112Sdeischen_spinlock_debug(spinlock_t *lck, char *fname __unused, int lineno __unused)
11936828Sjb{
120114187Sdeischen	_spinlock(lck);
12135509Sjb}
122115381Sdeischen
123115381Sdeischenstatic void
124115381Sdeischeninit_spinlock(spinlock_t *lck)
125115381Sdeischen{
126139023Sdeischen	_pthread_mutex_lock(&spinlock_static_lock);
127115381Sdeischen	if ((lck->fname == NULL) && (spinlock_count < MAX_SPINLOCKS)) {
128115381Sdeischen		lck->fname = (char *)&extra[spinlock_count];
129122073Sdeischen		extra[spinlock_count].owner = lck;
130115381Sdeischen		spinlock_count++;
131115381Sdeischen	}
132139023Sdeischen	_pthread_mutex_unlock(&spinlock_static_lock);
133123314Sdavidxu	if (lck->fname == NULL)
134123314Sdavidxu		PANIC("Exceeded max spinlocks");
135115381Sdeischen}
136115381Sdeischen
137115381Sdeischenvoid
138115381Sdeischen_thr_spinlock_init(void)
139115381Sdeischen{
140115381Sdeischen	int i;
141115381Sdeischen
142115381Sdeischen	if (initialized != 0) {
143123314Sdavidxu		_thr_mutex_reinit(&spinlock_static_lock);
144123314Sdavidxu		for (i = 0; i < spinlock_count; i++)
145123314Sdavidxu			_thr_mutex_reinit(&extra[i].lock);
146122073Sdeischen	} else {
147139023Sdeischen		if (_pthread_mutex_init(&spinlock_static_lock, &static_mattr))
148122073Sdeischen			PANIC("Cannot initialize spinlock_static_lock");
149115381Sdeischen		for (i = 0; i < MAX_SPINLOCKS; i++) {
150139023Sdeischen			if (_pthread_mutex_init(&extra[i].lock, &static_mattr))
151122073Sdeischen				PANIC("Cannot initialize spinlock extra");
152115381Sdeischen		}
153122073Sdeischen		initialized = 1;
154115381Sdeischen	}
155115381Sdeischen}
156