Panda3D
subprocessWindowBuffer.cxx
1 // Filename: subprocessWindowBuffer.cxx
2 // Created by: drose (11Jul09)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "subprocessWindowBuffer.h"
16 #include <sys/mman.h>
17 #include <fcntl.h>
18 #include <string.h>
19 #include <unistd.h>
20 
21 #include <iostream>
22 using namespace std;
23 
24 const char SubprocessWindowBuffer::
25 _magic_number[SubprocessWindowBuffer::magic_number_length] = "pNdaSWB";
26 
27 ////////////////////////////////////////////////////////////////////
28 // Function: SubprocessWindowBuffer::operator new
29 // Access: Private
30 // Description: Placement operator. Returns addr, a trivial
31 // pass-through.
32 ////////////////////////////////////////////////////////////////////
33 void *SubprocessWindowBuffer::
34 operator new(size_t, void *addr) {
35  return addr;
36 }
37 
38 ////////////////////////////////////////////////////////////////////
39 // Function: SubprocessWindowBuffer::Constructor
40 // Access: Private
41 // Description: This constructor is private; it is not intended to be
42 // called directly. It is used in make_buffer() to
43 // create a temporary local object, to determine the
44 // required mmap_size for a given window size.
45 ////////////////////////////////////////////////////////////////////
46 SubprocessWindowBuffer::
47 SubprocessWindowBuffer(int x_size, int y_size) {
48  memcpy(_this_magic, _magic_number, magic_number_length);
49  _x_size = x_size;
50  _y_size = y_size;
51  _row_size = _x_size * 4;
52  _framebuffer_size = _row_size * y_size;
53  _event_in = 0;
54  _event_out = 0;
55  _last_written = 0;
56  _last_read = 0;
57 
58  _mmap_size = sizeof(*this) + _framebuffer_size;
59 }
60 
61 ////////////////////////////////////////////////////////////////////
62 // Function: SubprocessWindowBuffer::Copy Constructor
63 // Access: Private
64 // Description:
65 ////////////////////////////////////////////////////////////////////
66 SubprocessWindowBuffer::
67 SubprocessWindowBuffer(const SubprocessWindowBuffer &copy) :
68  _mmap_size(copy._mmap_size),
69  _x_size(copy._x_size),
70  _y_size(copy._y_size),
71  _row_size(copy._row_size),
72  _framebuffer_size(copy._framebuffer_size)
73 {
74  memcpy(_this_magic, _magic_number, magic_number_length);
75  _event_in = 0;
76  _event_out = 0;
77  _last_written = 0;
78  _last_read = 0;
79 }
80 
81 ////////////////////////////////////////////////////////////////////
82 // Function: SubprocessWindowBuffer::Destructor
83 // Access: Private
84 // Description:
85 ////////////////////////////////////////////////////////////////////
86 SubprocessWindowBuffer::
87 ~SubprocessWindowBuffer() {
88 }
89 
90 ////////////////////////////////////////////////////////////////////
91 // Function: SubprocessWindowBuffer::new_buffer
92 // Access: Public, Static
93 // Description: Call this method to create a new buffer in shared
94 // memory space. Supply the desired size of the window.
95 //
96 // This method will create the required shared-memory
97 // buffer and return a SubprocessWindowBuffer allocated
98 // within that shared memory, or NULL if there is a
99 // failure allocating sufficient shared memory.
100 //
101 // It also creates a temporary file on disk and returns
102 // fd, mmap_size, and filename, which the caller must
103 // retain and eventually pass to destroy_buffer(). The
104 // filename should be passed to the child process to
105 // open with open_buffer().
106 ////////////////////////////////////////////////////////////////////
108 new_buffer(int &fd, size_t &mmap_size, string &filename,
109  int x_size, int y_size) {
110  mmap_size = 0;
111  fd = -1;
112 
113  filename = tmpnam(NULL);
114 
115  fd = open(filename.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
116  if (fd == -1) {
117  perror(filename.c_str());
118  return NULL;
119  }
120 
121  // Create a temporary object to determine the required size.
122  SubprocessWindowBuffer temp(x_size, y_size);
123  mmap_size = temp._mmap_size;
124 
125  // Ensure the disk file is large enough.
126  size_t zero_size = 1024;
127  char zero[zero_size];
128  memset(zero, 0, zero_size);
129  for (size_t bi = 0; bi < mmap_size; bi += zero_size) {
130  write(fd, zero, zero_size);
131  }
132 
133  void *shared_mem = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
134  MAP_SHARED, fd, 0);
135  if (shared_mem == (void *)-1) {
136  // Failure to map.
137  close(fd);
138  fd = -1;
139  mmap_size = 0;
140  return NULL;
141  }
142 
143  // Now create the actual object in the shared-memory buffer.
144  return new(shared_mem) SubprocessWindowBuffer(temp);
145 }
146 
147 ////////////////////////////////////////////////////////////////////
148 // Function: SubprocessWindowBuffer::destroy_buffer
149 // Access: Public, Static
150 // Description: Destroys a buffer object created via a previous call
151 // to new_buffer(). This destructs objects within the
152 // buffer, unmaps the shared memory, and closes the file
153 // descriptor.
154 ////////////////////////////////////////////////////////////////////
156 destroy_buffer(int fd, size_t mmap_size, const string &filename,
157  SubprocessWindowBuffer *buffer) {
158  buffer->~SubprocessWindowBuffer();
159  close_buffer(fd, mmap_size, filename, buffer);
160 
161  // Now we can unlink the file.
162  unlink(filename.c_str());
163 }
164 
165 ////////////////////////////////////////////////////////////////////
166 // Function: SubprocessWindowBuffer::open_buffer
167 // Access: Public, Static
168 // Description: Call this method to open a reference to an existing
169 // buffer in shared memory space. Supply the temporary
170 // filename returned by new_buffer(), above (presumably
171 // from the parent process).
172 //
173 // This method will mmap the required shared-memory
174 // buffer and return a SubprocessWindowBuffer allocated
175 // within that shared memory, or NULL if there is some
176 // failure. The caller must retain fd, mmap_size, and
177 // filename and eventually pass all three to
178 // close_buffer().
179 ////////////////////////////////////////////////////////////////////
181 open_buffer(int &fd, size_t &mmap_size, const string &filename) {
182  mmap_size = 0;
183 
184  fd = open(filename.c_str(), O_RDWR);
185  if (fd == -1) {
186  perror(filename.c_str());
187  return NULL;
188  }
189 
190  // Check that the disk file is large enough.
191  off_t file_size = lseek(fd, 0, SEEK_END);
192  if (file_size < sizeof(SubprocessWindowBuffer)) {
193  cerr << filename << " not large enough.\n";
194  close(fd);
195  fd = -1;
196  return NULL;
197  }
198 
199  // First, map enough memory to read the buffer object.
200  size_t initial_size = sizeof(SubprocessWindowBuffer);
201  void *shared_mem = mmap(NULL, initial_size, PROT_READ,
202  MAP_SHARED, fd, 0);
203  if (shared_mem == (void *)-1) {
204  perror("mmap");
205  cerr << "Couldn't map.\n";
206  close(fd);
207  fd = -1;
208  return NULL;
209  }
210 
211  SubprocessWindowBuffer *temp = (SubprocessWindowBuffer *)shared_mem;
212  if (!temp->verify_magic_number()) {
213  cerr << "Not a subprocess window buffer: " << filename << "\n";
214  munmap(shared_mem, initial_size);
215  close(fd);
216  fd = -1;
217  return NULL;
218  }
219 
220 
221  mmap_size = temp->_mmap_size;
222 
223  // Now unmap that and remap the proper-size buffer.
224  munmap(shared_mem, initial_size);
225 
226  if (file_size < mmap_size) {
227  cerr << filename << " not large enough.\n";
228  close(fd);
229  fd = -1;
230  return NULL;
231  }
232 
233  shared_mem = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
234  MAP_SHARED, fd, 0);
235  if (shared_mem == (void *)-1) {
236  perror("mmap");
237  cerr << "Couldn't map 2.\n";
238  return NULL;
239  }
240 
241  // Now that we've successfully opened and mapped the file, we can
242  // safely delete it from the file system.
243 
244  // Actually, unlinking it now prevents us from detaching and
245  // reattaching to the same file later. Boo.
246  // unlink(filename.c_str());
247 
248  SubprocessWindowBuffer *buffer = (SubprocessWindowBuffer *)shared_mem;
249  assert(buffer->_mmap_size == mmap_size);
250  return buffer;
251 }
252 
253 ////////////////////////////////////////////////////////////////////
254 // Function: SubprocessWindowBuffer::close_buffer
255 // Access: Public, Static
256 // Description: Closes a buffer object created via a previous call
257 // to open_buffer(). This unmaps the shared memory
258 // and closes the file descriptor, but does not molest
259 // the shared buffer itself.
260 ////////////////////////////////////////////////////////////////////
262 close_buffer(int fd, size_t mmap_size, const string &filename,
263  SubprocessWindowBuffer *buffer) {
264  munmap((void *)buffer, mmap_size);
265  close(fd);
266 }
267 
268 ////////////////////////////////////////////////////////////////////
269 // Function: SubprocessWindowBuffer::verify_magic_number
270 // Access: Public
271 // Description: Returns true if the buffer's magic number matches,
272 // false otherwise.
273 ////////////////////////////////////////////////////////////////////
276  return (memcmp(_this_magic, _magic_number, magic_number_length) == 0);
277 }
STL namespace.
bool verify_magic_number() const
Returns true if the buffer&#39;s magic number matches, false otherwise.
static void close_buffer(int fd, size_t mmap_size, const string &filename, SubprocessWindowBuffer *buffer)
Closes a buffer object created via a previous call to open_buffer().
static SubprocessWindowBuffer * new_buffer(int &fd, size_t &mmap_size, string &filename, int x_size, int y_size)
Call this method to create a new buffer in shared memory space.
This is a special class that is designed to faciliate SubprocessWindow.
static void destroy_buffer(int fd, size_t mmap_size, const string &filename, SubprocessWindowBuffer *buffer)
Destroys a buffer object created via a previous call to new_buffer().
static SubprocessWindowBuffer * open_buffer(int &fd, size_t &mmap_size, const string &filename)
Call this method to open a reference to an existing buffer in shared memory space.