jeudi 23 septembre 2021

Shared temp file management

My application starts several similar processes. First one creates a "global" temp file that they all red/write. When last process is destroyed, this file needs to be deleted. Later more processes may be spun up and this file should be re-created. From various examples I came up with a manager class that creates shared_ptr's with custom deleter (removing the file) and holds week_ptr to these objects to be able to hand them out upon request. This works but I'm wondering if there is a better approach, or any pitfalls can be spotted in my code.

#include <iostream>
#include <memory>
#include <string>
#include <set>

bool ptr_less(std::weak_ptr<std::string> lhs, std::weak_ptr<std::string> rhs) {
    auto left = lhs.lock();
    auto right= rhs.lock();
    if (!left || !right)
        return left < right;
    return *left < *right;
}

struct Manager {
    std::shared_ptr<std::string> get(const std::string& filename) {
        auto result = resources.find( std::weak_ptr<std::string>(std::make_shared<std::string>(filename)) );
        if (result != resources.end()) {
            std::cout << "Exists: " << filename << std::endl;
            if (auto sp = result->lock())
                return sp;
            resources.erase(result);            
        }
        // Create new object to manage, auto deleting
        std::shared_ptr<std::string> ptr(new std::string(filename), 
          [](std::string* str) { std::cout << "remove: " << *str << std::endl; delete str; });
        resources.emplace(std::weak_ptr<std::string>(ptr));
        //cleanup null pointers
        for (auto iter=resources.begin(); iter != resources.end(); ) {
            if (!iter->lock())
                iter = resources.erase(iter);
            else
                ++iter;
        }
        return ptr;
    }
    //Keep track of files to share. std::set comparing values not pointers
    std::set<std::weak_ptr<std::string>, decltype(ptr_less)*> resources{ptr_less};
};
static Manager custodian;

struct User {
    User(std::string name) : mName(std::move(name)) {
        std::cout << "New user: " << mName << std::endl;
        mGlob = custodian.get("global.json");
        mSet  = custodian.get("settings-" + mName + ".json");
        mVal  = custodian.get("values-"   + mName + ".json");
    }
    ~User() {
        std::cout << "~User(): " << mName << std::endl;
    }
    std::shared_ptr<std::string> mGlob;
    std::shared_ptr<std::string> mSet;    
    std::shared_ptr<std::string> mVal;    
    std::string mName;
};

int main()
{
    using namespace std;
    User* ps3 { nullptr };
    {
        User ps1("ps1");
        User ps2("ps2");
        ps3 = new User("ps3");
    }
    delete ps3;
    cout << "Resources size: " << custodian.resources.size() << endl;
    User ps1("ps1");
    cout << "Resources size: " << custodian.resources.size() << endl;
    return 0;
}

Demo here

Aucun commentaire:

Enregistrer un commentaire