////////////////////////////////////////////////////////////////////////// Implementation for the ConfigCache, it has to go here since we have a sort of circular dependency. Note that we always parse / get the configuration for the file, either from cache or by making one. The user of this just has to copy the relevant portions, but should not use the returned object directly (i.e. it must be copi
| 689 | // directly (i.e. it must be copied). |
| 690 | // |
| 691 | S3Config * |
| 692 | ConfigCache::get(const char *fname) |
| 693 | { |
| 694 | S3Config *s3; |
| 695 | |
| 696 | struct timeval tv; |
| 697 | |
| 698 | gettimeofday(&tv, nullptr); |
| 699 | |
| 700 | // Make sure the filename is an absolute path, prepending the config dir if needed |
| 701 | std::string config_fname = makeConfigPath(fname); |
| 702 | |
| 703 | auto it = _cache.find(config_fname); |
| 704 | |
| 705 | if (it != _cache.end()) { |
| 706 | unsigned update_status = it->second.update_status; |
| 707 | if (tv.tv_sec > (it->second.load_time + _ttl)) { |
| 708 | if (!(update_status & 1) && it->second.update_status.compare_exchange_strong(update_status, update_status + 1)) { |
| 709 | Dbg(dbg_ctl, "Configuration from %s is stale, reloading", config_fname.c_str()); |
| 710 | s3 = new S3Config(false); // false == this config does not get the continuation |
| 711 | |
| 712 | if (s3->parse_config(config_fname)) { |
| 713 | s3->set_conf_fname(fname); |
| 714 | } else { |
| 715 | // Failed the configuration parse... Set the cache response to nullptr |
| 716 | delete s3; |
| 717 | s3 = nullptr; |
| 718 | TSAssert(!"Configuration parsing / caching failed"); |
| 719 | } |
| 720 | |
| 721 | delete it->second.config; |
| 722 | it->second.config = s3; |
| 723 | it->second.load_time = tv.tv_sec; |
| 724 | |
| 725 | // Update is complete. |
| 726 | ++it->second.update_status; |
| 727 | } else { |
| 728 | // This thread lost the race with another thread that is also reloading |
| 729 | // the config for this file. Wait for the other thread to finish reloading. |
| 730 | while (it->second.update_status & 1) { |
| 731 | // Hopefully yielding will sleep the thread at least until the next |
| 732 | // scheduler interrupt, preventing a busy wait. |
| 733 | std::this_thread::yield(); |
| 734 | } |
| 735 | s3 = it->second.config; |
| 736 | } |
| 737 | } else { |
| 738 | Dbg(dbg_ctl, "Configuration from %s is fresh, reusing", config_fname.c_str()); |
| 739 | s3 = it->second.config; |
| 740 | } |
| 741 | } else { |
| 742 | // Create a new cached file. |
| 743 | s3 = new S3Config(false); // false == this config does not get the continuation |
| 744 | |
| 745 | Dbg(dbg_ctl, "Parsing and caching configuration from %s, version:%s", config_fname.c_str(), s3->versionString()); |
| 746 | if (s3->parse_config(config_fname)) { |
| 747 | s3->set_conf_fname(fname); |
| 748 | _cache.emplace(config_fname, _ConfigData(s3, tv.tv_sec)); |
no test coverage detected