1 |
/* -*- c++ -*- */ |
2 |
/* |
3 |
* Copyright 2003 Free Software Foundation, Inc. |
4 |
* |
5 |
* This file is part of GNU Radio |
6 |
* |
7 |
* GNU Radio is free software; you can redistribute it and/or modify |
8 |
* it under the terms of the GNU General Public License as published by |
9 |
* the Free Software Foundation; either version 2, or (at your option) |
10 |
* any later version. |
11 |
* |
12 |
* GNU Radio is distributed in the hope that it will be useful, |
13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
* GNU General Public License for more details. |
16 |
* |
17 |
* You should have received a copy of the GNU General Public License |
18 |
* along with GNU Radio; see the file COPYING. If not, write to |
19 |
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
20 |
* Boston, MA 02111-1307, USA. |
21 |
*/ |
22 |
|
23 |
#ifdef HAVE_CONFIG_H |
24 |
#include "config.h" |
25 |
#endif |
26 |
#include <gr_vmcircbuf_mmap_tmpfile.h> |
27 |
#include <stdexcept> |
28 |
#include <assert.h> |
29 |
#include <unistd.h> |
30 |
#include <stdlib.h> |
31 |
#ifdef HAVE_SYS_TYPES_H |
32 |
#include <sys/types.h> |
33 |
#endif |
34 |
#ifdef HAVE_SYS_MMAN_H |
35 |
#include <sys/mman.h> |
36 |
#endif |
37 |
#include <fcntl.h> |
38 |
#include <errno.h> |
39 |
#include <stdio.h> |
40 |
#include <string.h> |
41 |
#include <gr_pagesize.h> |
42 |
#include <gr_tmp_path.h> |
43 |
|
44 |
|
45 |
gr_vmcircbuf_mmap_tmpfile::gr_vmcircbuf_mmap_tmpfile (int size) |
46 |
: gr_vmcircbuf (size) |
47 |
{ |
48 |
#if !defined(HAVE_MMAP) |
49 |
fprintf (stderr, "gr_vmcircbuf_mmap_tmpfile: mmap or mkstemp is not available\n"); |
50 |
throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); |
51 |
#else |
52 |
|
53 |
if (size <= 0 || (size % gr_pagesize ()) != 0){ |
54 |
fprintf (stderr, "gr_vmcircbuf_mmap_tmpfile: invalid size = %d\n", size); |
55 |
throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); |
56 |
} |
57 |
|
58 |
int seg_fd = -1; |
59 |
char seg_name[1024]; |
60 |
|
61 |
static int s_seg_counter = 0; |
62 |
|
63 |
|
64 |
// open a temporary file that we'll map in a bit later |
65 |
|
66 |
while (1){ |
67 |
snprintf (seg_name, sizeof (seg_name), |
68 |
"%s/gnuradio-%d-%d-XXXXXX", gr_tmp_path (), getpid (), s_seg_counter); |
69 |
s_seg_counter++; |
70 |
|
71 |
seg_fd = open (seg_name, O_RDWR | O_CREAT | O_EXCL, 0600); |
72 |
if (seg_fd == -1){ |
73 |
if (errno == EEXIST) // File already exists (shouldn't happen). Try again |
74 |
continue; |
75 |
|
76 |
char msg[1024]; |
77 |
snprintf (msg, sizeof (msg), |
78 |
"gr_vmcircbuf_mmap_tmpfile: open [%s]", seg_name); |
79 |
perror (msg); |
80 |
throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); |
81 |
} |
82 |
break; |
83 |
} |
84 |
|
85 |
if (unlink (seg_name) == -1){ |
86 |
perror ("gr_vmcircbuf_mmap_tmpfile: unlink"); |
87 |
throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); |
88 |
} |
89 |
|
90 |
// We've got a valid file descriptor to a tmp file. |
91 |
// Now set it's length to 2x what we really want and mmap it in. |
92 |
|
93 |
if (ftruncate (seg_fd, (off_t) 2 * size) == -1){ |
94 |
close (seg_fd); // cleanup |
95 |
perror ("gr_vmcircbuf_mmap_tmpfile: ftruncate (1)"); |
96 |
throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); |
97 |
} |
98 |
|
99 |
void *first_copy = mmap (0, 2 * size, |
100 |
PROT_READ | PROT_WRITE, MAP_SHARED, |
101 |
seg_fd, (off_t) 0); |
102 |
|
103 |
if (first_copy == MAP_FAILED){ |
104 |
close (seg_fd); // cleanup |
105 |
perror ("gr_vmcircbuf_mmap_tmpfile: mmap (1)"); |
106 |
throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); |
107 |
} |
108 |
|
109 |
// unmap the 2nd half |
110 |
if (munmap ((char *) first_copy + size, size) == -1){ |
111 |
close (seg_fd); // cleanup |
112 |
perror ("gr_vmcircbuf_mmap_tmpfile: munmap (1)"); |
113 |
throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); |
114 |
} |
115 |
|
116 |
// map the first half into the now available hole where the |
117 |
// second half used to be. |
118 |
|
119 |
void *second_copy = mmap ((char *) first_copy + size, size, |
120 |
PROT_READ | PROT_WRITE, MAP_SHARED, |
121 |
seg_fd, (off_t) 0); |
122 |
|
123 |
if (second_copy == MAP_FAILED){ |
124 |
close (seg_fd); // cleanup |
125 |
perror ("gr_vmcircbuf_mmap_tmpfile: mmap (2)"); |
126 |
throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); |
127 |
} |
128 |
|
129 |
// cut the tmp file down to size |
130 |
if (ftruncate (seg_fd, (off_t) size) == -1){ |
131 |
close (seg_fd); // cleanup |
132 |
perror ("gr_vmcircbuf_mmap_tmpfile: ftruncate (2)"); |
133 |
throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile"); |
134 |
} |
135 |
|
136 |
close (seg_fd); // fd no longer needed. The mapping is retained. |
137 |
|
138 |
// Now remember the important stuff |
139 |
|
140 |
d_base = (char *) first_copy; |
141 |
d_size = size; |
142 |
#endif |
143 |
} |
144 |
|
145 |
gr_vmcircbuf_mmap_tmpfile::~gr_vmcircbuf_mmap_tmpfile () |
146 |
{ |
147 |
#if defined(HAVE_MMAP) |
148 |
if (munmap (d_base, 2 * d_size) == -1){ |
149 |
perror ("gr_vmcircbuf_mmap_tmpfile: munmap (2)"); |
150 |
} |
151 |
#endif |
152 |
} |
153 |
|
154 |
// ---------------------------------------------------------------- |
155 |
// The factory interface |
156 |
// ---------------------------------------------------------------- |
157 |
|
158 |
|
159 |
gr_vmcircbuf_factory *gr_vmcircbuf_mmap_tmpfile_factory::s_the_factory = 0; |
160 |
|
161 |
gr_vmcircbuf_factory * |
162 |
gr_vmcircbuf_mmap_tmpfile_factory::singleton () |
163 |
{ |
164 |
if (s_the_factory) |
165 |
return s_the_factory; |
166 |
|
167 |
s_the_factory = new gr_vmcircbuf_mmap_tmpfile_factory (); |
168 |
return s_the_factory; |
169 |
} |
170 |
|
171 |
int |
172 |
gr_vmcircbuf_mmap_tmpfile_factory::granularity () |
173 |
{ |
174 |
return gr_pagesize (); |
175 |
} |
176 |
|
177 |
gr_vmcircbuf * |
178 |
gr_vmcircbuf_mmap_tmpfile_factory::make (int size) |
179 |
{ |
180 |
try { |
181 |
return new gr_vmcircbuf_mmap_tmpfile (size); |
182 |
} |
183 |
catch (...){ |
184 |
return 0; |
185 |
} |
186 |
} |