/*
 * Copyright (c) 2018 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA. 
 *
 * $Id: //eng/vdo-releases/magnesium/src/c++/vdo/kernel/limiter.h#2 $
 */

#ifndef LIMITER_H
#define LIMITER_H

#include <linux/wait.h>

/*
 * A Limiter is a fancy counter used to limit resource usage.  We have a
 * limit to number of resources that we are willing to use, and a Limiter
 * holds us to that limit.
 */

typedef struct limiter {
  // A spinlock controlling access to the contents of this struct
  spinlock_t        lock;
  // The queue of threads waiting for a resource to become available
  wait_queue_head_t waiterQueue;
  // The number of resources in use
  uint32_t          active;
  // The maximum number number of resources that have ever been in use
  uint32_t          maximum;
  // The limit to the number of resources that are allowed to be used
  uint32_t          limit;
} Limiter;

/**
 * Get the Limiter variable values (atomically under the lock)
 *
 * @param limiter  The limiter
 * @param active   The number of requests in progress
 * @param maximum  The maximum number of requests that have ever been active
 **/
void getLimiterValuesAtomically(Limiter  *limiter,
                                uint32_t *active,
                                uint32_t *maximum);

/**
 * Initialize a Limiter
 *
 * @param limiter  The limiter
 * @param limit    The limit to the number of active resources
 **/
void initializeLimiter(Limiter *limiter, uint32_t limit);

/**
 * Determine whether there are any active resources
 *
 * @param limiter  The limiter
 *
 * @return true if there are no active resources
 **/
bool limiterIsIdle(Limiter *limiter);

/**
 * Release resources, making them available for other uses
 *
 * @param limiter  The limiter
 * @param count    The number of resources to release
 **/
void limiterReleaseMany(Limiter *limiter, uint32_t count);

/**
 * Release one resource, making it available for another use
 *
 * @param limiter  The limiter
 **/
static inline void limiterRelease(Limiter *limiter)
{
  limiterReleaseMany(limiter, 1);
}

/**
 * Wait until there are no active resources
 *
 * @param limiter  The limiter
 **/
void limiterWaitForIdle(Limiter *limiter);

/**
 * Prepare to start using one resource, waiting if there are too many resources
 * already in use. After returning from this routine, the caller may use the
 * resource, and must call limiterRelease after freeing the resource.
 *
 * @param limiter  The limiter
 **/
void limiterWaitForOneFree(Limiter *limiter);

/**
 * Attempt to reserve one resource, without waiting. After returning from this
 * routine, if allocation was successful, the caller may use the resource, and
 * must call limiterRelease after freeing the resource.
 *
 * @param limiter  The limiter
 *
 * @return true iff the resource was allocated
 **/
bool limiterPoll(Limiter *limiter);

#endif /* LIMITER_H */
