Panda3D
 All Classes Functions Variables Enumerations
subprocessWindowBuffer.cxx
00001 // Filename: subprocessWindowBuffer.cxx
00002 // Created by:  drose (11Jul09)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "subprocessWindowBuffer.h"
00016 #include <sys/mman.h>
00017 #include <fcntl.h>
00018 #include <string.h>
00019 
00020 #include <iostream>
00021 using namespace std;
00022 
00023 const char SubprocessWindowBuffer::
00024 _magic_number[SubprocessWindowBuffer::magic_number_length] = "pNdaSWB";
00025 
00026 ////////////////////////////////////////////////////////////////////
00027 //     Function: SubprocessWindowBuffer::operator new
00028 //       Access: Private
00029 //  Description: Placement operator.  Returns addr, a trivial
00030 //               pass-through.
00031 ////////////////////////////////////////////////////////////////////
00032 void *SubprocessWindowBuffer::
00033 operator new(size_t, void *addr) {
00034   return addr;
00035 }
00036 
00037 ////////////////////////////////////////////////////////////////////
00038 //     Function: SubprocessWindowBuffer::Constructor
00039 //       Access: Private
00040 //  Description: This constructor is private; it is not intended to be
00041 //               called directly.  It is used in make_buffer() to
00042 //               create a temporary local object, to determine the
00043 //               required mmap_size for a given window size.
00044 ////////////////////////////////////////////////////////////////////
00045 SubprocessWindowBuffer::
00046 SubprocessWindowBuffer(int x_size, int y_size) {
00047   memcpy(_this_magic, _magic_number, magic_number_length);
00048   _x_size = x_size;
00049   _y_size = y_size;
00050   _row_size = _x_size * 4;
00051   _framebuffer_size = _row_size * y_size;
00052   _event_in = 0;
00053   _event_out = 0;
00054   _last_written = 0;
00055   _last_read = 0;
00056 
00057   _mmap_size = sizeof(*this) + _framebuffer_size;
00058 }
00059 
00060 ////////////////////////////////////////////////////////////////////
00061 //     Function: SubprocessWindowBuffer::Copy Constructor
00062 //       Access: Private
00063 //  Description: 
00064 ////////////////////////////////////////////////////////////////////
00065 SubprocessWindowBuffer::
00066 SubprocessWindowBuffer(const SubprocessWindowBuffer &copy) :
00067   _mmap_size(copy._mmap_size),
00068   _x_size(copy._x_size),
00069   _y_size(copy._y_size),
00070   _row_size(copy._row_size),
00071   _framebuffer_size(copy._framebuffer_size)
00072 {
00073   memcpy(_this_magic, _magic_number, magic_number_length);
00074   _event_in = 0;
00075   _event_out = 0;
00076   _last_written = 0;
00077   _last_read = 0;
00078 }
00079 
00080 ////////////////////////////////////////////////////////////////////
00081 //     Function: SubprocessWindowBuffer::Destructor
00082 //       Access: Private
00083 //  Description: 
00084 ////////////////////////////////////////////////////////////////////
00085 SubprocessWindowBuffer::
00086 ~SubprocessWindowBuffer() {
00087 }
00088 
00089 ////////////////////////////////////////////////////////////////////
00090 //     Function: SubprocessWindowBuffer::new_buffer
00091 //       Access: Public, Static
00092 //  Description: Call this method to create a new buffer in shared
00093 //               memory space.  Supply the desired size of the window.
00094 //
00095 //               This method will create the required shared-memory
00096 //               buffer and return a SubprocessWindowBuffer allocated
00097 //               within that shared memory, or NULL if there is a
00098 //               failure allocating sufficient shared memory.
00099 //
00100 //               It also creates a temporary file on disk and returns
00101 //               fd, mmap_size, and filename, which the caller must
00102 //               retain and eventually pass to destroy_buffer().  The
00103 //               filename should be passed to the child process to
00104 //               open with open_buffer().
00105 ////////////////////////////////////////////////////////////////////
00106 SubprocessWindowBuffer *SubprocessWindowBuffer::
00107 new_buffer(int &fd, size_t &mmap_size, string &filename,
00108            int x_size, int y_size) {
00109   mmap_size = 0;
00110   fd = -1;
00111 
00112   filename = tmpnam(NULL);
00113 
00114   fd = open(filename.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
00115   if (fd == -1) {
00116     perror(filename.c_str());
00117     return NULL;
00118   }
00119 
00120   // Create a temporary object to determine the required size.
00121   SubprocessWindowBuffer temp(x_size, y_size);
00122   mmap_size = temp._mmap_size;
00123 
00124   // Ensure the disk file is large enough.
00125   size_t zero_size = 1024;
00126   char zero[zero_size];
00127   memset(zero, 0, zero_size);
00128   for (size_t bi = 0; bi < mmap_size; bi += zero_size) {
00129     write(fd, zero, zero_size);
00130   }
00131 
00132   void *shared_mem = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
00133                           MAP_SHARED, fd, 0);
00134   if (shared_mem == (void *)-1) {
00135     // Failure to map.
00136     close(fd);
00137     fd = -1;
00138     mmap_size = 0;
00139     return NULL;
00140   }
00141 
00142   // Now create the actual object in the shared-memory buffer.
00143   return new(shared_mem) SubprocessWindowBuffer(temp);
00144 }
00145 
00146 ////////////////////////////////////////////////////////////////////
00147 //     Function: SubprocessWindowBuffer::destroy_buffer
00148 //       Access: Public, Static
00149 //  Description: Destroys a buffer object created via a previous call
00150 //               to new_buffer().  This destructs objects within the
00151 //               buffer, unmaps the shared memory, and closes the file
00152 //               descriptor.
00153 ////////////////////////////////////////////////////////////////////
00154 void SubprocessWindowBuffer::
00155 destroy_buffer(int fd, size_t mmap_size, const string &filename,
00156                SubprocessWindowBuffer *buffer) {
00157   buffer->~SubprocessWindowBuffer();
00158   close_buffer(fd, mmap_size, filename, buffer);
00159 
00160   // Now we can unlink the file.
00161   unlink(filename.c_str());
00162 }
00163 
00164 ////////////////////////////////////////////////////////////////////
00165 //     Function: SubprocessWindowBuffer::open_buffer
00166 //       Access: Public, Static
00167 //  Description: Call this method to open a reference to an existing
00168 //               buffer in shared memory space.  Supply the temporary
00169 //               filename returned by new_buffer(), above (presumably
00170 //               from the parent process).
00171 //
00172 //               This method will mmap the required shared-memory
00173 //               buffer and return a SubprocessWindowBuffer allocated
00174 //               within that shared memory, or NULL if there is some
00175 //               failure.  The caller must retain fd, mmap_size, and
00176 //               filename and eventually pass all three to
00177 //               close_buffer().
00178 ////////////////////////////////////////////////////////////////////
00179 SubprocessWindowBuffer *SubprocessWindowBuffer::
00180 open_buffer(int &fd, size_t &mmap_size, const string &filename) {
00181   mmap_size = 0;
00182 
00183   fd = open(filename.c_str(), O_RDWR);
00184   if (fd == -1) {
00185     perror(filename.c_str());
00186     return NULL;
00187   }
00188 
00189   // Check that the disk file is large enough.
00190   off_t file_size = lseek(fd, 0, SEEK_END);
00191   if (file_size < sizeof(SubprocessWindowBuffer)) {
00192     cerr << filename << " not large enough.\n";
00193     close(fd);
00194     fd = -1;
00195     return NULL;
00196   }
00197 
00198   // First, map enough memory to read the buffer object.
00199   size_t initial_size = sizeof(SubprocessWindowBuffer);
00200   void *shared_mem = mmap(NULL, initial_size, PROT_READ,
00201                           MAP_SHARED, fd, 0);
00202   if (shared_mem == (void *)-1) {
00203     perror("mmap");
00204     cerr << "Couldn't map.\n";
00205     close(fd);
00206     fd = -1;
00207     return NULL;
00208   }
00209 
00210   SubprocessWindowBuffer *temp = (SubprocessWindowBuffer *)shared_mem;
00211   if (!temp->verify_magic_number()) {
00212     cerr << "Not a subprocess window buffer: " << filename << "\n";
00213     munmap(shared_mem, initial_size);
00214     close(fd);
00215     fd = -1;
00216     return NULL;
00217   }
00218     
00219 
00220   mmap_size = temp->_mmap_size;
00221 
00222   // Now unmap that and remap the proper-size buffer.
00223   munmap(shared_mem, initial_size);
00224 
00225   if (file_size < mmap_size) {
00226     cerr << filename << " not large enough.\n";
00227     close(fd);
00228     fd = -1;
00229     return NULL;
00230   }
00231 
00232   shared_mem = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
00233                     MAP_SHARED, fd, 0);
00234   if (shared_mem == (void *)-1) {
00235     perror("mmap");
00236     cerr << "Couldn't map 2.\n";
00237     return NULL;
00238   }
00239 
00240   // Now that we've successfully opened and mapped the file, we can
00241   // safely delete it from the file system.
00242 
00243   // Actually, unlinking it now prevents us from detaching and
00244   // reattaching to the same file later.  Boo.
00245   //  unlink(filename.c_str());
00246 
00247   SubprocessWindowBuffer *buffer = (SubprocessWindowBuffer *)shared_mem;
00248   assert(buffer->_mmap_size == mmap_size);
00249   return buffer;
00250 }
00251 
00252 ////////////////////////////////////////////////////////////////////
00253 //     Function: SubprocessWindowBuffer::close_buffer
00254 //       Access: Public, Static
00255 //  Description: Closes a buffer object created via a previous call
00256 //               to open_buffer().  This unmaps the shared memory
00257 //               and closes the file descriptor, but does not molest
00258 //               the shared buffer itself.
00259 ////////////////////////////////////////////////////////////////////
00260 void SubprocessWindowBuffer::
00261 close_buffer(int fd, size_t mmap_size, const string &filename,
00262              SubprocessWindowBuffer *buffer) {
00263   munmap((void *)buffer, mmap_size);
00264   close(fd);
00265 }
00266 
00267 ////////////////////////////////////////////////////////////////////
00268 //     Function: SubprocessWindowBuffer::verify_magic_number
00269 //       Access: Public
00270 //  Description: Returns true if the buffer's magic number matches,
00271 //               false otherwise.
00272 ////////////////////////////////////////////////////////////////////
00273 bool SubprocessWindowBuffer::
00274 verify_magic_number() const {
00275   return (memcmp(_this_magic, _magic_number, magic_number_length) == 0);
00276 }
 All Classes Functions Variables Enumerations