aboutsummaryrefslogtreecommitdiff
path: root/linuxthreads/spinlock.c
diff options
context:
space:
mode:
Diffstat (limited to 'linuxthreads/spinlock.c')
-rw-r--r--linuxthreads/spinlock.c233
1 files changed, 203 insertions, 30 deletions
diff --git a/linuxthreads/spinlock.c b/linuxthreads/spinlock.c
index a63c6535c9..38d6b8ef30 100644
--- a/linuxthreads/spinlock.c
+++ b/linuxthreads/spinlock.c
@@ -24,6 +24,11 @@
#include "spinlock.h"
#include "restart.h"
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
+static void __pthread_acquire(int * spinlock);
+#endif
+
+
/* The status field of a spinlock is a pointer whose least significant
bit is a locked flag.
@@ -59,10 +64,10 @@ void internal_function __pthread_lock(struct _pthread_fastlock * lock,
#if defined TEST_FOR_COMPARE_AND_SWAP
if (!__pthread_has_cas)
#endif
-#if !defined HAS_COMPARE_AND_SWAP
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
{
__pthread_acquire(&lock->__spinlock);
- return 0;
+ return;
}
#endif
@@ -147,7 +152,7 @@ int __pthread_unlock(struct _pthread_fastlock * lock)
#if defined TEST_FOR_COMPARE_AND_SWAP
if (!__pthread_has_cas)
#endif
-#if !defined HAS_COMPARE_AND_SWAP
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
{
WRITE_MEMORY_BARRIER();
lock->__spinlock = 0;
@@ -237,7 +242,9 @@ struct wait_node {
};
static long wait_node_free_list;
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
static int wait_node_free_list_spinlock;
+#endif
/* Allocate a new node from the head of the free list using an atomic
operation, or else using malloc if that list is empty. A fundamental
@@ -247,8 +254,33 @@ static int wait_node_free_list_spinlock;
static struct wait_node *wait_node_alloc(void)
{
+#if defined HAS_COMPARE_AND_SWAP
long oldvalue, newvalue;
+#endif
+
+#if defined TEST_FOR_COMPARE_AND_SWAP
+ if (!__pthread_has_cas)
+#endif
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
+ {
+ struct wait_node *new_node = 0;
+ __pthread_acquire(&wait_node_free_list_spinlock);
+ if (wait_node_free_list != 0) {
+ new_node = (struct wait_node *) wait_node_free_list;
+ wait_node_free_list = (long) new_node->next;
+ }
+ WRITE_MEMORY_BARRIER();
+ wait_node_free_list_spinlock = 0;
+
+ if (new_node == 0)
+ return malloc(sizeof *wait_node_alloc());
+
+ return new_node;
+ }
+#endif
+
+#if defined HAS_COMPARE_AND_SWAP
do {
oldvalue = wait_node_free_list;
@@ -257,10 +289,10 @@ static struct wait_node *wait_node_alloc(void)
newvalue = (long) ((struct wait_node *) oldvalue)->next;
WRITE_MEMORY_BARRIER();
- } while (! compare_and_swap(&wait_node_free_list, oldvalue, newvalue,
- &wait_node_free_list_spinlock));
+ } while (! __compare_and_swap(&wait_node_free_list, oldvalue, newvalue));
return (struct wait_node *) oldvalue;
+#endif
}
/* Return a node to the head of the free list using an atomic
@@ -268,37 +300,53 @@ static struct wait_node *wait_node_alloc(void)
static void wait_node_free(struct wait_node *wn)
{
+#if defined HAS_COMPARE_AND_SWAP
long oldvalue, newvalue;
+#endif
+
+#if defined TEST_FOR_COMPARE_AND_SWAP
+ if (!__pthread_has_cas)
+#endif
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
+ {
+ __pthread_acquire(&wait_node_free_list_spinlock);
+ wn->next = (struct wait_node *) wait_node_free_list;
+ wait_node_free_list = (long) wn;
+ WRITE_MEMORY_BARRIER();
+ wait_node_free_list_spinlock = 0;
+ return;
+ }
+#endif
+#if defined HAS_COMPARE_AND_SWAP
do {
oldvalue = wait_node_free_list;
wn->next = (struct wait_node *) oldvalue;
newvalue = (long) wn;
WRITE_MEMORY_BARRIER();
- } while (! compare_and_swap(&wait_node_free_list, oldvalue, newvalue,
- &wait_node_free_list_spinlock));
+ } while (! __compare_and_swap(&wait_node_free_list, oldvalue, newvalue));
+#endif
}
+#if defined HAS_COMPARE_AND_SWAP
+
/* Remove a wait node from the specified queue. It is assumed
that the removal takes place concurrently with only atomic insertions at the
head of the queue. */
static void wait_node_dequeue(struct wait_node **pp_head,
struct wait_node **pp_node,
- struct wait_node *p_node,
- int *spinlock)
+ struct wait_node *p_node)
{
- long oldvalue, newvalue;
-
/* If the node is being deleted from the head of the
list, it must be deleted using atomic compare-and-swap.
Otherwise it can be deleted in the straightforward way. */
if (pp_node == pp_head) {
- oldvalue = (long) p_node;
- newvalue = (long) p_node->next;
-
- if (compare_and_swap((long *) pp_node, oldvalue, newvalue, spinlock))
+ long oldvalue = (long) p_node;
+ long newvalue = (long) p_node->next;
+
+ if (__compare_and_swap((long *) pp_node, oldvalue, newvalue))
return;
/* Oops! Compare and swap failed, which means the node is
@@ -314,12 +362,46 @@ static void wait_node_dequeue(struct wait_node **pp_head,
return;
}
+#endif
+
void __pthread_alt_lock(struct _pthread_fastlock * lock,
pthread_descr self)
{
- struct wait_node wait_node;
+#if defined HAS_COMPARE_AND_SWAP
long oldstatus, newstatus;
+#endif
+ struct wait_node wait_node;
+
+#if defined TEST_FOR_COMPARE_AND_SWAP
+ if (!__pthread_has_cas)
+#endif
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
+ {
+ int suspend_needed = 0;
+ __pthread_acquire(&lock->__spinlock);
+
+ if (lock->__status == 0)
+ lock->__status = 1;
+ else {
+ if (self == NULL)
+ self = thread_self();
+
+ wait_node.abandoned = 0;
+ wait_node.next = (struct wait_node *) lock->__status;
+ wait_node.thr = self = thread_self();
+ suspend_needed = 1;
+ }
+ WRITE_MEMORY_BARRIER();
+ lock->__spinlock = 0;
+
+ if (suspend_needed)
+ suspend (self);
+ return;
+ }
+#endif
+
+#if defined HAS_COMPARE_AND_SWAP
do {
oldstatus = lock->__status;
if (oldstatus == 0) {
@@ -344,6 +426,7 @@ void __pthread_alt_lock(struct _pthread_fastlock * lock,
if (oldstatus != 0)
suspend(self);
+#endif
}
/* Timed-out lock operation; returns 0 to indicate timeout. */
@@ -351,8 +434,11 @@ void __pthread_alt_lock(struct _pthread_fastlock * lock,
int __pthread_alt_timedlock(struct _pthread_fastlock * lock,
pthread_descr self, const struct timespec *abstime)
{
+ long oldstatus;
+#if defined HAS_COMPARE_AND_SWAP
+ long newstatus;
+#endif
struct wait_node *p_wait_node = wait_node_alloc();
- long oldstatus, newstatus;
/* Out of memory, just give up and do ordinary lock. */
if (p_wait_node == 0) {
@@ -360,6 +446,32 @@ int __pthread_alt_timedlock(struct _pthread_fastlock * lock,
return 1;
}
+#if defined TEST_FOR_COMPARE_AND_SWAP
+ if (!__pthread_has_cas)
+#endif
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
+ {
+ __pthread_acquire(&lock->__spinlock);
+
+ if (lock->__status == 0)
+ lock->__status = 1;
+ else {
+ if (self == NULL)
+ self = thread_self();
+
+ p_wait_node->abandoned = 0;
+ p_wait_node->next = (struct wait_node *) lock->__status;
+ p_wait_node->thr = self = thread_self();
+ }
+
+ WRITE_MEMORY_BARRIER();
+ lock->__spinlock = 0;
+ oldstatus = 1; /* force suspend */
+ goto suspend;
+ }
+#endif
+
+#if defined HAS_COMPARE_AND_SWAP
do {
oldstatus = lock->__status;
if (oldstatus == 0) {
@@ -376,6 +488,11 @@ int __pthread_alt_timedlock(struct _pthread_fastlock * lock,
MEMORY_BARRIER();
} while(! compare_and_swap(&lock->__status, oldstatus, newstatus,
&lock->__spinlock));
+#endif
+
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
+ suspend:
+#endif
/* If we did not get the lock, do a timed suspend. If we wake up due
to a timeout, then there is a race; the old lock owner may try
@@ -402,24 +519,50 @@ int __pthread_alt_timedlock(struct _pthread_fastlock * lock,
void __pthread_alt_unlock(struct _pthread_fastlock *lock)
{
- long oldstatus;
struct wait_node *p_node, **pp_node, *p_max_prio, **pp_max_prio;
struct wait_node ** const pp_head = (struct wait_node **) &lock->__status;
int maxprio;
+#if defined TEST_FOR_COMPARE_AND_SWAP
+ if (!__pthread_has_cas)
+#endif
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
+ {
+ __pthread_acquire(&lock->__spinlock);
+ }
+#endif
+
while (1) {
/* If no threads are waiting for this lock, try to just
atomically release it. */
+#if defined TEST_FOR_COMPARE_AND_SWAP
+ if (!__pthread_has_cas)
+#endif
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
+ {
+ if (lock->__status == 0 || lock->__status == 1) {
+ lock->__status = 0;
+ break;
+ }
+ }
+#endif
- oldstatus = lock->__status;
- if (oldstatus == 0 || oldstatus == 1) {
- if (compare_and_swap_with_release_semantics (&lock->__status, oldstatus,
- 0, &lock->__spinlock))
- return;
- else
- continue;
+#if defined TEST_FOR_COMPARE_AND_SWAP
+ else
+#endif
+
+#if defined HAS_COMPARE_AND_SWAP
+ {
+ long oldstatus = lock->__status;
+ if (oldstatus == 0 || oldstatus == 1) {
+ if (__compare_and_swap_with_release_semantics (&lock->__status, oldstatus, 0))
+ break;
+ else
+ continue;
+ }
}
+#endif
/* Process the entire queue of wait nodes. Remove all abandoned
wait nodes and put them into the global free queue, and
@@ -435,7 +578,18 @@ void __pthread_alt_unlock(struct _pthread_fastlock *lock)
if (p_node->abandoned) {
/* Remove abandoned node. */
- wait_node_dequeue(pp_head, pp_node, p_node, &lock->__spinlock);
+#if defined TEST_FOR_COMPARE_AND_SWAP
+ if (!__pthread_has_cas)
+#endif
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
+ *pp_node = p_node->next;
+#endif
+#if defined TEST_FOR_COMPARE_AND_SWAP
+ else
+#endif
+#if defined HAS_COMPARE_AND_SWAP
+ wait_node_dequeue(pp_head, pp_node, p_node);
+#endif
wait_node_free(p_node);
READ_MEMORY_BARRIER();
p_node = *pp_node;
@@ -469,12 +623,33 @@ void __pthread_alt_unlock(struct _pthread_fastlock *lock)
whole unlock operation. */
if (!testandset(&p_max_prio->abandoned)) {
- wait_node_dequeue(pp_head, pp_max_prio, p_max_prio, &lock->__spinlock);
+#if defined TEST_FOR_COMPARE_AND_SWAP
+ if (!__pthread_has_cas)
+#endif
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
+ *pp_max_prio = p_max_prio->next;
+#endif
+#if defined TEST_FOR_COMPARE_AND_SWAP
+ else
+#endif
+#if defined HAS_COMPARE_AND_SWAP
+ wait_node_dequeue(pp_head, pp_max_prio, p_max_prio);
+#endif
WRITE_MEMORY_BARRIER();
restart(p_max_prio->thr);
- return;
+ break;
}
}
+
+#if defined TEST_FOR_COMPARE_AND_SWAP
+ if (!__pthread_has_cas)
+#endif
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
+ {
+ WRITE_MEMORY_BARRIER();
+ lock->__spinlock = 0;
+ }
+#endif
}
@@ -486,8 +661,6 @@ int __pthread_has_cas = 0;
#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
-static void __pthread_acquire(int * spinlock);
-
int __pthread_compare_and_swap(long * ptr, long oldval, long newval,
int * spinlock)
{