tags:

views:

82

answers:

3

I'm putting together a class I'm going to call a file.

The file object simply contains a pointer to a memory mapped file and a link.

The constructor takes a file and maps the file onto memory range. In summary it looks a bit like this:

class file
{
  public:
   file(unsigned char* filename) { open(filename); }         

   open(unsigned char* filename)
   {
       /// snip 
       length_ = fstat(.....)
       file_ = mmap(.....)
   }
  private:
   unsigned int length_;
   unsigned char* bytes_;
};

Now, This file object can be copied around.

Here comes the fun. Normally, a class like this would require a deep copy constructor to copy bytes_. However, I'm satisfied that I can just copy the pointer because the memory is shared and it should look at the same file anyway. I don't want to remap the file. But, obviously, to prevent a memory leak bytes_ at some point would need to be freed.

What mechanisms can I use to decide when to delete the memory and munmap?

I was thinking about using a boost::shared_ptr in order to free memory in the destructor only when it's the last reference, but I'd have to guard that with a mutex lock right? Are there some convenient boost functions for me to use already? I do not want to have to pull in another big library, it's not an option.

i.e.

boost::shared_ptr<unsigned char> bytes_;

~file()
{
    // enter some sort of critical section 
    if (bytes_.unique()){
      munmap(bytes_);
      bytes_ = 0;
    }
    // exit critical section
}
A: 

Look at mutexes (and other synchronization mechanism) that Boost offers. See this. Also, Boost has a threads library. Are you using a OS specific threading library? If so, Boost.Threads may be worth taking a look at. Also, shouldn't your file object be in shared memory. Passing around a pointer doesn't looks a bit dangerous to me.

dirkgently
They are stored in a global map structure.
Matt H
A: 

shared_ptr is thread-safe, as the link nos posted in a comment says.

An alternative would be to declare copy constructor and assignment operator private, allowing (forcing?) the user to select an appropriate management strategy for their circumstances, such as shared_ptr or ptr_container. Add move semantics for further flexibility.

chrispy
+1  A: 

I would do it slightly differently.

The problem is that shared_ptr is not meant to handle arrays, and then as you said there are those synchronization issues.

The simple alternative would be to use a Pimpl idiom:

class FileImpl: boost::noncopyable
{
public:
  FileImpl(char const* name): mLength(fstat(name)), mFile(mmap(name)) {}
  ~FileImpl() { munmap(mFile); }

  unsigned int GetLength() const { return mLength; }
  unsigned char* GetFile() const { return mFile; }
private:
  unsigned int mLength;
  unsigned char* mFile;
};

class FileHandle
{
public:
  FileHandle(char const* name): mFile(new FileImpl(name)) {}

  void open(char const* name) { mFile = new FileImpl(name); }

private:
  boost::shared_ptr<FileImpl> mFile;
};

And there you won't have any synchronization issue in the destruction (it's naturally handled by shared_ptr).

You may also wish to use a Factory because several creations of various FileHandle object with the same file name will results in several calls to mmap and I am not sure that this would not duplicate the file in memory. On the other hand, a factory centralizing the calls could simply return a copy of the already created FileHandle object in this case.

Matthieu M.
You'd need weak references in the factory.
chrispy
@chrispy: you may. The difficulty of the factory is to properly synchronize it in order to avoid creating multiple in-memory copies of the same file. It's you choice whether to immediately release the file or not when the last `FileHandle` die, you may wish to keep it alive for a moment just in case it's asked for again.
Matthieu M.
@Matthieu You'd still need to do something clever with weak references or you'll never be able to tell it needs deleting, because the shared pointer would never go out of scope.
chrispy
@chrispy: you could, or you could simply `lock; test unicity; reset; unlock;`. And of course you could NOT use `shared_ptr` either. There are many alternatives but I too consider than using a weak reference is a good idea if you want something simple. Sounds like you've got an interesting thesis by the way, I'll delve into it right now :)
Matthieu M.
@Matthieu Enjoy! If the early theory chapter is too heavy-going, the other chapters are readily understood without it. (Indeed, if you wade your way through that whole section, it's entirely possible you're the first!)
chrispy
@chrispy: I enjoyed it, though I'll readily admit I shamelessly skipped most of the mathish sections and focused on the ideas. I'm very interested in distributed computation (whether multithread, multiprocess or multihost) and the mechanisms of communication / synchronization :)
Matthieu M.