00001 #include <sys/shm.h>
00002 #include "artdaq/TransferPlugins/ShmemTransfer.hh"
00003
00004 #define SHMEM_SLEEP 0
00005
00006
00007
00008 artdaq::ShmemTransfer::ShmemTransfer(fhicl::ParameterSet const& pset, Role role) :
00009 TransferInterface(pset, role)
00010 , shm_segment_id_(-1)
00011 , shm_ptr_(NULL)
00012 , shm_key_(pset.get<int>("shm_key", std::hash<std::string>()(uniqueLabel())))
00013 , role_(role)
00014 {
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 if (buffer_count_ > 100)
00035 {
00036 throw cet::exception("ConfigurationException", "Buffer Count is too large for Shmem transfer!");
00037 }
00038
00039 size_t shmSize = buffer_count_ * max_fragment_size_words_ * sizeof(artdaq::RawDataType) + sizeof(ShmStruct);
00040
00041 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
00042
00043 if (shm_segment_id_ == -1)
00044 {
00045 shm_segment_id_ = shmget(shm_key_, shmSize, IPC_CREAT | 0666);
00046 }
00047
00048 TLOG_DEBUG(uniqueLabel()) << "shm_key == " << shm_key_ << ", shm_segment_id == " << shm_segment_id_ << TLOG_ENDL;
00049
00050 if (shm_segment_id_ > -1)
00051 {
00052 TLOG_DEBUG(uniqueLabel())
00053 << "Created/fetched shared memory segment with ID = " << shm_segment_id_
00054 << " and size " << shmSize
00055 << " bytes" << TLOG_ENDL;
00056 shm_ptr_ = (ShmStruct*)shmat(shm_segment_id_, 0, 0);
00057 TLOG_DEBUG(uniqueLabel())
00058 << "Attached to shared memory segment at address "
00059 << std::hex << shm_ptr_ << std::dec << TLOG_ENDL;
00060 if (shm_ptr_ && shm_ptr_ != (void *)-1)
00061 {
00062 if (role_ == Role::kReceive)
00063 {
00064 shm_ptr_->read_pos = 0;
00065 shm_ptr_->write_pos = 0;
00066 size_t offset = sizeof(ShmStruct);
00067 for (size_t ii = 0; ii < buffer_count_; ++ii)
00068 {
00069 TLOG_DEBUG(uniqueLabel()) << "Buffer " << ii << " is at 0x" << std::hex << offset << std::dec << TLOG_ENDL;
00070 shm_ptr_->buffers[ii].fragmentSizeWords = 0;
00071 shm_ptr_->buffers[ii].offset = offset;
00072 shm_ptr_->buffers[ii].sem = BUFFER_EMPTY;
00073 offset += max_fragment_size_words_ * sizeof(artdaq::RawDataType);
00074 }
00075 }
00076 }
00077 else
00078 {
00079 TLOG_ERROR(uniqueLabel()) << "Failed to attach to shared memory segment "
00080 << shm_segment_id_ << TLOG_ENDL;
00081 }
00082 }
00083 else
00084 {
00085 TLOG_ERROR(uniqueLabel()) << "Failed to connect to shared memory segment"
00086 << ", errno = " << errno << ". Please check "
00087 << "if a stale shared memory segment needs to "
00088 << "be cleaned up. (ipcs, ipcrm -m <segId>)" << TLOG_ENDL;
00089 }
00090 }
00091
00092 artdaq::ShmemTransfer::~ShmemTransfer()
00093 {
00094 TRACE(5, "ShmemTransfer::~ShmemTransfer called");
00095 if (shm_ptr_)
00096 {
00097 shmdt(shm_ptr_);
00098 shm_ptr_ = NULL;
00099 }
00100
00101 if (role_ == Role::kReceive && shm_segment_id_ > -1)
00102 {
00103 shmctl(shm_segment_id_, IPC_RMID, NULL);
00104 }
00105 TRACE(5, "ShmemTransfer::~ShmemTransfer done");
00106 }
00107
00108 bool artdaq::ShmemTransfer::readyForRead_()
00109 {
00110 return shm_ptr_->buffers[shm_ptr_->read_pos].sem == FRAGMENT_READY;
00111 }
00112
00113 bool artdaq::ShmemTransfer::readyForWrite_()
00114 {
00115 return shm_ptr_->buffers[shm_ptr_->write_pos].sem == BUFFER_EMPTY;
00116 }
00117
00118 int artdaq::ShmemTransfer::receiveFragment(artdaq::Fragment& fragment,
00119 size_t receiveTimeout)
00120 {
00121 if (shm_ptr_)
00122 {
00123 auto waitStart = std::chrono::steady_clock::now();
00124 while (!readyForRead_() && std::chrono::duration_cast<std::chrono::duration<size_t, std::ratio<1, 1000000>>>(std::chrono::steady_clock::now() - waitStart).count() < 1000)
00125 {
00126
00127 }
00128 if (!readyForRead_())
00129 {
00130 int64_t loopCount = 0;
00131 size_t sleepTime = 1000;
00132 int64_t nloops = (receiveTimeout - 1000) / sleepTime;
00133
00134 while (!readyForRead_() && loopCount < nloops)
00135 {
00136 usleep(sleepTime);
00137 ++loopCount;
00138 }
00139 }
00140
00141
00142
00143 if (readyForRead_())
00144 {
00145
00146
00147
00148
00149
00150
00151
00152 auto buf = &shm_ptr_->buffers[shm_ptr_->read_pos];
00153 auto initCount = buf->writeCount;
00154
00155 TRACE(TRANSFER_RECEIVE1, "Setting semaphore on buf %u", shm_ptr_->read_pos.load());
00156 buf->sem = READING_FRAGMENT;
00157 RawDataType* bufPtr = offsetToPtr(buf->offset);
00158
00159 fragment.resize(buf->fragmentSizeWords);
00160
00161 artdaq::RawDataType* fragAddr = fragment.headerAddress();
00162 size_t fragSize = fragment.size() * sizeof(artdaq::RawDataType);
00163
00164 memcpy(fragAddr, bufPtr, fragSize);
00165
00166
00167 auto wordsOfHeaderAndMetadata = &*fragment.dataBegin() - &*fragment.headerBegin();
00168 fragment.resize(buf->fragmentSizeWords - wordsOfHeaderAndMetadata);
00169
00170 if (buf->sem != READING_FRAGMENT || buf->writeCount != initCount)
00171 {
00172
00173 return RECV_TIMEOUT;
00174 }
00175
00176 shm_ptr_->read_pos++;
00177 if (shm_ptr_->read_pos >= buffer_count_) shm_ptr_->read_pos = 0;
00178 buf->sem = BUFFER_EMPTY;
00179
00180 if (fragment.type() != artdaq::Fragment::DataFragmentType)
00181 {
00182 TRACE(TRANSFER_RECEIVE2, "Recvd frag from shmem, type=%d, sequenceID=%zu, source_rank=%d", (int)fragment.type(), fragment.sequenceID(), source_rank());
00183 }
00184
00185 return source_rank();
00186 }
00187
00188 return artdaq::TransferInterface::RECV_TIMEOUT;
00189 }
00190 else
00191 {
00192 TLOG_ERROR(uniqueLabel()) << "Error in shared memory transfer plugin: pointer to shared memory segment is null, will sleep for "
00193 << receiveTimeout / 1.0e6 << " seconds and then return a timeout" << TLOG_ENDL;
00194 usleep(receiveTimeout);
00195 return artdaq::TransferInterface::RECV_TIMEOUT;
00196 }
00197 }
00198
00199 artdaq::TransferInterface::CopyStatus
00200 artdaq::ShmemTransfer::copyFragment(artdaq::Fragment& fragment, size_t send_timeout_usec)
00201 {
00202 return sendFragment(std::move(fragment), send_timeout_usec, false);
00203 }
00204
00205 artdaq::TransferInterface::CopyStatus
00206 artdaq::ShmemTransfer::moveFragment(artdaq::Fragment&& fragment,
00207 size_t send_timeout_usec)
00208 {
00209 return sendFragment(std::move(fragment), send_timeout_usec, true);
00210 }
00211
00212 artdaq::TransferInterface::CopyStatus
00213 artdaq::ShmemTransfer::sendFragment(artdaq::Fragment&& fragment, size_t send_timeout_usec, bool reliableMode)
00214 {
00215 if (!shm_ptr_) { return CopyStatus::kErrorNotRequiringException; }
00216
00217
00218
00219 if (send_timeout_usec > 0)
00220 {
00221 auto waitStart = std::chrono::steady_clock::now();
00222 while (!readyForWrite_() && std::chrono::duration_cast<std::chrono::duration<size_t, std::ratio<1, 1000000>>>(std::chrono::steady_clock::now() - waitStart).count() < 1000)
00223 {
00224
00225 }
00226 if (!readyForWrite_())
00227 {
00228 int64_t loopCount = 0;
00229 size_t sleepTime = 1000;
00230 int64_t nloops = (send_timeout_usec - 1000) / sleepTime;
00231
00232 while (reliableMode && !readyForWrite_() && loopCount < nloops)
00233 {
00234 usleep(sleepTime);
00235 ++loopCount;
00236 }
00237 }
00238 }
00239
00240
00241
00242
00243 if ((reliableMode && readyForWrite_()) || !reliableMode)
00244 {
00245 TRACE(TRANSFER_SEND2, "Sending fragment with seqID=%zu", fragment.sequenceID());
00246 artdaq::RawDataType* fragAddr = fragment.headerAddress();
00247 size_t fragSize = fragment.size() * sizeof(artdaq::RawDataType);
00248
00249
00250
00251 if (fragment.type() != artdaq::Fragment::InvalidFragmentType && fragSize < (max_fragment_size_words_ * sizeof(artdaq::RawDataType)))
00252 {
00253 auto buf = &shm_ptr_->buffers[shm_ptr_->write_pos];
00254
00255 buf->sem = WRITING_FRAGMENT;
00256
00257 memcpy(offsetToPtr(buf->offset), fragAddr, fragSize);
00258
00259 buf->fragmentSizeWords = fragment.size();
00260
00261 shm_ptr_->write_pos++;
00262 if (shm_ptr_->write_pos >= buffer_count_) shm_ptr_->write_pos = 0;
00263 if (shm_ptr_->write_pos == shm_ptr_->read_pos && !reliableMode)
00264 {
00265 shm_ptr_->read_pos++;
00266 if (shm_ptr_->read_pos >= buffer_count_) shm_ptr_->read_pos = 0;
00267 }
00268 buf->sem = FRAGMENT_READY;
00269
00270 return CopyStatus::kSuccess;
00271 }
00272 else
00273 {
00274 TLOG_WARNING(uniqueLabel()) << "Fragment invalid for shared memory! "
00275 << "fragment address and size = "
00276 << fragAddr << " " << fragSize << " "
00277 << "sequence ID, fragment ID, and type = "
00278 << fragment.sequenceID() << " "
00279 << fragment.fragmentID() << " "
00280 << ((int)fragment.type()) << TLOG_ENDL;
00281 return CopyStatus::kErrorNotRequiringException;
00282 }
00283 }
00284
00285 return CopyStatus::kTimeout;
00286 }
00287
00288 artdaq::RawDataType* artdaq::ShmemTransfer::offsetToPtr(size_t offset)
00289 {
00290 auto res = reinterpret_cast<RawDataType*>(reinterpret_cast<uint8_t*>(shm_ptr_) + offset);
00291
00292 return res;
00293 }
00294
00295 DEFINE_ARTDAQ_TRANSFER(artdaq::ShmemTransfer)
00296
00297
00298
00299