/** * Copyright 2019 Huawei Technologies Co., Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "dataset/util/storage_manager.h" #include #include #include #include #include "common/utils.h" #include "dataset/util/path.h" #include "dataset/util/services.h" #include "utils/log_adapter.h" namespace mindspore { namespace dataset { std::string StorageManager::GetBaseName(const std::string &prefix, int32_t file_id) { std::ostringstream oss; oss << prefix << std::setfill('0') << std::setw(5) << file_id; return oss.str(); } std::string StorageManager::ConstructFileName(const std::string &prefix, int32_t file_id, const std::string &suffix) { std::string base_name = GetBaseName(prefix, file_id); return (base_name + "." + suffix); } Status StorageManager::AddOneContainer() { const std::string kPrefix = "IMG"; const std::string kSuffix = "LB"; Path container_name = root_ / ConstructFileName(kPrefix, file_id_, kSuffix); std::shared_ptr sc; RETURN_IF_NOT_OK(StorageContainer::CreateStorageContainer(&sc, container_name.toString())); containers_.push_back(sc); file_id_++; return Status::OK(); } Status StorageManager::DoServiceStart() { containers_.reserve(1000); if (root_.IsDirectory()) { RETURN_IF_NOT_OK(AddOneContainer()); } else { RETURN_STATUS_UNEXPECTED("Not a directory"); } return Status::OK(); } Status StorageManager::Write(key_type *key, const std::vector &buf) { RETURN_UNEXPECTED_IF_NULL(key); size_t sz = 0; for (auto &v : buf) { sz += v.GetSize(); } if (sz == 0) { RETURN_STATUS_UNEXPECTED("Unexpected 0 length"); } std::shared_ptr cont; key_type out_key; value_type out_value; bool create_new_container = false; do { SharedLock lock_s(&rw_lock_); size_t num_containers = containers_.size(); if (create_new_container) { // Upgrade to exclusvie lock. lock_s.Upgrade(); create_new_container = false; // Check again if someone has already added a // new container after we got the x lock if (containers_.size() == num_containers) { RETURN_IF_NOT_OK(AddOneContainer()); } // Refresh how many containers there are. num_containers = containers_.size(); // Downgrade back to shared lock lock_s.Downgrade(); } if (num_containers == 0) { RETURN_STATUS_UNEXPECTED("num_containers is zero"); } // Go to the last container to insert. cont = containers_.at(num_containers - 1); off64_t offset; Status rc = cont->Insert(buf, &offset); if (rc.IsNoSpace()) { create_new_container = true; } else if (rc.IsOk()) { out_value = std::make_pair(num_containers - 1, std::make_pair(offset, sz)); RETURN_IF_NOT_OK(index_.insert(out_value, &out_key)); *key = out_key; break; } else { return rc; } } while (true); return Status::OK(); } Status StorageManager::Read(StorageManager::key_type key, WritableSlice *dest, size_t *bytesRead) const { RETURN_UNEXPECTED_IF_NULL(dest); auto r = index_.Search(key); if (r.second) { auto &it = r.first; value_type v = *it; int container_inx = v.first; off_t offset = v.second.first; size_t sz = v.second.second; if (dest->GetSize() < sz) { std::string errMsg = "Destination buffer too small. Expect at least " + std::to_string(sz) + " but length = " + std::to_string(dest->GetSize()); RETURN_STATUS_UNEXPECTED(errMsg); } if (bytesRead != nullptr) { *bytesRead = sz; } auto cont = containers_.at(container_inx); RETURN_IF_NOT_OK(cont->Read(dest, offset)); } else { RETURN_STATUS_UNEXPECTED("Key not found"); } return Status::OK(); } Status StorageManager::DoServiceStop() noexcept { Status rc; Status rc1; for (auto const &p : containers_) { // The destructor of StorageContainer is not called automatically until the use // count drops to 0. But it is not always the case. We will do it ourselves. rc = p.get()->Truncate(); if (rc.IsError()) { rc1 = rc; } } containers_.clear(); file_id_ = 0; return rc1; } StorageManager::StorageManager(const Path &root) : root_(root), file_id_(0), index_() {} StorageManager::~StorageManager() { (void)StorageManager::DoServiceStop(); } std::ostream &operator<<(std::ostream &os, const StorageManager &s) { os << "Dumping all containers ..." << "\n"; for (auto const &p : s.containers_) { os << *(p.get()); } return os; } } // namespace dataset } // namespace mindspore