LSSTApplications  1.1.2+25,10.0+13,10.0+132,10.0+133,10.0+224,10.0+41,10.0+8,10.0-1-g0f53050+14,10.0-1-g4b7b172+19,10.0-1-g61a5bae+98,10.0-1-g7408a83+3,10.0-1-gc1e0f5a+19,10.0-1-gdb4482e+14,10.0-11-g3947115+2,10.0-12-g8719d8b+2,10.0-15-ga3f480f+1,10.0-2-g4f67435,10.0-2-gcb4bc6c+26,10.0-28-gf7f57a9+1,10.0-3-g1bbe32c+14,10.0-3-g5b46d21,10.0-4-g027f45f+5,10.0-4-g86f66b5+2,10.0-4-gc4fccf3+24,10.0-40-g4349866+2,10.0-5-g766159b,10.0-5-gca2295e+25,10.0-6-g462a451+1
LSSTDataManagementBasePackage
ChunkManager.cc
Go to the documentation of this file.
1 // -*- lsst-c++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2008, 2009, 2010 LSST Corporation.
6  *
7  * This product includes software developed by the
8  * LSST Project (http://www.lsst.org/).
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the LSST License Statement and
21  * the GNU General Public License along with this program. If not,
22  * see <http://www.lsstcorp.org/LegalNotices/>.
23  */
24 
25 
33 #include <sys/mman.h> // for mmap, munmap, shm_open, shm_unlink
34 #include <time.h> // for nanosleep
35 #include <unistd.h> // for ftruncate
36 #include <fcntl.h>
37 #include <errno.h>
38 
39 #include <iostream>
40 
41 #include "boost/bind.hpp"
42 #include "boost/format.hpp"
43 
44 #include "lsst/pex/exceptions.h"
45 
46 #include "lsst/ap/Chunk.cc"
48 #include "lsst/ap/ChunkManager.h"
49 #include "lsst/ap/ScopeGuard.h"
50 #include "lsst/ap/SpatialUtil.h"
51 
52 namespace ex = lsst::pex::exceptions;
53 
54 
55 namespace lsst { namespace ap { namespace detail {
56 
57 // -- Explicit instantiations ----------------
58 
60 
63 template class SubManager<SharedMutex, Object>;
66 
68 
69 } // end of namespace detail
70 
72 template class ChunkRef<detail::ObjAllocator, Object>;
74 
75 
76 namespace detail {
77 
78 // -- Shared memory implementation details ----------------
79 
88 struct BootstrapLock {
89 
90  char const * const _name;
91  int _fd;
92 
93  BootstrapLock(char const * const name);
94  ~BootstrapLock();
95 };
96 
97 
98 BootstrapLock::BootstrapLock(char const * const name) : _name(name), _fd(-1) {
99 
100  assert(name != 0 && name[0] == '/' && "BootstrapLock: illegal name");
101 
102  TimeSpec ts;
103  ts.tv_nsec = 50000; // start off with 50usec sleep time
104  int tries = 22;
105  int fd = 0;
106 
107  while ((fd = ::shm_open(name, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR | S_IRGRP | S_IROTH)) == -1) {
108  if (tries == 0) {
109  throw LSST_EXCEPT(ex::TimeoutError,
110  (boost::format("Unable to obtain shared memory bootstrap lock %1%") % name).str());
111  }
112  ::nanosleep(&ts, 0);
113  ts += ts; // exponential backoff
114  --tries;
115  }
116  _fd = fd;
117 }
118 
119 
121  if (_fd != -1) {
122  ::close(_fd);
123  ::shm_unlink(_name);
124  }
125 }
126 
127 
128 template <typename ManagerT>
129 ManagerT * getSingleton(char const * const shmObjName, char const * const shmLockName) {
130 
131  static Mutex mutex;
132  static ManagerT * singleton = 0;
133 
134  ScopedLock<Mutex> lck(mutex);
135  if (singleton != 0) {
136  return singleton;
137  }
138 
139  // Block interference from other processes
140  BootstrapLock blck(shmLockName);
141 
142  std::size_t const pageSize = static_cast<std::size_t>(::getpagesize());
143  std::size_t const numBytes = (ManagerT::size() + pageSize - 1) & ~(pageSize - 1);
144 
145  // 1. try to create shared memory object
146  int fd = ::shm_open(shmObjName, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
147 
148  if (fd == -1) {
149 
150  // failed ...
151  if (errno != EEXIST) {
152  throw LSST_EXCEPT(ex::RuntimeError,
153  (boost::format("shm_open(): failed to create shared memory object %1%, errno: %2%")
154  % shmObjName % errno).str());
155  }
156  // because the shared memory object already exists -- so try to open existing object instead
157  fd = ::shm_open(shmObjName, O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
158  if (fd == -1) {
159  throw LSST_EXCEPT(ex::RuntimeError,
160  (boost::format("shm_open(): failed to open existing shared memory object %1%, errno: %2%")
161  % shmObjName % errno).str());
162  }
163  ScopeGuard g(boost::bind(::close, fd));
164 
165  void * mem = ::mmap(0, numBytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
166  if (mem == 0) {
167  throw LSST_EXCEPT(ex::RuntimeError,
168  (boost::format("mmap(): failed to map %1% bytes of shared memory object %2%, errno: %3%")
169  % numBytes % shmObjName % errno).str());
170  }
171  singleton = static_cast<ManagerT *>(mem);
172  g.dismiss();
173 
174  } else {
175 
176  // shared memory object was created (initially of zero size)
177  ScopeGuard g1(boost::bind(::shm_unlink, shmObjName));
178  ScopeGuard g2(boost::bind(::close, fd));
179 
180  // set size of shared memory object
181  if (::ftruncate(fd, numBytes) != 0) {
182  throw LSST_EXCEPT(ex::RuntimeError,
183  (boost::format(
184  "ftruncate(): failed to set size of shared memory object %1% to %2% bytes, errno: %3%")
185  % shmObjName % numBytes % errno).str());
186  }
187 
188  // map shared memory object
189  void * mem = ::mmap(0, numBytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
190  if (mem == 0) {
191  throw LSST_EXCEPT(ex::RuntimeError,
192  (boost::format("mmap(): failed to map %1% bytes of shared memory object %2%, errno: %3%")
193  % numBytes % shmObjName % errno).str());
194  }
195  ScopeGuard g3(boost::bind(::munmap, mem, numBytes));
196 
197  new (mem) ManagerT();
198  singleton = static_cast<ManagerT *>(mem);
199  g3.dismiss();
200  g2.dismiss();
201  g1.dismiss();
202  }
203 
204  // Try to lock chunk pages into memory to avoid swapping, but ignore failure to do so
205  // - Solaris : process must be run as root
206  // - Linux/Darwin : process must be run as root or RLIMIT_MEMLOCK should be set to a large value
207  ::mlock(static_cast<void *>(singleton), numBytes);
208 
209  // Note: we rely on the system to munmap and close the file descriptor above at process exit.
210 
211  return singleton;
212 }
213 
214 
215 // Note: GCC does not support setting template instantiation visibility via
216 // __attribute__((visibility)) : use pragma instead
217 #if defined(__GNUC__) && __GNUC__ > 3
218 # pragma GCC visibility push(hidden)
219 #endif
220 template ObjChunkMgr * getSingleton<ObjChunkMgr>(char const * const, char const * const);
223 #if defined(__GNUC__) && __GNUC__ > 3
224 # pragma GCC visibility pop
225 #endif
226 
227 } // end of namespace detail
228 
229 
230 // Warning: for portability reasons, these names should be no more than 14 characters long
231 static char const * const sSharedObjLock = "/ap_shlck";
232 static char const * const sSharedPrefix = "/ap_";
233 
234 
235 // -- SharedObjectChunkManager ----------------
236 
237 SharedObjectChunkManager::SharedObjectChunkManager(std::string const & name) : _manager(instance(name)) {}
238 
239 
241  std::string actualName(sSharedPrefix);
242  actualName += name;
243  return detail::getSingleton<detail::ObjChunkMgr>(actualName.c_str(), sSharedObjLock);
244 }
245 
246 
251 void SharedObjectChunkManager::destroyInstance(std::string const & name) {
252  std::string actualName(sSharedPrefix);
253  actualName += name;
254  int res = ::shm_unlink(actualName.c_str());
255  // Note: shm_unlink is broken on Mac OSX 10.4 - in violation of the documentation and standard,
256  // EINVAL (rather than ENOENT) is returned when trying to shm_unlink a non-existant shared
257  // memory object.
258  if (res != 0 && errno != ENOENT && errno != EINVAL) {
259  throw LSST_EXCEPT(ex::RuntimeError,
260  (boost::format("shm_unlink(): failed to unlink shared memory object %1%, errno: %2%")
261  % name % errno).str());
262  }
263 }
264 
265 
268 
269 
270 }} // end of namespace lsst::ap
ManagerT * getSingleton(char const *const shmObjName, char const *const shmLockName)
Class for managing chunks of Object instances in shared memory.
A manager for a set of chunks of a single type.
Wraps the C library timespec struct.
Definition: Time.h:48
Helper class for managing chunks of a particular type.
std::string const & _name
Definition: Mask.cc:677
Utility class for automatically invoking a function when leaving a scope.
Definition: ScopeGuard.h:51
SharedObjectChunkManager(std::string const &name)
A thread-safe memory block allocator that uses a Bitset to track which blocks (out of a fixed size po...
ChunkManagerImpl< SharedMutex, Object > ObjChunkMgr
Definition: ChunkManager.cc:67
static std::size_t size()
Returns the size in bytes of the underlying chunk manager and pool of memory blocks.
Mutual exclusion lock suitable for initializing shared memory objects.
Definition: ChunkManager.cc:88
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
A wrapper for a process private POSIX mutual exclusion lock.
Definition: Mutex.h:56
Chunk manager helper class implementations.
BlockAllocator< SharedMutex, Object > ObjAllocator
Definition: ChunkManager.cc:59
static void destroyInstance(std::string const &name)
Chunk class implementation.
static Manager * instance(std::string const &name)
Grants access to a mutex, enforcing the RAII principle.
Definition: Mutex.h:51
Include files required for standard LSST Exception handling.
Class and helper functions related to spatial partitioning.
Utility class for automatically invoking a function when leaving a scope.