/* Copyright (c) 2007 PrimeBase Technologies GmbH * * PrimeBase XT * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * 2007-10-31 Paul McCullagh * * H&G2JCtL * * The new table cache. Caches all non-index data. This includes the data * files and the row pointer files. */ #ifndef __tabcache_h__ #define __tabcache_h__ struct XTTable; struct XTOpenTable; struct XTTabCache; struct XTDatabase; #include "thread_xt.h" #include "filesys_xt.h" #include "lock_xt.h" #ifdef DEBUG //#define XT_USE_CACHE_DEBUG_SIZES //#define XT_NOT_INLINE #endif #ifdef XT_USE_CACHE_DEBUG_SIZES #define XT_TC_PAGE_SIZE (4*1024) #define XT_TC_SEGMENT_SHIFTS 1 #else #define XT_TC_PAGE_SIZE (32*1024) #define XT_TC_SEGMENT_SHIFTS 3 #endif #define XT_TIME_DIFF(start, now) (\ ((xtWord4) (now) < (xtWord4) (start)) ? \ ((xtWord4) 0XFFFFFFFF - ((xtWord4) (start) - (xtWord4) (now))) : \ ((xtWord4) (now) - (xtWord4) (start))) #define XT_TC_SEGMENT_COUNT ((off_t) 1 << XT_TC_SEGMENT_SHIFTS) #define XT_TC_SEGMENT_MASK (XT_TC_SEGMENT_COUNT - 1) typedef struct XTTabCachePage { xtWord1 tcp_dirty; /* TRUE if the page is dirty. */ xtWord1 tcp_seg; /* Segement number of the page. */ u_int tcp_lock_count; /* Number of read locks on this page. */ u_int tcp_hash_idx; /* The hash index of the page. */ u_int tcp_page_idx; /* The page address. */ u_int tcp_file_id; /* The file id of the page. */ xtDatabaseID tcp_db_id; /* The ID of the database. */ xtTableID tcp_tab_id; /* The ID of the table of this cache page. */ xtWord4 tcp_data_size; /* Size of the data on this page. */ xtOpSeqNo tcp_op_seq; /* The operation sequence number (dirty pages have a operations sequence) */ xtWord4 tcp_ru_time; /* If this is in the top 1/4 don't change position in MRU list. */ struct XTTabCachePage *tcp_next; /* Pointer to next page on hash list, or next free page on free list. */ struct XTTabCachePage *tcp_mr_used; /* More recently used pages. */ struct XTTabCachePage *tcp_lr_used; /* Less recently used pages. */ xtWord1 tcp_data[XT_TC_PAGE_SIZE]; /* This is actually tci_page_size! */ } XTTabCachePageRec, *XTTabCachePagePtr; /* * Each table has a "table operation sequence". This sequence is incremented by * each operation on the table. Each operation in the log is tagged by a * sequence number. * * The writter threads re-order operations in the log, and write the operations * to the database in sequence. * * It is safe to free a cache page when the sequence number of the cache page, * is less than or equal to the written sequence number. */ typedef struct XTTableSeq { xtOpSeqNo ts_next_seq; /* The next sequence number for operations on the table. */ xt_mutex_type ts_ns_lock; /* Lock for the next sequence number. */ xtBool ts_log_no_op(XTThreadPtr thread, xtTableID tab_id, xtOpSeqNo op_seq); /* Return the next operation sequence number. */ #ifdef XT_NOT_INLINE xtOpSeqNo ts_set_op_seq(XTTabCachePagePtr page); xtOpSeqNo ts_get_op_seq(); #else xtOpSeqNo ts_set_op_seq(XTTabCachePagePtr page) { xtOpSeqNo seq; xt_lock_mutex_ns(&ts_ns_lock); page->tcp_op_seq = seq = ts_next_seq++; xt_unlock_mutex_ns(&ts_ns_lock); return seq; } xtOpSeqNo ts_get_op_seq() { xtOpSeqNo seq; xt_lock_mutex_ns(&ts_ns_lock); seq = ts_next_seq++; xt_unlock_mutex_ns(&ts_ns_lock); return seq; } #endif void xt_op_seq_init(XTThreadPtr self) { xt_init_mutex_with_autoname(self, &ts_ns_lock); } void xt_op_seq_set(XTThreadPtr XT_UNUSED(self), xtOpSeqNo n) { ts_next_seq = n; } void xt_op_seq_exit(XTThreadPtr XT_UNUSED(self)) { xt_free_mutex(&ts_ns_lock); } #ifdef XT_NOT_INLINE static xtBool xt_op_is_before(register xtOpSeqNo now, register xtOpSeqNo then); #else static inline xtBool xt_op_is_before(register xtOpSeqNo now, register xtOpSeqNo then) { if (now >= then) { if ((now - then) > (xtOpSeqNo) 0xFFFFFFFF/2) return TRUE; return FALSE; } if ((then - now) > (xtOpSeqNo) 0xFFFFFFFF/2) return FALSE; return TRUE; } #endif } XTTableSeqRec, *XTTableSeqPtr; #ifdef XT_NO_ATOMICS #define TAB_CAC_USE_PTHREAD_RW #else //#define TAB_CAC_USE_RWMUTEX //#define TAB_CAC_USE_PTHREAD_RW //#define IDX_USE_SPINXSLOCK #define TAB_CAC_USE_XSMUTEX #endif #ifdef TAB_CAC_USE_XSMUTEX #define TAB_CAC_LOCK_TYPE XTXSMutexRec #define TAB_CAC_INIT_LOCK(s, i) xt_xsmutex_init_with_autoname(s, i) #define TAB_CAC_FREE_LOCK(s, i) xt_xsmutex_free(s, i) #define TAB_CAC_READ_LOCK(i, o) xt_xsmutex_slock(i, o) #define TAB_CAC_WRITE_LOCK(i, o) xt_xsmutex_xlock(i, o) #define TAB_CAC_UNLOCK(i, o) xt_xsmutex_unlock(i, o) #elif defined(TAB_CAC_USE_PTHREAD_RW) #define TAB_CAC_LOCK_TYPE xt_rwlock_type #define TAB_CAC_INIT_LOCK(s, i) xt_init_rwlock_with_autoname(s, i) #define TAB_CAC_FREE_LOCK(s, i) xt_free_rwlock(i) #define TAB_CAC_READ_LOCK(i, o) xt_slock_rwlock_ns(i) #define TAB_CAC_WRITE_LOCK(i, o) xt_xlock_rwlock_ns(i) #define TAB_CAC_UNLOCK(i, o) xt_unlock_rwlock_ns(i) #elif defined(TAB_CAC_USE_RWMUTEX) #define TAB_CAC_LOCK_TYPE XTRWMutexRec #define TAB_CAC_INIT_LOCK(s, i) xt_rwmutex_init_with_autoname(s, i) #define TAB_CAC_FREE_LOCK(s, i) xt_rwmutex_free(s, i) #define TAB_CAC_READ_LOCK(i, o) xt_rwmutex_slock(i, o) #define TAB_CAC_WRITE_LOCK(i, o) xt_rwmutex_xlock(i, o) #define TAB_CAC_UNLOCK(i, o) xt_rwmutex_unlock(i, o) #elif defined(TAB_CAC_USE_SPINXSLOCK) #define TAB_CAC_LOCK_TYPE XTSpinXSLockRec #define TAB_CAC_INIT_LOCK(s, i) xt_spinxslock_init_with_autoname(s, i) #define TAB_CAC_FREE_LOCK(s, i) xt_spinxslock_free(s, i) #define TAB_CAC_READ_LOCK(i, o) xt_spinxslock_slock(i, o) #define TAB_CAC_WRITE_LOCK(i, o) xt_spinxslock_xlock(i, o) #define TAB_CAC_UNLOCK(i, o) xt_spinxslock_unlock(i, o) #endif /* A disk cache segment. The cache is divided into a number of segments * to improve concurrency. */ typedef struct XTTabCacheSeg { TAB_CAC_LOCK_TYPE tcs_lock; /* The cache segment read/write lock. */ XTTabCachePagePtr *tcs_hash_table; size_t tcs_cache_in_use; } XTTabCacheSegRec, *XTTabCacheSegPtr; /* * The free'er thread has a list of tables to be purged from the cache. * If a table is in the list then it is not allowed to fetch a cache page from * that table. * The free'er thread goes through all the cache, and removes * all cache pages for any table in the purge list. * When a table has been purged it signals any threads waiting for the * purge to complete (this is usually due to a drop table). */ typedef struct XTTabCachePurge { int tcp_state; /* The state of the purge. */ XTTableSeqPtr tcp_tab_seq; /* Identifies the table to be purged from cache. */ } XTTabCachePurgeRec, *XTTabCachePurgePtr; typedef struct XTTabCacheMem { xt_mutex_type tcm_lock; /* The public cache lock. */ xt_cond_type tcm_cond; /* The public cache wait condition. */ XTTabCacheSegRec tcm_segment[XT_TC_SEGMENT_COUNT]; XTTabCachePagePtr tcm_lru_page; XTTabCachePagePtr tcm_mru_page; xtWord4 tcm_ru_now; size_t tcm_approx_page_count; size_t tcm_hash_size; u_int tcm_writer_thread_count; size_t tcm_cache_size; size_t tcm_cache_high; /* The high water level of cache allocation. */ size_t tcm_low_level; /* This is the level to which the freeer will free, once it starts working. */ size_t tcm_high_level; /* This is the level at which the freeer will start to work (to avoid waiting)! */ size_t tcm_mid_level; /* At this level the freeer will not sleep if there are threads waiting. */ /* The free'er thread: */ struct XTThread *tcm_freeer_thread; /* The freeer thread . */ xt_mutex_type tcm_freeer_lock; /* The public cache lock. */ xt_cond_type tcm_freeer_cond; /* The public cache wait condition. */ u_int tcm_purge_list_len; /* The length of the purge list. */ XTTabCachePurgePtr tcm_purge_list; /* Non-NULL if a table is to be purged. */ u_int tcm_threads_waiting; /* Count of the number of threads waiting for the freeer. */ xtBool tcm_freeer_busy; u_int tcm_free_try_count; } XTTabCacheMemRec, *XTTabCacheMemPtr; /* * This structure contains the information about a particular table * for the cache. Each table has its own page size, row size * and rows per page. * Tables also have */ typedef struct XTTabCache { struct XTTable *tci_table; size_t tci_header_size; size_t tci_page_size; size_t tci_rec_size; size_t tci_rows_per_page; public: void xt_tc_setup(struct XTTable *tab, size_t head_size, size_t row_size); xtBool xt_tc_write(XT_ROW_REC_FILE_PTR file, xtRefID ref_id, size_t offset, size_t size, xtWord1 *data, xtOpSeqNo *op_seq, xtBool read, XTThreadPtr thread); xtBool xt_tc_write_cond(XTThreadPtr self, XT_ROW_REC_FILE_PTR file, xtRefID ref_id, xtWord1 new_type, xtOpSeqNo *op_seq, xtXactID xn_id, xtRowID row_id, u_int stat_id, u_int rec_type); xtBool xt_tc_read(XT_ROW_REC_FILE_PTR file, xtRefID ref_id, size_t size, xtWord1 *data, XTThreadPtr thread); xtBool xt_tc_read_4(XT_ROW_REC_FILE_PTR file, xtRefID ref_id, xtWord4 *data, XTThreadPtr thread); xtBool xt_tc_read_page(XT_ROW_REC_FILE_PTR file, xtRefID ref_id, xtWord1 *data, XTThreadPtr thread); xtBool xt_tc_get_page(XT_ROW_REC_FILE_PTR file, xtRefID ref_id, xtBool load, XTTabCachePagePtr *page, size_t *offset, XTThreadPtr thread); void xt_tc_release_page(XT_ROW_REC_FILE_PTR file, XTTabCachePagePtr page, XTThreadPtr thread); xtBool tc_fetch(XT_ROW_REC_FILE_PTR file, xtRefID ref_id, XTTabCacheSegPtr *ret_seg, XTTabCachePagePtr *ret_page, size_t *offset, xtBool read, XTThreadPtr thread); private: xtBool tc_read_direct(XT_ROW_REC_FILE_PTR file, xtRefID ref_id, size_t size, xtWord1 *data, XTThreadPtr thread); xtBool tc_fetch_direct(XT_ROW_REC_FILE_PTR file, xtRefID ref_id, XTTabCacheSegPtr *ret_seg, XTTabCachePagePtr *ret_page, size_t *offset, XTThreadPtr thread); } XTTabCacheRec, *XTTabCachePtr; extern XTTabCacheMemRec xt_tab_cache; void xt_tc_init(XTThreadPtr self, size_t cache_size); void xt_tc_exit(XTThreadPtr self); void xt_tc_set_cache_size(size_t cache_size); xtInt8 xt_tc_get_usage(); xtInt8 xt_tc_get_size(); xtInt8 xt_tc_get_high(); void xt_load_pages(XTThreadPtr self, struct XTOpenTable *ot); #ifdef DEBUG void xt_check_table_cache(struct XTTable *tab); #endif void xt_quit_freeer(XTThreadPtr self); void xt_stop_freeer(XTThreadPtr self); void xt_start_freeer(XTThreadPtr self); void xt_wr_wake_freeer(XTThreadPtr self, struct XTDatabase *db); #endif