Commit 9aec732c authored by Zardosht Kasheff's avatar Zardosht Kasheff

refs #61, clean up code relating to cachefiles, expand the

cachefiles_list class and move some functionality in there.
parent e3e3f40c
...@@ -197,7 +197,13 @@ struct cachefile { ...@@ -197,7 +197,13 @@ struct cachefile {
int fd; /* Bug: If a file is opened read-only, then it is stuck in read-only. If it is opened read-write, then subsequent writers can write to it too. */ int fd; /* Bug: If a file is opened read-only, then it is stuck in read-only. If it is opened read-write, then subsequent writers can write to it too. */
CACHETABLE cachetable; CACHETABLE cachetable;
struct fileid fileid; struct fileid fileid;
// the filenum is used as an identifer of the cachefile
// for logging and recovery
FILENUM filenum; FILENUM filenum;
// number used to generate hashes for blocks in the cachefile
// used in toku_cachetable_hash
// this used to be the filenum.fileid, but now it is separate
uint32_t hash_id;
char *fname_in_env; /* Used for logging */ char *fname_in_env; /* Used for logging */
void *userdata; void *userdata;
...@@ -398,9 +404,10 @@ public: ...@@ -398,9 +404,10 @@ public:
private: private:
void pair_remove (PAIR p); void pair_remove (PAIR p);
void cf_pairs_remove (PAIR p); void cf_pairs_remove (PAIR p);
void remove_from_hash_chain(PAIR p);
void add_to_cf_list (PAIR p); void add_to_cf_list (PAIR p);
void add_to_clock (PAIR p); void add_to_clock (PAIR p);
PAIR remove_from_hash_chain (PAIR remove_me, PAIR list); void add_to_hash_chain(PAIR p);
}; };
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
...@@ -415,6 +422,13 @@ public: ...@@ -415,6 +422,13 @@ public:
void read_unlock(); void read_unlock();
void write_lock(); void write_lock();
void write_unlock(); void write_unlock();
int cachefile_of_iname_in_env(const char *iname_in_env, CACHEFILE *cf);
int cachefile_of_filenum(FILENUM filenum, CACHEFILE *cf);
void add_cf_unlocked(CACHEFILE newcf);
void remove_cf(CACHEFILE cf);
FILENUM reserve_filenum();
CACHEFILE find_cachefile_unlocked(struct fileid* fileid);
void verify_unused_filenum(FILENUM filenum);
// access to these fields are protected by the lock // access to these fields are protected by the lock
CACHEFILE m_head; CACHEFILE m_head;
FILENUM m_next_filenum_to_use; FILENUM m_next_filenum_to_use;
......
...@@ -342,45 +342,14 @@ toku_cachetable_set_env_dir(CACHETABLE ct, const char *env_dir) { ...@@ -342,45 +342,14 @@ toku_cachetable_set_env_dir(CACHETABLE ct, const char *env_dir) {
// Once the close has finished, there must not be a cachefile with that name // Once the close has finished, there must not be a cachefile with that name
// in the cachetable. // in the cachetable.
int toku_cachefile_of_iname_in_env (CACHETABLE ct, const char *iname_in_env, CACHEFILE *cf) { int toku_cachefile_of_iname_in_env (CACHETABLE ct, const char *iname_in_env, CACHEFILE *cf) {
ct->cf_list.read_lock(); return ct->cf_list.cachefile_of_iname_in_env(iname_in_env, cf);
CACHEFILE extant;
int r;
r = ENOENT;
for (extant = ct->cf_list.m_head; extant; extant = extant->next) {
if (extant->fname_in_env &&
!strcmp(extant->fname_in_env, iname_in_env)) {
*cf = extant;
r = 0;
break;
}
}
ct->cf_list.read_unlock();
return r;
} }
// What cachefile goes with particular fd? // What cachefile goes with particular fd?
// This function can only be called if the brt is still open, so file must // This function can only be called if the brt is still open, so file must
// still be open // still be open
int toku_cachefile_of_filenum (CACHETABLE ct, FILENUM filenum, CACHEFILE *cf) { int toku_cachefile_of_filenum (CACHETABLE ct, FILENUM filenum, CACHEFILE *cf) {
ct->cf_list.read_lock(); return ct->cf_list.cachefile_of_filenum(filenum, cf);
CACHEFILE extant;
int r = ENOENT;
*cf = NULL;
for (extant = ct->cf_list.m_head; extant; extant = extant->next) {
if (extant->filenum.fileid==filenum.fileid) {
*cf = extant;
r = 0;
break;
}
}
ct->cf_list.read_unlock();
return r;
}
static void cachefile_init_filenum(CACHEFILE cf, int fd, const char *fname_in_env, struct fileid fileid) {
cf->fd = fd;
cf->fileid = fileid;
cf->fname_in_env = toku_xstrdup(fname_in_env);
} }
// TEST-ONLY function // TEST-ONLY function
...@@ -393,30 +362,36 @@ int toku_cachetable_openfd (CACHEFILE *cfptr, CACHETABLE ct, int fd, const char ...@@ -393,30 +362,36 @@ int toku_cachetable_openfd (CACHEFILE *cfptr, CACHETABLE ct, int fd, const char
// Get a unique filenum from the cachetable // Get a unique filenum from the cachetable
FILENUM FILENUM
toku_cachetable_reserve_filenum(CACHETABLE ct) { toku_cachetable_reserve_filenum(CACHETABLE ct) {
CACHEFILE extant; return ct->cf_list.reserve_filenum();
FILENUM filenum; }
invariant(ct);
// TODO: (Zardosht) make this function a method on the cf_list static void create_new_cachefile(
// taking a write lock because we are modifying next_filenum_to_use CACHETABLE ct,
ct->cf_list.write_lock(); FILENUM filenum,
try_again: uint32_t hash_id,
for (extant = ct->cf_list.m_head; extant; extant = extant->next) { int fd,
if (ct->cf_list.m_next_filenum_to_use.fileid==extant->filenum.fileid) { const char *fname_in_env,
ct->cf_list.m_next_filenum_to_use.fileid++; struct fileid fileid,
goto try_again; CACHEFILE *cfptr
} ) {
} // File is not open. Make a new cachefile.
filenum = ct->cf_list.m_next_filenum_to_use; CACHEFILE newcf = NULL;
ct->cf_list.m_next_filenum_to_use.fileid++; XCALLOC(newcf);
ct->cf_list.write_unlock(); newcf->cachetable = ct;
return filenum; newcf->filenum = filenum;
newcf->hash_id = hash_id;
newcf->fd = fd;
newcf->fileid = fileid;
newcf->fname_in_env = toku_xstrdup(fname_in_env);
bjm_init(&newcf->bjm);
*cfptr = newcf;
} }
int toku_cachetable_openfd_with_filenum (CACHEFILE *cfptr, CACHETABLE ct, int fd, int toku_cachetable_openfd_with_filenum (CACHEFILE *cfptr, CACHETABLE ct, int fd,
const char *fname_in_env, const char *fname_in_env,
FILENUM filenum) { FILENUM filenum) {
int r; int r;
CACHEFILE extant, newcf; CACHEFILE newcf;
struct fileid fileid; struct fileid fileid;
assert(filenum.fileid != FILENUM_NONE.fileid); assert(filenum.fileid != FILENUM_NONE.fileid);
...@@ -427,41 +402,22 @@ int toku_cachetable_openfd_with_filenum (CACHEFILE *cfptr, CACHETABLE ct, int fd ...@@ -427,41 +402,22 @@ int toku_cachetable_openfd_with_filenum (CACHEFILE *cfptr, CACHETABLE ct, int fd
return r; return r;
} }
ct->cf_list.write_lock(); ct->cf_list.write_lock();
for (extant = ct->cf_list.m_head; extant; extant = extant->next) { CACHEFILE existing_cf = ct->cf_list.find_cachefile_unlocked(&fileid);
if (toku_fileids_are_equal(&extant->fileid, &fileid)) { if (existing_cf) {
// Clients must serialize cachefile open, close, and unlink // Reuse an existing cachefile and close the caller's fd, whose
// So, during open, we should never see a closing cachefile // responsibility has been passed to us.
// or one that has been marked as unlink on close. r = close(fd);
assert(!extant->unlink_on_close); assert(r == 0);
*cfptr = existing_cf;
// Reuse an existing cachefile and close the caller's fd, whose r = 0;
// responsibility has been passed to us. goto exit;
r = close(fd);
assert(r == 0);
*cfptr = extant;
r = 0;
goto exit;
}
} }
ct->cf_list.verify_unused_filenum(filenum);
// assert that the filenum is not in use create_new_cachefile(ct, filenum, filenum.fileid, fd, fname_in_env, fileid, &newcf);
for (extant = ct->cf_list.m_head; extant; extant = extant->next) {
invariant(extant->filenum.fileid != filenum.fileid);
}
// File is not open. Make a new cachefile. ct->cf_list.add_cf_unlocked(newcf);
XCALLOC(newcf);
newcf->cachetable = ct;
newcf->filenum = filenum;
cachefile_init_filenum(newcf, fd, fname_in_env, fileid);
newcf->next = ct->cf_list.m_head;
newcf->prev = NULL;
if (ct->cf_list.m_head) {
ct->cf_list.m_head->prev = newcf;
}
ct->cf_list.m_head = newcf;
bjm_init(&newcf->bjm);
*cfptr = newcf; *cfptr = newcf;
r = 0; r = 0;
exit: exit:
...@@ -485,38 +441,6 @@ int toku_cachetable_openf (CACHEFILE *cfptr, CACHETABLE ct, const char *fname_in ...@@ -485,38 +441,6 @@ int toku_cachetable_openf (CACHEFILE *cfptr, CACHETABLE ct, const char *fname_in
return r; return r;
} }
//Test-only function
int toku_cachefile_set_fd (CACHEFILE cf, int fd, const char *fname_in_env) {
struct fileid fileid;
int r = toku_os_get_unique_file_id(fd, &fileid);
if (r != 0) {
r = get_error_errno();
close(fd);
goto cleanup;
}
if (cf->close_userdata) {
cf->close_userdata(cf, cf->fd, cf->userdata, false, ZERO_LSN);
}
cf->close_userdata = NULL;
cf->checkpoint_userdata = NULL;
cf->begin_checkpoint_userdata = NULL;
cf->end_checkpoint_userdata = NULL;
cf->userdata = NULL;
close(cf->fd);
cf->fd = -1;
if (cf->fname_in_env) {
toku_free(cf->fname_in_env);
cf->fname_in_env = NULL;
}
//It is safe to have the name repeated since this is a ft-only test function.
//There isn't an environment directory so its both env/cwd.
cachefile_init_filenum(cf, fd, fname_in_env, fileid);
r = 0;
cleanup:
return r;
}
char * char *
toku_cachefile_fname_in_env (CACHEFILE cf) { toku_cachefile_fname_in_env (CACHEFILE cf) {
return cf->fname_in_env; return cf->fname_in_env;
...@@ -527,23 +451,6 @@ toku_cachefile_get_fd (CACHEFILE cf) { ...@@ -527,23 +451,6 @@ toku_cachefile_get_fd (CACHEFILE cf) {
return cf->fd; return cf->fd;
} }
static void remove_cf_from_cachefiles_list (CACHEFILE cf) {
CACHETABLE ct = cf->cachetable;
ct->cf_list.write_lock();
invariant(ct->cf_list.m_head != NULL);
if (cf->next) {
cf->next->prev = cf->prev;
}
if (cf->prev) {
cf->prev->next = cf->next;
}
if (cf == ct->cf_list.m_head) {
invariant(cf->prev == NULL);
ct->cf_list.m_head = cf->next;
}
ct->cf_list.write_unlock();
}
void toku_cachefile_close(CACHEFILE *cfp, bool oplsn_valid, LSN oplsn) { void toku_cachefile_close(CACHEFILE *cfp, bool oplsn_valid, LSN oplsn) {
CACHEFILE cf = *cfp; CACHEFILE cf = *cfp;
CACHETABLE ct = cf->cachetable; CACHETABLE ct = cf->cachetable;
...@@ -564,7 +471,8 @@ void toku_cachefile_close(CACHEFILE *cfp, bool oplsn_valid, LSN oplsn) { ...@@ -564,7 +471,8 @@ void toku_cachefile_close(CACHEFILE *cfp, bool oplsn_valid, LSN oplsn) {
cf->close_userdata(cf, cf->fd, cf->userdata, oplsn_valid, oplsn); cf->close_userdata(cf, cf->fd, cf->userdata, oplsn_valid, oplsn);
} }
remove_cf_from_cachefiles_list(cf); ct->cf_list.remove_cf(cf);
bjm_destroy(cf->bjm); bjm_destroy(cf->bjm);
cf->bjm = NULL; cf->bjm = NULL;
...@@ -617,7 +525,7 @@ static inline uint32_t final (uint32_t a, uint32_t b, uint32_t c) { ...@@ -617,7 +525,7 @@ static inline uint32_t final (uint32_t a, uint32_t b, uint32_t c) {
uint32_t toku_cachetable_hash (CACHEFILE cachefile, BLOCKNUM key) uint32_t toku_cachetable_hash (CACHEFILE cachefile, BLOCKNUM key)
// Effect: Return a 32-bit hash key. The hash key shall be suitable for using with bitmasking for a table of size power-of-two. // Effect: Return a 32-bit hash key. The hash key shall be suitable for using with bitmasking for a table of size power-of-two.
{ {
return final(cachefile->filenum.fileid, (uint32_t)(key.b>>32), (uint32_t)key.b); return final(cachefile->hash_id, (uint32_t)(key.b>>32), (uint32_t)key.b);
} }
#define CLOCK_SATURATION 15 #define CLOCK_SATURATION 15
...@@ -3324,9 +3232,7 @@ void pair_list::put(PAIR p) { ...@@ -3324,9 +3232,7 @@ void pair_list::put(PAIR p) {
this->add_to_clock(p); this->add_to_clock(p);
this->add_to_cf_list(p); this->add_to_cf_list(p);
uint32_t h = p->fullhash & (m_table_size - 1); this->add_to_hash_chain(p);
p->hash_chain = m_table[h];
m_table[h] = p;
m_n_in_table++; m_n_in_table++;
} }
...@@ -3338,21 +3244,10 @@ void pair_list::evict(PAIR p) { ...@@ -3338,21 +3244,10 @@ void pair_list::evict(PAIR p) {
this->pair_remove(p); this->pair_remove(p);
this->pending_pairs_remove(p); this->pending_pairs_remove(p);
this->cf_pairs_remove(p); this->cf_pairs_remove(p);
this->remove_from_hash_chain(p);
assert(m_n_in_table > 0); assert(m_n_in_table > 0);
m_n_in_table--; m_n_in_table--;
// Remove it from the hash chain.
unsigned int h = p->fullhash&(m_table_size - 1);
m_table[h] = this->remove_from_hash_chain(p, m_table[h]);
}
PAIR pair_list::remove_from_hash_chain (PAIR remove_me, PAIR list) {
if (remove_me == list) {
return list->hash_chain;
}
list->hash_chain = this->remove_from_hash_chain(remove_me, list->hash_chain);
return list;
} }
// //
...@@ -3423,6 +3318,22 @@ void pair_list::cf_pairs_remove(PAIR p) { ...@@ -3423,6 +3318,22 @@ void pair_list::cf_pairs_remove(PAIR p) {
cf->num_pairs--; cf->num_pairs--;
} }
void pair_list::remove_from_hash_chain(PAIR p) {
// Remove it from the hash chain.
unsigned int h = p->fullhash&(m_table_size - 1);
paranoid_invariant(m_table[h] != NULL);
if (m_table[h] == p) {
m_table[h] = p->hash_chain;
}
else {
PAIR curr = m_table[h];
while (curr->hash_chain != p) {
curr = curr->hash_chain;
}
// remove p from the singular linked list
curr->hash_chain = p->hash_chain;
}
}
// Returns a pair from the pair list, using the given // Returns a pair from the pair list, using the given
// pair. If the pair cannot be found, null is returned. // pair. If the pair cannot be found, null is returned.
...@@ -3486,6 +3397,15 @@ void pair_list::add_to_cf_list(PAIR p) { ...@@ -3486,6 +3397,15 @@ void pair_list::add_to_cf_list(PAIR p) {
cf->num_pairs++; cf->num_pairs++;
} }
// Add PAIR to the hashtable
//
// requires caller to have grabbed write lock on list
// and to have grabbed the p->mutex.
void pair_list::add_to_hash_chain(PAIR p) {
uint32_t h = p->fullhash & (m_table_size - 1);
p->hash_chain = m_table[h];
m_table[h] = p;
}
// test function // test function
// //
...@@ -4642,6 +4562,105 @@ void cachefile_list::write_lock() { ...@@ -4642,6 +4562,105 @@ void cachefile_list::write_lock() {
void cachefile_list::write_unlock() { void cachefile_list::write_unlock() {
toku_pthread_rwlock_wrunlock(&m_lock); toku_pthread_rwlock_wrunlock(&m_lock);
} }
int cachefile_list::cachefile_of_iname_in_env(const char *iname_in_env, CACHEFILE *cf) {
read_lock();
CACHEFILE extant;
int r;
r = ENOENT;
for (extant = m_head; extant; extant = extant->next) {
if (extant->fname_in_env &&
!strcmp(extant->fname_in_env, iname_in_env)) {
*cf = extant;
r = 0;
break;
}
}
read_unlock();
return r;
}
int cachefile_list::cachefile_of_filenum(FILENUM filenum, CACHEFILE *cf) {
read_lock();
CACHEFILE extant;
int r = ENOENT;
*cf = NULL;
for (extant = m_head; extant; extant = extant->next) {
if (extant->filenum.fileid==filenum.fileid) {
*cf = extant;
r = 0;
break;
}
}
read_unlock();
return r;
}
void cachefile_list::add_cf_unlocked(CACHEFILE cf) {
cf->next = m_head;
cf->prev = NULL;
if (m_head) {
m_head->prev = cf;
}
m_head = cf;
}
void cachefile_list::remove_cf(CACHEFILE cf) {
write_lock();
invariant(m_head != NULL);
if (cf->next) {
cf->next->prev = cf->prev;
}
if (cf->prev) {
cf->prev->next = cf->next;
}
if (cf == m_head) {
invariant(cf->prev == NULL);
m_head = cf->next;
}
write_unlock();
}
FILENUM cachefile_list::reserve_filenum() {
CACHEFILE extant;
FILENUM filenum;
// taking a write lock because we are modifying next_filenum_to_use
write_lock();
try_again:
for (extant = m_head; extant; extant = extant->next) {
if (m_next_filenum_to_use.fileid==extant->filenum.fileid) {
m_next_filenum_to_use.fileid++;
goto try_again;
}
}
filenum = m_next_filenum_to_use;
m_next_filenum_to_use.fileid++;
write_unlock();
return filenum;
}
CACHEFILE cachefile_list::find_cachefile_unlocked(struct fileid* fileid) {
CACHEFILE retval = NULL;
for (CACHEFILE extant = m_head; extant; extant = extant->next) {
if (toku_fileids_are_equal(&extant->fileid, fileid)) {
// Clients must serialize cachefile open, close, and unlink
// So, during open, we should never see a closing cachefile
// or one that has been marked as unlink on close.
assert(!extant->unlink_on_close);
retval = extant;
goto exit;
}
}
exit:
return retval;
}
void cachefile_list::verify_unused_filenum(FILENUM filenum) {
for (CACHEFILE extant = m_head; extant; extant = extant->next) {
invariant(extant->filenum.fileid != filenum.fileid);
}
}
void __attribute__((__constructor__)) toku_cachetable_helgrind_ignore(void); void __attribute__((__constructor__)) toku_cachetable_helgrind_ignore(void);
void void
......
...@@ -542,12 +542,6 @@ int toku_cachefile_get_fd (CACHEFILE); ...@@ -542,12 +542,6 @@ int toku_cachefile_get_fd (CACHEFILE);
// Return the filename // Return the filename
char * toku_cachefile_fname_in_env (CACHEFILE cf); char * toku_cachefile_fname_in_env (CACHEFILE cf);
// For test programs only.
// Set the cachefile's fd and fname.
// Effect: Bind the cachefile to a new fd and fname. The old fd is closed.
// Returns: 0 if success, otherwise an error number
int toku_cachefile_set_fd (CACHEFILE cf, int fd, const char *fname_relative_to_env);
// Make it so when the cachefile closes, the underlying file is unlinked // Make it so when the cachefile closes, the underlying file is unlinked
void toku_cachefile_unlink_on_close(CACHEFILE cf); void toku_cachefile_unlink_on_close(CACHEFILE cf);
......
...@@ -89,6 +89,7 @@ PATENT RIGHTS GRANT: ...@@ -89,6 +89,7 @@ PATENT RIGHTS GRANT:
#ident "The technology is licensed by the Massachusetts Institute of Technology, Rutgers State University of New Jersey, and the Research Foundation of State University of New York at Stony Brook under United States of America Serial No. 11/760379 and to the patents and/or patent applications resulting from it." #ident "The technology is licensed by the Massachusetts Institute of Technology, Rutgers State University of New Jersey, and the Research Foundation of State University of New York at Stony Brook under United States of America Serial No. 11/760379 and to the patents and/or patent applications resulting from it."
#include "test.h" #include "test.h"
#include "toku_os.h"
static void static void
...@@ -110,15 +111,19 @@ cachetable_fd_test (void) { ...@@ -110,15 +111,19 @@ cachetable_fd_test (void) {
// test set to good fd succeeds // test set to good fd succeeds
char fname2[TOKU_PATH_MAX+1]; char fname2[TOKU_PATH_MAX+1];
unlink(toku_path_join(fname2, 2, TOKU_TEST_FILENAME, "test2.dat")); unlink(toku_path_join(fname2, 2, TOKU_TEST_FILENAME, "test2.dat"));
int fd2 = open(fname2, O_RDWR | O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(fd2 >= 0 && fd1 != fd2); int fd2 = open(fname2, O_RDWR | O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO);
r = toku_cachefile_set_fd(cf, fd2, fname2); assert(r == 0); assert(fd2 >= 0 && fd1 != fd2);
assert(toku_cachefile_get_fd(cf) == fd2); struct fileid id;
r = toku_os_get_unique_file_id(fd2, &id);
assert(r == 0);
close(fd2);
// test set to bogus fd fails // test set to bogus fd fails
int fd3 = open(DEV_NULL_FILE, O_RDWR); assert(fd3 >= 0); int fd3 = open(DEV_NULL_FILE, O_RDWR); assert(fd3 >= 0);
r = close(fd3); assert(r == 0); r = close(fd3);
r = toku_cachefile_set_fd(cf, fd3, DEV_NULL_FILE); assert(r != 0); assert(r == 0);
assert(toku_cachefile_get_fd(cf) == fd2); r = toku_os_get_unique_file_id(fd3, &id);
assert(r < 0);
// test the filenum functions // test the filenum functions
FILENUM fn = toku_cachefile_filenum(cf); FILENUM fn = toku_cachefile_filenum(cf);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment