Imported Upstream version 3.0.3
[debian/gnuradio] / gnuradio-core / src / lib / runtime / gr_vmcircbuf_sysv_shm.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_sysv_shm.h>
27 #include <stdexcept>
28 #include <assert.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <fcntl.h>
32 #ifdef HAVE_SYS_IPC_H
33 #include <sys/ipc.h>
34 #endif
35 #ifdef HAVE_SYS_SHM_H
36 #include <sys/shm.h>
37 #endif
38 #include <errno.h>
39 #include <stdio.h>
40 #include <gr_pagesize.h>
41
42
43 gr_vmcircbuf_sysv_shm::gr_vmcircbuf_sysv_shm (int size)
44   : gr_vmcircbuf (size)
45 {
46 #if !defined(HAVE_SYS_SHM_H)
47   fprintf (stderr, "gr_vmcircbuf_sysv_shm: sysv shared memory is not available\n");
48   throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
49 #else
50
51   int pagesize = gr_pagesize();
52
53   if (size <= 0 || (size % pagesize) != 0){
54     fprintf (stderr, "gr_vmcircbuf_sysv_shm: invalid size = %d\n", size);
55     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
56   }
57
58   int shmid_guard = -1;
59   int shmid1 = -1;
60   int shmid2 = -1;
61
62   // We use this as a guard page.  We'll map it read-only on both ends of the buffer.
63   // Ideally we'd map it no access, but I don't think that's possible with SysV
64   if ((shmid_guard = shmget (IPC_PRIVATE, pagesize, IPC_CREAT | 0400)) == -1){
65     perror ("gr_vmcircbuf_sysv_shm: shmget (0)");
66     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
67   }
68
69   if ((shmid2 = shmget (IPC_PRIVATE, 2 * size + 2 * pagesize, IPC_CREAT | 0700)) == -1){
70     perror ("gr_vmcircbuf_sysv_shm: shmget (1)");
71     shmctl (shmid_guard, IPC_RMID, 0);
72     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
73   }
74
75   if ((shmid1 = shmget (IPC_PRIVATE, size, IPC_CREAT | 0700)) == -1){
76     perror ("gr_vmcircbuf_sysv_shm: shmget (2)");
77     shmctl (shmid_guard, IPC_RMID, 0);
78     shmctl (shmid2, IPC_RMID, 0);
79     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
80   }
81
82   void *first_copy = shmat (shmid2, 0, 0);
83   if (first_copy == (void *) -1){
84     perror ("gr_vmcircbuf_sysv_shm: shmat (1)");
85     shmctl (shmid_guard, IPC_RMID, 0);
86     shmctl (shmid2, IPC_RMID, 0);
87     shmctl (shmid1, IPC_RMID, 0);
88     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
89   }
90
91   shmctl (shmid2, IPC_RMID, 0);
92
93   // There may be a race between our detach and attach.
94   //
95   // If the system allocates all shared memory segments at the same
96   // virtual addresses in all processes and if the system allocates
97   // some other segment to first_copy or first_copoy + size between
98   // our detach and attach, the attaches below could fail [I've never
99   // seen it fail for this reason].
100   
101   shmdt (first_copy);
102
103   // first read-only guard page
104   if (shmat (shmid_guard, first_copy, SHM_RDONLY) == (void *) -1){
105     perror ("gr_vmcircbuf_sysv_shm: shmat (2)");
106     shmctl (shmid_guard, IPC_RMID, 0);
107     shmctl (shmid1, IPC_RMID, 0);
108     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
109   }
110
111   // first copy
112   if (shmat (shmid1, (char *) first_copy + pagesize, 0) == (void *) -1){
113     perror ("gr_vmcircbuf_sysv_shm: shmat (3)");
114     shmctl (shmid_guard, IPC_RMID, 0);
115     shmctl (shmid1, IPC_RMID, 0);
116     shmdt (first_copy);
117     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
118   }
119
120   // second copy
121   if (shmat (shmid1, (char *) first_copy + pagesize + size, 0) == (void *) -1){
122     perror ("gr_vmcircbuf_sysv_shm: shmat (4)");
123     shmctl (shmid_guard, IPC_RMID, 0);
124     shmctl (shmid1, IPC_RMID, 0);
125     shmdt ((char *)first_copy + pagesize);
126     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
127   }
128
129   // second read-only guard page
130   if (shmat (shmid_guard, (char *) first_copy + pagesize + 2 * size, SHM_RDONLY) == (void *) -1){
131     perror ("gr_vmcircbuf_sysv_shm: shmat (5)");
132     shmctl (shmid_guard, IPC_RMID, 0);
133     shmctl (shmid1, IPC_RMID, 0);
134     shmdt (first_copy);
135     shmdt ((char *)first_copy + pagesize);
136     shmdt ((char *)first_copy + pagesize + size);
137     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
138   }
139
140   shmctl (shmid1, IPC_RMID, 0);
141   shmctl (shmid_guard, IPC_RMID, 0);
142
143   // Now remember the important stuff
144
145   d_base = (char *) first_copy + pagesize;
146   d_size = size;
147 #endif
148 }
149
150 gr_vmcircbuf_sysv_shm::~gr_vmcircbuf_sysv_shm ()
151 {
152 #if defined(HAVE_SYS_SHM_H)
153   if (shmdt (d_base - gr_pagesize()) == -1
154       || shmdt (d_base) == -1
155       || shmdt (d_base + d_size) == -1
156       || shmdt (d_base + 2 * d_size) == -1){
157     perror ("gr_vmcircbuf_sysv_shm: shmdt (2)");
158   }
159 #endif
160 }
161
162 // ----------------------------------------------------------------
163 //                      The factory interface
164 // ----------------------------------------------------------------
165
166
167 gr_vmcircbuf_factory *gr_vmcircbuf_sysv_shm_factory::s_the_factory = 0;
168
169 gr_vmcircbuf_factory *
170 gr_vmcircbuf_sysv_shm_factory::singleton ()
171 {
172   if (s_the_factory)
173     return s_the_factory;
174
175   s_the_factory = new gr_vmcircbuf_sysv_shm_factory ();
176   return s_the_factory;
177 }
178
179 int
180 gr_vmcircbuf_sysv_shm_factory::granularity ()
181 {
182   return gr_pagesize ();
183 }
184
185 gr_vmcircbuf *
186 gr_vmcircbuf_sysv_shm_factory::make (int size)
187 {
188   try {
189     return new gr_vmcircbuf_sysv_shm (size);
190   }
191   catch (...){
192     return 0;
193   }
194 }