| /* | 
 |  * Recursive FIFO lock | 
 |  * | 
 |  * Copyright Red Hat, Inc. 2013 | 
 |  * | 
 |  * Authors: | 
 |  *  Stefan Hajnoczi   <stefanha@redhat.com> | 
 |  * | 
 |  * This work is licensed under the terms of the GNU LGPL, version 2 or later. | 
 |  * See the COPYING.LIB file in the top-level directory. | 
 |  * | 
 |  */ | 
 |  | 
 | #include <assert.h> | 
 | #include "qemu/rfifolock.h" | 
 |  | 
 | void rfifolock_init(RFifoLock *r, void (*cb)(void *), void *opaque) | 
 | { | 
 |     qemu_mutex_init(&r->lock); | 
 |     r->head = 0; | 
 |     r->tail = 0; | 
 |     qemu_cond_init(&r->cond); | 
 |     r->nesting = 0; | 
 |     r->cb = cb; | 
 |     r->cb_opaque = opaque; | 
 | } | 
 |  | 
 | void rfifolock_destroy(RFifoLock *r) | 
 | { | 
 |     qemu_cond_destroy(&r->cond); | 
 |     qemu_mutex_destroy(&r->lock); | 
 | } | 
 |  | 
 | /* | 
 |  * Theory of operation: | 
 |  * | 
 |  * In order to ensure FIFO ordering, implement a ticketlock.  Threads acquiring | 
 |  * the lock enqueue themselves by incrementing the tail index.  When the lock | 
 |  * is unlocked, the head is incremented and waiting threads are notified. | 
 |  * | 
 |  * Recursive locking does not take a ticket since the head is only incremented | 
 |  * when the outermost recursive caller unlocks. | 
 |  */ | 
 | void rfifolock_lock(RFifoLock *r) | 
 | { | 
 |     qemu_mutex_lock(&r->lock); | 
 |  | 
 |     /* Take a ticket */ | 
 |     unsigned int ticket = r->tail++; | 
 |  | 
 |     if (r->nesting > 0 && qemu_thread_is_self(&r->owner_thread)) { | 
 |         r->tail--; /* put ticket back, we're nesting */ | 
 |     } else { | 
 |         while (ticket != r->head) { | 
 |             /* Invoke optional contention callback */ | 
 |             if (r->cb) { | 
 |                 r->cb(r->cb_opaque); | 
 |             } | 
 |             qemu_cond_wait(&r->cond, &r->lock); | 
 |         } | 
 |     } | 
 |  | 
 |     qemu_thread_get_self(&r->owner_thread); | 
 |     r->nesting++; | 
 |     qemu_mutex_unlock(&r->lock); | 
 | } | 
 |  | 
 | void rfifolock_unlock(RFifoLock *r) | 
 | { | 
 |     qemu_mutex_lock(&r->lock); | 
 |     assert(r->nesting > 0); | 
 |     assert(qemu_thread_is_self(&r->owner_thread)); | 
 |     if (--r->nesting == 0) { | 
 |         r->head++; | 
 |         qemu_cond_broadcast(&r->cond); | 
 |     } | 
 |     qemu_mutex_unlock(&r->lock); | 
 | } |