// Filename: subprocessWindowBuffer.cxx // Created by: drose (11Jul09) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) Carnegie Mellon University. All rights reserved. // // All use of this software is subject to the terms of the revised BSD // license. You should have received a copy of this license along // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// #include "subprocessWindowBuffer.h" #include #include #include #include using namespace std; const char SubprocessWindowBuffer:: _magic_number[SubprocessWindowBuffer::magic_number_length] = "pNdaSWB"; //////////////////////////////////////////////////////////////////// // Function: SubprocessWindowBuffer::operator new // Access: Private // Description: Placement operator. Returns addr, a trivial // pass-through. //////////////////////////////////////////////////////////////////// void *SubprocessWindowBuffer:: operator new(size_t, void *addr) { return addr; } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindowBuffer::Constructor // Access: Private // Description: This constructor is private; it is not intended to be // called directly. It is used in make_buffer() to // create a temporary local object, to determine the // required mmap_size for a given window size. //////////////////////////////////////////////////////////////////// SubprocessWindowBuffer:: SubprocessWindowBuffer(int x_size, int y_size) { memcpy(_this_magic, _magic_number, magic_number_length); _x_size = x_size; _y_size = y_size; _row_size = _x_size * 4; _framebuffer_size = _row_size * y_size; _event_in = 0; _event_out = 0; _last_written = 0; _last_read = 0; _mmap_size = sizeof(*this) + _framebuffer_size; } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindowBuffer::Copy Constructor // Access: Private // Description: //////////////////////////////////////////////////////////////////// SubprocessWindowBuffer:: SubprocessWindowBuffer(const SubprocessWindowBuffer ©) : _x_size(copy._x_size), _y_size(copy._y_size), _row_size(copy._row_size), _framebuffer_size(copy._framebuffer_size), _mmap_size(copy._mmap_size) { memcpy(_this_magic, _magic_number, magic_number_length); _event_in = 0; _event_out = 0; _last_written = 0; _last_read = 0; } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindowBuffer::Destructor // Access: Private // Description: //////////////////////////////////////////////////////////////////// SubprocessWindowBuffer:: ~SubprocessWindowBuffer() { } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindowBuffer::new_buffer // Access: Public, Static // Description: Call this method to create a new buffer in shared // memory space. Supply the desired size of the window. // // This method will create the required shared-memory // buffer and return a SubprocessWindowBuffer allocated // within that shared memory, or NULL if there is a // failure allocating sufficient shared memory. // // It also creates a temporary file on disk and returns // fd, mmap_size, and filename, which the caller must // retain and eventually pass to destroy_buffer(). The // filename should be passed to the child process to // open with open_buffer(). //////////////////////////////////////////////////////////////////// SubprocessWindowBuffer *SubprocessWindowBuffer:: new_buffer(int &fd, size_t &mmap_size, string &filename, int x_size, int y_size) { mmap_size = 0; fd = -1; filename = tmpnam(NULL); fd = open(filename.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600); if (fd == -1) { perror(filename.c_str()); return NULL; } // Create a temporary object to determine the required size. SubprocessWindowBuffer temp(x_size, y_size); mmap_size = temp._mmap_size; // Ensure the disk file is large enough. size_t zero_size = 1024; char zero[zero_size]; memset(zero, 0, zero_size); for (size_t bi = 0; bi < mmap_size; bi += zero_size) { write(fd, zero, zero_size); } void *shared_mem = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (shared_mem == (void *)-1) { // Failure to map. close(fd); fd = -1; mmap_size = 0; return NULL; } // Now create the actual object in the shared-memory buffer. return new(shared_mem) SubprocessWindowBuffer(temp); } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindowBuffer::destroy_buffer // Access: Public, Static // Description: Destroys a buffer object created via a previous call // to new_buffer(). This destructs objects within the // buffer, unmaps the shared memory, and closes the file // descriptor. //////////////////////////////////////////////////////////////////// void SubprocessWindowBuffer:: destroy_buffer(int fd, size_t mmap_size, const string &filename, SubprocessWindowBuffer *buffer) { buffer->~SubprocessWindowBuffer(); close_buffer(fd, mmap_size, filename, buffer); // Now we can unlink the file. unlink(filename.c_str()); } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindowBuffer::open_buffer // Access: Public, Static // Description: Call this method to open a reference to an existing // buffer in shared memory space. Supply the temporary // filename returned by new_buffer(), above (presumably // from the parent process). // // This method will mmap the required shared-memory // buffer and return a SubprocessWindowBuffer allocated // within that shared memory, or NULL if there is some // failure. The caller must retain fd, mmap_size, and // filename and eventually pass all three to // close_buffer(). //////////////////////////////////////////////////////////////////// SubprocessWindowBuffer *SubprocessWindowBuffer:: open_buffer(int &fd, size_t &mmap_size, const string &filename) { mmap_size = 0; fd = open(filename.c_str(), O_RDWR); if (fd == -1) { perror(filename.c_str()); return NULL; } // Check that the disk file is large enough. off_t file_size = lseek(fd, 0, SEEK_END); if (file_size < sizeof(SubprocessWindowBuffer)) { cerr << filename << " not large enough.\n"; close(fd); fd = -1; return NULL; } // First, map enough memory to read the buffer object. size_t initial_size = sizeof(SubprocessWindowBuffer); void *shared_mem = mmap(NULL, initial_size, PROT_READ, MAP_SHARED, fd, 0); if (shared_mem == (void *)-1) { perror("mmap"); cerr << "Couldn't map.\n"; close(fd); fd = -1; return NULL; } SubprocessWindowBuffer *temp = (SubprocessWindowBuffer *)shared_mem; if (!temp->verify_magic_number()) { cerr << "Not a subprocess window buffer: " << filename << "\n"; munmap(shared_mem, initial_size); close(fd); fd = -1; return NULL; } mmap_size = temp->_mmap_size; // Now unmap that and remap the proper-size buffer. munmap(shared_mem, initial_size); if (file_size < mmap_size) { cerr << filename << " not large enough.\n"; close(fd); fd = -1; return NULL; } shared_mem = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (shared_mem == (void *)-1) { perror("mmap"); cerr << "Couldn't map 2.\n"; return NULL; } // Now that we've successfully opened and mapped the file, we can // safely delete it from the file system. // Actually, unlinking it now prevents us from detaching and // reattaching to the same file later. Boo. // unlink(filename.c_str()); SubprocessWindowBuffer *buffer = (SubprocessWindowBuffer *)shared_mem; assert(buffer->_mmap_size == mmap_size); return buffer; } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindowBuffer::close_buffer // Access: Public, Static // Description: Closes a buffer object created via a previous call // to open_buffer(). This unmaps the shared memory // and closes the file descriptor, but does not molest // the shared buffer itself. //////////////////////////////////////////////////////////////////// void SubprocessWindowBuffer:: close_buffer(int fd, size_t mmap_size, const string &filename, SubprocessWindowBuffer *buffer) { munmap((void *)buffer, mmap_size); close(fd); } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindowBuffer::verify_magic_number // Access: Public // Description: Returns true if the buffer's magic number matches, // false otherwise. //////////////////////////////////////////////////////////////////// bool SubprocessWindowBuffer:: verify_magic_number() const { return (memcmp(_this_magic, _magic_number, magic_number_length) == 0); }