00001 #define TRACE_NAME "SharedMemoryManager"
00002 #include <cstring>
00003 #include <vector>
00004 #include <sys/ipc.h>
00005 #include <sys/shm.h>
00006 #ifndef SHM_DEST // Lynn reports that this is missing on Mac OS X?!?
00007 #define SHM_DEST 01000
00008 #endif
00009 #include "tracemf.h"
00010 #include <signal.h>
00011 #include "cetlib_except/exception.h"
00012 #include "artdaq-core/Core/SharedMemoryManager.hh"
00013 #include "artdaq-core/Utilities/TraceLock.hh"
00014
00015 #define TLVL_DETACH 11
00016
00017 static std::vector<artdaq::SharedMemoryManager const*> instances = std::vector<artdaq::SharedMemoryManager const*>();
00018
00019 static std::unordered_map<int, struct sigaction> old_actions = std::unordered_map<int, struct sigaction>();
00020 static bool sighandler_init = false;
00021 static void signal_handler(int signum)
00022 {
00023
00024 TRACE_STREAMER(TLVL_ERROR, &("SharedMemoryManager")[0], 0, 0, 0) << "A signal of type " << signum << " (" << std::string(strsignal(signum)) << ") was caught by SharedMemoryManager. Detaching all Shared Memory segments, then proceeding with default handlers!";
00025 for (auto ii : instances)
00026 {
00027
00028 if (ii)
00029 {
00030 const_cast<artdaq::SharedMemoryManager*>(ii)->Detach(false, "", "", false);
00031 }
00032 ii = nullptr;
00033 }
00034
00035 sigset_t set;
00036 pthread_sigmask(SIG_UNBLOCK, NULL, &set);
00037 pthread_sigmask(SIG_UNBLOCK, &set, NULL);
00038
00039 TRACE_STREAMER(TLVL_ERROR, &("SharedMemoryManager")[0], 0, 0, 0) << "Calling default signal handler";
00040 if (signum != SIGUSR2)
00041 {
00042 sigaction(signum, &old_actions[signum], NULL);
00043 kill(getpid(), signum);
00044 }
00045 else
00046 {
00047
00048 sigaction(SIGINT, &old_actions[SIGINT], NULL);
00049 kill(getpid(), SIGINT);
00050 }
00051 }
00052
00053 artdaq::SharedMemoryManager::SharedMemoryManager(uint32_t shm_key, size_t buffer_count, size_t buffer_size, uint64_t buffer_timeout_us, bool destructive_read_mode)
00054 : shm_segment_id_(-1)
00055 , shm_ptr_(NULL)
00056 , shm_key_(shm_key)
00057 , manager_id_(-1)
00058 , buffer_mutexes_()
00059 , last_seen_id_(0)
00060 {
00061 requested_shm_parameters_.buffer_count = buffer_count;
00062 requested_shm_parameters_.buffer_size = buffer_size;
00063 requested_shm_parameters_.buffer_timeout_us = buffer_timeout_us;
00064 requested_shm_parameters_.destructive_read_mode = destructive_read_mode;
00065
00066 instances.push_back(this);
00067 Attach();
00068
00069 static std::mutex sighandler_mutex;
00070 std::unique_lock<std::mutex> lk(sighandler_mutex);
00071
00072 if (!sighandler_init)
00073 {
00074 sighandler_init = true;
00075 std::vector<int> signals = { SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, SIGUSR2 };
00076 for (auto signal : signals)
00077 {
00078 struct sigaction old_action;
00079 sigaction(signal, NULL, &old_action);
00080
00081
00082
00083 if (old_action.sa_handler != SIG_IGN)
00084 {
00085 struct sigaction action;
00086 action.sa_handler = signal_handler;
00087 sigemptyset(&action.sa_mask);
00088 for (auto sigblk : signals)
00089 {
00090 sigaddset(&action.sa_mask, sigblk);
00091 }
00092 action.sa_flags = 0;
00093
00094
00095 sigaction(signal, &action, NULL);
00096 old_actions[signal] = old_action;
00097 }
00098 }
00099 }
00100 }
00101
00102 artdaq::SharedMemoryManager::~SharedMemoryManager() noexcept
00103 {
00104 TLOG(TLVL_DEBUG) << "~SharedMemoryManager called";
00105 Detach();
00106 TLOG(TLVL_DEBUG) << "~SharedMemoryManager done";
00107 }
00108
00109 void artdaq::SharedMemoryManager::Attach()
00110 {
00111 if (IsValid())
00112 {
00113 if (manager_id_ == 0) return;
00114 Detach();
00115 }
00116 auto start_time = std::chrono::steady_clock::now();
00117 last_seen_id_ = 0;
00118 size_t shmSize = requested_shm_parameters_.buffer_count * (requested_shm_parameters_.buffer_size + sizeof(ShmBuffer)) + sizeof(ShmStruct);
00119
00120 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
00121 if (shm_segment_id_ == -1 && requested_shm_parameters_.buffer_count > 0 && manager_id_ <= 0)
00122 {
00123 TLOG(TLVL_DEBUG) << "Creating shared memory segment with key 0x" << std::hex << shm_key_ << " and size " << shmSize;
00124 shm_segment_id_ = shmget(shm_key_, shmSize, IPC_CREAT | 0666);
00125 manager_id_ = 0;
00126
00127 if (shm_segment_id_ == -1)
00128 {
00129 TLOG(TLVL_ERROR) << "Error creating shared memory segment with key 0x" << std::hex << shm_key_ << ", errno=" << errno << " (" << strerror(errno) << ")";
00130 }
00131 }
00132 else
00133 {
00134 while (shm_segment_id_ == -1 && TimeUtils::GetElapsedTimeMilliseconds(start_time) < 1000)
00135 {
00136 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
00137
00138 }
00139 }
00140 TLOG(TLVL_DEBUG) << "shm_key == 0x" << std::hex << shm_key_ << ", shm_segment_id == " << shm_segment_id_;
00141
00142 if (shm_segment_id_ > -1)
00143 {
00144 TLOG(TLVL_DEBUG)
00145 << "Attached to shared memory segment with ID = " << shm_segment_id_
00146 << " and size " << shmSize
00147 << " bytes";
00148 shm_ptr_ = (ShmStruct*)shmat(shm_segment_id_, 0, 0);
00149 TLOG(TLVL_DEBUG)
00150 << "Attached to shared memory segment at address "
00151 << std::hex << (void*)shm_ptr_ << std::dec;
00152 if (shm_ptr_ && shm_ptr_ != (void *)-1)
00153 {
00154 if (manager_id_ == 0)
00155 {
00156 if (shm_ptr_->ready_magic == 0xCAFE1111)
00157 {
00158 TLOG(TLVL_ERROR) << "Owner encountered already-initialized Shared Memory!";
00159 exit(-2);
00160 }
00161 TLOG(TLVL_DEBUG) << "Owner initializing Shared Memory";
00162 shm_ptr_->next_id = 1;
00163 shm_ptr_->next_sequence_id = 0;
00164 shm_ptr_->reader_pos = 0;
00165 shm_ptr_->writer_pos = 0;
00166 shm_ptr_->buffer_size = requested_shm_parameters_.buffer_size;
00167 shm_ptr_->buffer_count = requested_shm_parameters_.buffer_count;
00168 shm_ptr_->buffer_timeout_us = requested_shm_parameters_.buffer_timeout_us;
00169 shm_ptr_->destructive_read_mode = requested_shm_parameters_.destructive_read_mode;
00170
00171 for (int ii = 0; ii < static_cast<int>(requested_shm_parameters_.buffer_count); ++ii)
00172 {
00173 getBufferInfo_(ii)->writePos = 0;
00174 getBufferInfo_(ii)->readPos = 0;
00175 getBufferInfo_(ii)->sem = BufferSemaphoreFlags::Empty;
00176 getBufferInfo_(ii)->sem_id = -1;
00177 getBufferInfo_(ii)->last_touch_time = TimeUtils::gettimeofday_us();
00178 }
00179
00180 shm_ptr_->ready_magic = 0xCAFE1111;
00181 }
00182 else
00183 {
00184 TLOG(TLVL_DEBUG) << "Waiting for owner to initalize Shared Memory";
00185 while (shm_ptr_->ready_magic != 0xCAFE1111) { usleep(1000); }
00186 TLOG(TLVL_DEBUG) << "Getting ID from Shared Memory";
00187 GetNewId();
00188 shm_ptr_->lowest_seq_id_read = 0;
00189 TLOG(TLVL_DEBUG) << "Getting Shared Memory Size parameters";
00190 }
00191
00192 TLOG(TLVL_DEBUG) << "Initialization Complete: "
00193 << "key: 0x" << std::hex << shm_key_
00194 << ", manager ID: " << manager_id_
00195 << ", Buffer size: " << shm_ptr_->buffer_size
00196 << ", Buffer count: " << shm_ptr_->buffer_count;
00197 return;
00198 }
00199 else
00200 {
00201 TLOG(TLVL_ERROR) << "Failed to attach to shared memory segment "
00202 << shm_segment_id_;
00203 }
00204 }
00205 else
00206 {
00207 TLOG(TLVL_ERROR) << "Failed to connect to shared memory segment with key 0x" << std::hex << shm_key_
00208 << ", errno = " << strerror(errno) << ". Please check "
00209 << "if a stale shared memory segment needs to "
00210 << "be cleaned up. (ipcs, ipcrm -m <segId>)";
00211 }
00212 return;
00213 }
00214
00215 int artdaq::SharedMemoryManager::GetBufferForReading()
00216 {
00217 TLOG(13) << "GetBufferForReading BEGIN";
00218
00219 std::unique_lock<std::mutex> lk(search_mutex_);
00220
00221 auto rp = shm_ptr_->reader_pos.load();
00222
00223 TLOG(13) << "GetBufferForReading lock acquired, scanning buffers";
00224 bool retry = true;
00225 int buffer_num = -1;
00226 while (retry)
00227 {
00228 ShmBuffer* buffer_ptr = nullptr;
00229 uint64_t seqID = -1;
00230 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
00231 {
00232 auto buffer = (ii + rp) % shm_ptr_->buffer_count;
00233
00234
00235 TLOG(14) << "GetBufferForReading Checking if buffer " << buffer << " is stale";
00236 ResetBuffer(buffer);
00237
00238 auto buf = getBufferInfo_(buffer);
00239 TLOG(14) << "GetBufferForReading: Buffer " << buffer << ": sem=" << FlagToString(buf->sem) << " (expected " << FlagToString(BufferSemaphoreFlags::Full) << "), sem_id=" << buf->sem_id << " )";
00240 if (buf->sem == BufferSemaphoreFlags::Full && (buf->sem_id == -1 || buf->sem_id == manager_id_) && buf->sequence_id > last_seen_id_)
00241 {
00242 if (buf->sequence_id < seqID)
00243 {
00244 buffer_ptr = buf;
00245 seqID = buf->sequence_id;
00246 buffer_num = buffer;
00247 }
00248 }
00249 }
00250
00251 if (!buffer_ptr || (buffer_ptr && buffer_ptr->sem_id != -1 && buffer_ptr->sem_id != manager_id_))
00252 {
00253 continue;
00254 }
00255
00256 if (buffer_num >= 0)
00257 {
00258 TLOG(13) << "GetBufferForReading Found buffer " << buffer_num;
00259 buffer_ptr->sem_id = manager_id_;
00260 buffer_ptr->sem = BufferSemaphoreFlags::Reading;
00261 if (buffer_ptr->sem_id != manager_id_) { continue; }
00262 buffer_ptr->readPos = 0;
00263 touchBuffer_(buffer_ptr);
00264 if (buffer_ptr->sem_id != manager_id_) { continue; }
00265 if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_)
00266 {
00267 shm_ptr_->lowest_seq_id_read = seqID;
00268 }
00269 last_seen_id_ = seqID;
00270 if (shm_ptr_->destructive_read_mode) shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count;
00271 }
00272 retry = false;
00273 }
00274
00275 TLOG(13) << "GetBufferForReading returning -1 because no buffers are ready";
00276 return buffer_num;
00277 }
00278
00279 int artdaq::SharedMemoryManager::GetBufferForWriting(bool overwrite)
00280 {
00281 TLOG(14) << "GetBufferForWriting BEGIN";
00282 std::unique_lock<std::mutex> lk(search_mutex_);
00283
00284 auto wp = shm_ptr_->writer_pos.load();
00285
00286
00287 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
00288 {
00289 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
00290
00291 ResetBuffer(buffer);
00292
00293 auto buf = getBufferInfo_(buffer);
00294 if (buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1)
00295 {
00296 buf->sem_id = manager_id_;
00297 buf->sem = BufferSemaphoreFlags::Writing;
00298 touchBuffer_(buf);
00299 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
00300 if (buf->sem_id != manager_id_) continue;
00301 buf->sequence_id = ++shm_ptr_->next_sequence_id;
00302 buf->writePos = 0;
00303 TLOG(14) << "GetBufferForWriting returning " << buffer;
00304 return buffer;
00305 }
00306 }
00307
00308 if (overwrite)
00309 {
00310
00311 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
00312 {
00313 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
00314
00315 ResetBuffer(buffer);
00316
00317 auto buf = getBufferInfo_(buffer);
00318 if (buf->sem == BufferSemaphoreFlags::Full)
00319 {
00320 buf->sem_id = manager_id_;
00321 buf->sem = BufferSemaphoreFlags::Writing;
00322 if (buf->sem_id != manager_id_) continue;
00323 buf->sequence_id = ++shm_ptr_->next_sequence_id;
00324 buf->writePos = 0;
00325 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
00326 touchBuffer_(buf);
00327 TLOG(14) << "GetBufferForWriting returning " << buffer;
00328 return buffer;
00329 }
00330 }
00331
00332
00333 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
00334 {
00335 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
00336
00337 ResetBuffer(buffer);
00338
00339 auto buf = getBufferInfo_(buffer);
00340 if (buf->sem == BufferSemaphoreFlags::Reading)
00341 {
00342 buf->sem_id = manager_id_;
00343 buf->sem = BufferSemaphoreFlags::Writing;
00344 if (buf->sem_id != manager_id_) continue;
00345 buf->sequence_id = ++shm_ptr_->next_sequence_id;
00346 buf->writePos = 0;
00347 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
00348 TLOG(14) << "GetBufferForWriting returning " << buffer;
00349 touchBuffer_(buf);
00350 return buffer;
00351 }
00352 }
00353 }
00354
00355 TLOG(14) << "GetBufferForWriting Returning -1 because no buffers are ready";
00356 return -1;
00357 }
00358
00359 size_t artdaq::SharedMemoryManager::ReadReadyCount()
00360 {
00361 if (!IsValid()) return 0;
00362 TLOG(23) << "0x" << std::hex << shm_key_ << " ReadReadyCount BEGIN";
00363 std::unique_lock<std::mutex> lk(search_mutex_);
00364
00365 size_t count = 0;
00366 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
00367 {
00368 TLOG(24) << "0x" << std::hex << shm_key_ << std::dec << " ReadReadyCount: Checking if buffer " << ii << " is stale.";
00369 ResetBuffer(ii);
00370 auto buf = getBufferInfo_(ii);
00371 TLOG(25) << "0x" << std::hex << shm_key_ << std::dec << " ReadReadyCount: Buffer " << ii << ": sem=" << FlagToString(buf->sem) << " (expected " << FlagToString(BufferSemaphoreFlags::Full) << "), sem_id=" << buf->sem_id << " )";
00372 if (buf->sem == BufferSemaphoreFlags::Full && (buf->sem_id == -1 || buf->sem_id == manager_id_) && buf->sequence_id > last_seen_id_)
00373 {
00374 TLOG(26) << "0x" << std::hex << shm_key_ << std::dec << " ReadReadyCount: Buffer " << ii << " is either unowned or owned by this manager, and is marked full.";
00375 ++count;
00376 }
00377 }
00378 return count;
00379 }
00380
00381 size_t artdaq::SharedMemoryManager::WriteReadyCount(bool overwrite)
00382 {
00383 if (!IsValid()) return 0;
00384 std::unique_lock<std::mutex> lk(search_mutex_);
00385
00386 size_t count = 0;
00387 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
00388 {
00389 ResetBuffer(ii);
00390 auto buf = getBufferInfo_(ii);
00391 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1)
00392 || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
00393 {
00394 ++count;
00395 }
00396 }
00397 return count;
00398 }
00399
00400 bool artdaq::SharedMemoryManager::ReadyForRead()
00401 {
00402 if (!IsValid()) return false;
00403 TLOG(23) << "0x" << std::hex << shm_key_ << " ReadyForRead BEGIN";
00404 std::unique_lock<std::mutex> lk(search_mutex_);
00405
00406
00407 auto rp = shm_ptr_->reader_pos.load();
00408
00409 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
00410 {
00411 auto buffer = (rp + ii) % shm_ptr_->buffer_count;
00412 TLOG(24) << "0x" << std::hex << shm_key_ << std::dec << " ReadyForRead: Checking if buffer " << buffer << " is stale.";
00413 ResetBuffer(buffer);
00414 auto buf = getBufferInfo_(buffer);
00415 TLOG(25) << "0x" << std::hex << shm_key_ << std::dec << " ReadyForRead: Buffer " << buffer << ": sem=" << FlagToString(buf->sem) << " (expected " << FlagToString(BufferSemaphoreFlags::Full) << "), sem_id=" << buf->sem_id << " )";
00416 if (buf->sem == BufferSemaphoreFlags::Full && (buf->sem_id == -1 || buf->sem_id == manager_id_) && buf->sequence_id > last_seen_id_)
00417 {
00418 TLOG(26) << "0x" << std::hex << shm_key_ << std::dec << " ReadyForRead: Buffer " << buffer << " is either unowned or owned by this manager, and is marked full.";
00419 return true;
00420 }
00421 }
00422 return false;
00423 }
00424
00425 bool artdaq::SharedMemoryManager::ReadyForWrite(bool overwrite)
00426 {
00427 if (!IsValid()) return false;
00428 std::unique_lock<std::mutex> lk(search_mutex_);
00429
00430
00431 auto wp = shm_ptr_->writer_pos.load();
00432
00433 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
00434 {
00435 auto buffer = (wp + ii) % shm_ptr_->buffer_count;
00436 ResetBuffer(buffer);
00437 auto buf = getBufferInfo_(buffer);
00438 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1)
00439 || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
00440 {
00441 return true;
00442 }
00443 }
00444 return false;
00445 }
00446
00447 std::deque<int> artdaq::SharedMemoryManager::GetBuffersOwnedByManager(bool locked)
00448 {
00449 std::deque<int> output;
00450 if (!IsValid()) return output;
00451 if (locked)
00452 {
00453 std::unique_lock<std::mutex> lk(search_mutex_);
00454
00455 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
00456 {
00457 auto buf = getBufferInfo_(ii);
00458 if (buf->sem_id == manager_id_)
00459 {
00460 output.push_back(ii);
00461 }
00462 }
00463 }
00464 else
00465 {
00466 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
00467 {
00468 auto buf = getBufferInfo_(ii);
00469 if (buf->sem_id == manager_id_)
00470 {
00471 output.push_back(ii);
00472 }
00473 }
00474 }
00475
00476 return output;
00477 }
00478
00479 size_t artdaq::SharedMemoryManager::BufferDataSize(int buffer)
00480 {
00481 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
00482
00483 auto buf = getBufferInfo_(buffer);
00484 touchBuffer_(buf);
00485 return buf->writePos;
00486 }
00487
00488
00489 void artdaq::SharedMemoryManager::ResetReadPos(int buffer)
00490 {
00491 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
00492
00493 auto buf = getBufferInfo_(buffer);
00494 touchBuffer_(buf);
00495 buf->readPos = 0;
00496 }
00497
00498 void artdaq::SharedMemoryManager::ResetWritePos(int buffer)
00499 {
00500 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
00501
00502 auto buf = getBufferInfo_(buffer);
00503 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
00504 touchBuffer_(buf);
00505 buf->writePos = 0;
00506 }
00507
00508 void artdaq::SharedMemoryManager::IncrementReadPos(int buffer, size_t read)
00509 {
00510 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
00511
00512 auto buf = getBufferInfo_(buffer);
00513 touchBuffer_(buf);
00514 TLOG(15) << "IncrementReadPos: buffer= " << buffer << ", readPos=" << buf->readPos << ", bytes read=" << read;
00515 buf->readPos = buf->readPos + read;
00516 TLOG(15) << "IncrementReadPos: buffer= " << buffer << ", New readPos is " << buf->readPos;
00517 if (read == 0) Detach(true, "LogicError", "Cannot increment Read pos by 0! (buffer=" + std::to_string(buffer) + ", readPos=" + std::to_string(buf->readPos) + ", writePos=" + std::to_string(buf->writePos) + ")");
00518 }
00519
00520 bool artdaq::SharedMemoryManager::IncrementWritePos(int buffer, size_t written)
00521 {
00522 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
00523
00524 auto buf = getBufferInfo_(buffer);
00525 touchBuffer_(buf);
00526 if (buf->writePos + written > shm_ptr_->buffer_size)
00527 {
00528 TLOG(TLVL_ERROR) << "Requested write size is larger than the buffer size! (sz=" << std::hex << shm_ptr_->buffer_size << ", cur + req=" << buf->writePos + written << ")";
00529 return false;
00530 }
00531 TLOG(16) << "IncrementWritePos: buffer= " << buffer << ", writePos=" << buf->writePos << ", bytes written=" << written;
00532 buf->writePos += written;
00533 TLOG(16) << "IncrementWritePos: buffer= " << buffer << ", New writePos is " << buf->writePos;
00534 if (written == 0) Detach(true, "LogicError", "Cannot increment Write pos by 0!");
00535
00536 return true;
00537 }
00538
00539 bool artdaq::SharedMemoryManager::MoreDataInBuffer(int buffer)
00540 {
00541 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
00542
00543 auto buf = getBufferInfo_(buffer);
00544 TLOG(17) << "MoreDataInBuffer: buffer= " << buffer << ", readPos=" << std::to_string(buf->readPos) << ", writePos=" << buf->writePos;
00545 return buf->readPos < buf->writePos;
00546 }
00547
00548 bool artdaq::SharedMemoryManager::CheckBuffer(int buffer, BufferSemaphoreFlags flags)
00549 {
00550 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
00551
00552 return checkBuffer_(getBufferInfo_(buffer), flags, false);
00553 }
00554
00555 void artdaq::SharedMemoryManager::MarkBufferFull(int buffer, int destination)
00556 {
00557 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
00558
00559 auto shmBuf = getBufferInfo_(buffer);
00560 touchBuffer_(shmBuf);
00561 if (shmBuf->sem_id == manager_id_)
00562 {
00563 if (shmBuf->sem != BufferSemaphoreFlags::Full)
00564 shmBuf->sem = BufferSemaphoreFlags::Full;
00565
00566 shmBuf->sem_id = destination;
00567 }
00568 }
00569
00570 void artdaq::SharedMemoryManager::MarkBufferEmpty(int buffer, bool force)
00571 {
00572 TLOG(18) << "MarkBufferEmpty BEGIN";
00573 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
00574
00575 auto shmBuf = getBufferInfo_(buffer);
00576 if (!force)
00577 {
00578 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, true);
00579 }
00580 touchBuffer_(shmBuf);
00581
00582 shmBuf->readPos = 0;
00583 shmBuf->sem = BufferSemaphoreFlags::Full;
00584
00585 if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
00586 {
00587 TLOG(18) << "MarkBufferEmpty Resetting buffer to Empty state";
00588 shmBuf->writePos = 0;
00589 shmBuf->sem = BufferSemaphoreFlags::Empty;
00590 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
00591 {
00592 TLOG(18) << "MarkBufferEmpty Broadcast mode; incrementing reader_pos";
00593 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
00594 }
00595 }
00596 shmBuf->sem_id = -1;
00597 TLOG(18) << "MarkBufferEmpty END";
00598 }
00599
00600 bool artdaq::SharedMemoryManager::ResetBuffer(int buffer)
00601 {
00602 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
00603
00604 auto shmBuf = getBufferInfo_(buffer);
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615 size_t delta = TimeUtils::gettimeofday_us() - shmBuf->last_touch_time;
00616 if (delta > 0xFFFFFFFF)
00617 {
00618 TLOG(TLVL_TRACE) << "Buffer has touch time in the future, setting it to current time and ignoring...";
00619 shmBuf->last_touch_time = TimeUtils::gettimeofday_us();
00620 return false;
00621 }
00622 if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty) return false;
00623 TLOG(27) << "Buffer " << buffer << " is stale, time=" << TimeUtils::gettimeofday_us() << ", last touch=" << shmBuf->last_touch_time << ", d=" << delta << ", timeout=" << shm_ptr_->buffer_timeout_us;
00624
00625 if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
00626 {
00627 return true;
00628 }
00629 if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full)
00630 {
00631 TLOG(TLVL_DEBUG) << "Resetting old broadcast mode buffer";
00632 shmBuf->writePos = 0;
00633 shmBuf->sem = BufferSemaphoreFlags::Empty;
00634 shmBuf->sem_id = -1;
00635 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer)) shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
00636 return true;
00637 }
00638
00639 if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
00640 {
00641 TLOG(TLVL_WARNING) << "Stale Read buffer ( " << delta << " / " << shm_ptr_->buffer_timeout_us << " us ) detected! Resetting...";
00642 shmBuf->readPos = 0;
00643 shmBuf->sem = BufferSemaphoreFlags::Full;
00644 shmBuf->sem_id = -1;
00645 return true;
00646 }
00647 return false;
00648 }
00649
00650 bool artdaq::SharedMemoryManager::IsEndOfData() const
00651 {
00652 if (!IsValid()) return true;
00653
00654 struct shmid_ds info;
00655 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
00656 if (sts < 0)
00657 {
00658 TLOG(TLVL_TRACE) << "Error accessing Shared Memory info: " << errno << " (" << strerror(errno) << ").";
00659 return true;
00660 }
00661
00662 if (info.shm_perm.mode & SHM_DEST)
00663 {
00664 TLOG(TLVL_INFO) << "Shared Memory marked for destruction. Probably an end-of-data condition!";
00665 return true;
00666 }
00667
00668 return false;
00669 }
00670
00671 uint16_t artdaq::SharedMemoryManager::GetAttachedCount() const
00672 {
00673 if (!IsValid()) return 0;
00674
00675 struct shmid_ds info;
00676 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
00677 if (sts < 0)
00678 {
00679 TLOG(TLVL_TRACE) << "Error accessing Shared Memory info: " << errno << " (" << strerror(errno) << ").";
00680 return 0;
00681 }
00682
00683 return info.shm_nattch;
00684 }
00685
00686 size_t artdaq::SharedMemoryManager::Write(int buffer, void* data, size_t size)
00687 {
00688 TLOG(19) << "Write BEGIN";
00689 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
00690
00691 auto shmBuf = getBufferInfo_(buffer);
00692 checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
00693 touchBuffer_(shmBuf);
00694 TLOG(19) << "Buffer Write Pos is " << shmBuf->writePos << ", write size is " << size;
00695 if (shmBuf->writePos + size > shm_ptr_->buffer_size) Detach(true, "SharedMemoryWrite", "Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
00696
00697 auto pos = GetWritePos(buffer);
00698 memcpy(pos, data, size);
00699 shmBuf->writePos = shmBuf->writePos + size;
00700 if (shmBuf->sequence_id > last_seen_id_)
00701 {
00702 last_seen_id_ = shmBuf->sequence_id;
00703 }
00704 TLOG(19) << "Write END";
00705 return size;
00706 }
00707
00708 bool artdaq::SharedMemoryManager::Read(int buffer, void* data, size_t size)
00709 {
00710 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
00711
00712 auto shmBuf = getBufferInfo_(buffer);
00713 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
00714 touchBuffer_(shmBuf);
00715 if (shmBuf->readPos + size > shm_ptr_->buffer_size) Detach(true, "SharedMemoryRead", "Attempted to read more data than exists in Shared Memory!");
00716
00717 auto pos = GetReadPos(buffer);
00718 memcpy(data, pos, size);
00719 shmBuf->readPos += size;
00720 touchBuffer_(shmBuf);
00721 return checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, false);
00722 }
00723
00724 std::string artdaq::SharedMemoryManager::toString()
00725 {
00726 std::ostringstream ostr;
00727 ostr << "ShmStruct: " << std::endl
00728 << "Reader Position: " << shm_ptr_->reader_pos << std::endl
00729 << "Writer Position: " << shm_ptr_->writer_pos << std::endl
00730 << "Next ID Number: " << shm_ptr_->next_id << std::endl
00731 << "Buffer Count: " << shm_ptr_->buffer_count << std::endl
00732 << "Buffer Size: " << std::to_string(shm_ptr_->buffer_size) << " bytes" << std::endl
00733 << "Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
00734 << "Rank of Writer: " << shm_ptr_->rank << std::endl
00735 << "Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::endl << std::endl;
00736
00737 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
00738 {
00739 auto buf = getBufferInfo_(ii);
00740 ostr << "ShmBuffer " << std::dec << ii << std::endl
00741 << "sequenceID: " << std::to_string(buf->sequence_id) << std::endl
00742 << "writePos: " << std::to_string(buf->writePos) << std::endl
00743 << "readPos: " << std::to_string(buf->readPos) << std::endl
00744 << "sem: " << FlagToString(buf->sem) << std::endl
00745 << "Owner: " << std::to_string(buf->sem_id.load()) << std::endl
00746 << "Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl << std::endl;
00747 }
00748
00749 return ostr.str();
00750 }
00751
00752 void* artdaq::SharedMemoryManager::GetReadPos(int buffer)
00753 {
00754 auto buf = getBufferInfo_(buffer);
00755 return bufferStart_(buffer) + buf->readPos;
00756 }
00757 void* artdaq::SharedMemoryManager::GetWritePos(int buffer)
00758 {
00759 auto buf = getBufferInfo_(buffer);
00760 return bufferStart_(buffer) + buf->writePos;
00761 }
00762
00763 void* artdaq::SharedMemoryManager::GetBufferStart(int buffer)
00764 {
00765 return bufferStart_(buffer);
00766 }
00767
00768 uint8_t* artdaq::SharedMemoryManager::dataStart_() const
00769 {
00770 return reinterpret_cast<uint8_t*>(shm_ptr_ + 1) + shm_ptr_->buffer_count * sizeof(ShmBuffer);
00771 }
00772
00773 uint8_t* artdaq::SharedMemoryManager::bufferStart_(int buffer)
00774 {
00775 if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
00776 return dataStart_() + buffer * shm_ptr_->buffer_size;
00777 }
00778
00779 artdaq::SharedMemoryManager::ShmBuffer* artdaq::SharedMemoryManager::getBufferInfo_(int buffer)
00780 {
00781 if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
00782 return reinterpret_cast<ShmBuffer*>(reinterpret_cast<uint8_t*>(shm_ptr_ + 1) + buffer * sizeof(ShmBuffer));
00783 }
00784
00785 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags, bool exceptions)
00786 {
00787 TLOG(TLVL_TRACE) << "checkBuffer_: Checking that buffer " << buffer->sequence_id << " has sem_id " << manager_id_ << " (Current: " << buffer->sem_id << ") and is in state " << FlagToString(flags) << " (current: " << FlagToString(buffer->sem) << ")";
00788 if (exceptions)
00789 {
00790 if (buffer->sem != flags) Detach(true, "StateAccessViolation", "Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) + ", actual " + FlagToString(buffer->sem) + ")");
00791 if (buffer->sem_id != manager_id_) Detach(true, "OwnerAccessViolation", "Shared Memory buffer is not owned by this manager instance! (Expected: " + std::to_string(manager_id_) + ", Actual: " + std::to_string(buffer->sem_id) + ")");
00792 }
00793 bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
00794
00795 if (!ret)
00796 {
00797 TLOG(TLVL_WARNING) << "CheckBuffer detected issue with buffer " << buffer->sequence_id << "!"
00798 << " ID: " << buffer->sem_id << " (" << manager_id_ << "), Flag: " << FlagToString(buffer->sem) << " (" << FlagToString(flags) << "). "
00799 << "ID -1 is okay if desired flag is \"Full\" or \"Empty\".";
00800 }
00801
00802 return ret;
00803 }
00804
00805 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
00806 {
00807 if (buffer->sem_id != manager_id_) return;
00808 TLOG(TLVL_TRACE) << "touchBuffer_: Touching buffer with sequence_id " << buffer->sequence_id;
00809 buffer->last_touch_time = TimeUtils::gettimeofday_us();
00810 }
00811
00812 void artdaq::SharedMemoryManager::Detach(bool throwException, std::string category, std::string message, bool force)
00813 {
00814 TLOG(TLVL_DETACH) << "Detach BEGIN: throwException: " << std::boolalpha << throwException << ", force: " << force;
00815 if (IsValid())
00816 {
00817 TLOG(TLVL_DETACH) << "Detach: Resetting owned buffers";
00818 auto bufs = GetBuffersOwnedByManager(false);
00819 for (auto buf : bufs)
00820 {
00821 auto shmBuf = getBufferInfo_(buf);
00822 if (shmBuf->sem == BufferSemaphoreFlags::Writing)
00823 {
00824 shmBuf->sem = BufferSemaphoreFlags::Empty;
00825 }
00826 else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
00827 {
00828 shmBuf->sem = BufferSemaphoreFlags::Full;
00829 }
00830 shmBuf->sem_id = -1;
00831 }
00832 }
00833
00834 if (shm_ptr_)
00835 {
00836 TLOG(TLVL_DETACH) << "Detach: Detaching shared memory";
00837 shmdt(shm_ptr_);
00838 shm_ptr_ = NULL;
00839 }
00840
00841 if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
00842 {
00843 TLOG(TLVL_DETACH) << "Detach: Marking Shared memory for removal";
00844 shmctl(shm_segment_id_, IPC_RMID, NULL);
00845 shm_segment_id_ = -1;
00846 }
00847
00848 if (category.size() > 0 && message.size() > 0)
00849 {
00850 TLOG(TLVL_ERROR) << category << ": " << message;
00851
00852 if (throwException)
00853 {
00854 throw cet::exception(category) << message;
00855 }
00856 }
00857 }
00858
00859
00860
00861
00862
00863