#include <cstdio>
#include <libpmemobj++/pool.hpp>
#include <libpmemobj++/transaction.hpp>
#include <libpmemobj++/persistent_ptr.hpp>
#include <libpmemobj++/make_persistent.hpp>

const char * const poolPath = "/mnt/pmem/pool_for_017";

class pointless {
    struct element {
        element(pmem::obj::persistent_ptr<element> & n) : next(n) {}
        pmem::obj::persistent_ptr<element> next;
    };
    pmem::obj::persistent_ptr<element> head = nullptr;
public:
    pmem::obj::p<int> count = 0;
    void add(){
        head = pmem::obj::make_persistent<element>(head);
        count.get_rw()++;
    }
};

// $ ./017; ./017 100000
// Count: 75664 (err: failed to allocate persistent memory object)

// $ ./017; let i=100000; while let i--; do ./017  1 || break; done
// Count:    36 (err: Could not add an object to the transaction.)

// $ ./017; let i=100000; while let i--; do ./017 10 || break; done
// Count:   360 (err: Could not add an object to the transaction.)

int main(int argc, char ** argv) {
    
    switch (argc){
        case 1: {
            remove(poolPath);
            pmem::obj::pool<pointless> pop = pmem::obj::pool<pointless>::create(poolPath, std::string(), 16*1024*1024);
            pmem::obj::transaction::exec_tx(pop, [&] {
                new (pop.get_root().get()) pointless();
            });
            return 0;
        }
        case 2: {
            pmem::obj::pool<pointless> pop = pmem::obj::pool<pointless>::open(poolPath, std::string());
    
            int count = atoi(argv[1]);
            try {
                while(count--) {
                    pmem::obj::transaction::exec_tx(pop, [&] {
                        pop.get_root()->add();
                    });
                }
            } catch(const pmem::transaction_error & e) {
                printf("Count: %5d (err: %s)\n", pop.get_root()->count.get_ro(), e.what());
                return 1;
            }
            return 0;
        }
        default: throw;
    }
}
