rogue
Loading...
Searching...
No Matches
Transaction.cpp
Go to the documentation of this file.
1
17#include "rogue/Directives.h"
18
20
21#include <inttypes.h>
22#include <stdarg.h>
23#include <sys/time.h>
24
25#include <cstdio>
26#include <memory>
27#include <string>
28
29#include "rogue/GeneralError.h"
30#include "rogue/GilRelease.h"
31#include "rogue/ScopedGil.h"
35
37
38#ifndef NO_PYTHON
39 #include <boost/python.hpp>
40namespace bp = boost::python;
41#endif
42
43// Init class counter
44uint32_t rim::Transaction::classIdx_ = 0;
45
47std::mutex rim::Transaction::classMtx_;
48
50rim::TransactionPtr rim::Transaction::create(struct timeval timeout) {
51 rim::TransactionPtr m = std::make_shared<rim::Transaction>(timeout);
52 return (m);
53}
54
55void rim::Transaction::setup_python() {
56#ifndef NO_PYTHON
57 bp::class_<rim::Transaction, rim::TransactionPtr, boost::noncopyable>("Transaction", bp::no_init)
58 .def("lock", &rim::Transaction::lock)
59 .def("id", &rim::Transaction::id)
60 .def("address", &rim::Transaction::address)
61 .def("size", &rim::Transaction::size)
62 .def("type", &rim::Transaction::type)
63 .def("done", &rim::Transaction::done)
64 .def("error", &rim::Transaction::errorStr)
65 .def("expired", &rim::Transaction::expired)
66 .def("setData", &rim::Transaction::setData)
67 .def("getData", &rim::Transaction::getData);
68#endif
69}
70
72rim::Transaction::Transaction(struct timeval timeout) : timeout_(timeout) {
73 gettimeofday(&startTime_, NULL);
74
75 endTime_.tv_sec = 0;
76 endTime_.tv_usec = 0;
77 warnTime_.tv_sec = 0;
78 warnTime_.tv_usec = 0;
79
80 pyValid_ = false;
81
82 iter_ = NULL;
83 address_ = 0;
84 size_ = 0;
85 type_ = 0;
86 error_ = "";
87 done_ = false;
88
89 isSubTransaction_ = false;
91
92 log_ = rogue::Logging::create("memory.Transaction", true);
93
94 classMtx_.lock();
95 if (classIdx_ == 0) classIdx_ = 1;
96 id_ = classIdx_;
97 classIdx_++;
98 classMtx_.unlock();
99}
100
102rim::Transaction::~Transaction() {}
103
105rim::TransactionLockPtr rim::Transaction::lock() {
106 return (rim::TransactionLock::create(shared_from_this()));
107}
108
110bool rim::Transaction::expired() {
111 bool done = false;
112 if (isSubTransaction_) {
113 done = parentTransaction_.expired();
114 }
115 return done || (iter_ == NULL || done_);
116}
117
119uint32_t rim::Transaction::id() {
120 return id_;
121}
122
124uint64_t rim::Transaction::address() {
125 return address_;
126}
127
129uint32_t rim::Transaction::size() {
130 return size_;
131}
132
134uint32_t rim::Transaction::type() {
135 return type_;
136}
137
139rim::TransactionPtr rim::Transaction::createSubTransaction() {
140 // Create a new transaction and set up pointers back and forth
141 rim::TransactionPtr subTran = std::make_shared<rim::Transaction>(timeout_);
142 subTran->parentTransaction_ = shared_from_this();
143 subTran->isSubTransaction_ = true;
144 subTranMap_[subTran->id()] = subTran;
145 log_->debug("Created subTransaction id=%" PRIu32 ", parent=%" PRIu32, subTran->id_, this->id_);
146
147 // Should subtransactions be given default address identical to parent?
148 return (subTran);
149}
150
151void rim::Transaction::doneSubTransactions() {
152 doneCreatingSubTransactions_ = true;
153}
154
156void rim::Transaction::done() {
157 log_->debug("Transaction done. type=%" PRIu32 " id=%" PRIu32 ", address=0x%016" PRIx64 ", size=%" PRIu32,
158 type_,
159 id_,
160 address_,
161 size_);
162
163 error_ = "";
164 done_ = true;
165 cond_.notify_all();
166
167 // If applicable, notify parent transaction about completion of a sub-transaction
168 if (isSubTransaction_) {
169 // Get a shared_ptr to the parent transaction
170 rim::TransactionPtr parentTran = this->parentTransaction_.lock();
171 if (parentTran) {
172 // Remove own ID from parent subtransaction map
173 parentTran->subTranMap_.erase(id_);
174
175 // If this is the last sub-transaction, notify parent transaction it is all done
176 if (parentTran->subTranMap_.empty() && parentTran->doneCreatingSubTransactions_) parentTran->done();
177 }
178 }
179}
180
182void rim::Transaction::errorStr(std::string error) {
183 error_ += error;
184 done_ = true;
185
186 log_->debug("Transaction error. type=%" PRIu32 " id=%" PRIu32 ", address=0x%016" PRIx64 ", size=%" PRIu32
187 ", error=%s",
188 type_,
189 id_,
190 address_,
191 size_,
192 error_.c_str());
193
194 cond_.notify_all();
195
196 // If applicable, notify parent transaction about completion of a sub-transaction
197 if (isSubTransaction_) {
198 // Get a shared_ptr to the parent transaction
199 rim::TransactionPtr parentTran = parentTransaction_.lock();
200 if (parentTran) {
201 // Remove own ID from parent subtransaction map
202 parentTran->subTranMap_.erase(id_);
203
204 // If this is the last sub-transaction, notify parent transaction it is all done
205 if (parentTran->subTranMap_.empty() && parentTran->doneCreatingSubTransactions_)
206 parentTran->error("Transaction error. Subtransaction %" PRIu32 " failed with error: %s.\n",
207 id_,
208 error.c_str());
209 }
210 }
211}
212
214void rim::Transaction::error(const char* fmt, ...) {
215 va_list args;
216 char buffer[10000];
217
218 va_start(args, fmt);
219 vsnprintf(buffer, sizeof(buffer), fmt, args);
220 va_end(args);
221
222 errorStr(std::string(buffer));
223}
224
226std::string rim::Transaction::wait() {
227 struct timeval currTime;
228
229 std::unique_lock<std::mutex> lock(lock_);
230
231 while (!done_) {
232 // Timeout?
233 gettimeofday(&currTime, NULL);
234 if (endTime_.tv_sec != 0 && endTime_.tv_usec != 0 && timercmp(&currTime, &(endTime_), >)) {
235 done_ = true;
236 error_ = "Timeout waiting for register transaction " + std::to_string(id_) + " message response.";
237
238 log_->debug("Transaction timeout. type=%" PRIu32 " id=%" PRIu32 ", address=0x%" PRIx64 ", size=%" PRIu32,
239 type_,
240 id_,
241 address_,
242 size_);
243 } else {
244 cond_.wait_for(lock, std::chrono::microseconds(1000));
245 }
246 }
247
248 // Reset
249 if (pyValid_) {
251#ifndef NO_PYTHON
252 PyBuffer_Release(&(pyBuf_));
253#endif
254 }
255 iter_ = NULL;
256 pyValid_ = false;
257
258 return (error_);
259}
260
262void rim::Transaction::refreshTimer(rim::TransactionPtr ref) {
263 struct timeval currTime;
264 struct timeval nextTime;
265
266 gettimeofday(&currTime, NULL);
267 std::lock_guard<std::mutex> lock(lock_);
268
269 // Refresh if start time is later then the reference
270 if (ref == NULL || timercmp(&startTime_, &(ref->startTime_), >=)) {
271 timeradd(&currTime, &timeout_, &endTime_);
272
273 if (warnTime_.tv_sec == 0 && warnTime_.tv_usec == 0) {
274 warnTime_ = endTime_;
275 } else if (timercmp(&warnTime_, &currTime, >=)) {
276 log_->warning("Transaction timer refresh! Possible slow link! type=%" PRIu32 " id=%" PRIu32
277 ", address=0x%016" PRIx64 ", size=%" PRIu32,
278 type_,
279 id_,
280 address_,
281 size_);
282 warnTime_ = endTime_;
283 }
284 }
285}
286
288rim::Transaction::iterator rim::Transaction::begin() {
289 if (iter_ == NULL) throw(rogue::GeneralError("Transaction::begin", "Invalid data"));
290 return iter_;
291}
292
294rim::Transaction::iterator rim::Transaction::end() {
295 if (iter_ == NULL) throw(rogue::GeneralError("Transaction::end", "Invalid data"));
296 return iter_ + size_;
297}
298
299#ifndef NO_PYTHON
300
302void rim::Transaction::setData(boost::python::object p, uint32_t offset) {
303 Py_buffer pyBuf;
304
305 if (PyObject_GetBuffer(p.ptr(), &pyBuf, PyBUF_SIMPLE) < 0)
306 throw(rogue::GeneralError("Transaction::setData", "Python Buffer Error In Frame"));
307
308 uint32_t count = pyBuf.len;
309
310 if ((offset + count) > size_) {
311 PyBuffer_Release(&pyBuf);
312 throw(rogue::GeneralError::create("Transaction::setData",
313 "Attempt to set %" PRIu32 " bytes at offset %" PRIu32
314 " to python buffer with size %" PRIu32,
315 count,
316 offset,
317 size_));
318 }
319
320 std::memcpy(begin() + offset, reinterpret_cast<uint8_t*>(pyBuf.buf), count);
321 PyBuffer_Release(&pyBuf);
322}
323
325void rim::Transaction::getData(boost::python::object p, uint32_t offset) {
326 Py_buffer pyBuf;
327
328 if (PyObject_GetBuffer(p.ptr(), &pyBuf, PyBUF_CONTIG) < 0)
329 throw(rogue::GeneralError("Transaction::getData", "Python Buffer Error In Frame"));
330
331 uint32_t count = pyBuf.len;
332
333 if ((offset + count) > size_) {
334 PyBuffer_Release(&pyBuf);
335 throw(rogue::GeneralError::create("Transaction::getData",
336 "Attempt to get %" PRIu32 " bytes from offset %" PRIu32
337 " to python buffer with size %" PRIu32,
338 count,
339 offset,
340 size_));
341 }
342
343 std::memcpy(reinterpret_cast<uint8_t*>(pyBuf.buf), begin() + offset, count);
344 PyBuffer_Release(&pyBuf);
345}
346
347#endif
Generic Rogue exception type.
static GeneralError create(std::string src, const char *fmt,...)
Creates a formatted error instance.
static std::shared_ptr< rogue::Logging > create(const std::string &name, bool quiet=false)
Creates a logger instance.
Definition Logging.cpp:60
RAII helper that acquires the Python GIL for a scope.
Definition ScopedGil.h:35
std::shared_ptr< rogue::Logging > log_
std::shared_ptr< rogue::interfaces::memory::TransactionLock > TransactionLockPtr
Shared pointer alias for TransactionLock.
std::shared_ptr< rogue::interfaces::memory::Transaction > TransactionPtr
Shared pointer alias for Transaction.