$treeview $search $mathjax $extrastylesheet
artdaq_core
v3_05_11
$projectbrief
|
$projectbrief
|
$searchbox |
00001 #define TRACE_NAME "SharedMemoryManager" 00002 #include <sys/ipc.h> 00003 #include <sys/shm.h> 00004 #include <cstring> 00005 #include <list> 00006 #include <unordered_map> 00007 #ifndef SHM_DEST // Lynn reports that this is missing on Mac OS X?!? 00008 #define SHM_DEST 01000 00009 #endif 00010 #include <signal.h> 00011 #include "artdaq-core/Core/SharedMemoryManager.hh" 00012 #include "artdaq-core/Utilities/TraceLock.hh" 00013 #include "cetlib_except/exception.h" 00014 #include "tracemf.h" 00015 00016 #define TLVL_DETACH 11 00017 #define TLVL_BUFFER 40 00018 #define TLVL_BUFLCK 41 00019 00020 static std::list<artdaq::SharedMemoryManager const*> instances = std::list<artdaq::SharedMemoryManager const*>(); 00021 00022 static std::unordered_map<int, struct sigaction> old_actions = std::unordered_map<int, struct sigaction>(); 00023 static bool sighandler_init = false; 00024 static std::mutex sighandler_mutex; 00025 00026 static void signal_handler(int signum) 00027 { 00028 // Messagefacility may already be gone at this point, TRACE ONLY! 00029 TRACE_STREAMER(TLVL_ERROR, &("SharedMemoryManager")[0], 0, 0, 0) << "A signal of type " << signum << " was caught by SharedMemoryManager. Detaching all Shared Memory segments, then proceeding with default handlers!"; 00030 for (auto ii : instances) 00031 { 00032 if (ii) 00033 { 00034 const_cast<artdaq::SharedMemoryManager*>(ii)->Detach(false, "", "", false /* don't force destruct segment, allows reconnection (applicable for 00035 restart and/or multiple art processes (i.e. dispatcher)) */ 00036 ); 00037 } 00038 ii = nullptr; 00039 } 00040 00041 sigset_t set; 00042 pthread_sigmask(SIG_UNBLOCK, NULL, &set); 00043 pthread_sigmask(SIG_UNBLOCK, &set, NULL); 00044 00045 TRACE_STREAMER(TLVL_ERROR, &("SharedMemoryManager")[0], 0, 0, 0) << "Calling default signal handler"; 00046 if (signum != SIGUSR2) 00047 { 00048 sigaction(signum, &old_actions[signum], NULL); 00049 kill(getpid(), signum); // Only send signal to self 00050 } 00051 else 00052 { 00053 // Send Interrupt signal if parsing SIGUSR2 (i.e. user-defined exception that should tear down ARTDAQ) 00054 sigaction(SIGINT, &old_actions[SIGINT], NULL); 00055 kill(getpid(), SIGINT); // Only send signal to self 00056 } 00057 } 00058 00059 artdaq::SharedMemoryManager::SharedMemoryManager(uint32_t shm_key, size_t buffer_count, size_t buffer_size, uint64_t buffer_timeout_us, bool destructive_read_mode) 00060 : shm_segment_id_(-1) 00061 , shm_ptr_(NULL) 00062 , shm_key_(shm_key) 00063 , manager_id_(-1) 00064 , buffer_ptrs_() 00065 , buffer_mutexes_() 00066 , last_seen_id_(0) 00067 { 00068 requested_shm_parameters_.buffer_count = buffer_count; 00069 requested_shm_parameters_.buffer_size = buffer_size; 00070 requested_shm_parameters_.buffer_timeout_us = buffer_timeout_us; 00071 requested_shm_parameters_.destructive_read_mode = destructive_read_mode; 00072 00073 instances.push_back(this); 00074 Attach(); 00075 00076 std::lock_guard<std::mutex> lk(sighandler_mutex); 00077 00078 if (!sighandler_init) //&& manager_id_ == 0) // ELF 3/22/18: Taking out manager_id_==0 requirement as I think kill(getpid()) is enough protection 00079 { 00080 sighandler_init = true; 00081 std::vector<int> signals = {SIGINT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, SIGUSR2, SIGHUP}; // SIGQUIT is used by art in normal operation 00082 for (auto signal : signals) 00083 { 00084 struct sigaction old_action; 00085 sigaction(signal, NULL, &old_action); 00086 00087 //If the old handler wasn't SIG_IGN (it's a handler that just 00088 // "ignore" the signal) 00089 if (old_action.sa_handler != SIG_IGN) 00090 { 00091 struct sigaction action; 00092 action.sa_handler = signal_handler; 00093 sigemptyset(&action.sa_mask); 00094 for (auto sigblk : signals) 00095 { 00096 sigaddset(&action.sa_mask, sigblk); 00097 } 00098 action.sa_flags = 0; 00099 00100 //Replace the signal handler of SIGINT with the one described by new_action 00101 sigaction(signal, &action, NULL); 00102 } 00103 old_actions[signal] = old_action; 00104 } 00105 } 00106 } 00107 00108 artdaq::SharedMemoryManager::~SharedMemoryManager() noexcept 00109 { 00110 TLOG(TLVL_DEBUG) << "~SharedMemoryManager called"; 00111 { 00112 static std::mutex destructor_mutex; 00113 std::lock_guard<std::mutex> lk(destructor_mutex); 00114 for (auto it = instances.begin(); it != instances.end(); ++it) 00115 { 00116 if (*it == this) 00117 { 00118 it = instances.erase(it); 00119 break; 00120 } 00121 } 00122 } 00123 Detach(); 00124 { 00125 std::lock_guard<std::mutex> lk(sighandler_mutex); 00126 00127 // Restore signal handlers 00128 if (sighandler_init && instances.size() == 0) 00129 { 00130 sighandler_init = false; 00131 for (auto signal : old_actions) 00132 { 00133 sigaction(signal.first, &signal.second, NULL); 00134 } 00135 old_actions.clear(); 00136 } 00137 } 00138 TLOG(TLVL_DEBUG) << "~SharedMemoryManager done"; 00139 } 00140 00141 bool artdaq::SharedMemoryManager::Attach(size_t timeout_usec) 00142 { 00143 if (IsValid()) 00144 { 00145 if (manager_id_ == 0) return true; 00146 Detach(); 00147 } 00148 00149 size_t timeout_us = timeout_usec > 0 ? timeout_usec : 1000000; 00150 auto start_time = std::chrono::steady_clock::now(); 00151 last_seen_id_ = 0; 00152 size_t shmSize = requested_shm_parameters_.buffer_count * (requested_shm_parameters_.buffer_size + sizeof(ShmBuffer)) + sizeof(ShmStruct); 00153 00154 // 19-Feb-2019, KAB: separating out the determination of whether a given process owns the shared 00155 // memory (indicated by manager_id_ == 0) and whether or not the shared memory already exists. 00156 if (requested_shm_parameters_.buffer_count > 0 && requested_shm_parameters_.buffer_size > 0 && manager_id_ <= 0) 00157 { 00158 manager_id_ = 0; 00159 } 00160 00161 shm_segment_id_ = shmget(shm_key_, shmSize, 0666); 00162 if (shm_segment_id_ == -1) 00163 { 00164 if (manager_id_ == 0) 00165 { 00166 TLOG(TLVL_DEBUG) << "Creating shared memory segment with key 0x" << std::hex << shm_key_ << " and size " << std::dec << shmSize; 00167 shm_segment_id_ = shmget(shm_key_, shmSize, IPC_CREAT | 0666); 00168 00169 if (shm_segment_id_ == -1) 00170 { 00171 TLOG(TLVL_ERROR) << "Error creating shared memory segment with key 0x" << std::hex << shm_key_ << ", errno=" << std::dec << errno << " (" << strerror(errno) << ")"; 00172 } 00173 } 00174 else 00175 { 00176 while (shm_segment_id_ == -1 && TimeUtils::GetElapsedTimeMicroseconds(start_time) < timeout_us) 00177 { 00178 shm_segment_id_ = shmget(shm_key_, shmSize, 0666); 00179 } 00180 } 00181 } 00182 TLOG(TLVL_DEBUG) << "shm_key == 0x" << std::hex << shm_key_ << ", shm_segment_id == " << std::dec << shm_segment_id_; 00183 00184 if (shm_segment_id_ > -1) 00185 { 00186 TLOG(TLVL_DEBUG) 00187 << "Attached to shared memory segment with ID = " << shm_segment_id_ 00188 << " and size " << shmSize 00189 << " bytes"; 00190 shm_ptr_ = (ShmStruct*)shmat(shm_segment_id_, 0, 0); 00191 TLOG(TLVL_DEBUG) 00192 << "Attached to shared memory segment at address " 00193 << std::hex << (void*)shm_ptr_ << std::dec; 00194 if (shm_ptr_ && shm_ptr_ != (void*)-1) 00195 { 00196 if (manager_id_ == 0) 00197 { 00198 if (shm_ptr_->ready_magic == 0xCAFE1111) 00199 { 00200 TLOG(TLVL_WARNING) << "Owner encountered already-initialized Shared Memory! " 00201 << "Once the system is shut down, you can use one of the following commands " 00202 << "to clean up this shared memory: 'ipcrm -M 0x" << std::hex << shm_key_ 00203 << "' or 'ipcrm -m " << std::dec << shm_segment_id_ << "'."; 00204 //exit(-2); 00205 } 00206 TLOG(TLVL_DEBUG) << "Owner initializing Shared Memory"; 00207 shm_ptr_->next_id = 1; 00208 shm_ptr_->next_sequence_id = 0; 00209 shm_ptr_->reader_pos = 0; 00210 shm_ptr_->writer_pos = 0; 00211 shm_ptr_->buffer_size = requested_shm_parameters_.buffer_size; 00212 shm_ptr_->buffer_count = requested_shm_parameters_.buffer_count; 00213 shm_ptr_->buffer_timeout_us = requested_shm_parameters_.buffer_timeout_us; 00214 shm_ptr_->destructive_read_mode = requested_shm_parameters_.destructive_read_mode; 00215 00216 buffer_ptrs_ = std::vector<ShmBuffer*>(shm_ptr_->buffer_count); 00217 for (int ii = 0; ii < static_cast<int>(requested_shm_parameters_.buffer_count); ++ii) 00218 { 00219 buffer_ptrs_[ii] = reinterpret_cast<ShmBuffer*>( 00220 reinterpret_cast<uint8_t*>(shm_ptr_ + 1) + ii * sizeof(ShmBuffer)); 00221 if (!getBufferInfo_(ii)) return false; 00222 getBufferInfo_(ii)->writePos = 0; 00223 getBufferInfo_(ii)->readPos = 0; 00224 getBufferInfo_(ii)->sem = BufferSemaphoreFlags::Empty; 00225 getBufferInfo_(ii)->sem_id = -1; 00226 getBufferInfo_(ii)->last_touch_time = TimeUtils::gettimeofday_us(); 00227 } 00228 00229 shm_ptr_->ready_magic = 0xCAFE1111; 00230 } 00231 else 00232 { 00233 TLOG(TLVL_DEBUG) << "Waiting for owner to initalize Shared Memory"; 00234 while (shm_ptr_->ready_magic != 0xCAFE1111) { usleep(1000); } 00235 TLOG(TLVL_DEBUG) << "Getting ID from Shared Memory"; 00236 GetNewId(); 00237 shm_ptr_->lowest_seq_id_read = 0; 00238 TLOG(TLVL_DEBUG) << "Getting Shared Memory Size parameters"; 00239 00240 requested_shm_parameters_.buffer_count = shm_ptr_->buffer_count; 00241 buffer_ptrs_ = std::vector<ShmBuffer*>(shm_ptr_->buffer_count); 00242 for (int ii = 0; ii < shm_ptr_->buffer_count; ++ii) 00243 { 00244 buffer_ptrs_[ii] = reinterpret_cast<ShmBuffer*>(reinterpret_cast<uint8_t*>(shm_ptr_ + 1) + ii * sizeof(ShmBuffer)); 00245 } 00246 } 00247 00248 //last_seen_id_ = shm_ptr_->next_sequence_id; 00249 buffer_mutexes_ = std::vector<std::mutex>(shm_ptr_->buffer_count); 00250 00251 TLOG(TLVL_DEBUG) << "Initialization Complete: " 00252 << "key: 0x" << std::hex << shm_key_ 00253 << ", manager ID: " << std::dec << manager_id_ 00254 << ", Buffer size: " << shm_ptr_->buffer_size 00255 << ", Buffer count: " << shm_ptr_->buffer_count; 00256 return true; 00257 } 00258 else 00259 { 00260 TLOG(TLVL_ERROR) << "Failed to attach to shared memory segment " 00261 << shm_segment_id_; 00262 return false; 00263 } 00264 } 00265 else 00266 { 00267 TLOG(TLVL_ERROR) << "Failed to connect to shared memory segment with key 0x" << std::hex << shm_key_ 00268 << ", errno=" << std::dec << errno << " (" << strerror(errno) << ")" 00269 << ". Please check " 00270 << "if a stale shared memory segment needs to " 00271 << "be cleaned up. (ipcs, ipcrm -m <segId>)"; 00272 return false; 00273 } 00274 00275 // Should not get here... 00276 return false; 00277 } 00278 00279 int artdaq::SharedMemoryManager::GetBufferForReading() 00280 { 00281 TLOG(13) << "GetBufferForReading BEGIN"; 00282 00283 std::lock_guard<std::mutex> lk(search_mutex_); 00284 //TraceLock lk(search_mutex_, 11, "GetBufferForReadingSearch"); 00285 auto rp = shm_ptr_->reader_pos.load(); 00286 00287 TLOG(13) << "GetBufferForReading lock acquired, scanning " << shm_ptr_->buffer_count << " buffers"; 00288 00289 for (int retry = 0; retry < 5; retry++) 00290 { 00291 BufferSemaphoreFlags sem; 00292 short sem_id; 00293 int buffer_num = -1; 00294 ShmBuffer* buffer_ptr = nullptr; 00295 uint64_t seqID = -1; 00296 00297 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii) 00298 { 00299 auto buffer = (ii + rp) % shm_ptr_->buffer_count; 00300 00301 TLOG(14) << "GetBufferForReading Checking if buffer " << buffer << " is stale. Shm destructive_read_mode=" << shm_ptr_->destructive_read_mode; 00302 ResetBuffer(buffer); 00303 00304 auto buf = getBufferInfo_(buffer); 00305 if (!buf) continue; 00306 00307 sem = buf->sem.load(); 00308 sem_id = buf->sem_id.load(); 00309 00310 TLOG(14) << "GetBufferForReading: Buffer " << buffer << ": sem=" << FlagToString(sem) 00311 << " (expected " << FlagToString(BufferSemaphoreFlags::Full) << "), sem_id=" << sem_id << ", seq_id=" << buf->sequence_id << " )"; 00312 if (sem == BufferSemaphoreFlags::Full && (sem_id == -1 || sem_id == manager_id_) && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_)) 00313 { 00314 if (buf->sequence_id < seqID) 00315 { 00316 buffer_ptr = buf; 00317 seqID = buf->sequence_id; 00318 buffer_num = buffer; 00319 touchBuffer_(buf); 00320 if (shm_ptr_->destructive_read_mode || seqID == last_seen_id_ + 1) break; 00321 } 00322 } 00323 } 00324 00325 if (buffer_ptr) 00326 { 00327 sem = buffer_ptr->sem.load(); 00328 sem_id = buffer_ptr->sem_id.load(); 00329 } 00330 00331 if (!buffer_ptr || (sem_id != -1 && sem_id != manager_id_) || sem != BufferSemaphoreFlags::Full) 00332 { 00333 continue; 00334 } 00335 00336 if (buffer_num >= 0) 00337 { 00338 TLOG(13) << "GetBufferForReading Found buffer " << buffer_num; 00339 touchBuffer_(buffer_ptr); 00340 if (!buffer_ptr->sem_id.compare_exchange_strong(sem_id, manager_id_)) continue; 00341 if (!buffer_ptr->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Reading)) continue; 00342 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading, false)) 00343 { 00344 TLOG(13) << "GetBufferForReading: Failed to acquire buffer " << buffer_num << " (someone else changed manager ID while I was changing sem)"; 00345 continue; 00346 } 00347 buffer_ptr->readPos = 0; 00348 touchBuffer_(buffer_ptr); 00349 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading, false)) 00350 { 00351 TLOG(13) << "GetBufferForReading: Failed to acquire buffer " << buffer_num << " (someone else changed manager ID while I was touching buffer SHOULD NOT HAPPEN!)"; 00352 continue; 00353 } 00354 if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_) 00355 { 00356 shm_ptr_->lowest_seq_id_read = seqID; 00357 } 00358 last_seen_id_ = seqID; 00359 if (shm_ptr_->destructive_read_mode) shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count; 00360 00361 TLOG(13) << "GetBufferForReading returning " << buffer_num; 00362 return buffer_num; 00363 } 00364 retry = 5; 00365 } 00366 00367 TLOG(13) << "GetBufferForReading returning -1 because no buffers are ready"; 00368 return -1; 00369 } 00370 00371 int artdaq::SharedMemoryManager::GetBufferForWriting(bool overwrite) 00372 { 00373 TLOG(14) << "GetBufferForWriting BEGIN, overwrite=" << (overwrite ? "true" : "false"); 00374 00375 std::lock_guard<std::mutex> lk(search_mutex_); 00376 //TraceLock lk(search_mutex_, 12, "GetBufferForWritingSearch"); 00377 auto wp = shm_ptr_->writer_pos.load(); 00378 00379 TLOG(13) << "GetBufferForWriting lock acquired, scanning " << shm_ptr_->buffer_count << " buffers"; 00380 00381 // First, only look for "Empty" buffers 00382 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii) 00383 { 00384 auto buffer = (ii + wp) % shm_ptr_->buffer_count; 00385 00386 ResetBuffer(buffer); 00387 00388 auto buf = getBufferInfo_(buffer); 00389 if (!buf) continue; 00390 00391 auto sem = buf->sem.load(); 00392 auto sem_id = buf->sem_id.load(); 00393 00394 if (sem == BufferSemaphoreFlags::Empty && sem_id == -1) 00395 { 00396 touchBuffer_(buf); 00397 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_)) continue; 00398 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing)) continue; 00399 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false)) continue; 00400 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count; 00401 buf->sequence_id = ++shm_ptr_->next_sequence_id; 00402 buf->writePos = 0; 00403 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false)) continue; 00404 touchBuffer_(buf); 00405 TLOG(14) << "GetBufferForWriting returning " << buffer; 00406 return buffer; 00407 } 00408 } 00409 00410 if (overwrite) 00411 { 00412 // Then, look for "Full" buffers 00413 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii) 00414 { 00415 auto buffer = (ii + wp) % shm_ptr_->buffer_count; 00416 00417 ResetBuffer(buffer); 00418 00419 auto buf = getBufferInfo_(buffer); 00420 if (!buf) continue; 00421 00422 auto sem = buf->sem.load(); 00423 auto sem_id = buf->sem_id.load(); 00424 00425 if (sem == BufferSemaphoreFlags::Full) 00426 { 00427 touchBuffer_(buf); 00428 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_)) continue; 00429 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing)) continue; 00430 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false)) continue; 00431 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count; 00432 buf->sequence_id = ++shm_ptr_->next_sequence_id; 00433 buf->writePos = 0; 00434 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false)) continue; 00435 touchBuffer_(buf); 00436 TLOG(14) << "GetBufferForWriting returning " << buffer; 00437 return buffer; 00438 } 00439 } 00440 00441 // Finally, if we still haven't found a buffer, we have to clobber a reader... 00442 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii) 00443 { 00444 auto buffer = (ii + wp) % shm_ptr_->buffer_count; 00445 00446 ResetBuffer(buffer); 00447 00448 auto buf = getBufferInfo_(buffer); 00449 if (!buf) continue; 00450 00451 auto sem = buf->sem.load(); 00452 auto sem_id = buf->sem_id.load(); 00453 00454 if (sem == BufferSemaphoreFlags::Reading) 00455 { 00456 touchBuffer_(buf); 00457 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_)) continue; 00458 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing)) continue; 00459 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false)) continue; 00460 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count; 00461 buf->sequence_id = ++shm_ptr_->next_sequence_id; 00462 buf->writePos = 0; 00463 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false)) continue; 00464 touchBuffer_(buf); 00465 TLOG(14) << "GetBufferForWriting returning " << buffer; 00466 return buffer; 00467 } 00468 } 00469 } 00470 TLOG(14) << "GetBufferForWriting Returning -1 because no buffers are ready"; 00471 return -1; 00472 } 00473 00474 size_t artdaq::SharedMemoryManager::ReadReadyCount() 00475 { 00476 if (!IsValid()) return 0; 00477 TLOG(23) << "0x" << std::hex << shm_key_ << " ReadReadyCount BEGIN" << std::dec; 00478 std::unique_lock<std::mutex> lk(search_mutex_); 00479 TLOG(23) << "ReadReadyCount lock acquired, scanning " << shm_ptr_->buffer_count << " buffers"; 00480 //TraceLock lk(search_mutex_, 14, "ReadReadyCountSearch"); 00481 size_t count = 0; 00482 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii) 00483 { 00484 #ifndef __OPTIMIZE__ 00485 TLOG(24) << "0x" << std::hex << shm_key_ << std::dec << " ReadReadyCount: Checking if buffer " << ii << " is stale."; 00486 #endif 00487 ResetBuffer(ii); 00488 auto buf = getBufferInfo_(ii); 00489 if (!buf) continue; 00490 00491 #ifndef __OPTIMIZE__ 00492 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 << " )"; 00493 #endif 00494 if (buf->sem == BufferSemaphoreFlags::Full && (buf->sem_id == -1 || buf->sem_id == manager_id_) && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_)) 00495 { 00496 #ifndef __OPTIMIZE__ 00497 TLOG(26) << "0x" << std::hex << shm_key_ << std::dec << " ReadReadyCount: Buffer " << ii << " is either unowned or owned by this manager, and is marked full."; 00498 #endif 00499 touchBuffer_(buf); 00500 ++count; 00501 } 00502 } 00503 return count; 00504 } 00505 00506 size_t artdaq::SharedMemoryManager::WriteReadyCount(bool overwrite) 00507 { 00508 if (!IsValid()) return 0; 00509 TLOG(28) << "0x" << std::hex << shm_key_ << " ReadReadyCount BEGIN" << std::dec; 00510 std::unique_lock<std::mutex> lk(search_mutex_); 00511 //TraceLock lk(search_mutex_, 15, "WriteReadyCountSearch"); 00512 TLOG(28) << "WriteReadyCount(" << overwrite << ") lock acquired, scanning " << shm_ptr_->buffer_count << " buffers"; 00513 size_t count = 0; 00514 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii) 00515 { 00516 // ELF, 3/19/2019: This TRACE call is a major performance hit with many buffers 00517 #ifndef __OPTIMIZE__ 00518 TLOG(29) << "0x" << std::hex << shm_key_ << std::dec << " WriteReadyCount: Checking if buffer " << ii << " is stale."; 00519 #endif 00520 ResetBuffer(ii); 00521 auto buf = getBufferInfo_(ii); 00522 if (!buf) continue; 00523 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing)) 00524 { 00525 #ifndef __OPTIMIZE__ 00526 TLOG(29) << "0x" << std::hex << shm_key_ << std::dec << " WriteReadyCount: Buffer " << ii << " is either empty or is available for overwrite."; 00527 #endif 00528 ++count; 00529 } 00530 } 00531 return count; 00532 } 00533 00534 bool artdaq::SharedMemoryManager::ReadyForRead() 00535 { 00536 if (!IsValid()) return false; 00537 TLOG(23) << "0x" << std::hex << shm_key_ << " ReadyForRead BEGIN" << std::dec; 00538 std::unique_lock<std::mutex> lk(search_mutex_); 00539 //TraceLock lk(search_mutex_, 14, "ReadyForReadSearch"); 00540 00541 auto rp = shm_ptr_->reader_pos.load(); 00542 00543 TLOG(23) << "ReadyForRead lock acquired, scanning " << shm_ptr_->buffer_count << " buffers"; 00544 00545 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii) 00546 { 00547 auto buffer = (rp + ii) % shm_ptr_->buffer_count; 00548 00549 #ifndef __OPTIMIZE__ 00550 TLOG(24) << "0x" << std::hex << shm_key_ << std::dec << " ReadyForRead: Checking if buffer " << buffer << " is stale."; 00551 #endif 00552 ResetBuffer(buffer); 00553 auto buf = getBufferInfo_(buffer); 00554 if (!buf) continue; 00555 00556 #ifndef __OPTIMIZE__ 00557 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 << " )" 00558 << " seq_id=" << buf->sequence_id << " >? " << last_seen_id_; 00559 #endif 00560 00561 if (buf->sem == BufferSemaphoreFlags::Full && (buf->sem_id == -1 || buf->sem_id == manager_id_) && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_)) 00562 { 00563 TLOG(26) << "0x" << std::hex << shm_key_ << std::dec << " ReadyForRead: Buffer " << buffer << " is either unowned or owned by this manager, and is marked full."; 00564 touchBuffer_(buf); 00565 return true; 00566 } 00567 } 00568 return false; 00569 } 00570 00571 bool artdaq::SharedMemoryManager::ReadyForWrite(bool overwrite) 00572 { 00573 if (!IsValid()) return false; 00574 TLOG(28) << "0x" << std::hex << shm_key_ << " ReadyForWrite BEGIN" << std::dec; 00575 00576 std::lock_guard<std::mutex> lk(search_mutex_); 00577 //TraceLock lk(search_mutex_, 15, "ReadyForWriteSearch"); 00578 00579 auto wp = shm_ptr_->writer_pos.load(); 00580 00581 TLOG(28) << "ReadyForWrite lock acquired, scanning " << shm_ptr_->buffer_count << " buffers"; 00582 00583 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii) 00584 { 00585 auto buffer = (wp + ii) % shm_ptr_->buffer_count; 00586 TLOG(29) << "0x" << std::hex << shm_key_ << std::dec << " ReadyForWrite: Checking if buffer " << buffer << " is stale."; 00587 ResetBuffer(buffer); 00588 auto buf = getBufferInfo_(buffer); 00589 if (!buf) continue; 00590 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing)) 00591 { 00592 TLOG(29) << "0x" << std::hex << shm_key_ 00593 << std::dec 00594 << " WriteReadyCount: Buffer " << ii << " is either empty or available for overwrite."; 00595 return true; 00596 } 00597 } 00598 return false; 00599 } 00600 00601 std::deque<int> artdaq::SharedMemoryManager::GetBuffersOwnedByManager(bool locked) 00602 { 00603 std::deque<int> output; 00604 if (!IsValid()) return output; 00605 TLOG(TLVL_BUFFER) << "GetBuffersOwnedByManager BEGIN. Locked? " << locked; 00606 if (locked) 00607 { 00608 TLOG(TLVL_BUFLCK) << "GetBuffersOwnedByManager obtaining search_mutex"; 00609 std::lock_guard<std::mutex> lk(search_mutex_); 00610 TLOG(TLVL_BUFLCK) << "GetBuffersOwnedByManager obtained search_mutex"; 00611 //TraceLock lk(search_mutex_, 16, "GetOwnedSearch"); 00612 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii) 00613 { 00614 auto buf = getBufferInfo_(ii); 00615 if (!buf) continue; 00616 if (buf->sem_id == manager_id_) 00617 { 00618 output.push_back(ii); 00619 } 00620 } 00621 } 00622 else 00623 { 00624 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii) 00625 { 00626 auto buf = getBufferInfo_(ii); 00627 if (!buf) continue; 00628 if (buf->sem_id == manager_id_) 00629 { 00630 output.push_back(ii); 00631 } 00632 } 00633 } 00634 00635 TLOG(TLVL_BUFFER) << "GetBuffersOwnedByManager: own " << output.size() << " / " << shm_ptr_->buffer_count << " buffers."; 00636 return output; 00637 } 00638 00639 size_t artdaq::SharedMemoryManager::BufferDataSize(int buffer) 00640 { 00641 TLOG(TLVL_BUFFER) << "BufferDataSize(" << buffer << ") called."; 00642 00643 if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!"); 00644 00645 TLOG(TLVL_BUFLCK) << "BufferDataSize obtaining buffer_mutex for buffer " << buffer; 00646 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]); 00647 TLOG(TLVL_BUFLCK) << "BufferDataSize obtained buffer_mutex for buffer " << buffer; 00648 //TraceLock lk(buffer_mutexes_[buffer], 17, "DataSizeBuffer" + std::to_string(buffer)); 00649 00650 auto buf = getBufferInfo_(buffer); 00651 if (!buf) return 0; 00652 touchBuffer_(buf); 00653 00654 TLOG(TLVL_BUFFER) << "BufferDataSize: buffer " << buffer << ", size=" << buf->writePos; 00655 return buf->writePos; 00656 } 00657 00658 void artdaq::SharedMemoryManager::ResetReadPos(int buffer) 00659 { 00660 TLOG(15) << "ResetReadPos(" << buffer << ") called."; 00661 00662 if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!"); 00663 00664 TLOG(TLVL_BUFLCK) << "ResetReadPos obtaining buffer_mutex for buffer " << buffer; 00665 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]); 00666 TLOG(TLVL_BUFLCK) << "ResetReadPos obtained buffer_mutex for buffer " << buffer; 00667 00668 //TraceLock lk(buffer_mutexes_[buffer], 18, "ResetReadPosBuffer" + std::to_string(buffer)); 00669 auto buf = getBufferInfo_(buffer); 00670 if (!buf || buf->sem_id != manager_id_) return; 00671 touchBuffer_(buf); 00672 buf->readPos = 0; 00673 00674 TLOG(15) << "ResetReadPos(" << buffer << ") ended."; 00675 } 00676 00677 void artdaq::SharedMemoryManager::ResetWritePos(int buffer) 00678 { 00679 TLOG(16) << "ResetWritePos(" << buffer << ") called."; 00680 00681 if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!"); 00682 00683 TLOG(TLVL_BUFLCK) << "ResetWritePos obtaining buffer_mutex for buffer " << buffer; 00684 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]); 00685 TLOG(TLVL_BUFLCK) << "ResetWritePos obtained buffer_mutex for buffer " << buffer; 00686 00687 //TraceLock lk(buffer_mutexes_[buffer], 18, "ResetWritePosBuffer" + std::to_string(buffer)); 00688 auto buf = getBufferInfo_(buffer); 00689 if (!buf) return; 00690 checkBuffer_(buf, BufferSemaphoreFlags::Writing); 00691 touchBuffer_(buf); 00692 buf->writePos = 0; 00693 00694 TLOG(16) << "ResetWritePos(" << buffer << ") ended."; 00695 } 00696 00697 void artdaq::SharedMemoryManager::IncrementReadPos(int buffer, size_t read) 00698 { 00699 TLOG(15) << "IncrementReadPos called: buffer= " << buffer << ", bytes to read=" << read; 00700 00701 if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!"); 00702 00703 TLOG(TLVL_BUFLCK) << "IncrementReadPos obtaining buffer_mutex for buffer " << buffer; 00704 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]); 00705 TLOG(TLVL_BUFLCK) << "IncrementReadPos obtained buffer_mutex for buffer " << buffer; 00706 //TraceLock lk(buffer_mutexes_[buffer], 19, "IncReadPosBuffer" + std::to_string(buffer)); 00707 auto buf = getBufferInfo_(buffer); 00708 if (!buf || buf->sem_id != manager_id_) return; 00709 touchBuffer_(buf); 00710 TLOG(15) << "IncrementReadPos: buffer= " << buffer << ", readPos=" << buf->readPos << ", bytes read=" << read; 00711 buf->readPos = buf->readPos + read; 00712 TLOG(15) << "IncrementReadPos: buffer= " << buffer << ", New readPos is " << buf->readPos; 00713 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) + ")"); 00714 } 00715 00716 bool artdaq::SharedMemoryManager::IncrementWritePos(int buffer, size_t written) 00717 { 00718 TLOG(16) << "IncrementWritePos called: buffer= " << buffer << ", bytes written=" << written; 00719 00720 if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!"); 00721 00722 TLOG(TLVL_BUFLCK) << "IncrementWritePos obtaining buffer_mutex for buffer " << buffer; 00723 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]); 00724 TLOG(TLVL_BUFLCK) << "IncrementWritePos obtained buffer_mutex for buffer " << buffer; 00725 //TraceLock lk(buffer_mutexes_[buffer], 20, "IncWritePosBuffer" + std::to_string(buffer)); 00726 auto buf = getBufferInfo_(buffer); 00727 if (!buf) return false; 00728 checkBuffer_(buf, BufferSemaphoreFlags::Writing); 00729 touchBuffer_(buf); 00730 if (buf->writePos + written > shm_ptr_->buffer_size) 00731 { 00732 TLOG(TLVL_ERROR) << "Requested write size is larger than the buffer size! (sz=" << std::hex << shm_ptr_->buffer_size << ", cur + req=" << std::dec << buf->writePos + written << ")"; 00733 return false; 00734 } 00735 TLOG(16) << "IncrementWritePos: buffer= " << buffer << ", writePos=" << buf->writePos << ", bytes written=" << written; 00736 buf->writePos += written; 00737 TLOG(16) << "IncrementWritePos: buffer= " << buffer << ", New writePos is " << buf->writePos; 00738 if (written == 0) Detach(true, "LogicError", "Cannot increment Write pos by 0!"); 00739 00740 return true; 00741 } 00742 00743 bool artdaq::SharedMemoryManager::MoreDataInBuffer(int buffer) 00744 { 00745 TLOG(17) << "MoreDataInBuffer(" << buffer << ") called."; 00746 00747 if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!"); 00748 00749 TLOG(TLVL_BUFLCK) << "MoreDataInBuffer obtaining buffer_mutex for buffer " << buffer; 00750 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]); 00751 TLOG(TLVL_BUFLCK) << "MoreDataInBuffer obtained buffer_mutex for buffer " << buffer; 00752 //TraceLock lk(buffer_mutexes_[buffer], 21, "MoreDataInBuffer" + std::to_string(buffer)); 00753 auto buf = getBufferInfo_(buffer); 00754 if (!buf) return false; 00755 TLOG(17) << "MoreDataInBuffer: buffer= " << buffer << ", readPos=" << std::to_string(buf->readPos) << ", writePos=" << buf->writePos; 00756 return buf->readPos < buf->writePos; 00757 } 00758 00759 bool artdaq::SharedMemoryManager::CheckBuffer(int buffer, BufferSemaphoreFlags flags) 00760 { 00761 if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!"); 00762 00763 TLOG(TLVL_BUFLCK) << "CheckBuffer obtaining buffer_mutex for buffer " << buffer; 00764 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]); 00765 TLOG(TLVL_BUFLCK) << "CheckBuffer obtained buffer_mutex for buffer " << buffer; 00766 //TraceLock lk(buffer_mutexes_[buffer], 22, "CheckBuffer" + std::to_string(buffer)); 00767 return checkBuffer_(getBufferInfo_(buffer), flags, false); 00768 } 00769 00770 void artdaq::SharedMemoryManager::MarkBufferFull(int buffer, int destination) 00771 { 00772 if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!"); 00773 00774 TLOG(TLVL_BUFLCK) << "MarkBufferFull obtaining buffer_mutex for buffer " << buffer; 00775 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]); 00776 TLOG(TLVL_BUFLCK) << "MarkBufferFull obtained buffer_mutex for buffer " << buffer; 00777 00778 //TraceLock lk(buffer_mutexes_[buffer], 23, "FillBuffer" + std::to_string(buffer)); 00779 auto shmBuf = getBufferInfo_(buffer); 00780 if (!shmBuf) return; 00781 touchBuffer_(shmBuf); 00782 if (shmBuf->sem_id == manager_id_) 00783 { 00784 if (shmBuf->sem != BufferSemaphoreFlags::Full) 00785 shmBuf->sem = BufferSemaphoreFlags::Full; 00786 00787 shmBuf->sem_id = destination; 00788 } 00789 } 00790 00791 void artdaq::SharedMemoryManager::MarkBufferEmpty(int buffer, bool force) 00792 { 00793 TLOG(18) << "MarkBufferEmpty BEGIN, buffer=" << buffer << ", force=" << force << ", manager_id_=" << manager_id_; 00794 if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!"); 00795 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]); 00796 //TraceLock lk(buffer_mutexes_[buffer], 24, "EmptyBuffer" + std::to_string(buffer)); 00797 auto shmBuf = getBufferInfo_(buffer); 00798 if (!shmBuf) return; 00799 if (!force) 00800 { 00801 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, true); 00802 } 00803 touchBuffer_(shmBuf); 00804 00805 shmBuf->readPos = 0; 00806 shmBuf->sem = BufferSemaphoreFlags::Full; 00807 00808 if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode)) 00809 { 00810 TLOG(18) << "MarkBufferEmpty Resetting buffer " << buffer << " to Empty state"; 00811 shmBuf->writePos = 0; 00812 shmBuf->sem = BufferSemaphoreFlags::Empty; 00813 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode) 00814 { 00815 TLOG(18) << "MarkBufferEmpty Broadcast mode; incrementing reader_pos from " << shm_ptr_->reader_pos << " to " << (buffer + 1) % shm_ptr_->buffer_count; 00816 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count; 00817 } 00818 } 00819 shmBuf->sem_id = -1; 00820 TLOG(18) << "MarkBufferEmpty END, buffer=" << buffer << ", force=" << force; 00821 ; 00822 } 00823 00824 bool artdaq::SharedMemoryManager::ResetBuffer(int buffer) 00825 { 00826 if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!"); 00827 00828 // ELF, 3/19/2019: These TRACE calls are a major performance hit with many buffers. 00829 //TLOG(TLVL_BUFLCK) << "ResetBuffer: obtaining buffer_mutex lock for buffer " << buffer; 00830 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]); 00831 //TLOG(TLVL_BUFLCK) << "ResetBuffer: obtained buffer_mutex lock for buffer " << buffer; 00832 00833 //TraceLock lk(buffer_mutexes_[buffer], 25, "ResetBuffer" + std::to_string(buffer)); 00834 auto shmBuf = getBufferInfo_(buffer); 00835 if (!shmBuf) return false; 00836 /* 00837 if (shmBuf->sequence_id < shm_ptr_->lowest_seq_id_read - size() && shmBuf->sem == BufferSemaphoreFlags::Full) 00838 { 00839 TLOG(TLVL_DEBUG) << "Buffer " << buffer << " has been passed by all readers, marking Empty" ; 00840 shmBuf->writePos = 0; 00841 shmBuf->sem = BufferSemaphoreFlags::Empty; 00842 shmBuf->sem_id = -1; 00843 return true; 00844 }*/ 00845 00846 size_t delta = TimeUtils::gettimeofday_us() - shmBuf->last_touch_time; 00847 if (delta > 0xFFFFFFFF) 00848 { 00849 TLOG(TLVL_TRACE) << "Buffer has touch time in the future, setting it to current time and ignoring..."; 00850 shmBuf->last_touch_time = TimeUtils::gettimeofday_us(); 00851 return false; 00852 } 00853 if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty) return false; 00854 TLOG(27) << "Buffer " << buffer << " at " << (void*)shmBuf << " is stale, time=" << TimeUtils::gettimeofday_us() << ", last touch=" << shmBuf->last_touch_time << ", d=" << delta << ", timeout=" << shm_ptr_->buffer_timeout_us; 00855 00856 if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing) 00857 { 00858 return true; 00859 } 00860 00861 if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full && (shmBuf->sequence_id < last_seen_id_ || manager_id_ == 0)) 00862 { 00863 TLOG(TLVL_DEBUG) << "Resetting old broadcast mode buffer " << buffer << " (seqid=" << shmBuf->sequence_id << "). State: Full-->Empty"; 00864 shmBuf->writePos = 0; 00865 shmBuf->sem = BufferSemaphoreFlags::Empty; 00866 shmBuf->sem_id = -1; 00867 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer)) shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count; 00868 return true; 00869 } 00870 00871 if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading) 00872 { 00873 // Ron wants to re-check for potential interleave of buffer state updates 00874 size_t delta = TimeUtils::gettimeofday_us() - shmBuf->last_touch_time; 00875 if (delta <= shm_ptr_->buffer_timeout_us) return false; 00876 TLOG(TLVL_WARNING) << "Stale Read buffer " << buffer << " at " << (void*)shmBuf 00877 << " ( " << delta << " / " << shm_ptr_->buffer_timeout_us << " us ) detected! (seqid=" 00878 << shmBuf->sequence_id << ") Resetting... Reading-->Full"; 00879 shmBuf->readPos = 0; 00880 shmBuf->sem = BufferSemaphoreFlags::Full; 00881 shmBuf->sem_id = -1; 00882 return true; 00883 } 00884 return false; 00885 } 00886 00887 bool artdaq::SharedMemoryManager::IsEndOfData() const 00888 { 00889 if (!IsValid()) return true; 00890 00891 struct shmid_ds info; 00892 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info); 00893 if (sts < 0) 00894 { 00895 TLOG(TLVL_TRACE) << "Error accessing Shared Memory info: " << errno << " (" << strerror(errno) << ")."; 00896 return true; 00897 } 00898 00899 if (info.shm_perm.mode & SHM_DEST) 00900 { 00901 TLOG(TLVL_INFO) << "Shared Memory marked for destruction. Probably an end-of-data condition!"; 00902 return true; 00903 } 00904 00905 return false; 00906 } 00907 00908 uint16_t artdaq::SharedMemoryManager::GetAttachedCount() const 00909 { 00910 if (!IsValid()) return 0; 00911 00912 struct shmid_ds info; 00913 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info); 00914 if (sts < 0) 00915 { 00916 TLOG(TLVL_TRACE) << "Error accessing Shared Memory info: " << errno << " (" << strerror(errno) << ")."; 00917 return 0; 00918 } 00919 00920 return info.shm_nattch; 00921 } 00922 00923 size_t artdaq::SharedMemoryManager::Write(int buffer, void* data, size_t size) 00924 { 00925 TLOG(19) << "Write BEGIN"; 00926 if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!"); 00927 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]); 00928 //TraceLock lk(buffer_mutexes_[buffer], 26, "WriteBuffer" + std::to_string(buffer)); 00929 auto shmBuf = getBufferInfo_(buffer); 00930 if (!shmBuf) return -1; 00931 checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing); 00932 touchBuffer_(shmBuf); 00933 TLOG(19) << "Buffer Write Pos is " << shmBuf->writePos << ", write size is " << size; 00934 if (shmBuf->writePos + size > shm_ptr_->buffer_size) 00935 { 00936 TLOG(TLVL_ERROR) << "Attempted to write more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size 00937 << ",writePos=" << shmBuf->writePos << ",writeSize=" << size; 00938 Detach(true, "SharedMemoryWrite", "Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!"); 00939 } 00940 00941 auto pos = GetWritePos(buffer); 00942 memcpy(pos, data, size); 00943 touchBuffer_(shmBuf); 00944 shmBuf->writePos = shmBuf->writePos + size; 00945 00946 auto last_seen = last_seen_id_.load(); 00947 while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {} 00948 00949 TLOG(19) << "Write END"; 00950 return size; 00951 } 00952 00953 bool artdaq::SharedMemoryManager::Read(int buffer, void* data, size_t size) 00954 { 00955 if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!"); 00956 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]); 00957 //TraceLock lk(buffer_mutexes_[buffer], 27, "ReadBuffer" + std::to_string(buffer)); 00958 auto shmBuf = getBufferInfo_(buffer); 00959 if (!shmBuf) return false; 00960 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading); 00961 touchBuffer_(shmBuf); 00962 if (shmBuf->readPos + size > shm_ptr_->buffer_size) 00963 { 00964 TLOG(TLVL_ERROR) << "Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size 00965 << ",readPos=" << shmBuf->readPos << ",readSize=" << size; 00966 Detach(true, "SharedMemoryRead", "Attempted to read more data than exists in Shared Memory!"); 00967 } 00968 00969 auto pos = GetReadPos(buffer); 00970 TLOG(TLVL_TRACE) << "Before memcpy in Read(), size is " << size; 00971 memcpy(data, pos, size); 00972 TLOG(TLVL_TRACE) << "After memcpy in Read()"; 00973 auto sts = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, false); 00974 if (sts) 00975 { 00976 shmBuf->readPos += size; 00977 touchBuffer_(shmBuf); 00978 return true; 00979 } 00980 return false; 00981 } 00982 00983 std::string artdaq::SharedMemoryManager::toString() 00984 { 00985 std::ostringstream ostr; 00986 ostr << "ShmStruct: " << std::endl 00987 << "Reader Position: " << shm_ptr_->reader_pos << std::endl 00988 << "Writer Position: " << shm_ptr_->writer_pos << std::endl 00989 << "Next ID Number: " << shm_ptr_->next_id << std::endl 00990 << "Buffer Count: " << shm_ptr_->buffer_count << std::endl 00991 << "Buffer Size: " << std::to_string(shm_ptr_->buffer_size) << " bytes" << std::endl 00992 << "Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl 00993 << "Rank of Writer: " << shm_ptr_->rank << std::endl 00994 << "Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::dec << std::endl 00995 << std::endl; 00996 00997 for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii) 00998 { 00999 auto buf = getBufferInfo_(ii); 01000 if (!buf) continue; 01001 01002 ostr << "ShmBuffer " << std::dec << ii << std::endl 01003 << "sequenceID: " << std::to_string(buf->sequence_id) << std::endl 01004 << "writePos: " << std::to_string(buf->writePos) << std::endl 01005 << "readPos: " << std::to_string(buf->readPos) << std::endl 01006 << "sem: " << FlagToString(buf->sem) << std::endl 01007 << "Owner: " << std::to_string(buf->sem_id.load()) << std::endl 01008 << "Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl 01009 << std::endl; 01010 } 01011 01012 return ostr.str(); 01013 } 01014 01015 void* artdaq::SharedMemoryManager::GetReadPos(int buffer) 01016 { 01017 auto buf = getBufferInfo_(buffer); 01018 if (!buf) return nullptr; 01019 return bufferStart_(buffer) + buf->readPos; 01020 } 01021 void* artdaq::SharedMemoryManager::GetWritePos(int buffer) 01022 { 01023 auto buf = getBufferInfo_(buffer); 01024 if (!buf) return nullptr; 01025 return bufferStart_(buffer) + buf->writePos; 01026 } 01027 01028 void* artdaq::SharedMemoryManager::GetBufferStart(int buffer) 01029 { 01030 return bufferStart_(buffer); 01031 } 01032 01033 std::vector<std::pair<int, artdaq::SharedMemoryManager::BufferSemaphoreFlags>> artdaq::SharedMemoryManager::GetBufferReport() 01034 { 01035 auto output = std::vector<std::pair<int, BufferSemaphoreFlags>>(size()); 01036 for (size_t ii = 0; ii < size(); ++ii) 01037 { 01038 auto buf = getBufferInfo_(ii); 01039 output[ii] = std::make_pair(buf->sem_id.load(), buf->sem.load()); 01040 } 01041 return output; 01042 } 01043 01044 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags, bool exceptions) 01045 { 01046 if (!buffer) 01047 { 01048 if (exceptions) 01049 { 01050 Detach(true, "BufferNotThereException", "Request to check buffer that does not exist!"); 01051 } 01052 return false; 01053 } 01054 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) << ")"; 01055 if (exceptions) 01056 { 01057 if (buffer->sem != flags) Detach(true, "StateAccessViolation", "Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) + ", actual " + FlagToString(buffer->sem) + ")"); 01058 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) + ")"); 01059 } 01060 bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags; 01061 01062 if (!ret) 01063 { 01064 TLOG(TLVL_WARNING) << "CheckBuffer detected issue with buffer " << buffer->sequence_id << "!" 01065 << " ID: " << buffer->sem_id << " (Expected " << manager_id_ << "), Flag: " << FlagToString(buffer->sem) << " (Expected " << FlagToString(flags) << "). " 01066 << "ID -1 is okay if expected flag is \"Full\" or \"Empty\"."; 01067 } 01068 01069 return ret; 01070 } 01071 01072 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer) 01073 { 01074 if (!buffer || (buffer->sem_id != -1 && buffer->sem_id != manager_id_)) return; 01075 TLOG(TLVL_TRACE) << "touchBuffer_: Touching buffer at " << (void*)buffer << " with sequence_id " << buffer->sequence_id; 01076 buffer->last_touch_time = TimeUtils::gettimeofday_us(); 01077 } 01078 01079 void artdaq::SharedMemoryManager::Detach(bool throwException, std::string category, std::string message, bool force) 01080 { 01081 TLOG(TLVL_DETACH) << "Detach BEGIN: throwException: " << std::boolalpha << throwException << ", force: " << force; 01082 if (IsValid()) 01083 { 01084 TLOG(TLVL_DETACH) << "Detach: Resetting owned buffers"; 01085 auto bufs = GetBuffersOwnedByManager(false); 01086 for (auto buf : bufs) 01087 { 01088 auto shmBuf = getBufferInfo_(buf); 01089 if (!shmBuf) continue; 01090 if (shmBuf->sem == BufferSemaphoreFlags::Writing) 01091 { 01092 shmBuf->sem = BufferSemaphoreFlags::Empty; 01093 } 01094 else if (shmBuf->sem == BufferSemaphoreFlags::Reading) 01095 { 01096 shmBuf->sem = BufferSemaphoreFlags::Full; 01097 } 01098 shmBuf->sem_id = -1; 01099 } 01100 } 01101 01102 if (shm_ptr_) 01103 { 01104 TLOG(TLVL_DETACH) << "Detach: Detaching shared memory"; 01105 shmdt(shm_ptr_); 01106 shm_ptr_ = NULL; 01107 } 01108 01109 if ((force || manager_id_ == 0) && shm_segment_id_ > -1) 01110 { 01111 TLOG(TLVL_DETACH) << "Detach: Marking Shared memory for removal"; 01112 shmctl(shm_segment_id_, IPC_RMID, NULL); 01113 shm_segment_id_ = -1; 01114 } 01115 01116 // Reset manager_id_ 01117 manager_id_ = -1; 01118 01119 if (category.size() > 0 && message.size() > 0) 01120 { 01121 TLOG(TLVL_ERROR) << category << ": " << message; 01122 01123 if (throwException) 01124 { 01125 throw cet::exception(category) << message; 01126 } 01127 } 01128 } 01129 01130 // Local Variables: 01131 // mode: c++ 01132 // End: