79 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			79 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			C
		
	
	
	
/*
 | 
						|
 * 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);
 | 
						|
}
 |