<commit_before>/* $Id$ */

/*
 * List caches are thread-safe lists which allow threads to wait
 * for entries to become available.
 */

#ifndef _PFL_LISTCACHE_H_
#define _PFL_LISTCACHE_H_

#include <sys/types.h>

#include <errno.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <time.h>

#include "psc_ds/list.h"
#include "psc_ds/listguts.h"
#include "psc_ds/lockedlist.h"
#include "psc_util/alloc.h"
#include "psc_util/lock.h"
#include "psc_util/log.h"
#include "psc_util/waitq.h"

extern struct psc_lockedlist	pscListCaches;

struct psc_listcache {
	struct psc_listguts	lc_guts;
	int			lc_flags;

	struct psc_waitq	lc_wq_want;	/* when someone wants an ent */
	struct psc_waitq	lc_wq_empty;	/* when we're empty */
#define lc_index_lentry		lc_guts.plg_index_lentry
#define lc_lock			lc_guts.plg_lock
#define lc_name			lc_guts.plg_name
#define lc_listhd		lc_guts.plg_listhd
#define lc_size			lc_guts.plg_size
#define lc_nseen		lc_guts.plg_nseen
#define lc_entsize		lc_guts.plg_entsize
#define lc_offset		lc_guts.plg_offset
};
typedef struct psc_listcache list_cache_t;

#define PLCF_DYING	(1 << 0)	/* listcache is about to go away */

#define LIST_CACHE_FOREACH(p, plc)				\
	psclist_for_each_entry2((p), &(plc)->lc_listhd,		\
	    (plc)->lc_offset)

#define LIST_CACHE_LOCK(lc)	spinlock(&(lc)->lc_lock)
#define LIST_CACHE_ULOCK(lc)	freelock(&(lc)->lc_lock)
#define LIST_CACHE_TRYLOCK(lc)	trylock(&(lc)->lc_lock)

/**
 * lc_sz - how many items are in here.
 */
static inline ssize_t
lc_sz(struct psc_listcache *lc)
{
	int locked;
	ssize_t sz;

	locked = reqlock(&lc->lc_lock);
	sz = lc->lc_size;
	ureqlock(&lc->lc_lock, locked);
	return (sz);
}

static inline void
lc_del(struct psclist_head *e, struct psc_listcache *lc)
{
	int locked;

	locked = reqlock(&lc->lc_lock);
	psc_assert(lc->lc_size > 0);
	psclist_del(e);
	lc->lc_size--;
	ureqlock(&lc->lc_lock, locked);
}

/**
 * lc_remove - remove an item from a list cache.
 * @lc: the list cache.
 * @p: the item containing a psclist_head member.
 */
static inline void
lc_remove(struct psc_listcache *lc, void *p)
{
	int locked;
	void *e;

	psc_assert(p);
	e = (char *)p + lc->lc_offset;
	locked = reqlock(&lc->lc_lock);
	psc_assert(lc->lc_size > 0);
	psclist_del(e);
	lc->lc_size--;
	ureqlock(&lc->lc_lock, locked);
}

enum psclc_pos {
	PLCP_TAIL,
	PLCP_HEAD
};

#define PLCP_STACK	PLCP_HEAD
#define PLCP_QUEUE	PLCP_TAIL

/* lc_get() flags */
#define PLCGF_NOBLOCK	(1 << 0)	/* return NULL if unavail */
#define PLCGF_WARN	(1 << 1)	/* emit messages */
#define PLCGF_PEEK	(1 << 2)	/* don't remove item */

static __inline struct psclist_head *
_lc_get(struct psc_listcache *lc, struct timespec *abstime,
    enum psclc_pos pos, int flags)
{
	struct psclist_head *e;
	int locked, rc;

	psc_assert(pos == PLCP_HEAD || pos ==  PLCP_TAIL);

	locked = reqlock(&lc->lc_lock);
	while (psclist_empty(&lc->lc_listhd)) {
		if ((lc->lc_flags & PLCF_DYING) ||
		    (flags & PLCGF_NOBLOCK)) {
			ureqlock(&lc->lc_lock, locked);
			return (NULL);
		}

		/* Alert listeners who want to know about exhaustion. */
		psc_waitq_wakeall(&lc->lc_wq_want);
		if (abstime)
			psc_logx(flags & PLCGF_WARN ? PLL_WARN : PLL_TRACE,
			    "lc_get(%s:%p): waiting %p", lc->lc_name, lc, abstime);
		else
			psc_logx(flags & PLCGF_WARN ? PLL_WARN : PLL_TRACE,
			    "lc_get(%s:%p): blocking wait", lc->lc_name, lc);
		if (abstime) {
			rc = psc_waitq_waitabs(&lc->lc_wq_empty,
			    &lc->lc_lock, abstime);
			if (rc) {
				psc_assert(rc == ETIMEDOUT);
				errno = rc;
				return (NULL);
			}
		} else
			psc_waitq_wait(&lc->lc_wq_empty, &lc->lc_lock);
		spinlock(&lc->lc_lock);
	}
	e = pos & PLCP_HEAD ?
	    psclist_first(&lc->lc_listhd) :
	    psclist_last(&lc->lc_listhd);
	if ((flags & PLCGF_PEEK) == 0) {
		psc_assert(lc->lc_size > 0);
		psclist_del(e);
		lc->lc_size--;
	}
	ureqlock(&lc->lc_lock, locked);
	return (e);
}

/**
 * lc_gettimed - try to grab an item from the head of a listcache.
 * @l: the list cache to access.
 * @abstime: timer which tells how long to wait.
 */
static inline void *
lc_gettimed(struct psc_listcache *lc, struct timespec *abstime)
{
	void *p = _lc_get(lc, abstime, PLCP_HEAD, 0);

	return (p ? (char *)p - lc->lc_offset : NULL);
}

/**
 * lc_getwait - grab an item from a listcache,
 *	blocking if necessary until an item becomes available.
 * @lc: the list cache to access.
 */
static inline void *
lc_getwait(struct psc_listcache *lc)
{
	void *p = _lc_get(lc, NULL, PLCP_HEAD, 0);

	return (p ? (char *)p - lc->lc_offset : NULL);
}

/**
 * lc_getnb - grab an item from a listcache or NULL if none are available.
 * @lc: the list cache to access.
 */
static inline void *
lc_getnb(struct psc_listcache *lc)
{
	void *p = _lc_get(lc, NULL, PLCP_HEAD, PLCGF_NOBLOCK);

	return (p ? (char *)p - lc->lc_offset : NULL);
}

/**
 * lc_gettailnb - grab tail item or NULL if unavailable.
 * @lc: the list cache to access.
 */
static inline void *
lc_peektail(struct psc_listcache *lc)
{
	void *p = _lc_get(lc, NULL, PLCP_TAIL, PLCGF_NOBLOCK | PLCGF_PEEK);

	return (p ? (char *)p - lc->lc_offset : NULL);
}

/*
 * lc_kill - list wants to go away, notify waitors.
 * @lc: list cache to kill.
 */
static inline void<commit_msg>now that all psclist_head usage of lc_get() have been eradicated, convert lc_get() to return the item pointer itself, as well as simplify the remaining users of the past behavior.  and add lc_peekhead()<commit_after>/* $Id$ */

/*
 * List caches are thread-safe lists which allow threads to wait
 * for entries to become available.
 */

#ifndef _PFL_LISTCACHE_H_
#define _PFL_LISTCACHE_H_

#include <sys/types.h>

#include <errno.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <time.h>

#include "psc_ds/list.h"
#include "psc_ds/listguts.h"
#include "psc_ds/lockedlist.h"
#include "psc_util/alloc.h"
#include "psc_util/lock.h"
#include "psc_util/log.h"
#include "psc_util/waitq.h"

extern struct psc_lockedlist	pscListCaches;

struct psc_listcache {
	struct psc_listguts	lc_guts;
	int			lc_flags;

	struct psc_waitq	lc_wq_want;	/* when someone wants an ent */
	struct psc_waitq	lc_wq_empty;	/* when we're empty */
#define lc_index_lentry		lc_guts.plg_index_lentry
#define lc_lock			lc_guts.plg_lock
#define lc_name			lc_guts.plg_name
#define lc_listhd		lc_guts.plg_listhd
#define lc_size			lc_guts.plg_size
#define lc_nseen		lc_guts.plg_nseen
#define lc_entsize		lc_guts.plg_entsize
#define lc_offset		lc_guts.plg_offset
};
typedef struct psc_listcache list_cache_t;

#define PLCF_DYING	(1 << 0)	/* listcache is about to go away */

#define LIST_CACHE_FOREACH(p, plc)				\
	psclist_for_each_entry2((p), &(plc)->lc_listhd,		\
	    (plc)->lc_offset)

#define LIST_CACHE_LOCK(lc)	spinlock(&(lc)->lc_lock)
#define LIST_CACHE_ULOCK(lc)	freelock(&(lc)->lc_lock)
#define LIST_CACHE_TRYLOCK(lc)	trylock(&(lc)->lc_lock)

/**
 * lc_sz - how many items are in here.
 */
static inline ssize_t
lc_sz(struct psc_listcache *lc)
{
	int locked;
	ssize_t sz;

	locked = reqlock(&lc->lc_lock);
	sz = lc->lc_size;
	ureqlock(&lc->lc_lock, locked);
	return (sz);
}

static inline void
lc_del(struct psclist_head *e, struct psc_listcache *lc)
{
	int locked;

	locked = reqlock(&lc->lc_lock);
	psc_assert(lc->lc_size > 0);
	psclist_del(e);
	lc->lc_size--;
	ureqlock(&lc->lc_lock, locked);
}

/**
 * lc_remove - remove an item from a list cache.
 * @lc: the list cache.
 * @p: the item containing a psclist_head member.
 */
static inline void
lc_remove(struct psc_listcache *lc, void *p)
{
	int locked;
	void *e;

	psc_assert(p);
	e = (char *)p + lc->lc_offset;
	locked = reqlock(&lc->lc_lock);
	psc_assert(lc->lc_size > 0);
	psclist_del(e);
	lc->lc_size--;
	ureqlock(&lc->lc_lock, locked);
}

enum psclc_pos {
	PLCP_TAIL,
	PLCP_HEAD
};

/* lc_get() position to grab from */
#define PLCP_STACK	PLCP_HEAD
#define PLCP_QUEUE	PLCP_TAIL

/* lc_get() flags */
#define PLCGF_NOBLOCK	(1 << 0)	/* return NULL if unavail */
#define PLCGF_WARN	(1 << 1)	/* emit messages */
#define PLCGF_PEEK	(1 << 2)	/* don't remove item */

static __inline void *
_lc_get(struct psc_listcache *lc, struct timespec *abstime,
    enum psclc_pos pos, int flags)
{
	struct psclist_head *e;
	int locked, rc;

	psc_assert(pos == PLCP_HEAD || pos ==  PLCP_TAIL);

	locked = reqlock(&lc->lc_lock);
	while (psclist_empty(&lc->lc_listhd)) {
		if ((lc->lc_flags & PLCF_DYING) ||
		    (flags & PLCGF_NOBLOCK)) {
			ureqlock(&lc->lc_lock, locked);
			return (NULL);
		}

		/* Alert listeners who want to know about exhaustion. */
		psc_waitq_wakeall(&lc->lc_wq_want);
		if (abstime)
			psc_logx(flags & PLCGF_WARN ? PLL_WARN : PLL_TRACE,
			    "lc_get(%s:%p): waiting %p", lc->lc_name, lc, abstime);
		else
			psc_logx(flags & PLCGF_WARN ? PLL_WARN : PLL_TRACE,
			    "lc_get(%s:%p): blocking wait", lc->lc_name, lc);
		if (abstime) {
			rc = psc_waitq_waitabs(&lc->lc_wq_empty,
			    &lc->lc_lock, abstime);
			if (rc) {
				psc_assert(rc == ETIMEDOUT);
				errno = rc;
				return (NULL);
			}
		} else
			psc_waitq_wait(&lc->lc_wq_empty, &lc->lc_lock);
		spinlock(&lc->lc_lock);
	}
	e = pos & PLCP_HEAD ?
	    psclist_first(&lc->lc_listhd) :
	    psclist_last(&lc->lc_listhd);
	if ((flags & PLCGF_PEEK) == 0) {
		psc_assert(lc->lc_size > 0);
		psclist_del(e);
		lc->lc_size--;
	}
	ureqlock(&lc->lc_lock, locked);
	if (e)
		return ((char *)e - lc->lc_offset);
	return (NULL);
}

/**
 * lc_gettimed - try to grab an item from the head of a listcache.
 * @lc: the list cache to access.
 * @abstime: timer which tells how long to wait.
 */
#define lc_gettimed(lc, tm)	_lc_get((lc), (tm), PLCP_HEAD, 0)

/**
 * lc_getwait - grab an item from a listcache,
 *	blocking if necessary until an item becomes available.
 * @lc: the list cache to access.
 */
#define lc_getwait(lc)		_lc_get((lc), NULL, PLCP_HEAD, 0)

/**
 * lc_getnb - grab an item from a listcache or NULL if none are available.
 * @lc: the list cache to access.
 */
#define lc_getnb(lc)		_lc_get((lc), NULL, PLCP_HEAD, PLCGF_NOBLOCK)

/**
 * lc_peekhead - peek at head item or NULL if unavailable.
 * @lc: the list cache to access.
 */
#define lc_peekhead(lc)		_lc_get((lc), NULL, PLCP_HEAD, PLCGF_NOBLOCK | PLCGF_PEEK)

/**
 * lc_peektail - peek at tail item or NULL if unavailable.
 * @lc: the list cache to access.
 */
#define lc_peektail(lc)		_lc_get((lc), NULL, PLCP_TAIL, PLCGF_NOBLOCK | PLCGF_PEEK)

/*
 * lc_kill - list wants to go away, notify waitors.
 * @lc: list cache to kill.
 */
static inline void
{'fraction_non_alphanumeric': 0.13873718501485102, 'fraction_numerical': 0.002874389192296637, 'mean_word_length': 4.042512077294686, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 94, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '965682', 'n_tokens_mistral': 4597, 'n_tokens_neox': 4370, 'n_words': 1057}