Beispiel #1
0
/**
 * Puts work item to the global pool or deallocates it. Also deallocates 
 * the events associated with the work item. NOTE: this code is designed 
 * to be efficient, not compact
 */
STATIC void WKQ_ReleaseWorkItem(WorkQueueModule * module, WorkItem * w)
{
    Bool locked = False;

    ASSERT(module->initcount > 0);
    ASSERT(!w->submitQ.queue);
    ASSERT(!w->itemsQ.queue);

    /* deallocate waiters */
    while (w->waiters) {
        Waiter * waiter = w->waiters;
        Waiter * next = waiter->next;

        if (module->nwait < module->maxwait) {
            if (locked) {
                WKQ_WaiterToPool(module, waiter);
                waiter = NULL;
            } else {
                locked = MUTEX_Lock(&module->mutex);
                if (module->nwait < module->maxwait) {                    
                    WKQ_WaiterToPool(module, waiter);
                    waiter = NULL;
                }
            }
        }

        if (waiter) {
            EVENT_Destroy(&waiter->event);
            MEM_Free(waiter);
        }

        w->waiters = next;
    }

    if (QUEUE_Size(&module->itempool) < module->maxitems) {
        if (locked) {
            w->flags = WKI_DETACHED;
            QUEUE_InsertTail(&module->itempool, &w->itemsQ);
        } else {
            locked = MUTEX_Lock(&module->mutex);
            if (QUEUE_Size(&module->itempool) < module->maxitems) {
                w->flags = WKI_DETACHED;
                QUEUE_InsertTail(&module->itempool, &w->itemsQ);
            } else {
                MEM_Free(w);
            }
        }
    } else {
        MEM_Free(w);
    }

    if (locked) {
        MUTEX_Unlock(&module->mutex);
    }
}
Beispiel #2
0
/**
 * Moves all the entries from the source queue to the tail of the destination
 * queue. DOES NOT remove the entries that are already in the destination 
 * queue. Returns number of moved entries.
 */
int QUEUE_Move(Queue * dest, Queue * src)
{
    int n = 0;
    if (src && src != dest && QUEUE_Size(src) > 0) {
        QEntry * e;
        while ((e = QUEUE_RemoveHead(src)) != NULL) {
            QUEUE_InsertTail(dest, e);
            n++;
        }
        ASSERT(QUEUE_Size(src) == 0);
    }
    return n;
}
Beispiel #3
0
/**
 * Puts RWLockWaiter structure into the cache or deallocates it. This is
 * enough for a shared waiter but not for exclusive waiter (see function
 * below). Must be used under synchronization.
 */
STATIC void RWLOCK_ReleaseWaiter(RWLock * lock, RWLockWaiter * w)
{
    QUEUE_RemoveEntry(&w->entry);
    if (QUEUE_Size(&lock->waiterCache) < MAX_WAITER_CACHE_SIZE) {
        w->index = 0;
        w->event = NULL;
        QUEUE_InsertTail(&lock->waiterCache, &w->entry);
    } else {
        MEM_Free(w);
    }
}
Beispiel #4
0
/**
 * Waits until all work items associated with this work queue are done ane 
 * deallocates the work queue.
 */
void WKQ_Delete(WorkQueue * q)
{
    if (q) {
        WKQ_Stop(q, True);

        if (q->flags & WKQ_KILLME) {
            ASSERT(QUEUE_Size(&q->items) == 1);
        } else {
            ASSERT(QUEUE_IsEmpty(&q->items));
            WKQ_Free(q);
        }
    }
}
Beispiel #5
0
/**
 * Locks resource for non-exclusive use, waits if necessary. Returns True 
 * if lock has been successfully acquired, otherwise False.
 */
Bool RWLOCK_TimeReadLock(RWLock * lock, long ms) 
{
    Bool ok = True;
    Bool success = False;
    RWLockWaiter * waiter = NULL;
    Time deadline = 0;

    /*
     * this flag is False if we have found that current thread is NOT 
     * an owner of the resource, so that we don't scan the lock entries
     * more than once.
     */
    Bool maybeOwner = True;
    RWEntry * entry = NULL;

    /* calculate the deadline if it's a wait with timeout */
    if (ms > 0) {
        deadline = TIME_Now() + ms;
    }

    MUTEX_Lock(&lock->mutex);
    while (ok) {
        
        Time now = 0;

        /*
         * if this thread already owns this resource either exclusively 
         * or shared, we are all set. All we need is to increment entry
         * count. NOTE that we don't touch the "exclusive" flag, meaning
         * that if resource has been acquired exclusively, it remains
         * this way.   
         */
        if (maybeOwner) {
            entry = RWLOCK_FindEntry(lock);
            if (entry) {
                success = True;
                if (lock->flags & RWLOCK_FLAG_EXCLUSIVE_LOCK) {
                    ASSERT(entry->write > 0);
                    entry->write++;
                } else { 
                    ASSERT(entry->write == 0);
                    entry->read++;
                }
                break;
            } else {
                maybeOwner = False; /* don't scan entry table again */
            }
        }

        /* if resource is not owned and no one is waiting, we can have it */
        if (lock->locks <= 0 && 
            QUEUE_Size(&lock->shareWaiters) == 0 &&
            QUEUE_Size(&lock->exclusiveWaiters) == 0) {

            /*
             * note that it's quite possible that resource is not owned
             * but the wait queue is not empty. this can happen for example
             * if this thread just released the resource and waiters didn't
             * yet have the chance to run. in such case, this thread should
             * be place into the queue to avoid starving the waiters
             */
            entry = RWLOCK_GetEntry(lock);
            if (entry) {
                success = True;
                lock->flags &= ~RWLOCK_FLAG_EXCLUSIVE_LOCK;
                entry->read++;
            }
            break;
        }

        /* 
         * if resource is owned in shared mode, there's a good chance that
         * we can have it immediately. Some restrictions apply (see below)
         */
        if (!(lock->flags & RWLOCK_FLAG_EXCLUSIVE_LOCK)) {

            /*
             * normally we allow this thread to access the resource
             * in readonly mode even if there's an exclusive waiter.
             * However, if we always did that, the exclusive waiter
             * might end up waiting forever if new readonly waters
             * keep coming. To prevent this from happening, we count
             * the number of times an exclusive waiter has been bypassed
             * by a lucky late-coming reader. If this number exceeds
             * the limit, everyone has to stay in the line.
             */
            if (QUEUE_Size(&lock->exclusiveWaiters) == 0  ||
                lock->bypassCount < RWLOCK_MAX_BYPASS_COUNT) {
                entry = RWLOCK_GetEntry(lock);
                if (entry) {
                    if (QUEUE_Size(&lock->exclusiveWaiters) > 0) {
                        lock->bypassCount++;
                    }
                    ASSERT(entry->write == 0);
                    success = True;
                    entry->read++;
                }
                break; 
            }
        }

        /*
         * resource cannot be acquired immediately for exclusive access. 
         * If we cannot wait (any longer), break the loop. 
         */
        if (ms == 0) {
            break;
        } else if (ms > 0) {
            /* check for timeout */
            now = TIME_Now();
            if (now >= deadline) {
                break;
            }
        }

        /* 
         * release the mutex and wait for event to be signalled, then 
         * start it all over again. 
         */
        lock->contentions++;
        if (!waiter) {
            waiter = RWLOCK_GetShareWaiter(lock);
            if (!waiter) break;
        }

        EVENT_Reset(&lock->shareEvent);
        MUTEX_Unlock(&lock->mutex);

        /* wait */
        if (ms > 0) {
            long tmo = (long)(deadline - now);
            if (EVENT_TimeWait(waiter->event, tmo) == WAIT_STATE_ERROR) {
                ok = False;
            }
        } else {
            ok = BoolValue(EVENT_Wait(waiter->event) == WAIT_STATE_OK);
        }

        MUTEX_Lock(&lock->mutex);
    }

    if (success) lock->locks++;
    if (waiter) RWLOCK_ReleaseWaiter(lock, waiter);
    MUTEX_Unlock(&lock->mutex);
    return success;
}
Beispiel #6
0
/**
 * Locks resource for exclusive use, waits if necessary. Returns True if lock 
 * has been successfully acquired, otherwise False.
 */
Bool RWLOCK_TimeWriteLock(RWLock * lock, long ms) 
{
    Bool ok = True;
    Bool success = False;
    RWLockWaiter * waiter = NULL;
    Time deadline = 0;

    /*
     * this flag is False if we have found that current thread is NOT 
     * an owner of the resource, so that we don't scan the lock entries
     * more than once.
     */
    Bool maybeOwner = True;
    RWEntry * entry = NULL;

    /* calculate the deadline if it's a wait with timeout */
    if (ms > 0) {
        deadline = TIME_Now() + ms;
    }

    /*
     * we can acquire the resource immediately if
     * 1. resource is unowned and no one is waiting; or
     * 2. this thread is the only one that is using the resource, either 
     *    shared or exclusively 
     */
    MUTEX_Lock(&lock->mutex);
    while (ok) {

        Time now = 0;
        
        /*
         * if this thread already owns this resource exclusively, 
         * we are all set. All we need is to increment entry count.
         */
        if (lock->entriesActive == 1 && maybeOwner) {
            if (!entry) {
                entry = RWLOCK_FindEntry(lock);
            }
            if (entry) {
                success = True;
                lock->flags |= RWLOCK_FLAG_EXCLUSIVE_LOCK;
                entry->write++; /* convert shared to exclusive */
                break;
            } else {
                maybeOwner = False;
            }
        }

        /* if resource is not owned and no one is waiting, we can have it */
        if (lock->locks <= 0) {
            Bool gotIt = False;
            if (waiter) {
                gotIt = BoolValue(lock->exclusiveWaiters.head.next == 
                    &waiter->entry);
            } else {
                gotIt = BoolValue(QUEUE_Size(&lock->shareWaiters) == 0 &&
                                  QUEUE_Size(&lock->exclusiveWaiters) == 0);
            }

            /*
             * note that it's quite possible that resource is not owned
             * but the wait queue is not empty. this can happen for example
             * if this thread just released the resource and waiters didn't
             * yet have the chance to run. in such case, this thread should
             * be place into the queue to avoid starving the waiters
             */
            if (gotIt) {
                if (!entry) {
                    entry = RWLOCK_GetEntry(lock);
                }
                if (entry) {
                    success = True;
                    lock->flags |= RWLOCK_FLAG_EXCLUSIVE_LOCK;
                    entry->write++;
                }
                break;
            }
        }

        /*
         * resource cannot be acquired immediately for exclusive access. 
         * If we cannot wait (any longer), break the loop. 
         */
        if (ms == 0) {
            break;
        } else if (ms > 0) {
            /* check for timeout */
            now = TIME_Now();
            if (now >= deadline) {
                break;
            }
        }

        /* 
         * release the mutex and wait for event to be signaled, then 
         * start it all over again. 
         */
        lock->contentions++;
        if (!waiter) {
            waiter = RWLOCK_GetExclusiveWaiter(lock);
            if (!waiter) break;
        }

        EVENT_Reset(waiter->event);
        MUTEX_Unlock(&lock->mutex);

        /* wait */
        if (ms > 0) {
            long tmo = (long)(deadline - now);
            if (EVENT_TimeWait(waiter->event,tmo) == WAIT_STATE_ERROR) {
                ok = False;
            }
        } else {
            ok = BoolValue(EVENT_Wait(waiter->event) == WAIT_STATE_OK);
        }
        MUTEX_Lock(&lock->mutex);
    }
    if (success) lock->locks++;
    if (waiter) RWLOCK_ReleaseExclusiveWaiter(lock, waiter);
    lock->bypassCount = 0;
    MUTEX_Unlock(&lock->mutex);
    return success;
}