/*
* Copyright (c) 2003-2005 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*
* $Id: edbc.h,v 1.17 2005/11/18 18:15:44 ca Exp $
*/
#ifndef SM_EDBC_H
#define SM_EDBC_H 1
#include "sm/generic.h"
#include "sm/error.h"
#include "sm/magic.h"
#include "sm/time.h"
#include "sm/mta.h"
#include "sm/pthread.h"
#include "sm/bsd-tree.h"
#include "sm/queue.h"
/*
** Envelope Database Cache: this structure stores the recipients IDs
** to try in the order of "next time to try" (next_try, ntt for short).
** It is organized as an AVL tree where each node is the head of a list
** in which all recipient IDs with the same "next_try" are stored.
** Note: it would be nice to write a generic structure for this
** (similar to a hash table) so it can be used in other places too.
**
** The key must be next_try but that isn't unique...
** Which data structure allows for that?
** Do we have to combine trees with list? That is, if the entry is
** already there, just add it to the node (compare hash tables)?
** Some of the algorithms below would be simpler if the list (edbc_node)
** has a pointer back to the "head" (the tnode). Then the time (next_try)
** would only be stored in the head since it is the same for every element
** in the list.
**
** It might be useful to keep some unused nodes in a list for faster
** (re)allocation. Could we make this somehow generic ("slab" allocator)?
*/
typedef struct edbc_tnode_S edbc_tnode_T, *edbc_tnode_P;
typedef struct edbc_node_S edbc_node_T, *edbc_node_P;
typedef struct edbc_tree_S edbc_tree_T, *edbc_tree_P;
typedef struct edbc_ctx_S edbc_ctx_T, *edbc_ctx_P;
typedef TAILQ_HEAD(, edbc_node_S) ecn_hd_T, *ecn_hd_P;
/*
** Envelope database cache context.
** Contains link to root of tree.
*/
struct edbc_ctx_S
{
sm_magic_T sm_magic;
pthread_mutex_t edbc_mutex;
uint edbc_max_entries;
uint edbc_entries;
uint edbc_flags;
#if 0
time_T edbc_max_ntt; /* maximum valid next time to try */
#endif
RB_HEAD(edbc_tree_S, edbc_tnode_S) edbc_root;
};
#define EDBC_FL_NONE 0x00000000
/* AQ is currently "full", at least wrt putting entries from EDB into it */
#define EDBC_FL_AQFULL 0x00000001
#if 0
/* EDBC is currently full, edbc_max_ntt is valid */
#define EDBC_FL_FULL 0x00000002
#endif
#define EDBC_SET_FLAG(edbc_ctx, fl) (edbc_ctx)->edbc_flags |= (fl)
#define EDBC_CLR_FLAG(edbc_ctx, fl) (edbc_ctx)->edbc_flags &= (fl)
#define EDBC_IS_FLAG(edbc_ctx, fl) (((edbc_ctx)->edbc_flags & (fl)) != 0)
#define SM_IS_EDBC(edbc_ctx) SM_REQUIRE_ISA((edbc_ctx), SM_EDBC_MAGIC)
/*
** Envelope database cache node which contains the data.
** Has link to other nodes with same key (next_try).
** Should next_try be only in the head of the list? That makes access
** to it more complicated (see also edbc_next() about access to head).
** Hmm, can we trade time_T with a pointer (same amount of memory)?
*/
struct edbc_node_S
{
rcpt_id_T ecn_rcpt_id;
time_T ecn_next_try;
TAILQ_ENTRY(edbc_node_S) ecn_link; /* link to next elem */
};
/*
** Note: it might be useful to add a locktype parameter to most functions.
** Currently locking is provided by the caller, see edbc.c
*/
sm_ret_T edbc_open(edbc_ctx_P *_pedbc_ctx, uint _size, uint _max_size);
sm_ret_T edbc_close(edbc_ctx_P _edbc_ctx);
sm_ret_T edbc_add(edbc_ctx_P _edbc_ctx, rcpt_id_T _rcpt_id, time_T _next_try, bool _check);
bool edbc_exists(edbc_ctx_P _edbc_ctx, rcpt_id_T _rcpt_id, edbc_node_P *_pedbc_node);
sm_ret_T edbc_rm(edbc_ctx_P _edbc_ctx, edbc_node_P _edbc_node);
sm_ret_T edbc_mv(edbc_ctx_P _edbc_ctx, edbc_node_P _edbc_node, time_T _next_try);
sm_ret_T edbc_rmentry(edbc_ctx_P _edbc_ctx, rcpt_id_T _rcpt_id);
edbc_node_P edbc_first(edbc_ctx_P _edbc_ctx);
edbc_node_P edbc_next(edbc_ctx_P _edbc_ctx, edbc_node_P _edbc_node);
#if QMGR_DEBUG
void ebdc_print(edbc_ctx_P _edbc_ctx);
#endif
#if 0
/* put into "local" include file? */
RB_PROTOTYPE(edbc_tree_S, edbc_tnode_S, ectn_entry, edbc_cmp)
#endif
sm_ret_T edbct_rm(edbc_ctx_P _edbc_ctx, edbc_tnode_P _edbc_tnode);
edbc_tnode_P edbct_last(edbc_ctx_P _edbc_ctx);
sm_ret_T edbc_insert(edbc_ctx_P _edbc_ctx, rcpt_id_T _rcpt_id, time_T _next_try, bool _check);
#endif /* SM_EDBC_H */
syntax highlighted by Code2HTML, v. 0.9.1