1144518Sdavidxu/*-
2144518Sdavidxu * Copyright (c) 2003 David Xu <davidxu@freebsd.org>
3297138Skib * Copyright (c) 2016 The FreeBSD Foundation
4144518Sdavidxu * All rights reserved.
5144518Sdavidxu *
6297138Skib * Portions of this software were developed by Konstantin Belousov
7297138Skib * under sponsorship from the FreeBSD Foundation.
8297138Skib *
9144518Sdavidxu * Redistribution and use in source and binary forms, with or without
10144518Sdavidxu * modification, are permitted provided that the following conditions
11144518Sdavidxu * are met:
12144518Sdavidxu * 1. Redistributions of source code must retain the above copyright
13144518Sdavidxu *    notice, this list of conditions and the following disclaimer.
14144518Sdavidxu * 2. Redistributions in binary form must reproduce the above copyright
15144518Sdavidxu *    notice, this list of conditions and the following disclaimer in the
16144518Sdavidxu *    documentation and/or other materials provided with the distribution.
17144518Sdavidxu *
18144518Sdavidxu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19144518Sdavidxu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20144518Sdavidxu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21144518Sdavidxu * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22144518Sdavidxu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23144518Sdavidxu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24144518Sdavidxu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25144518Sdavidxu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26144518Sdavidxu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27144518Sdavidxu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28144518Sdavidxu * SUCH DAMAGE.
29144518Sdavidxu */
30144518Sdavidxu
31297706Skib#include <sys/cdefs.h>
32297706Skib__FBSDID("$FreeBSD$");
33297706Skib
34157457Sdavidxu#include "namespace.h"
35144518Sdavidxu#include <errno.h>
36144518Sdavidxu#include <stdlib.h>
37144518Sdavidxu#include <pthread.h>
38157457Sdavidxu#include "un-namespace.h"
39157457Sdavidxu
40144518Sdavidxu#include "thr_private.h"
41144518Sdavidxu
42297701Skib_Static_assert(sizeof(struct pthread_spinlock) <= PAGE_SIZE,
43297701Skib    "pthread_spinlock is too large for off-page");
44297701Skib
45144518Sdavidxu#define SPIN_COUNT 100000
46144518Sdavidxu
47144518Sdavidxu__weak_reference(_pthread_spin_init, pthread_spin_init);
48144518Sdavidxu__weak_reference(_pthread_spin_destroy, pthread_spin_destroy);
49144518Sdavidxu__weak_reference(_pthread_spin_trylock, pthread_spin_trylock);
50144518Sdavidxu__weak_reference(_pthread_spin_lock, pthread_spin_lock);
51144518Sdavidxu__weak_reference(_pthread_spin_unlock, pthread_spin_unlock);
52144518Sdavidxu
53144518Sdavidxuint
54144518Sdavidxu_pthread_spin_init(pthread_spinlock_t *lock, int pshared)
55144518Sdavidxu{
56144518Sdavidxu	struct pthread_spinlock	*lck;
57144518Sdavidxu
58297138Skib	if (lock == NULL)
59297138Skib		return (EINVAL);
60297138Skib	if (pshared == PTHREAD_PROCESS_PRIVATE) {
61297138Skib		lck = malloc(sizeof(struct pthread_spinlock));
62297138Skib		if (lck == NULL)
63297138Skib			return (ENOMEM);
64144518Sdavidxu		*lock = lck;
65297138Skib	} else if (pshared == PTHREAD_PROCESS_SHARED) {
66297138Skib		lck = __thr_pshared_offpage(lock, 1);
67297138Skib		if (lck == NULL)
68297138Skib			return (EFAULT);
69297138Skib		*lock = THR_PSHARED_PTR;
70297138Skib	} else {
71297138Skib		return (EINVAL);
72144518Sdavidxu	}
73297138Skib	_thr_umutex_init(&lck->s_lock);
74297138Skib	return (0);
75144518Sdavidxu}
76144518Sdavidxu
77144518Sdavidxuint
78144518Sdavidxu_pthread_spin_destroy(pthread_spinlock_t *lock)
79144518Sdavidxu{
80297138Skib	void *l;
81144518Sdavidxu	int ret;
82144518Sdavidxu
83297138Skib	if (lock == NULL || *lock == NULL) {
84144518Sdavidxu		ret = EINVAL;
85297138Skib	} else if (*lock == THR_PSHARED_PTR) {
86297138Skib		l = __thr_pshared_offpage(lock, 0);
87297138Skib		if (l != NULL)
88297138Skib			__thr_pshared_destroy(l);
89297138Skib		ret = 0;
90297138Skib	} else {
91144518Sdavidxu		free(*lock);
92144518Sdavidxu		*lock = NULL;
93144518Sdavidxu		ret = 0;
94144518Sdavidxu	}
95144518Sdavidxu	return (ret);
96144518Sdavidxu}
97144518Sdavidxu
98144518Sdavidxuint
99144518Sdavidxu_pthread_spin_trylock(pthread_spinlock_t *lock)
100144518Sdavidxu{
101144518Sdavidxu	struct pthread_spinlock	*lck;
102144518Sdavidxu
103297138Skib	if (lock == NULL || *lock == NULL)
104297138Skib		return (EINVAL);
105297138Skib	lck = *lock == THR_PSHARED_PTR ? __thr_pshared_offpage(lock, 0) : *lock;
106297138Skib	if (lck == NULL)
107297138Skib		return (EINVAL);
108297138Skib	return (THR_UMUTEX_TRYLOCK(_get_curthread(), &lck->s_lock));
109144518Sdavidxu}
110144518Sdavidxu
111144518Sdavidxuint
112144518Sdavidxu_pthread_spin_lock(pthread_spinlock_t *lock)
113144518Sdavidxu{
114297138Skib	struct pthread *curthread;
115144518Sdavidxu	struct pthread_spinlock	*lck;
116297138Skib	int count;
117144518Sdavidxu
118297138Skib	if (lock == NULL)
119297138Skib		return (EINVAL);
120297138Skib	lck = *lock == THR_PSHARED_PTR ? __thr_pshared_offpage(lock, 0) : *lock;
121297138Skib	if (lck == NULL)
122297138Skib		return (EINVAL);
123297138Skib
124297138Skib	curthread = _get_curthread();
125297138Skib	count = SPIN_COUNT;
126297138Skib	while (THR_UMUTEX_TRYLOCK(curthread, &lck->s_lock) != 0) {
127297138Skib		while (lck->s_lock.m_owner) {
128297138Skib			if (!_thr_is_smp) {
129297138Skib				_pthread_yield();
130297138Skib			} else {
131297138Skib				CPU_SPINWAIT;
132297138Skib				if (--count <= 0) {
133297138Skib					count = SPIN_COUNT;
134161068Sdavidxu					_pthread_yield();
135144518Sdavidxu				}
136144518Sdavidxu			}
137144518Sdavidxu		}
138144518Sdavidxu	}
139297138Skib	return (0);
140144518Sdavidxu}
141144518Sdavidxu
142144518Sdavidxuint
143144518Sdavidxu_pthread_spin_unlock(pthread_spinlock_t *lock)
144144518Sdavidxu{
145144518Sdavidxu	struct pthread_spinlock	*lck;
146144518Sdavidxu
147297138Skib	if (lock == NULL)
148297138Skib		return (EINVAL);
149297138Skib	lck = *lock == THR_PSHARED_PTR ? __thr_pshared_offpage(lock, 0) : *lock;
150297138Skib	if (lck == NULL)
151297138Skib		return (EINVAL);
152297138Skib	return (THR_UMUTEX_UNLOCK(_get_curthread(), &lck->s_lock));
153144518Sdavidxu}
154