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