commit f5d4ef14bb47294046d98c06478b0b58c97cf521 Author: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Sat May 3 01:02:35 2014 +0200 libports: implement lockless reference counting * libports/refcount.h: New file with reference counting primitives. * libports/ports.h (struct port_info): Use the new type. * libports/bucket-iterate.c: Adjust accordingly. * libports/complete-deallocate.c: Likewise. * libports/create-internal.c: Likewise. * libports/get-right.c: Likewise. * libports/import-port.c: Likewise. * libports/lookup-port.c: Likewise. * libports/port-deref-weak.c: Likewise. * libports/port-deref.c: Likewise. * libports/port-ref-weak.c: Likewise. * libports/port-ref.c: Likewise. * libports/reallocate-from-external.c: Likewise. * libports/transfer-right.c: Likewise. * utils/rpctrace.c: Likewise. diff --git a/libports/bucket-iterate.c b/libports/bucket-iterate.c index 498cf13..38a9f7a 100644 --- a/libports/bucket-iterate.c +++ b/libports/bucket-iterate.c @@ -55,7 +55,7 @@ _ports_bucket_class_iterate (struct port_bucket *bucket, if (class == 0 || pi->class == class) { - pi->refcnt++; + ports_port_ref (pi); p[n] = pi; n++; } diff --git a/libports/complete-deallocate.c b/libports/complete-deallocate.c index 8ce095b..7e7d467 100644 --- a/libports/complete-deallocate.c +++ b/libports/complete-deallocate.c @@ -35,6 +35,8 @@ _ports_complete_deallocate (struct port_info *pi) pi->port_right = MACH_PORT_NULL; } + pthread_mutex_lock (&_ports_lock); + *pi->prevp = pi->next; if (pi->next) pi->next->prevp = pi->prevp; diff --git a/libports/create-internal.c b/libports/create-internal.c index 8551297..d56637c 100644 --- a/libports/create-internal.c +++ b/libports/create-internal.c @@ -54,8 +54,7 @@ _ports_create_port_internal (struct port_class *class, } pi->class = class; - pi->refcnt = 1; - pi->weakrefcnt = 0; + refcount_init (&pi->refcounts, 1, 0); pi->cancel_threshold = 0; pi->mscount = 0; pi->flags = 0; diff --git a/libports/get-right.c b/libports/get-right.c index 89050c6..42bfa2b 100644 --- a/libports/get-right.c +++ b/libports/get-right.c @@ -41,7 +41,7 @@ ports_get_right (void *port) if ((pi->flags & PORT_HAS_SENDRIGHTS) == 0) { pi->flags |= PORT_HAS_SENDRIGHTS; - pi->refcnt++; + ports_port_ref (pi); err = mach_port_request_notification (mach_task_self (), pi->port_right, MACH_NOTIFY_NO_SENDERS, diff --git a/libports/import-port.c b/libports/import-port.c index 226f47e..5c66685 100644 --- a/libports/import-port.c +++ b/libports/import-port.c @@ -48,8 +48,7 @@ ports_import_port (struct port_class *class, struct port_bucket *bucket, return ENOMEM; pi->class = class; - pi->refcnt = 1 + !!stat.mps_srights; - pi->weakrefcnt = 0; + refcount_init (&pi->refcounts, 1 + !!stat.mps_srights, 0); pi->cancel_threshold = 0; pi->mscount = stat.mps_mscount; pi->flags = stat.mps_srights ? PORT_HAS_SENDRIGHTS : 0; diff --git a/libports/lookup-port.c b/libports/lookup-port.c index f79f6f0..289369f 100644 --- a/libports/lookup-port.c +++ b/libports/lookup-port.c @@ -44,7 +44,7 @@ ports_lookup_port (struct port_bucket *bucket, pi = 0; if (pi) - pi->refcnt++; + ports_port_ref (pi); pthread_mutex_unlock (&_ports_lock); diff --git a/libports/port-deref-weak.c b/libports/port-deref-weak.c index beb4842..48e5354 100644 --- a/libports/port-deref-weak.c +++ b/libports/port-deref-weak.c @@ -25,12 +25,8 @@ void ports_port_deref_weak (void *portstruct) { struct port_info *pi = portstruct; - - pthread_mutex_lock (&_ports_lock); - assert (pi->weakrefcnt); - pi->weakrefcnt--; - if (pi->refcnt == 0 && pi->weakrefcnt == 0) + struct refs result; + refcount_deref_weak (&pi->refcounts, &result); + if (result.hard == 0 && result.weak == 0) _ports_complete_deallocate (pi); - else - pthread_mutex_unlock (&_ports_lock); } diff --git a/libports/port-deref.c b/libports/port-deref.c index cf9b238..056159c 100644 --- a/libports/port-deref.c +++ b/libports/port-deref.c @@ -26,25 +26,19 @@ ports_port_deref (void *portstruct) { struct port_info *pi = portstruct; int trieddroppingweakrefs = 0; + struct refs result; retry: - - pthread_mutex_lock (&_ports_lock); - - if (pi->refcnt == 1 && pi->weakrefcnt + refcount_deref (&pi->refcounts, &result); + if (result.hard == 0 && result.weak > 1 && pi->class->dropweak_routine && !trieddroppingweakrefs) { - pthread_mutex_unlock (&_ports_lock); + refcount_ref (&pi->refcounts, NULL); (*pi->class->dropweak_routine) (pi); trieddroppingweakrefs = 1; goto retry; } - - assert (pi->refcnt); - pi->refcnt--; - if (pi->refcnt == 0 && pi->weakrefcnt == 0) + if (result.hard == 0 && result.weak == 0) _ports_complete_deallocate (pi); - else - pthread_mutex_unlock (&_ports_lock); } diff --git a/libports/port-ref-weak.c b/libports/port-ref-weak.c index c7d3c69..8b75005 100644 --- a/libports/port-ref-weak.c +++ b/libports/port-ref-weak.c @@ -25,9 +25,7 @@ void ports_port_ref_weak (void *portstruct) { struct port_info *pi = portstruct; - - pthread_mutex_lock (&_ports_lock); - assert (pi->refcnt || pi->weakrefcnt); - pi->weakrefcnt++; - pthread_mutex_unlock (&_ports_lock); + struct refs result; + refcount_ref_weak (&pi->refcounts, &result); + assert (result.hard > 0 || result.weak > 1); } diff --git a/libports/port-ref.c b/libports/port-ref.c index 92b7118..6310902 100644 --- a/libports/port-ref.c +++ b/libports/port-ref.c @@ -25,9 +25,7 @@ void ports_port_ref (void *portstruct) { struct port_info *pi = portstruct; - - pthread_mutex_lock (&_ports_lock); - assert (pi->refcnt || pi->weakrefcnt); - pi->refcnt++; - pthread_mutex_unlock (&_ports_lock); + struct refs result; + refcount_ref (&pi->refcounts, &result); + assert (result.hard > 1 || result.weak > 0); } diff --git a/libports/ports.h b/libports/ports.h index 7f13124..7088b2d 100644 --- a/libports/ports.h +++ b/libports/ports.h @@ -22,12 +22,15 @@ #define _HURD_PORTS_ #include +#include #include #include #include #include #include +#include "refcount.h" + /* These are global values for common flags used in the various structures. Not all of these are meaningful in all flag fields. */ #define PORTS_INHIBITED 0x0100 /* block RPC's */ @@ -39,8 +42,7 @@ struct port_info { struct port_class *class; - int refcnt; - int weakrefcnt; + reference_counts refcounts; mach_port_mscount_t mscount; mach_msg_seqno_t cancel_threshold; int flags; diff --git a/libports/reallocate-from-external.c b/libports/reallocate-from-external.c index 8cccb2a..4dfc59c 100644 --- a/libports/reallocate-from-external.c +++ b/libports/reallocate-from-external.c @@ -53,7 +53,7 @@ ports_reallocate_from_external (void *portstruct, mach_port_t receive) else if (((pi->flags & PORT_HAS_SENDRIGHTS) == 0) && stat.mps_srights) { pi->flags |= PORT_HAS_SENDRIGHTS; - pi->refcnt++; + ports_port_ref (pi); } pi->port_right = receive; diff --git a/libports/refcount.h b/libports/refcount.h new file mode 100644 index 0000000..43da5b4 --- /dev/null +++ b/libports/refcount.h @@ -0,0 +1,72 @@ +#ifndef _PORTS_REFCOUNT_H_ +#define _PORTS_REFCOUNT_H_ + +typedef uint64_t reference_counts; + +struct refs { + uint32_t hard; + uint32_t weak; +}; + +union _refs { + struct refs refs; + reference_counts rc; +}; + +extern inline void +refcount_init (reference_counts *ref, uint32_t hard, uint32_t weak) +{ + ((union _refs *) ref)->refs = + (struct refs) { .hard = hard, .weak = weak }; +} + +extern inline void +refcount_ref (reference_counts *ref, struct refs *result) +{ + const union _refs op = { .refs = { .hard = 1 } }; + reference_counts r = __atomic_add_fetch (ref, op.rc, __ATOMIC_RELAXED); + if (result) + ((union _refs *) result)->rc = r; +} + +extern inline void +refcount_deref (reference_counts *ref, struct refs *result) +{ + const union _refs op = { .refs = { .hard = 1 } }; + ((union _refs *) result)->rc = + __atomic_sub_fetch (ref, op.rc, __ATOMIC_RELAXED); +} + +extern inline void +refcount_ref_weak (reference_counts *ref, struct refs *result) +{ + const union _refs op = { .refs = { .weak = 1 } }; + ((union _refs *) result)->rc = + __atomic_add_fetch (ref, op.rc, __ATOMIC_RELAXED); +} + +extern inline void +refcount_deref_weak (reference_counts *ref, struct refs *result) +{ + const union _refs op = { .refs = { .weak = 1 } }; + ((union _refs *) result)->rc = + __atomic_sub_fetch (ref, op.rc, __ATOMIC_RELAXED); +} + +extern inline uint32_t +refcount_hard_refs (reference_counts *ref) +{ + union _refs result; + result.rc = __atomic_load_n (ref, __ATOMIC_RELAXED); + return result.refs.hard; +} + +extern inline uint32_t +refcount_weak_refs (reference_counts *ref) +{ + union _refs result; + result.rc = __atomic_load_n (ref, __ATOMIC_RELAXED); + return result.refs.weak; +} + +#endif /* _PORTS_REFCOUNT_H_ */ diff --git a/libports/transfer-right.c b/libports/transfer-right.c index 72488a9..f4e0c86 100644 --- a/libports/transfer-right.c +++ b/libports/transfer-right.c @@ -66,7 +66,7 @@ ports_transfer_right (void *tostruct, else if (((topi->flags & PORT_HAS_SENDRIGHTS) == 0) && hassendrights) { topi->flags |= PORT_HAS_SENDRIGHTS; - topi->refcnt++; + ports_port_ref (topi); } } diff --git a/utils/rpctrace.c b/utils/rpctrace.c index fc913e3..248d4e2 100644 --- a/utils/rpctrace.c +++ b/utils/rpctrace.c @@ -431,7 +431,8 @@ destroy_receiver_info (struct receiver_info *info) while (send_wrapper) { struct sender_info *next = send_wrapper->next; - assert (TRACED_INFO (send_wrapper)->pi.refcnt == 1); + assert (refcount_hard_refs (&TRACED_INFO (send_wrapper)->pi.refcounts) + == 1); /* Reset the receive_right of the send wrapper in advance to avoid * destroy_receiver_info is called when the port info is destroyed. */ send_wrapper->receive_right = NULL; @@ -848,7 +849,10 @@ rewrite_right (mach_port_t *right, mach_msg_type_name_t *type, hurd_ihash_locp_remove (&traced_names, receiver_info->locp); send_wrapper2 = get_send_wrapper (receiver_info, dest, &rr); - assert (TRACED_INFO (send_wrapper2)->pi.refcnt == 1); + assert ( + refcount_hard_refs (&TRACED_INFO (send_wrapper2)->pi.refcounts) + == 1); + name = TRACED_INFO (send_wrapper2)->name; TRACED_INFO (send_wrapper2)->name = NULL; /* send_wrapper2 isn't destroyed normally, so we need to unlink