// SimpleUniqueIdGenerator.cxx #include "dataset_id/SimpleUniqueIdGenerator.h" #include #include #include #include #include #include #include #if ( __GNUC__ == 2 ) #include template class numeric_limits { public: static T max() { assert(false); return 0; } }; template<> class numeric_limits { public: static unsigned long max() { return ULONG_MAX; } }; #else #include #endif #include #include #include #include #include "dataset_util/getcwd.h" #include "dataset_util/get_hostname.h" #include "dataset_util/FileStatus.h" #include "dataset_util/FileName.h" #include "dataset_util/mkdir.h" #include "dataset_util/Text.h" using std::set; using std::string; using std::vector; using std::ifstream; using std::ofstream; using std::ostream; using std::cerr; using std::endl; using std::istringstream; using std::ostringstream; using std::numeric_limits; //********************************************************************** // Local definitions. //********************************************************************** typedef SimpleUniqueIdGenerator::Index Index; typedef set GenSet; namespace { //********************************************************************** // Base directory. // Nonblank argument is used to define the base directory. string basedir(string indir ="") { static PThreadMutex mtx; mtx.lock(); static string dir = ""; if ( indir != "" ) { dir = indir; } if ( dir == "" ) { dir = getcwd() + "/UniqueId"; } if ( dir == "" ) { mtx.unlock(); return dir; } if ( dir[0] != FileName::delimiter() ) { dir = FileName(dir).fullpath().name(); } FileStatus fstat(dir); if ( ! fstat.is_directory() || ! fstat.is_readable() ) { dir == ""; } mtx.unlock(); return dir; } //********************************************************************** // Class to make sure that all generators are deleted. // Generators are removed from this list when they are deleted so // there is no danger in dereferencing these pointers. class GenList : public GenSet { public: ~GenList() { for ( GenSet::size_type count=size(); count>0; --count ) { assert( size() == count ); delete *begin(); } assert( size() == 0 ); } }; //********************************************************************** // The list of pointers to generators to be deleted. GenList GENLIST; //********************************************************************** // The PID to the process where the GENLIST was created. pid_t TOP_PID = getpid(); //********************************************************************** typedef vector Names; // Retrieve the readable ordinary files in a directory. // Returns zero if directory is opened succesfully. int dirfiles(string dirname, Names& fnames) { static PThreadMutex mtx; mtx.lock(); assert( fnames.size() == 0 ); DIR* pdir = opendir(dirname.c_str()); if ( pdir==0 ) { perror("SimpleUniqueIdGenerator"); mtx.unlock(); return 1; } struct dirent* pent = readdir(pdir); while ( (pent = readdir(pdir)) != 0 ) { string fname = pent->d_name; // Skip . and .. if ( fname == "." || fname == ".." ) continue; string fullname = dirname; fullname += "/"; fullname += fname; FileStatus fstat(fullname); if ( ! fstat.is_regular() ) continue; if ( ! fstat.is_readable() ) continue; fnames.push_back( pent->d_name ); } int close_stat = closedir(pdir); assert( close_stat == 0 ); sort(fnames.begin(), fnames.end()); mtx.unlock(); return 0; } //********************************************************************** // Creator. UniqueIdGenerator* create(string context, string conn) { // Decompose the connection string. Text::WordList words = Text::split(conn, ":", false); if ( words.size() != 4 ) return 0; string type = words[0]; string dir = words[1]; string sretry = words[2]; string sverbose = words[3]; if ( type != "SimpleUniqueIdGenerator" ) return 0; if ( dir.size() ) SimpleUniqueIdGenerator::base_directory(dir); int nretry = 0; if ( sretry.size() ) { istringstream ssretry(sretry); ssretry >> nretry; } bool verbose = false; if ( sverbose == "true" ) verbose = true; return SimpleUniqueIdGenerator:: find_generator(context, nretry, verbose); } //********************************************************************** // Register creator. int stat_SimpleUniqueIdGenerator = UniqueIdGenerator:: register_creator("SimpleUniqueIdGenerator", create); //********************************************************************** } // end unnamed namespace //********************************************************************** // Static functions. //********************************************************************** // Find a generator. SimpleUniqueIdGenerator* SimpleUniqueIdGenerator:: find_generator(string context, unsigned int maxtry0, bool verbose) { // Create and lock mutex. static PThreadMutex mtx; mtx.lock(); // Set maxtry. unsigned int maxtry = maxtry0; if ( maxtry == 0 ) maxtry = 10; if ( maxtry > 10000 ) maxtry = 1000; // Create directory name. string dir = basedir(); dir += "/"; dir += context; // Check if directory exists. FileStatus dstat(dir); if ( !dstat.is_directory() || !dstat.is_readable() ) { if ( verbose ) { cerr << "SimpleUniqueIdGenerator::find_generator: " << "Unable to find generator directory:" << endl; cerr << " " << dstat << endl; } mtx.unlock(); return 0; } // Search for a collection file. Names fnames; int istat = dirfiles(dir,fnames); if ( istat != 0 ) { if ( verbose ) { cerr << "SimpleUniqueIdGenerator::find_generator: " << "Unable to read generator directory:" << endl; cerr << " " << dir << endl; } mtx.unlock(); return 0; } // Loop over attempts to find directory. for ( unsigned int itry=0; itrysize() != 10 ) continue; // Construct generator from file. string gfname = dir; gfname += "/" + *inam; SimpleUniqueIdGenerator* pgen = new SimpleUniqueIdGenerator(gfname); if ( pgen->is_valid() && pgen->is_open() && pgen->context() == context ) { mtx.unlock(); return pgen; } delete pgen; pgen = 0; } if ( verbose ) { cerr << "SimpleUniqueIdGenerator::find_generator: " << "Unable to find collection in " << endl; cerr << " " << dir << endl; } if ( itry < maxtry ) { if ( verbose ) { cerr << " Trying again..." << endl; } sleep(5*rand()/RAND_MAX+1); } } if ( verbose ) { cerr << "SimpleUniqueIdGenerator::find_generator: " << "Giving up in " << endl; cerr << " " << dir << endl; } mtx.unlock(); return 0; } //********************************************************************** // Create a test collection. int SimpleUniqueIdGenerator:: create_collection(string context, Index col, Index ent) { static PThreadMutex mtx; mtx.lock(); if ( col<1 || col>999 ) { mtx.unlock(); return 1; } string dir = basedir() + "/" + context; string file = dir + "/" + index_to_string(col,'0'); FileStatus dstat(dir); if ( ! dstat.exists() ) { mkfulldir(dir); dstat.update(); if ( ! dstat.exists() ) { mtx.unlock(); return 3; } } FileStatus fstat(file.c_str()); if ( fstat.exists() ) { mtx.unlock(); return 4; } Text gtxt; gtxt.append(context); gtxt.append(index_to_string(col)); gtxt.append(index_to_string(ent)); gtxt.write(file.c_str()); fstat.update(); if ( ! fstat.exists() ) { mtx.unlock(); return 5; } if ( ! fstat.is_regular() ) { mtx.unlock(); return 6; } if ( ! fstat.is_readable() ) { mtx.unlock(); return 7; } mtx.unlock(); return 0; } //********************************************************************** // Set base directory. string SimpleUniqueIdGenerator::base_directory(string dir) { return basedir(dir); } //********************************************************************** int SimpleUniqueIdGenerator:: set_as_default(std::string basedir, unsigned int maxtry, bool verbose) { ostringstream scon; scon << "SimpleUniqueIdGenerator"; scon << ":"; scon << basedir; scon << ":"; scon << maxtry; scon << ":"; if ( verbose ) scon << "true"; else scon << "false"; string conn = scon.str(); return UniqueIdGenerator::set_generator(conn); } //********************************************************************** // Member functions. //********************************************************************** // Constructor from file name. SimpleUniqueIdGenerator::SimpleUniqueIdGenerator(string filename) : _fname(filename), m_savename(""), _valid(true), _open(true), m_pid(getpid()) { // Take control of the input file. ostringstream ssave; ssave << filename << "." << get_hostname() << "." << getpid(); m_savename = ssave.str(); int cstat = rename(filename.c_str(), m_savename.c_str() ); if ( cstat != 0 ) { assert( cstat == -1 ); _valid = false; _open = false; return; } // Open and read the file. { ifstream infile(m_savename.c_str()); infile >> _context; infile >> _col; infile >> _ent; //cerr << "SimpleUniqueIdGenerator::ctor read entry " << size_assigned() << endl; if ( ! infile ) { _valid = false; _open = false; } } // Add to the list of objects to be deleted. GENLIST.insert(this); } //********************************************************************** // Constructor for a local generator (collection 0). SimpleUniqueIdGenerator::SimpleUniqueIdGenerator() : _context(""), _col(0), _ent(0), _fname(""), m_savename(""), _valid(true), _open(true), m_pid(getpid()) { // Add to the list of objects to be deleted. GENLIST.insert(this); } //********************************************************************** // Destructor. SimpleUniqueIdGenerator::~SimpleUniqueIdGenerator() { close(); // Remove from the list of objects to be deleted. GENLIST.erase(this); } //********************************************************************** // Return the next ID. UniqueId SimpleUniqueIdGenerator::next() { mutex().lock(); if ( ! is_open() ) { mutex().unlock(); return UniqueId(); } assert( size_remaining() > 0 ); ++_ent; if ( size_remaining() == 0 ) { close(); } else { int sstat = save(); if ( sstat != 0 ) { cerr << "SimpleUniqueIdGenerator::next: Save failed with error " << sstat << endl; } } UniqueId id = UniqueId(_col, _ent); mutex().unlock(); return id; } //********************************************************************** // Save the state of the gnerator. int SimpleUniqueIdGenerator::save() { // Check state. if ( ! is_valid() ) return 1; if ( ! is_open() ) return 2; if ( collection() == 0 ) return 0; // Lock mutex. mutex().lock(); // Write out the state of the generator. string tmpname = m_savename + ".new"; ofstream outfile(tmpname.c_str()); if ( ! outfile ) { cerr << "SimpleUniqueIdGenerator::save: Unable to open file:" << endl; cerr << " " << tmpname << endl; cerr << "PID's: " << m_pid << " " << getpid() << endl; cerr << *this << endl; unlink(tmpname.c_str()); mutex().unlock(); return 21; } assert(outfile); outfile << context() << endl; outfile << collection() << endl; outfile << size_assigned() << endl; outfile.close(); assert( FileStatus(tmpname).exists() ); // Check the output. // Typical failure is insufficient space or quota. ifstream infile(tmpname.c_str()); string context = ""; UniqueId::Index col = 0; UniqueId::Index ent = 0; infile >> context; infile >> col; infile >> ent; if ( context!=_context || col!=_col || ent!=_ent ) { cerr << "SimpleUniqueIdGenerator::close: Error writing file:" << endl; cerr << " " << tmpname << endl; cerr << "PID's: " << m_pid << " " << getpid() << endl; unlink(tmpname.c_str()); mutex().unlock(); return 22; } // Rename the output to the standard name. int rstat = rename(tmpname.c_str(), m_savename.c_str()); assert( rstat == 0 ); assert( FileStatus(m_savename).exists() ); // Unlock mutex and return. mutex().unlock(); return 0; } //********************************************************************** // Close the generator and associated file. // Do not allow any process but the creator or the top level to close // the generator. This prevents forked processes from closing the // file. int SimpleUniqueIdGenerator::close() { // Check state. if ( ! is_valid() ) return 1; if ( ! is_open() ) return 2; if ( collection() == 0 ) { _open = false; return 0; } // Lock mutex. mutex().lock(); // Check thread. pid_t pid = getpid(); if ( pid != m_pid && pid != TOP_PID ) { cerr << "SimpleUniqueIdGenerator::close: Wrong PID." << endl; cerr << " Found " << pid << "; expected " << TOP_PID << endl; mutex().unlock(); return 11; } // Save the state of the generator. int sstat = save(); if ( sstat != 0 ) { return sstat; } // Rename the save file standard name so that this and other // processes can reopen the generator. int rstat = rename(m_savename.c_str(), _fname.c_str()); assert( rstat == 0 ); if ( rstat != 0 ) { cerr << "SimpleUniqueIdGenerator::close() - File rename error " << errno << endl; cerr << strerror(errno) << endl; cerr << " " << m_savename << endl; } // Exit success. m_savename = ""; _open = false; mutex().unlock(); return 0; } //********************************************************************** // Return the number of unused entries. Index SimpleUniqueIdGenerator::size_remaining() const { mutex().lock(); Index count = numeric_limits::max() - size_assigned(); mutex().unlock(); return count; } //********************************************************************** // Output stream. ostream& SimpleUniqueIdGenerator::ostr(ostream& lhs) const { mutex().lock(); if ( ! is_valid() ) { lhs << "Invalid simple unique index generator"; mutex().unlock(); return lhs; } lhs << "Valid simple unique index generator is "; if ( is_open() ) lhs << "open"; else lhs << "closed"; lhs << endl; lhs << " context = " << context() << endl; lhs << " collection = " << collection() << endl; lhs << " # assigned = " << size_assigned() << endl; lhs << " # remaining = " << size_remaining(); if ( collection() > 0 ) { lhs << endl; lhs << " File = " << filename(); } mutex().unlock(); return lhs; } //**********************************************************************