]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ipc/mem/Segment.cc
Add base class for registered runners that work with shared memory.
[thirdparty/squid.git] / src / ipc / mem / Segment.cc
1 /*
2 * $Id$
3 *
4 * DEBUG: section 54 Interprocess Communication
5 *
6 */
7
8 #include "config.h"
9 #include "base/TextException.h"
10 #include "compat/shm.h"
11 #include "ipc/mem/Segment.h"
12 #include "protos.h"
13
14 #include <fcntl.h>
15 #include <sys/mman.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18
19 void *
20 Ipc::Mem::Segment::reserve(size_t chunkSize)
21 {
22 Must(theMem);
23 // check for overflows
24 // chunkSize >= 0 may result in warnings on systems where off_t is unsigned
25 assert(!chunkSize || static_cast<off_t>(chunkSize) > 0);
26 assert(static_cast<off_t>(chunkSize) <= theSize);
27 assert(theReserved <= theSize - static_cast<off_t>(chunkSize));
28 void *result = reinterpret_cast<char*>(theMem) + theReserved;
29 theReserved += chunkSize;
30 return result;
31 }
32
33 #if HAVE_SHM
34
35 Ipc::Mem::Segment::Segment(const char *const id):
36 theFD(-1), theName(GenerateName(id)), theMem(NULL),
37 theSize(0), theReserved(0), doUnlink(false)
38 {
39 }
40
41 Ipc::Mem::Segment::~Segment()
42 {
43 if (theFD >= 0) {
44 detach();
45 if (close(theFD) != 0)
46 debugs(54, 5, HERE << "close " << theName << ": " << xstrerror());
47 }
48 if (doUnlink)
49 unlink();
50 }
51
52 // fake Ipc::Mem::Segment::Enabled (!HAVE_SHM) is more selective
53 bool
54 Ipc::Mem::Segment::Enabled()
55 {
56 return true;
57 }
58
59 void
60 Ipc::Mem::Segment::create(const off_t aSize)
61 {
62 assert(aSize > 0);
63 assert(theFD < 0);
64
65 theFD = shm_open(theName.termedBuf(), O_CREAT | O_RDWR | O_TRUNC,
66 S_IRUSR | S_IWUSR);
67 if (theFD < 0) {
68 debugs(54, 5, HERE << "shm_open " << theName << ": " << xstrerror());
69 fatal("Ipc::Mem::Segment::create failed to shm_open");
70 }
71
72 if (ftruncate(theFD, aSize)) {
73 debugs(54, 5, HERE << "ftruncate " << theName << ": " << xstrerror());
74 fatal("Ipc::Mem::Segment::create failed to ftruncate");
75 }
76
77 assert(statSize("Ipc::Mem::Segment::create") == aSize); // paranoid
78
79 theSize = aSize;
80 theReserved = 0;
81 doUnlink = true;
82
83 debugs(54, 3, HERE << "created " << theName << " segment: " << theSize);
84
85 attach();
86 }
87
88 void
89 Ipc::Mem::Segment::open()
90 {
91 assert(theFD < 0);
92
93 theFD = shm_open(theName.termedBuf(), O_RDWR, 0);
94 if (theFD < 0) {
95 debugs(54, 5, HERE << "shm_open " << theName << ": " << xstrerror());
96 String s = "Ipc::Mem::Segment::open failed to shm_open ";
97 s.append(theName);
98 fatal(s.termedBuf());
99 }
100
101 theSize = statSize("Ipc::Mem::Segment::open");
102
103 debugs(54, 3, HERE << "opened " << theName << " segment: " << theSize);
104
105 attach();
106 }
107
108 /// Map the shared memory segment to the process memory space.
109 void
110 Ipc::Mem::Segment::attach()
111 {
112 assert(theFD >= 0);
113 assert(!theMem);
114
115 // mmap() accepts size_t for the size; we give it off_t which might
116 // be bigger; assert overflows until we support multiple mmap()s?
117 assert(theSize == static_cast<off_t>(static_cast<size_t>(theSize)));
118
119 void *const p =
120 mmap(NULL, theSize, PROT_READ | PROT_WRITE, MAP_SHARED, theFD, 0);
121 if (p == MAP_FAILED) {
122 debugs(54, 5, HERE << "mmap " << theName << ": " << xstrerror());
123 fatal("Ipc::Mem::Segment::attach failed to mmap");
124 }
125 theMem = p;
126 }
127
128 /// Unmap the shared memory segment from the process memory space.
129 void
130 Ipc::Mem::Segment::detach()
131 {
132 if (!theMem)
133 return;
134
135 if (munmap(theMem, theSize)) {
136 debugs(54, 5, HERE << "munmap " << theName << ": " << xstrerror());
137 fatal("Ipc::Mem::Segment::detach failed to munmap");
138 }
139 theMem = 0;
140 }
141
142 void
143 Ipc::Mem::Segment::unlink()
144 {
145 if (shm_unlink(theName.termedBuf()) != 0)
146 debugs(54, 5, HERE << "shm_unlink(" << theName << "): " << xstrerror());
147 else
148 debugs(54, 3, HERE << "unlinked " << theName << " segment");
149 }
150
151 /// determines the size of the underlying "file"
152 off_t
153 Ipc::Mem::Segment::statSize(const char *context) const
154 {
155 Must(theFD >= 0);
156
157 struct stat s;
158 memset(&s, 0, sizeof(s));
159
160 if (fstat(theFD, &s) != 0) {
161 debugs(54, 5, HERE << "fstat " << theName << ": " << xstrerror());
162 String s = context;
163 s.append("failed to fstat(2) ");
164 s.append(theName);
165 fatal(s.termedBuf());
166 }
167
168 return s.st_size;
169 }
170
171 /// Generate name for shared memory segment. Replaces all slashes with dots.
172 String
173 Ipc::Mem::Segment::GenerateName(const char *id)
174 {
175 String name("/squid-");
176 for (const char *slash = strchr(id, '/'); slash; slash = strchr(id, '/')) {
177 if (id != slash) {
178 name.append(id, slash - id);
179 name.append('.');
180 }
181 id = slash + 1;
182 }
183 name.append(id);
184 return name;
185 }
186
187 #else // HAVE_SHM
188
189 #include <map>
190
191 typedef std::map<String, Ipc::Mem::Segment *> SegmentMap;
192 static SegmentMap Segments;
193
194 Ipc::Mem::Segment::Segment(const char *const id):
195 theName(id), theMem(NULL), theSize(0), theReserved(0), doUnlink(false)
196 {
197 }
198
199 Ipc::Mem::Segment::~Segment()
200 {
201 if (doUnlink) {
202 delete [] static_cast<char *>(theMem);
203 debugs(54, 3, HERE << "deleted " << theName << " segment");
204 }
205 }
206
207 bool
208 Ipc::Mem::Segment::Enabled()
209 {
210 return !InDaemonMode() || (!UsingSmp() && IamWorkerProcess());
211 }
212
213 void
214 Ipc::Mem::Segment::create(const off_t aSize)
215 {
216 assert(aSize > 0);
217 assert(!theMem);
218 checkSupport("Fake segment creation");
219
220 const bool inserted = Segments.insert(std::make_pair(theName, this)).second;
221 if (!inserted)
222 fatalf("Duplicate fake segment creation: %s", theName.termedBuf());
223
224 theMem = new char[aSize];
225 theSize = aSize;
226 doUnlink = true;
227
228 debugs(54, 3, HERE << "created " << theName << " fake segment: " << theSize);
229 }
230
231 void
232 Ipc::Mem::Segment::open()
233 {
234 assert(!theMem);
235 checkSupport("Fake segment open");
236
237 const SegmentMap::const_iterator i = Segments.find(theName);
238 if (i == Segments.end())
239 fatalf("Fake segment not found: %s", theName.termedBuf());
240
241 const Segment &segment = *i->second;
242 theMem = segment.theMem;
243 theSize = segment.theSize;
244
245 debugs(54, 3, HERE << "opened " << theName << " fake segment: " << theSize);
246 }
247
248 void
249 Ipc::Mem::Segment::checkSupport(const char *const context)
250 {
251 if (!Enabled()) {
252 debugs(54, 5, HERE << "True shared memory segments are not supported. "
253 "Cannot fake shared segments in SMP config.");
254 fatalf("%s failed", context);
255 }
256 }
257
258 #endif // HAVE_SHM
259
260 void
261 Ipc::Mem::RegisteredRunner::run(const RunnerRegistry &r)
262 {
263 // If Squid is built with real segments, we create() real segments
264 // in the master process only. Otherwise, we create() fake
265 // segments in each worker process. We assume that only workers
266 // need and can work with fake segments.
267 #if HAVE_SHM
268 if (IamMasterProcess())
269 #else
270 if (IamWorker())
271 #endif
272 create(r);
273
274 // we assume that master process does not need shared segments
275 // unless it is also a worker
276 if (!InDaemonMode() || !IamMasterProcess())
277 open(r);
278 }