Imported Upstream version 3.0
[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 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., 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
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     munmap(first_copy, size);                                   // cleanup
125     close (seg_fd);             
126     perror ("gr_vmcircbuf_mmap_tmpfile: mmap (2)");
127     throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile");
128   }
129
130   // check for contiguity
131   if ((char *) second_copy != (char *) first_copy + size){
132     munmap(first_copy, size);                                   // cleanup
133     munmap(second_copy, size);
134     close (seg_fd);                                             
135     perror ("gr_vmcircbuf_mmap_tmpfile: non-contiguous second copy");
136     throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile");
137   }
138
139   // cut the tmp file down to size
140   if (ftruncate (seg_fd, (off_t) size) == -1){
141     munmap(first_copy, size);                                   // cleanup
142     munmap(second_copy, size);
143     close (seg_fd);
144     perror ("gr_vmcircbuf_mmap_tmpfile: ftruncate (2)");
145     throw std::runtime_error ("gr_vmcircbuf_mmap_tmpfile");
146   }
147
148   close (seg_fd);       // fd no longer needed.  The mapping is retained.
149
150   // Now remember the important stuff
151
152   d_base = (char *) first_copy;
153   d_size = size;
154 #endif
155 }
156
157 gr_vmcircbuf_mmap_tmpfile::~gr_vmcircbuf_mmap_tmpfile ()
158 {
159 #if defined(HAVE_MMAP)
160   if (munmap (d_base, 2 * d_size) == -1){
161     perror ("gr_vmcircbuf_mmap_tmpfile: munmap (2)");
162   }
163 #endif
164 }
165
166 // ----------------------------------------------------------------
167 //                      The factory interface
168 // ----------------------------------------------------------------
169
170
171 gr_vmcircbuf_factory *gr_vmcircbuf_mmap_tmpfile_factory::s_the_factory = 0;
172
173 gr_vmcircbuf_factory *
174 gr_vmcircbuf_mmap_tmpfile_factory::singleton ()
175 {
176   if (s_the_factory)
177     return s_the_factory;
178
179   s_the_factory = new gr_vmcircbuf_mmap_tmpfile_factory ();
180   return s_the_factory;
181 }
182
183 int
184 gr_vmcircbuf_mmap_tmpfile_factory::granularity ()
185 {
186   return gr_pagesize ();
187 }
188
189 gr_vmcircbuf *
190 gr_vmcircbuf_mmap_tmpfile_factory::make (int size)
191 {
192   try {
193     return new gr_vmcircbuf_mmap_tmpfile (size);
194   }
195   catch (...){
196     return 0;
197   }
198 }