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