Imported Upstream version 3.0
[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     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
72   }
73
74   if ((shmid1 = shmget (IPC_PRIVATE, size, IPC_CREAT | 0700)) == -1){
75     perror ("gr_vmcircbuf_sysv_shm: shmget (2)");
76     shmctl (shmid2, IPC_RMID, 0);
77     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
78   }
79
80   void *first_copy = shmat (shmid2, 0, 0);
81   if (first_copy == (void *) -1){
82     perror ("gr_vmcircbuf_sysv_shm: shmat (1)");
83     shmctl (shmid_guard, IPC_RMID, 0);
84     shmctl (shmid2, IPC_RMID, 0);
85     shmctl (shmid1, IPC_RMID, 0);
86     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
87   }
88
89   shmctl (shmid2, IPC_RMID, 0);
90
91   // There may be a race between our detach and attach.
92   //
93   // If the system allocates all shared memory segments at the same
94   // virtual addresses in all processes and if the system allocates
95   // some other segment to first_copy or first_copoy + size between
96   // our detach and attach, the attaches below could fail [I've never
97   // seen it fail for this reason].
98   
99   shmdt (first_copy);
100
101   // first read-only guard page
102   if (shmat (shmid_guard, first_copy, SHM_RDONLY) == (void *) -1){
103     perror ("gr_vmcircbuf_sysv_shm: shmat (2)");
104     shmctl (shmid_guard, IPC_RMID, 0);
105     shmctl (shmid1, IPC_RMID, 0);
106     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
107   }
108
109   // first copy
110   if (shmat (shmid1, (char *) first_copy + pagesize, 0) == (void *) -1){
111     perror ("gr_vmcircbuf_sysv_shm: shmat (3)");
112     shmctl (shmid_guard, IPC_RMID, 0);
113     shmctl (shmid1, IPC_RMID, 0);
114     shmdt (first_copy);
115     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
116   }
117
118   // second copy
119   if (shmat (shmid1, (char *) first_copy + pagesize + size, 0) == (void *) -1){
120     perror ("gr_vmcircbuf_sysv_shm: shmat (4)");
121     shmctl (shmid_guard, IPC_RMID, 0);
122     shmctl (shmid1, IPC_RMID, 0);
123     shmdt ((char *)first_copy + pagesize);
124     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
125   }
126
127   // second read-only guard page
128   if (shmat (shmid_guard, (char *) first_copy + pagesize + 2 * size, SHM_RDONLY) == (void *) -1){
129     perror ("gr_vmcircbuf_sysv_shm: shmat (5)");
130     shmctl (shmid_guard, IPC_RMID, 0);
131     shmctl (shmid1, IPC_RMID, 0);
132     shmdt (first_copy);
133     shmdt ((char *)first_copy + pagesize);
134     shmdt ((char *)first_copy + pagesize + size);
135     throw std::runtime_error ("gr_vmcircbuf_sysv_shm");
136   }
137
138   shmctl (shmid1, IPC_RMID, 0);
139   shmctl (shmid_guard, IPC_RMID, 0);
140
141   // Now remember the important stuff
142
143   d_base = (char *) first_copy + pagesize;
144   d_size = size;
145 #endif
146 }
147
148 gr_vmcircbuf_sysv_shm::~gr_vmcircbuf_sysv_shm ()
149 {
150 #if defined(HAVE_SYS_SHM_H)
151   if (shmdt (d_base - gr_pagesize()) == -1
152       || shmdt (d_base) == -1
153       || shmdt (d_base + d_size) == -1
154       || shmdt (d_base + 2 * d_size) == -1){
155     perror ("gr_vmcircbuf_sysv_shm: shmdt (2)");
156   }
157 #endif
158 }
159
160 // ----------------------------------------------------------------
161 //                      The factory interface
162 // ----------------------------------------------------------------
163
164
165 gr_vmcircbuf_factory *gr_vmcircbuf_sysv_shm_factory::s_the_factory = 0;
166
167 gr_vmcircbuf_factory *
168 gr_vmcircbuf_sysv_shm_factory::singleton ()
169 {
170   if (s_the_factory)
171     return s_the_factory;
172
173   s_the_factory = new gr_vmcircbuf_sysv_shm_factory ();
174   return s_the_factory;
175 }
176
177 int
178 gr_vmcircbuf_sysv_shm_factory::granularity ()
179 {
180   return gr_pagesize ();
181 }
182
183 gr_vmcircbuf *
184 gr_vmcircbuf_sysv_shm_factory::make (int size)
185 {
186   try {
187     return new gr_vmcircbuf_sysv_shm (size);
188   }
189   catch (...){
190     return 0;
191   }
192 }