Blame source/vdo/base/waitQueue.c

Packit Service cbade1
/*
Packit Service cbade1
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service cbade1
 *
Packit Service cbade1
 * This program is free software; you can redistribute it and/or
Packit Service cbade1
 * modify it under the terms of the GNU General Public License
Packit Service cbade1
 * as published by the Free Software Foundation; either version 2
Packit Service cbade1
 * of the License, or (at your option) any later version.
Packit Service cbade1
 * 
Packit Service cbade1
 * This program is distributed in the hope that it will be useful,
Packit Service cbade1
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service cbade1
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service cbade1
 * GNU General Public License for more details.
Packit Service cbade1
 * 
Packit Service cbade1
 * You should have received a copy of the GNU General Public License
Packit Service cbade1
 * along with this program; if not, write to the Free Software
Packit Service cbade1
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service cbade1
 * 02110-1301, USA. 
Packit Service cbade1
 *
Packit Service cbade1
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/waitQueue.c#1 $
Packit Service cbade1
 */
Packit Service cbade1
Packit Service cbade1
#include "waitQueue.h"
Packit Service cbade1
Packit Service cbade1
#include "permassert.h"
Packit Service cbade1
Packit Service cbade1
#include "statusCodes.h"
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
int enqueueWaiter(WaitQueue *queue, Waiter *waiter)
Packit Service cbade1
{
Packit Service cbade1
  int result = ASSERT((waiter->nextWaiter == NULL),
Packit Service cbade1
                      "new waiter must not already be in a waiter queue");
Packit Service cbade1
  if (result != VDO_SUCCESS) {
Packit Service cbade1
    return result;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  if (queue->lastWaiter == NULL) {
Packit Service cbade1
    // The queue is empty, so form the initial circular list by self-linking
Packit Service cbade1
    // the initial waiter.
Packit Service cbade1
    waiter->nextWaiter = waiter;
Packit Service cbade1
  } else {
Packit Service cbade1
    // Splice the new waiter in at the end of the queue.
Packit Service cbade1
    waiter->nextWaiter = queue->lastWaiter->nextWaiter;
Packit Service cbade1
    queue->lastWaiter->nextWaiter = waiter;
Packit Service cbade1
  }
Packit Service cbade1
  // In both cases, the waiter we added to the ring becomes the last waiter.
Packit Service cbade1
  queue->lastWaiter   = waiter;
Packit Service cbade1
  queue->queueLength += 1;
Packit Service cbade1
  return VDO_SUCCESS;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
void transferAllWaiters(WaitQueue *fromQueue, WaitQueue *toQueue)
Packit Service cbade1
{
Packit Service cbade1
  // If the source queue is empty, there's nothing to do.
Packit Service cbade1
  if (!hasWaiters(fromQueue)) {
Packit Service cbade1
    return;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  if (hasWaiters(toQueue)) {
Packit Service cbade1
    // Both queues are non-empty. Splice the two circular lists together by
Packit Service cbade1
    // swapping the next (head) pointers in the list tails.
Packit Service cbade1
    Waiter *fromHead = fromQueue->lastWaiter->nextWaiter;
Packit Service cbade1
    Waiter *toHead   = toQueue->lastWaiter->nextWaiter;
Packit Service cbade1
    toQueue->lastWaiter->nextWaiter   = fromHead;
Packit Service cbade1
    fromQueue->lastWaiter->nextWaiter = toHead;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  toQueue->lastWaiter   = fromQueue->lastWaiter;
Packit Service cbade1
  toQueue->queueLength += fromQueue->queueLength;
Packit Service cbade1
  initializeWaitQueue(fromQueue);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
void notifyAllWaiters(WaitQueue      *queue,
Packit Service cbade1
                      WaiterCallback *callback,
Packit Service cbade1
                      void           *context)
Packit Service cbade1
{
Packit Service cbade1
  // Copy and empty the queue first, avoiding the possibility of an infinite
Packit Service cbade1
  // loop if entries are returned to the queue by the callback function.
Packit Service cbade1
  WaitQueue waiters;
Packit Service cbade1
  initializeWaitQueue(&waiters);
Packit Service cbade1
  transferAllWaiters(queue, &waiters);
Packit Service cbade1
Packit Service cbade1
  // Drain the copied queue, invoking the callback on every entry.
Packit Service cbade1
  while (notifyNextWaiter(&waiters, callback, context)) {
Packit Service cbade1
    // All the work is done by the loop condition.
Packit Service cbade1
  }
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
Waiter *getFirstWaiter(const WaitQueue *queue)
Packit Service cbade1
{
Packit Service cbade1
  Waiter *lastWaiter = queue->lastWaiter;
Packit Service cbade1
  if (lastWaiter == NULL) {
Packit Service cbade1
    // There are no waiters, so we're done.
Packit Service cbade1
    return NULL;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  // The queue is circular, so the last entry links to the head of the queue.
Packit Service cbade1
  return lastWaiter->nextWaiter;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
int dequeueMatchingWaiters(WaitQueue   *queue,
Packit Service cbade1
                           WaiterMatch *matchMethod,
Packit Service cbade1
                           void        *matchContext,
Packit Service cbade1
                           WaitQueue   *matchedQueue)
Packit Service cbade1
{
Packit Service cbade1
  WaitQueue matchedWaiters;
Packit Service cbade1
  initializeWaitQueue(&matchedWaiters);
Packit Service cbade1
Packit Service cbade1
  WaitQueue iterationQueue;
Packit Service cbade1
  initializeWaitQueue(&iterationQueue);
Packit Service cbade1
  transferAllWaiters(queue, &iterationQueue);
Packit Service cbade1
  while (hasWaiters(&iterationQueue)) {
Packit Service cbade1
    Waiter *waiter = dequeueNextWaiter(&iterationQueue);
Packit Service cbade1
    int     result = VDO_SUCCESS;
Packit Service cbade1
    if (!matchMethod(waiter, matchContext)) {
Packit Service cbade1
      result = enqueueWaiter(queue, waiter);
Packit Service cbade1
    } else {
Packit Service cbade1
      result = enqueueWaiter(&matchedWaiters, waiter);
Packit Service cbade1
    }
Packit Service cbade1
    if (result != VDO_SUCCESS) {
Packit Service cbade1
      transferAllWaiters(&matchedWaiters, queue);
Packit Service cbade1
      transferAllWaiters(&iterationQueue, queue);
Packit Service cbade1
      return result;
Packit Service cbade1
    }
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  transferAllWaiters(&matchedWaiters, matchedQueue);
Packit Service cbade1
  return VDO_SUCCESS;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
Waiter *dequeueNextWaiter(WaitQueue *queue)
Packit Service cbade1
{
Packit Service cbade1
  Waiter *firstWaiter = getFirstWaiter(queue);
Packit Service cbade1
  if (firstWaiter == NULL) {
Packit Service cbade1
    return NULL;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  Waiter *lastWaiter = queue->lastWaiter;
Packit Service cbade1
  if (firstWaiter == lastWaiter) {
Packit Service cbade1
    // The queue has a single entry, so just empty it out by nulling the tail.
Packit Service cbade1
    queue->lastWaiter = NULL;
Packit Service cbade1
  } else {
Packit Service cbade1
    // The queue has more than one entry, so splice the first waiter out of
Packit Service cbade1
    // the circular queue.
Packit Service cbade1
    lastWaiter->nextWaiter = firstWaiter->nextWaiter;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  // The waiter is no longer in a wait queue.
Packit Service cbade1
  firstWaiter->nextWaiter  = NULL;
Packit Service cbade1
  queue->queueLength      -= 1;
Packit Service cbade1
  return firstWaiter;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
bool notifyNextWaiter(WaitQueue      *queue,
Packit Service cbade1
                      WaiterCallback *callback,
Packit Service cbade1
                      void           *context)
Packit Service cbade1
{
Packit Service cbade1
  Waiter *waiter = dequeueNextWaiter(queue);
Packit Service cbade1
  if (waiter == NULL) {
Packit Service cbade1
    return false;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  if (callback == NULL) {
Packit Service cbade1
    callback = waiter->callback;
Packit Service cbade1
  }
Packit Service cbade1
  (*callback)(waiter, context);
Packit Service cbade1
  return true;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
const Waiter *getNextWaiter(const WaitQueue *queue, const Waiter *waiter)
Packit Service cbade1
{
Packit Service cbade1
  Waiter *firstWaiter = getFirstWaiter(queue);
Packit Service cbade1
  if (waiter == NULL) {
Packit Service cbade1
    return firstWaiter;
Packit Service cbade1
  }
Packit Service cbade1
  return ((waiter->nextWaiter != firstWaiter) ? waiter->nextWaiter : NULL);
Packit Service cbade1
}