Replace list<TZ_DB> with tzdb_list

* tzdb_list is a singly linked list with an atomic head
* push_front() and front() are thread safe.
This commit is contained in:
Howard Hinnant
2017-08-06 18:25:07 -04:00
parent 80a142407a
commit 859a50a70e
2 changed files with 159 additions and 41 deletions

108
tz.cpp
View File

@@ -96,7 +96,6 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <iterator> #include <iterator>
#include <list>
#include <memory> #include <memory>
#if USE_OS_TZDB #if USE_OS_TZDB
# include <queue> # include <queue>
@@ -320,21 +319,54 @@ struct undocumented {explicit undocumented() = default;};
static_assert(min_year <= max_year, "Configuration error"); static_assert(min_year <= max_year, "Configuration error");
#endif #endif
static TZ_DB init_tzdb(); static std::unique_ptr<TZ_DB> init_tzdb();
tzdb_list::~tzdb_list()
{
const TZ_DB* ptr = head_;
head_ = nullptr;
while (ptr != nullptr)
{
auto next = ptr->next;
delete ptr;
ptr = next;
}
}
tzdb_list::tzdb_list(tzdb_list&& x) noexcept
: head_{x.head_.exchange(nullptr)}
{
}
void
tzdb_list::push_front(TZ_DB* tzdb) noexcept
{
tzdb->next = head_;
head_ = tzdb;
}
tzdb_list::const_iterator
tzdb_list::erase_after(const_iterator p) noexcept
{
auto t = p.p_->next;
p.p_->next = p.p_->next->next;
delete t;
return ++p;
}
static static
std::list<TZ_DB> tzdb_list
create_tzdb() create_tzdb()
{ {
std::list<TZ_DB> tz_db; tzdb_list tz_db;
tz_db.push_back(init_tzdb()); tz_db.push_front(init_tzdb().release());
return tz_db; return tz_db;
} }
std::list<TZ_DB>& tzdb_list&
get_tzdb_list() get_tzdb_list()
{ {
static std::list<TZ_DB> tz_db = create_tzdb(); static tzdb_list tz_db = create_tzdb();
return tz_db; return tz_db;
} }
@@ -2493,10 +2525,10 @@ get_version()
# endif # endif
static static
TZ_DB std::unique_ptr<TZ_DB>
init_tzdb() init_tzdb()
{ {
TZ_DB db; std::unique_ptr<TZ_DB> db(new TZ_DB);
//Iterate through folders //Iterate through folders
std::queue<std::string> subfolders; std::queue<std::string> subfolders;
@@ -2535,22 +2567,22 @@ init_tzdb()
} }
else else
{ {
db.zones.emplace_back(subname.substr(sizeof(tz_dir)), db->zones.emplace_back(subname.substr(sizeof(tz_dir)),
detail::undocumented{}); detail::undocumented{});
} }
} }
} }
closedir(dir); closedir(dir);
} }
db.zones.shrink_to_fit(); db->zones.shrink_to_fit();
std::sort(db.zones.begin(), db.zones.end()); std::sort(db->zones.begin(), db->zones.end());
# if !MISSING_LEAP_SECONDS # if !MISSING_LEAP_SECONDS
std::ifstream in(tz_dir + std::string(1, folder_delimiter) + "right/UTC", std::ifstream in(tz_dir + std::string(1, folder_delimiter) + "right/UTC",
std::ios_base::binary); std::ios_base::binary);
if (in) if (in)
{ {
in.exceptions(std::ios::failbit | std::ios::badbit); in.exceptions(std::ios::failbit | std::ios::badbit);
db.leaps = load_just_leaps(in); db->leaps = load_just_leaps(in);
} }
else else
{ {
@@ -2559,11 +2591,11 @@ init_tzdb()
if (!in) if (!in)
throw std::runtime_error("Unable to extract leap second information"); throw std::runtime_error("Unable to extract leap second information");
in.exceptions(std::ios::failbit | std::ios::badbit); in.exceptions(std::ios::failbit | std::ios::badbit);
db.leaps = load_just_leaps(in); db->leaps = load_just_leaps(in);
} }
# endif // !MISSING_LEAP_SECONDS # endif // !MISSING_LEAP_SECONDS
# ifdef __APPLE__ # ifdef __APPLE__
db.version = get_version(); db->version = get_version();
# endif # endif
return db; return db;
} }
@@ -3200,7 +3232,7 @@ get_version(const std::string& path)
} }
static static
TZ_DB std::unique_ptr<TZ_DB>
init_tzdb() init_tzdb()
{ {
using namespace date; using namespace date;
@@ -3208,7 +3240,7 @@ init_tzdb()
const std::string path = install + folder_delimiter; const std::string path = install + folder_delimiter;
std::string line; std::string line;
bool continue_zone = false; bool continue_zone = false;
TZ_DB db; std::unique_ptr<TZ_DB> db(new TZ_DB);
#if AUTO_DOWNLOAD #if AUTO_DOWNLOAD
if (!file_exists(install)) if (!file_exists(install))
@@ -3233,18 +3265,18 @@ init_tzdb()
msg += "\""; msg += "\"";
throw std::runtime_error(msg); throw std::runtime_error(msg);
} }
db.version = get_version(path); db->version = get_version(path);
} }
else else
{ {
db.version = get_version(path); db->version = get_version(path);
auto rv = remote_version(); auto rv = remote_version();
if (!rv.empty() && db.version != rv) if (!rv.empty() && db->version != rv)
{ {
if (remote_download(rv)) if (remote_download(rv))
{ {
remote_install(rv); remote_install(rv);
db.version = get_version(path); db->version = get_version(path);
} }
} }
} }
@@ -3256,7 +3288,7 @@ init_tzdb()
msg += "\""; msg += "\"";
throw std::runtime_error(msg); throw std::runtime_error(msg);
} }
db.version = get_version(path); db->version = get_version(path);
#endif // !AUTO_DOWNLOAD #endif // !AUTO_DOWNLOAD
CONSTDATA char*const files[] = CONSTDATA char*const files[] =
@@ -3278,27 +3310,27 @@ init_tzdb()
in >> word; in >> word;
if (word == "Rule") if (word == "Rule")
{ {
db.rules.push_back(Rule(line)); db->rules.push_back(Rule(line));
continue_zone = false; continue_zone = false;
} }
else if (word == "Link") else if (word == "Link")
{ {
db.links.push_back(link(line)); db->links.push_back(link(line));
continue_zone = false; continue_zone = false;
} }
else if (word == "Leap") else if (word == "Leap")
{ {
db.leaps.push_back(leap(line, detail::undocumented{})); db->leaps.push_back(leap(line, detail::undocumented{}));
continue_zone = false; continue_zone = false;
} }
else if (word == "Zone") else if (word == "Zone")
{ {
db.zones.push_back(time_zone(line, detail::undocumented{})); db->zones.push_back(time_zone(line, detail::undocumented{}));
continue_zone = true; continue_zone = true;
} }
else if (line[0] == '\t' && continue_zone) else if (line[0] == '\t' && continue_zone)
{ {
db.zones.back().add(line); db->zones.back().add(line);
} }
else else
{ {
@@ -3307,19 +3339,19 @@ init_tzdb()
} }
} }
} }
std::sort(db.rules.begin(), db.rules.end()); std::sort(db->rules.begin(), db->rules.end());
Rule::split_overlaps(db.rules); Rule::split_overlaps(db->rules);
std::sort(db.zones.begin(), db.zones.end()); std::sort(db->zones.begin(), db->zones.end());
db.zones.shrink_to_fit(); db->zones.shrink_to_fit();
std::sort(db.links.begin(), db.links.end()); std::sort(db->links.begin(), db->links.end());
db.links.shrink_to_fit(); db->links.shrink_to_fit();
std::sort(db.leaps.begin(), db.leaps.end()); std::sort(db->leaps.begin(), db->leaps.end());
db.leaps.shrink_to_fit(); db->leaps.shrink_to_fit();
#ifdef _WIN32 #ifdef _WIN32
std::string mapping_file = get_install() + folder_delimiter + "windowsZones.xml"; std::string mapping_file = get_install() + folder_delimiter + "windowsZones.xml";
db.mappings = load_timezone_mappings_from_xml_file(mapping_file); db->mappings = load_timezone_mappings_from_xml_file(mapping_file);
sort_zone_mappings(db.mappings); sort_zone_mappings(db->mappings);
#endif // _WIN32 #endif // _WIN32
return db; return db;
@@ -3333,7 +3365,7 @@ reload_tzdb()
if (!v.empty() && v == remote_version()) if (!v.empty() && v == remote_version())
return get_tzdb_list().front(); return get_tzdb_list().front();
#endif // AUTO_DOWNLOAD #endif // AUTO_DOWNLOAD
get_tzdb_list().push_front(init_tzdb()); get_tzdb_list().push_front(init_tzdb().release());
return get_tzdb_list().front(); return get_tzdb_list().front();
} }

90
tz.h
View File

@@ -110,10 +110,10 @@ static_assert(HAS_REMOTE_API == 0 ? AUTO_DOWNLOAD == 0 : true,
#endif #endif
#include <algorithm> #include <algorithm>
#include <atomic>
#include <cassert> #include <cassert>
#include <chrono> #include <chrono>
#include <istream> #include <istream>
#include <list>
#include <locale> #include <locale>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@@ -1144,6 +1144,7 @@ struct TZ_DB
#ifdef _WIN32 #ifdef _WIN32
std::vector<detail::timezone_mapping> mappings; std::vector<detail::timezone_mapping> mappings;
#endif #endif
TZ_DB* next = nullptr;
TZ_DB() = default; TZ_DB() = default;
#if !defined(_MSC_VER) || (_MSC_VER >= 1900) #if !defined(_MSC_VER) || (_MSC_VER >= 1900)
@@ -1183,7 +1184,92 @@ DATE_API std::ostream&
operator<<(std::ostream& os, const TZ_DB& db); operator<<(std::ostream& os, const TZ_DB& db);
DATE_API const TZ_DB& get_tzdb(); DATE_API const TZ_DB& get_tzdb();
DATE_API std::list<TZ_DB>& get_tzdb_list();
class tzdb_list
{
std::atomic<TZ_DB*> head_{nullptr};
public:
~tzdb_list();
tzdb_list() = default;
tzdb_list(tzdb_list&& x) noexcept;
void push_front(TZ_DB* tzdb) noexcept;
const TZ_DB& front() const noexcept {return *head_;}
class const_iterator;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
const_iterator erase_after(const_iterator p) noexcept;
};
class tzdb_list::const_iterator
{
TZ_DB* p_ = nullptr;
explicit const_iterator(TZ_DB* p) noexcept : p_{p} {}
public:
const_iterator() = default;
using iterator_category = std::forward_iterator_tag;
using value_type = TZ_DB;
using reference = const value_type&;
using pointer = const value_type*;
using difference_type = std::ptrdiff_t;
reference operator*() const noexcept {return *p_;}
pointer operator->() const noexcept {return p_;}
const_iterator& operator++() noexcept {p_ = p_->next; return *this;}
const_iterator operator++(int) noexcept {auto t = *this; ++(*this); return t;}
friend
bool
operator==(const const_iterator& x, const const_iterator& y) noexcept
{return x.p_ == y.p_;}
friend
bool
operator!=(const const_iterator& x, const const_iterator& y) noexcept
{return !(x == y);}
friend class tzdb_list;
};
inline
tzdb_list::const_iterator
tzdb_list::begin() const noexcept
{
return const_iterator{head_};
}
inline
tzdb_list::const_iterator
tzdb_list::end() const noexcept
{
return const_iterator{nullptr};
}
inline
tzdb_list::const_iterator
tzdb_list::cbegin() const noexcept
{
return begin();
}
inline
tzdb_list::const_iterator
tzdb_list::cend() const noexcept
{
return end();
}
DATE_API tzdb_list& get_tzdb_list();
#if !USE_OS_TZDB #if !USE_OS_TZDB