1119909Sdavidxu/*-
2119909Sdavidxu * Copyright (c) 2003 David Xu <davidxu@freebsd.org>
3119909Sdavidxu * All rights reserved.
4119909Sdavidxu *
5119909Sdavidxu * Redistribution and use in source and binary forms, with or without
6119909Sdavidxu * modification, are permitted provided that the following conditions
7119909Sdavidxu * are met:
8119909Sdavidxu * 1. Redistributions of source code must retain the above copyright
9119909Sdavidxu *    notice, this list of conditions and the following disclaimer.
10119909Sdavidxu * 2. Redistributions in binary form must reproduce the above copyright
11119909Sdavidxu *    notice, this list of conditions and the following disclaimer in the
12119909Sdavidxu *    documentation and/or other materials provided with the distribution.
13119909Sdavidxu *
14119909Sdavidxu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15119909Sdavidxu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16119909Sdavidxu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17119909Sdavidxu * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18119909Sdavidxu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19119909Sdavidxu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20119909Sdavidxu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21119909Sdavidxu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22119909Sdavidxu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23119909Sdavidxu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24119909Sdavidxu * SUCH DAMAGE.
25119909Sdavidxu *
26119909Sdavidxu * $FreeBSD$
27119909Sdavidxu */
28119909Sdavidxu
29174112Sdeischen#include "namespace.h"
30149691Sstefanf#include <sys/types.h>
31119909Sdavidxu#include <errno.h>
32149691Sstefanf#include <pthread.h>
33149691Sstefanf#include <stdint.h>
34119909Sdavidxu#include <stdlib.h>
35174112Sdeischen#include "un-namespace.h"
36149691Sstefanf
37149691Sstefanf#include "atomic_ops.h"
38119909Sdavidxu#include "thr_private.h"
39119909Sdavidxu
40119909Sdavidxu#define SPIN_COUNT 10000
41119909Sdavidxu
42122071Sdeischen__weak_reference(_pthread_spin_init, pthread_spin_init);
43122071Sdeischen__weak_reference(_pthread_spin_destroy, pthread_spin_destroy);
44122071Sdeischen__weak_reference(_pthread_spin_trylock, pthread_spin_trylock);
45122071Sdeischen__weak_reference(_pthread_spin_lock, pthread_spin_lock);
46122071Sdeischen__weak_reference(_pthread_spin_unlock, pthread_spin_unlock);
47122071Sdeischen
48119909Sdavidxuint
49122071Sdeischen_pthread_spin_init(pthread_spinlock_t *lock, int pshared)
50119909Sdavidxu{
51119909Sdavidxu	struct pthread_spinlock	*lck;
52119909Sdavidxu	int ret;
53119909Sdavidxu
54119909Sdavidxu	if (lock == NULL || pshared != PTHREAD_PROCESS_PRIVATE)
55119909Sdavidxu		ret = EINVAL;
56119909Sdavidxu	else if ((lck = malloc(sizeof(struct pthread_spinlock))) == NULL)
57119909Sdavidxu		ret = ENOMEM;
58119909Sdavidxu	else {
59119909Sdavidxu		lck->s_lock = 0;
60119909Sdavidxu		lck->s_owner= NULL;
61119909Sdavidxu		*lock = lck;
62119909Sdavidxu		ret = 0;
63119909Sdavidxu	}
64119909Sdavidxu
65119909Sdavidxu	return (ret);
66119909Sdavidxu}
67119909Sdavidxu
68119909Sdavidxuint
69122071Sdeischen_pthread_spin_destroy(pthread_spinlock_t *lock)
70119909Sdavidxu{
71119909Sdavidxu	int ret;
72119909Sdavidxu
73119909Sdavidxu	if (lock == NULL || *lock == NULL)
74119909Sdavidxu		ret = EINVAL;
75119909Sdavidxu	else if ((*lock)->s_owner != NULL)
76119909Sdavidxu		ret = EBUSY;
77119909Sdavidxu	else {
78119909Sdavidxu		free(*lock);
79119909Sdavidxu		*lock = NULL;
80119909Sdavidxu		ret = 0;
81119909Sdavidxu	}
82119909Sdavidxu
83119909Sdavidxu	return (ret);
84119909Sdavidxu}
85119909Sdavidxu
86119909Sdavidxuint
87122071Sdeischen_pthread_spin_trylock(pthread_spinlock_t *lock)
88119909Sdavidxu{
89119909Sdavidxu	struct pthread_spinlock	*lck;
90119909Sdavidxu	struct pthread *self = _pthread_self();
91119909Sdavidxu	int oldval, ret;
92119909Sdavidxu
93119909Sdavidxu	if (lock == NULL || (lck = *lock) == NULL)
94119909Sdavidxu		ret = EINVAL;
95119909Sdavidxu	else if (lck->s_owner == self)
96119909Sdavidxu		ret = EDEADLK;
97119909Sdavidxu	else if (lck->s_lock != 0)
98119909Sdavidxu		ret = EBUSY;
99119909Sdavidxu	else {
100174112Sdeischen		atomic_swap_int(&(lck)->s_lock, 1, &oldval);
101119909Sdavidxu		if (oldval)
102119909Sdavidxu			ret = EBUSY;
103119909Sdavidxu		else {
104119909Sdavidxu			lck->s_owner = _pthread_self();
105119909Sdavidxu			ret = 0;
106119909Sdavidxu		}
107119909Sdavidxu	}
108119909Sdavidxu	return (ret);
109119909Sdavidxu}
110119909Sdavidxu
111119909Sdavidxuint
112122071Sdeischen_pthread_spin_lock(pthread_spinlock_t *lock)
113119909Sdavidxu{
114119909Sdavidxu	struct pthread_spinlock	*lck;
115119909Sdavidxu	struct pthread *self = _pthread_self();
116119909Sdavidxu	int count, oldval, ret;
117119909Sdavidxu
118119909Sdavidxu	if (lock == NULL || (lck = *lock) == NULL)
119119909Sdavidxu		ret = EINVAL;
120119909Sdavidxu	else if (lck->s_owner == self)
121119909Sdavidxu		ret = EDEADLK;
122119909Sdavidxu	else {
123119909Sdavidxu		do {
124119909Sdavidxu			count = SPIN_COUNT;
125119909Sdavidxu			while (lck->s_lock) {
126119909Sdavidxu#ifdef __i386__
127119909Sdavidxu				/* tell cpu we are spinning */
128119909Sdavidxu				__asm __volatile("pause");
129119909Sdavidxu#endif
130119909Sdavidxu				if (--count <= 0) {
131119909Sdavidxu					count = SPIN_COUNT;
132119909Sdavidxu					_pthread_yield();
133119909Sdavidxu				}
134119909Sdavidxu			}
135174112Sdeischen			atomic_swap_int(&(lck)->s_lock, 1, &oldval);
136119909Sdavidxu		} while (oldval);
137119909Sdavidxu
138119909Sdavidxu		lck->s_owner = self;
139119909Sdavidxu		ret = 0;
140119909Sdavidxu	}
141119909Sdavidxu
142119909Sdavidxu	return (ret);
143119909Sdavidxu}
144119909Sdavidxu
145119909Sdavidxuint
146122071Sdeischen_pthread_spin_unlock(pthread_spinlock_t *lock)
147119909Sdavidxu{
148119909Sdavidxu	struct pthread_spinlock	*lck;
149119909Sdavidxu	int ret;
150119909Sdavidxu
151119909Sdavidxu	if (lock == NULL || (lck = *lock) == NULL)
152119909Sdavidxu		ret = EINVAL;
153119909Sdavidxu	else {
154119909Sdavidxu		if (lck->s_owner != _pthread_self())
155119909Sdavidxu			ret = EPERM;
156119909Sdavidxu		else {
157119909Sdavidxu			lck->s_owner = NULL;
158174112Sdeischen			atomic_swap_int(&lck->s_lock, 0, &ret);
159119909Sdavidxu			ret = 0;
160119909Sdavidxu		}
161119909Sdavidxu	}
162119909Sdavidxu
163119909Sdavidxu	return (ret);
164119909Sdavidxu}
165119909Sdavidxu
166