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