/* * 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 */