Panda3D
Loading...
Searching...
No Matches
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
15#include <sys/mman.h>
16#include <fcntl.h>
17#include <string.h>
18#include <unistd.h>
19
20#include <iostream>
21
22using std::cerr;
23using std::string;
24
25const char SubprocessWindowBuffer::
26_magic_number[SubprocessWindowBuffer::magic_number_length] = "pNdaSWB";
27
28/**
29 * Placement operator. Returns addr, a trivial pass-through.
30 */
31void *SubprocessWindowBuffer::
32operator 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 */
41SubprocessWindowBuffer::
42SubprocessWindowBuffer(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 */
59SubprocessWindowBuffer::
60SubprocessWindowBuffer(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 */
77SubprocessWindowBuffer::
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 */
95new_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 */
140destroy_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 */
160open_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
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 */
237close_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 */
247verify_magic_number() const {
248 return (memcmp(_this_magic, _magic_number, magic_number_length) == 0);
249}
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 * 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.
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 * 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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.