diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 7c6042e..caa66dd 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -74,7 +74,7 @@ BRIEF_MEMBER_DESC = YES # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. -REPEAT_BRIEF = YES +REPEAT_BRIEF = NO # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string @@ -270,7 +270,7 @@ SUBGROUPING = YES # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. -TYPEDEF_HIDES_STRUCT = NO +TYPEDEF_HIDES_STRUCT = YES # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. @@ -384,7 +384,7 @@ HIDE_SCOPE_NAMES = NO # will put a list of the files that are included by a file in the documentation # of that file. -SHOW_INCLUDE_FILES = YES +SHOW_INCLUDE_FILES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. @@ -396,7 +396,7 @@ INLINE_INFO = YES # alphabetically by member name. If set to NO the members will appear in # declaration order. -SORT_MEMBER_DOCS = YES +SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically diff --git a/doc/modules b/doc/modules index d11bced..beaa5e1 100644 --- a/doc/modules +++ b/doc/modules @@ -3,17 +3,24 @@ *//** @defgroup BUFFER_MANAGER @ingroup LLADD_CORE -*//** @defgroup BUFFER_MANAGER_IMPLEMENTATIONS - @ingroup BUFFER_MANAGER +*//** + @defgroup BUFFER_MANAGER_IMPLEMENTATIONS + @ingroup BUFFER_MANAGER *//** @defgroup PAGE_EVICTION @ingroup BUFFER_MANAGER *//** @defgroup LOGGER @ingroup LLADD_CORE +*//** + @defgroup LOGGING_IMPLEMENTATIONS + @ingroup LOGGER +*//** + @defgroup LOGGING_INTERFACES + @ingroup LOGGER *//** @defgroup LOGGING_DISCIPLINES - @ingroup LOGGER + @ingroup LOGGER *//** @defgroup RECOVERY @ingroup LLADD_CORE diff --git a/src/stasis/logger/inMemoryLog.c b/src/stasis/logger/inMemoryLog.c index 01b025d..889df06 100644 --- a/src/stasis/logger/inMemoryLog.c +++ b/src/stasis/logger/inMemoryLog.c @@ -2,6 +2,13 @@ #include #include #include +/** + * @file + * + * A non-durable Stasis log implementation stores log entries in RAM. + * + * @ingroup LOGGING_IMPLEMENTATIONS + */ typedef struct { rwl * flushedLSN_lock; diff --git a/src/stasis/logger/safeWrites.c b/src/stasis/logger/safeWrites.c index 15380eb..e7bcba3 100644 --- a/src/stasis/logger/safeWrites.c +++ b/src/stasis/logger/safeWrites.c @@ -39,6 +39,8 @@ authors grant the U.S. Government and others acting in its behalf permission to use and distribute the software in accordance with the terms specified in this license. ---*/ + + #include #include #include @@ -58,6 +60,16 @@ terms specified in this license. #include #include +/** + * @file + * + * A Stasis log implementation that uses safe writes (copy tail, force, rename) to perform truncation. + * + * @todo The safeWrites log implementation is not optimized for reading old entries. + * + * @ingroup LOGGING_IMPLEMENTATIONS + */ + /** Latch order: truncate_mutex, write_mutex, read_mutex */ @@ -478,11 +490,7 @@ static int close_LogWriter(stasis_log_t* log) { return 0; } -void stasis_log_safe_writes_delete(const char* log_filename) { - remove(log_filename); -} - -const LogEntry * readLSNEntry_LogWriter(stasis_log_t * log, const lsn_t LSN) { +static const LogEntry * readLSNEntry_LogWriter(stasis_log_t * log, const lsn_t LSN) { stasis_log_safe_writes_state* sw = log->impl; LogEntry * ret; @@ -549,7 +557,7 @@ const LogEntry * readLSNEntry_LogWriter(stasis_log_t * log, const lsn_t LSN) { Perform the move, and release the latch. */ -int truncateLog_LogWriter(stasis_log_t* log, lsn_t LSN) { +static int truncateLog_LogWriter(stasis_log_t* log, lsn_t LSN) { stasis_log_safe_writes_state* sw = log->impl; FILE *tmpLog; @@ -734,7 +742,7 @@ int truncateLog_LogWriter(stasis_log_t* log, lsn_t LSN) { } -lsn_t firstLogEntry_LogWriter(stasis_log_t* log) { +static lsn_t firstLogEntry_LogWriter(stasis_log_t* log) { stasis_log_safe_writes_state* sw = log->impl; assert(sw->fp); @@ -890,3 +898,7 @@ stasis_log_t* stasis_log_safe_writes_open(const char * filename, return log; } + +void stasis_log_safe_writes_delete(const char* log_filename) { + remove(log_filename); +} diff --git a/src/stasis/replacementPolicy/lru.c b/src/stasis/replacementPolicy/lru.c index 22eed31..c993f51 100644 --- a/src/stasis/replacementPolicy/lru.c +++ b/src/stasis/replacementPolicy/lru.c @@ -8,32 +8,32 @@ #include #include -typedef struct entry { +typedef struct stasis_replacement_policy_lru_entry { void * value; uint64_t clock; -} entry; +} stasis_replacement_policy_lru_entry; -typedef struct lru { +typedef struct stasis_replacement_policy_lru_t { uint64_t now; struct LH_ENTRY(table) * hash; struct RB_ENTRY(tree) * lru; void * (*getNode)(void * page, void * conf); void (*setNode)(void * page, void * n, void * conf); void * conf; -} lru; +} stasis_replacement_policy_lru_t; -int cmp(const void * ap, const void * bp, const void * ignored) { - const entry * a = ap; - const entry * b = bp; +static int stasis_replacement_policy_lru_entry_cmp(const void * ap, const void * bp, const void * ignored) { + const stasis_replacement_policy_lru_entry * a = ap; + const stasis_replacement_policy_lru_entry * b = bp; - return a->clock < b->clock ? -1 - : a->clock == b->clock ? 0 + return a->clock < b->clock ? -1 + : a->clock == b->clock ? 0 : 1; } -static void lruDeinit(replacementPolicy* r) { +static void stasis_replacement_policy_lru_deinit(replacementPolicy* r) { //XXX free other stuff - lru * l = r->impl; + stasis_replacement_policy_lru_t * l = r->impl; LH_ENTRY(destroy)(l->hash); RB_ENTRY(destroy)(l->lru); @@ -43,56 +43,56 @@ static void lruDeinit(replacementPolicy* r) { /** @todo handle clock wraps properly! */ -static void lruHit(replacementPolicy* r, void * p) { - lru * l = r->impl; - entry * e = l->getNode(p, l->conf); +static void stasis_replacement_policy_lru_hit(replacementPolicy* r, void * p) { + stasis_replacement_policy_lru_t * l = r->impl; + stasis_replacement_policy_lru_entry * e = l->getNode(p, l->conf); assert(e); - entry * old = (entry * ) RB_ENTRY(delete)(e, l->lru); + stasis_replacement_policy_lru_entry * old = (stasis_replacement_policy_lru_entry * ) RB_ENTRY(delete)(e, l->lru); assert(e == old); e->clock = l->now; - l->now++; - old = (entry *)RB_ENTRY(search)(e, l->lru); + l->now++; + old = (stasis_replacement_policy_lru_entry *)RB_ENTRY(search)(e, l->lru); assert(e == old); } -static void * lruGetStale(replacementPolicy* r) { - lru * l = r->impl; - entry * e = (entry * ) RB_ENTRY(min)(l->lru); +static void * stasis_replacement_policy_lru_get_stale(replacementPolicy* r) { + stasis_replacement_policy_lru_t * l = r->impl; + stasis_replacement_policy_lru_entry * e = (stasis_replacement_policy_lru_entry * ) RB_ENTRY(min)(l->lru); return e ? e->value : 0; } -static void* lruRemove(replacementPolicy* r, void * p) { - lru * l = r->impl; - entry * e = l->getNode(p, l->conf); +static void* stasis_replacement_policy_lru_remove(replacementPolicy* r, void * p) { + stasis_replacement_policy_lru_t * l = r->impl; + stasis_replacement_policy_lru_entry * e = l->getNode(p, l->conf); assert(e); - entry * old = (entry *) RB_ENTRY(delete)(e, l->lru); + stasis_replacement_policy_lru_entry * old = (stasis_replacement_policy_lru_entry *) RB_ENTRY(delete)(e, l->lru); assert(old == e); void * ret = e->value; free(e); return ret; } -static void lruInsert(replacementPolicy* r, void * p) { - lru * l = r->impl; - entry * e = malloc(sizeof(entry)); +static void stasis_replacement_policy_lru_insert(replacementPolicy* r, void * p) { + stasis_replacement_policy_lru_t * l = r->impl; + stasis_replacement_policy_lru_entry * e = malloc(sizeof(stasis_replacement_policy_lru_entry)); e->value = p; e->clock = l->now; l->now++; l->setNode(p, l->conf, e); - entry * old = (entry *) RB_ENTRY(search)(e, l->lru); + stasis_replacement_policy_lru_entry * old = (stasis_replacement_policy_lru_entry *) RB_ENTRY(search)(e, l->lru); assert(e == old); } -replacementPolicy * lruInit() { +replacementPolicy * stasis_replacement_policy_lru_init() { replacementPolicy * ret = malloc(sizeof(replacementPolicy)); - lru * l = malloc(sizeof(lru)); + stasis_replacement_policy_lru_t * l = malloc(sizeof(stasis_replacement_policy_lru_t)); l->now = 0; l->hash = LH_ENTRY(create)(10); // l->lru = RB_ENTRY(init)((int(*)(const void*,const void*,const void*))cmp, 0); - l->lru = RB_ENTRY(init)(cmp, 0); - ret->init = lruInit; - ret->deinit = lruDeinit; - ret->hit = lruHit; - ret->getStale = lruGetStale; - ret->remove = lruRemove; - ret->insert = lruInsert; + l->lru = RB_ENTRY(init)(stasis_replacement_policy_lru_entry_cmp, 0); + ret->init = stasis_replacement_policy_lru_init; + ret->deinit = stasis_replacement_policy_lru_deinit; + ret->hit = stasis_replacement_policy_lru_hit; + ret->getStale = stasis_replacement_policy_lru_get_stale; + ret->remove = stasis_replacement_policy_lru_remove; + ret->insert = stasis_replacement_policy_lru_insert; ret->impl = l; return ret; } diff --git a/stasis/logger/inMemoryLog.h b/stasis/logger/inMemoryLog.h index 248cc50..509914a 100644 --- a/stasis/logger/inMemoryLog.h +++ b/stasis/logger/inMemoryLog.h @@ -2,7 +2,9 @@ #define __INMEMORYLOG #include - +/** + * Allocate a new non-persistent Stasis log. + */ stasis_log_t* stasis_log_impl_in_memory_open(); #endif diff --git a/stasis/logger/logEntry.h b/stasis/logger/logEntry.h index b0d1802..e7acbe2 100644 --- a/stasis/logger/logEntry.h +++ b/stasis/logger/logEntry.h @@ -123,6 +123,7 @@ LogEntry * allocCLRLogEntry(const LogEntry * e); void freeLogEntry(const LogEntry * e); /** @param lh The log handle the entry will be stored in. (Needed because some log entries are of type INTERNALLOG) May be NULL if e is not of type INTERNALLOG. + @param e A log entry of any type. @return the length, in bytes, of e. */ lsn_t sizeofLogEntry(stasis_log_t * lh, const LogEntry * e); diff --git a/stasis/logger/logger2.h b/stasis/logger/logger2.h index e80edf6..4152cfe 100644 --- a/stasis/logger/logger2.h +++ b/stasis/logger/logger2.h @@ -43,21 +43,18 @@ terms specified in this license. /** * @file * - * Interface to Stasis' log file. - * - * @ingroup LOGGING_DISCIPLINE - * - * $Id$ + * Transaction-level log manipulation routines (commit, update, etc...) that maintain consistency with the transaction tables. * + * @ingroup LOGGING_INTERFACES */ - #ifndef __LOGGER2_H__ #define __LOGGER2_H__ #include typedef struct stasis_log_t stasis_log_t; + typedef struct stasis_log_group_force_t stasis_log_group_force_t; typedef enum { @@ -94,77 +91,162 @@ typedef int (guard_fcn_t)(const LogEntry *, void *); */ extern TransactionLog stasis_transaction_table[MAX_TRANSACTIONS]; - - +/** + * Interface provided by Stasis log implementations. + * + * This struct defines the interface provided by Stasis log + * implementations. New log implementations should provide a + * method that populates a stasis_log_t with appropriate function + * pointers and runtime state. + * + * @see safeWrites.c, inMemoryLog.c for example implementations. + * + * @ingroup LOGGING_IMPLEMENTATIONS + */ struct stasis_log_t { /** - Needed by sizeofLogEntry + Return the size of an implementation-specific log entry. + + Log implementations may store extra information in "internal entries". + These entries will be ignored by higher-level code. In order to + facilitate memory management, Stasis' sizeofLogEntry() method supports + internal entries by calling this method. + + @param log "this" log object + @param e A log entry with type INTERNALLOG + @return the length of e, in bytes. */ lsn_t (*sizeof_internal_entry)(struct stasis_log_t* log, const LogEntry * e); /** Append a log entry to the end of the log. - @param e This call sets e->LSN to entry's offset. + Append a log entry to the end of the log. + + @param log "this" log object + @param e The entry to be written to log. After the call returns, e->LSN will be the new entry's offset. @return 0 on success */ int (*write_entry)(struct stasis_log_t* log, LogEntry * e); /** Read a log entry, given its LSN. - @param lsn The lsn of the log entry to be read. + + Read a log entry, given its LSN. + + @param log "this" log object + @param lsn The LSN of the log entry to be read. This must be the LSN of a valid log entry. + @return The LogEntry of interest. Should be freed with freeLogEntry(). */ const LogEntry* (*read_entry)(struct stasis_log_t* log, lsn_t lsn); /** Given a log entry, return the LSN of the next entry. + + This method returns the LSN of the log entry that will succeed the + given entry. Since the meaning of the LSN field is defined by the + underlying log implementation, this could return the offset into some + underlying file, or simply e->LSN + 1. + + @param log "this" log object + @param e A LogEntry that has already been stored in this log. + @return the LSN of the next entry. Since LSN's must define an order + over the log, this must be greater than e->LSN. */ lsn_t (*next_entry)(struct stasis_log_t* log, const LogEntry * e); /** - This function returns the LSN of the most recent - log entry that has not been flushed to disk. If the entire log - is flushed, this function returns the LSN of the entry that will - be allocated the next time the log is appended to. + Return the LSN of the earliest log entry that may not survive a crash. - @param log The log file, which may or may not support durability. + Return the LSN of the earliest log entry that may not survive a crash. + If the entire log is stable, or the log does not support durability, + this function returns the LSN of the entry that will be allocated + the next time the log is appended to. + + @param log "this" log object, which may or may not support durability. @param mode The mode in which the log entries must have been forced. */ lsn_t (*first_unstable_lsn)(struct stasis_log_t* log, stasis_log_force_mode_t mode); /** - This function returns the LSN of the next log entry passed to - write_entry. This shouldn't be used to determine which entry a - particular call will assign; rather it is used to provide a lower - bound on the LSN of newly-loaded LSN-free pages. + Return the LSN that will be assigned to the next entry written to this log. + + This function returns the LSN that will be assigned to the next entry + written to log. Because multiple threads may be accessing the same + stasis_log_t object, this method should not be used to determine which + LSN will actually be assigned; rather it is used to compute a valid + lower bound of the LSN of newly-loaded LSN-free pages. + + @param log "this" log object */ lsn_t (*next_available_lsn)(struct stasis_log_t* log); /** - Force any enqueued, unwritten entries to disk + Force any enqueued, unwritten entries to disk. + + Once this method returns, any log entries written before the call began + should survive subsequent crashes. If the underlying log implementation + is not durable, then this method has no effect. + + This method should not attempt to amortize the cost of multiple + concurrent calls; stasis_log_t::group_force provides takes care of this. + If group_force is non-null, callers should invoke methods on it rather + than call this method directly. + + @param log "this" log object, which may or may not support durability. + @param mode The reason the log tail should be forced; in certain + environments, force writes that maintain the write-ahead invariant are + treated differently than those for transaction commit. */ void (*force_tail)(struct stasis_log_t* log, stasis_log_force_mode_t mode); /** - @param lsn The first lsn that will be available after truncation. - @return 0 on success + Delete a prefix of the log. + + This method allows the log to "forget" about old log entries. Its + behavior is implementation defined. A call to truncate amounts to a + promise that subsequent calls to stasis_log_t::read_entry will not + request entries before the truncation point. + + @param log "this" log object. + @param lsn The truncation point; the first lsn that will be available after truncation. + @return 0 on success */ int (*truncate)(struct stasis_log_t* log, lsn_t lsn); /** - Returns the LSN of the first entry of the log. If the log is - empty, return the LSN that will be assigned to the next log - entry that is appended to the log. + Return the LSN of the first entry of the log. + + This function returns the LSN of the earliest entry in the log, which + must be less than or equal to the highest value ever passed into + stasis_log_t::truncate(). If the log is empty, this function returns + the same value as stasis_log_t::next_available_lsn(). + + @param log "this" log object + @return A valid LSN that may be passed into stasis_log_t::read_entry(). */ lsn_t (*truncation_point)(struct stasis_log_t* log); /** + Ensure that the tail of the log is durable, and free any associated resources. + + Ensure that the tail of the log is durable, and free any associated resources. + @return 0 on success */ int (*close)(struct stasis_log_t* log); + /** + * Determine whether or not this log provides durability. + * + * @return true if this log implementation is durable, zero otherwise. + */ int (*is_durable)(struct stasis_log_t* log); - + /** + * @see groupForce.c + */ stasis_log_group_force_t * group_force; - + /** + * Implementation-specific state. + */ void* impl; }; diff --git a/stasis/logger/safeWrites.h b/stasis/logger/safeWrites.h index 056d122..a1476d8 100644 --- a/stasis/logger/safeWrites.h +++ b/stasis/logger/safeWrites.h @@ -43,40 +43,13 @@ permission to use and distribute the software in accordance with the terms specified in this license. ---*/ -/** - * @file - * - * New version of logstreamer; designed to work with logEntry, and has - * a simplified API. - * - * logstreamer is the implementation of writing the log tail - * It must be bufferred -- in that when something is written to the log tail it - * is not immediately written to disk, but rather just to memory. But - * logstreamer must be able to force flush to disk, which will be done when a - * commit log entry is written to the log tail - * - * Note: using the stdio FILEs for this, and by default it is "fully" buffered. - * The log tail may be flushed to disk without an explicit call to fflush (when - * the program terminates, the file closes), but this is acceptable because it - * never hurts to have more flushes to disk, as long as it doesn't hurt - * performance. - * - * @todo Everything in this file cores on failure (no error handling yet) - * @todo logWriter is optimized for forward operation, scanning backwards - * (as in abort()) is very slow. Perhaps we should maintian a - * 'write behind' buffer? - * - * $Id$ - * - */ - #include #include BEGIN_C_DECLS /** Start a new log stream by opening the log file for reading. This - is stasis's default log implementation, and uses safe writes to + is Stasis's default log implementation, and uses safe writes to perform truncation. @return NULL on error diff --git a/stasis/replacementPolicy.h b/stasis/replacementPolicy.h index 03d2d49..7c8dcbf 100644 --- a/stasis/replacementPolicy.h +++ b/stasis/replacementPolicy.h @@ -32,7 +32,7 @@ typedef struct replacementPolicy { void * impl; } replacementPolicy; -replacementPolicy * lruInit(); +replacementPolicy * stasis_replacement_policy_lru_init(); replacementPolicy * lruFastInit( struct LL_ENTRY(node_t) * (*getNode)(void * page, void * conf), void (*setNode)(void * page, diff --git a/stasis/transactional.h b/stasis/transactional.h index caf6333..462e98c 100644 --- a/stasis/transactional.h +++ b/stasis/transactional.h @@ -472,11 +472,26 @@ terms specified in this license. * @todo Write a brief howto to explain the implementation of new operations. * */ +/** + * @defgroup LOGGER Log + * + * Stasis' log API consists of three components. + */ +/** + * @defgroup LOGGING_IMPLEMENTATIONS Log implementations + * + * Stasis provides a number of default log formats, and supports third-party + * implementations. + * + */ +/** + * @defgroup LOGGING_INTERFACES High-level log interfaces + */ /** * @defgroup LOGGING_DISCIPLINES Logging Disciplines * - * Stasis' log API provides a number of methods that directly - * manipulate the log. + * Transactional data-structures must choose one of Stasis' recovery modes, + * and interact with the log accordingly. * * @section SNF STEAL/NO-FORCE recovery * Stasis includes a function, Tupdate(), that @@ -489,10 +504,10 @@ terms specified in this license. * during commit. Recovery works by "repeating history"; all actions * are redone up to some point in time after the last successful * transaction committed, but before the crash. Conceptually, any - * partially commited transactions are then rolled back using + * partially committed transactions are then rolled back using * Tabort(), as they would be during normal operation. For more * information about STEAL/NO-FORCE recovery strategies, see the ARIES - * paper (XXX cite aries properly) + * paper (XXX cite Aries properly) * * * @section SF STEAL/FORCE and bulk-logged recovery