Imported Upstream version 3.2.2
[debian/gnuradio] / gnuradio-core / src / lib / runtime / gr_vmcircbuf_mmap_tmpfile.cc
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 3, 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., 51 Franklin Street,
20  * Boston, MA 02110-1301, 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 gr_vmcircbuf_mmap_tmpfile::gr_vmcircbuf_mmap_tmpfile (int size)
45   : gr_vmcircbuf (size)
46 {
47 #if !defined(HAVE_MMAP)
48   fprintf (stderr, "gr_vmcircbuf_mmap_tmpfile: mmap or mkstemp is not available\n");
49   throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile");
50 #else
51
52   if (size <= 0 || (size % gr_pagesize ()) != 0){
53     fprintf (stderr, "gr_vmcircbuf_mmap_tmpfile: invalid size = %d\n", size);
54     throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile");
55   }
56
57   int   seg_fd = -1;
58   char  seg_name[1024];
59   
60   static int s_seg_counter = 0;
61
62
63   // open a temporary file that we'll map in a bit later
64
65   while (1){
66     snprintf (seg_name, sizeof (seg_name),
67               "%s/gnuradio-%d-%d-XXXXXX", gr_tmp_path (), getpid (), s_seg_counter);
68     s_seg_counter++;
69
70     seg_fd = open (seg_name, O_RDWR | O_CREAT | O_EXCL, 0600);
71     if (seg_fd == -1){
72       if (errno == EEXIST)      // File already exists (shouldn't happen).  Try again
73         continue;
74
75       char msg[1024];
76       snprintf (msg, sizeof (msg),
77                 "gr_vmcircbuf_mmap_tmpfile: open [%s]", seg_name);
78       perror (msg);
79       throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile");
80     }
81     break;
82   }
83
84   if (unlink (seg_name) == -1){
85     perror ("gr_vmcircbuf_mmap_tmpfile: unlink");
86     throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile");
87   }
88
89   // We've got a valid file descriptor to a tmp file.
90   // Now set it's length to 2x what we really want and mmap it in.
91
92   if (ftruncate (seg_fd, (off_t) 2 * size) == -1){
93     close (seg_fd);                                             // cleanup
94     perror ("gr_vmcircbuf_mmap_tmpfile: ftruncate (1)");
95     throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile");
96   }
97
98   void *first_copy = mmap (0, 2 * size,
99                            PROT_READ | PROT_WRITE, MAP_SHARED,
100                            seg_fd, (off_t) 0);
101
102   if (first_copy == MAP_FAILED){
103     close (seg_fd);                                             // cleanup
104     perror ("gr_vmcircbuf_mmap_tmpfile: mmap (1)");
105     throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile");
106   }
107
108   // unmap the 2nd half
109   if (munmap ((char *) first_copy + size, size) == -1){
110     close (seg_fd);                                             // cleanup
111     perror ("gr_vmcircbuf_mmap_tmpfile: munmap (1)");
112     throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile");
113   }
114
115   // map the first half into the now available hole where the
116   // second half used to be.
117
118   void *second_copy = mmap ((char *) first_copy + size, size,
119                             PROT_READ | PROT_WRITE, MAP_SHARED,
120                             seg_fd, (off_t) 0);
121
122   if (second_copy == MAP_FAILED){
123     munmap(first_copy, size);                                   // cleanup
124     close (seg_fd);             
125     perror ("gr_vmcircbuf_mmap_tmpfile: mmap (2)");
126     throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile");
127   }
128
129   // check for contiguity
130   if ((char *) second_copy != (char *) first_copy + size){
131     munmap(first_copy, size);                                   // cleanup
132     munmap(second_copy, size);
133     close (seg_fd);                                             
134     perror ("gr_vmcircbuf_mmap_tmpfile: non-contiguous second copy");
135     throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile");
136   }
137
138   // cut the tmp file down to size
139   if (ftruncate (seg_fd, (off_t) size) == -1){
140     munmap(first_copy, size);                                   // cleanup
141     munmap(second_copy, size);
142     close (seg_fd);
143     perror ("gr_vmcircbuf_mmap_tmpfile: ftruncate (2)");
144     throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile");
145   }
146
147   close (seg_fd);       // fd no longer needed.  The mapping is retained.
148
149   // Now remember the important stuff
150
151   d_base = (char *) first_copy;
152   d_size = size;
153 #endif
154 }
155
156 gr_vmcircbuf_mmap_tmpfile::~gr_vmcircbuf_mmap_tmpfile ()
157 {
158 #if defined(HAVE_MMAP)
159   if (munmap (d_base, 2 * d_size) == -1){
160     perror ("gr_vmcircbuf_mmap_tmpfile: munmap (2)");
161   }
162 #endif
163 }
164
165 // ----------------------------------------------------------------
166 //                      The factory interface
167 // ----------------------------------------------------------------
168
169
170 gr_vmcircbuf_factory *gr_vmcircbuf_mmap_tmpfile_factory::s_the_factory = 0;
171
172 gr_vmcircbuf_factory *
173 gr_vmcircbuf_mmap_tmpfile_factory::singleton ()
174 {
175   if (s_the_factory)
176     return s_the_factory;
177
178   s_the_factory = new gr_vmcircbuf_mmap_tmpfile_factory ();
179   return s_the_factory;
180 }
181
182 int
183 gr_vmcircbuf_mmap_tmpfile_factory::granularity ()
184 {
185   return gr_pagesize ();
186 }
187
188 gr_vmcircbuf *
189 gr_vmcircbuf_mmap_tmpfile_factory::make (int size)
190 {
191   try {
192     return new gr_vmcircbuf_mmap_tmpfile (size);
193   }
194   catch (...){
195     return 0;
196   }
197 }