/* $NetBSD$ */ /*- * Copyright (c) 2015 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Taylor R. Campbell. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _SYS_REFCOUNT_H #define _SYS_REFCOUNT_H #include #include #include #include #include #include #include struct refcount { volatile unsigned rc_value; }; #if DIAGNOSTIC static inline bool refcount_referenced_p(const struct refcount *refcount) { return 0 < refcount->rc_value; } static inline bool refcount_exclusive_p(const struct refcount *refcount) { KASSERT(refcount_referenced_p(refcount)); return refcount->rc_value == 1; } #endif static inline void refcount_init(struct refcount *refcount) { refcount->rc_value = 1; } static inline void refcount_fini(struct refcount *refcount) { KASSERT(!refcount_referenced_p(refcount)); refcount->rc_value = UINT_MAX; } static inline int refcount_inc(struct refcount *refcount) { unsigned old, new; do { old = refcount->rc_value; if (old == UINT_MAX) return EBUSY; KASSERT(0 < old); new = old + 1; } while (atomic_cas_uint(&refcount->rc_value, old, new) != old); return 0; } static inline bool refcount_dec_local(struct refcount *refcount) { unsigned old, new; do { old = refcount->rc_value; KASSERT(0 < old); if (old == 1) { /* * Avoid an atomic if we don't need it. Caller * guarantees that if the reference count is 1, * nobody else can acquire new references. */ refcount->rc_value = 0; return true; } KASSERT(1 < old); new = old - 1; } while (atomic_cas_uint(&refcount->rc_value, old, new) != old); KASSERT(0 < new); return false; } static inline bool refcount_dec_lock(struct refcount *refcount, kmutex_t *interlock) { unsigned old, new; do { old = refcount->rc_value; KASSERT(0 < old); if (old == 1) { /* * Transition from 1->0 is allowed only under * the interlock. */ mutex_enter(interlock); new = atomic_dec_uint_nv(&refcount->rc_value); KASSERT(new != UINT_MAX); if (new == 0) return true; mutex_exit(interlock); return false; } KASSERT(1 < old); new = old - 1; } while (atomic_cas_uint(&refcount->rc_value, old, new) != old); KASSERT(0 < new); return false; } static inline void refcount_dec_signal(struct refcount *refcount, kmutex_t *interlock, kcondvar_t *cv) { if (refcount_dec_lock(refcount, interlock)) { cv_signal(cv); mutex_exit(interlock); } } static inline void refcount_dec_broadcast(struct refcount *refcount, kmutex_t *interlock, kcondvar_t *cv) { if (refcount_dec_lock(refcount, interlock)) { cv_broadcast(cv); mutex_exit(interlock); } } static inline void refcount_dec_drain(struct refcount *refcount, kmutex_t *interlock, kcondvar_t *cv) { KASSERT(mutex_owned(interlock)); if (0 < atomic_dec_uint_nv(&refcount->rc_value)) do cv_wait(cv, interlock); while (0 < refcount->rc_value); } #endif /* _SYS_REFCOUNT_H */