Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:12:22

0001 /*
0002  *  callback_t.cc
0003  *  EDMProto
0004  *  Created by Chris Jones on 4/17/05.
0005  *  Changed by Viji Sundararajan on 03-Jul-05.
0006  *
0007  */
0008 
0009 #include "cppunit/extensions/HelperMacros.h"
0010 #include "FWCore/Utilities/interface/do_nothing_deleter.h"
0011 #include "FWCore/Framework/interface/Callback.h"
0012 #include "FWCore/Framework/interface/ESProducts.h"
0013 #include "FWCore/Framework/interface/ComponentDescription.h"
0014 #include "FWCore/Concurrency/interface/ThreadsController.h"
0015 #include "FWCore/Concurrency/interface/WaitingTaskHolder.h"
0016 #include "FWCore/Concurrency/interface/FinalWaitingTask.h"
0017 
0018 #include "FWCore/Framework/interface/eventsetuprecord_registration_macro.h"
0019 
0020 #include <memory>
0021 #include <cassert>
0022 
0023 namespace callbacktest {
0024   struct Data {
0025     Data() : value_(0) {}
0026     Data(int iValue) : value_(iValue) {}
0027     virtual ~Data() {}
0028     int value_;
0029   };
0030 
0031   struct Double {
0032     Double() : value_(0) {}
0033     Double(double iValue) : value_(iValue) {}
0034     virtual ~Double() {}
0035     double value_;
0036   };
0037 
0038   struct Record {
0039     void setImpl(void const* iImpl,
0040                  unsigned int transitionID,
0041                  void const* getTokenIndices,
0042                  void const* iEventSetupImpl,
0043                  void const* ESParentContext) {}
0044     constexpr static bool allowConcurrentIOVs_ = false;
0045   };
0046 
0047   struct Queue {
0048     template <typename T>
0049     void push(oneapi::tbb::task_group&, T&& iT) {
0050       iT();
0051     }
0052   };
0053 
0054   struct Base {
0055     template <typename A, typename B>
0056     std::optional<std::vector<edm::ESResolverIndex>> updateFromMayConsumes(A const&, B const&) const {
0057       return {};
0058     }
0059     static constexpr edm::ESResolverIndex const* getTokenIndices(unsigned int) { return nullptr; }
0060     static constexpr edm::ESRecordIndex const* getTokenRecordIndices(unsigned int) { return nullptr; }
0061     static constexpr size_t numberOfTokenIndices(unsigned int) { return 0; }
0062     static constexpr bool hasMayConsumes() { return false; }
0063     static edm::eventsetup::ComponentDescription const& description() {
0064       static const edm::eventsetup::ComponentDescription s_description;
0065       return s_description;
0066     }
0067 
0068     Queue queue() { return Queue(); }
0069   };
0070 
0071   struct UniquePtrProd : public Base {
0072     constexpr UniquePtrProd() : value_(0) {}
0073     std::unique_ptr<Data> method(const Record&) {
0074       ++value_;
0075       if (produce_)
0076         return std::make_unique<Data>(value_);
0077       else
0078         return nullptr;
0079     }
0080 
0081     int value_;
0082     bool produce_ = true;
0083   };
0084 
0085   struct SharedPtrProd : public Base {
0086     SharedPtrProd() : ptr_(new Data()) {}
0087     std::shared_ptr<Data> method(const Record&) {
0088       ++ptr_->value_;
0089       if (produce_)
0090         return ptr_;
0091       else
0092         return nullptr;
0093     }
0094     std::shared_ptr<Data> ptr_;
0095     bool produce_ = true;
0096   };
0097 
0098   struct OptionalProd : public Base {
0099     constexpr OptionalProd() : value_(0) {}
0100     std::optional<Data> method(const Record&) {
0101       ++value_;
0102       if (produce_)
0103         return Data(value_);
0104       else
0105         return {};
0106     }
0107 
0108     int value_;
0109     bool produce_ = true;
0110   };
0111 
0112   struct PtrProductsProd : public Base {
0113     PtrProductsProd() : data_(), double_() {}
0114     edm::ESProducts<std::shared_ptr<Data>, std::shared_ptr<Double>> method(const Record&) {
0115       using namespace edm::es;
0116       auto dataT = std::shared_ptr<Data>(&data_, edm::do_nothing_deleter());
0117       auto doubleT = std::shared_ptr<Double>(&double_, edm::do_nothing_deleter());
0118       ++data_.value_;
0119       ++double_.value_;
0120       return products(dataT, doubleT);
0121     }
0122 
0123     Data data_;
0124     Double double_;
0125   };
0126 }  // namespace callbacktest
0127 
0128 EVENTSETUP_RECORD_REG(callbacktest::Record);
0129 
0130 namespace {
0131   template <typename CALLBACK>
0132   void call(CALLBACK& iCallback) {
0133     edm::ActivityRegistry ar;
0134     edm::eventsetup::EventSetupRecordImpl rec(edm::eventsetup::EventSetupRecordKey::makeKey<callbacktest::Record>(),
0135                                               &ar);
0136     oneapi::tbb::task_group group;
0137     edm::FinalWaitingTask task{group};
0138     edm::ServiceToken token;
0139     iCallback.prefetchAsync(edm::WaitingTaskHolder(group, &task), &rec, nullptr, token, edm::ESParentContext{});
0140     task.wait();
0141   }
0142 }  // namespace
0143 
0144 using namespace callbacktest;
0145 using namespace edm::eventsetup;
0146 
0147 class testCallback : public CppUnit::TestFixture {
0148   CPPUNIT_TEST_SUITE(testCallback);
0149 
0150   CPPUNIT_TEST(uniquePtrTest);
0151   CPPUNIT_TEST(sharedPtrTest);
0152   CPPUNIT_TEST(optionalTest);
0153   CPPUNIT_TEST(ptrProductsTest);
0154 
0155   CPPUNIT_TEST(uniquePtrLambdaTest);
0156   CPPUNIT_TEST(uniquePtrLambdaCaptureTest);
0157   CPPUNIT_TEST(sharedPtrLambdaTest);
0158   CPPUNIT_TEST(optionalLambdaTest);
0159   CPPUNIT_TEST(ptrProductsLambdaTest);
0160 
0161   CPPUNIT_TEST_SUITE_END();
0162 
0163 public:
0164   void setUp() { m_scheduler = std::make_unique<edm::ThreadsController>(1); }
0165   void tearDown() {}
0166 
0167   void uniquePtrTest();
0168   void sharedPtrTest();
0169   void optionalTest();
0170   void ptrProductsTest();
0171 
0172   void uniquePtrLambdaTest();
0173   void uniquePtrLambdaCaptureTest();
0174   void sharedPtrLambdaTest();
0175   void optionalLambdaTest();
0176   void ptrProductsLambdaTest();
0177 
0178 private:
0179   edm::propagate_const<std::unique_ptr<edm::ThreadsController>> m_scheduler;
0180 };
0181 
0182 ///registration of the test so that the runner can find it
0183 CPPUNIT_TEST_SUITE_REGISTRATION(testCallback);
0184 
0185 template <typename P, typename F>
0186 using UniquePtrCallbackT = Callback<P, F, std::unique_ptr<Data>, Record>;
0187 
0188 void testCallback::uniquePtrTest() {
0189   UniquePtrProd prod;
0190 
0191   auto func = [&prod](Record const& rec) { return prod.method(rec); };
0192   using UniquePtrCallback = UniquePtrCallbackT<UniquePtrProd, decltype(func)>;
0193   UniquePtrCallback callback(&prod, func, 0);
0194   std::unique_ptr<Data> handle;
0195   callback.holdOntoPointer(&handle);
0196 
0197   auto callback2 = std::unique_ptr<UniquePtrCallback>(callback.clone());
0198   std::unique_ptr<Data> handle2;
0199   callback2->holdOntoPointer(&handle2);
0200 
0201   callback.newRecordComing();
0202   call(callback);
0203   CPPUNIT_ASSERT(0 != handle.get());
0204   CPPUNIT_ASSERT(prod.value_ == 1);
0205   assert(0 != handle.get());
0206   CPPUNIT_ASSERT(prod.value_ == handle->value_);
0207 
0208   //since haven't cleared, should not have changed
0209   call(callback);
0210   CPPUNIT_ASSERT(prod.value_ == 1);
0211   CPPUNIT_ASSERT(prod.value_ == handle->value_);
0212 
0213   handle.release();
0214 
0215   callback.newRecordComing();
0216 
0217   call(callback);
0218   CPPUNIT_ASSERT(0 != handle.get());
0219   CPPUNIT_ASSERT(prod.value_ == 2);
0220   assert(0 != handle.get());
0221   CPPUNIT_ASSERT(prod.value_ == handle->value_);
0222 
0223   call(*callback2);
0224   CPPUNIT_ASSERT(handle2->value_ == 3);
0225   CPPUNIT_ASSERT(handle->value_ == 2);
0226 
0227   call(callback);
0228   call(*callback2);
0229   CPPUNIT_ASSERT(handle2->value_ == 3);
0230   CPPUNIT_ASSERT(handle->value_ == 2);
0231 
0232   callback2->newRecordComing();
0233   call(callback);
0234   call(*callback2);
0235   CPPUNIT_ASSERT(handle2->value_ == 4);
0236   CPPUNIT_ASSERT(handle->value_ == 2);
0237 
0238   callback.newRecordComing();
0239   call(callback);
0240   call(*callback2);
0241   CPPUNIT_ASSERT(handle2->value_ == 4);
0242   CPPUNIT_ASSERT(handle->value_ == 5);
0243 
0244   // null products
0245   prod.produce_ = false;
0246   callback.newRecordComing();
0247   call(callback);
0248   call(*callback2);
0249   CPPUNIT_ASSERT(handle2->value_ == 4);
0250   CPPUNIT_ASSERT(handle.get() == nullptr);
0251 
0252   callback2->newRecordComing();
0253   call(callback);
0254   call(*callback2);
0255   CPPUNIT_ASSERT(handle2.get() == nullptr);
0256   CPPUNIT_ASSERT(handle.get() == nullptr);
0257 
0258   prod.produce_ = true;
0259   callback.newRecordComing();
0260   call(callback);
0261   call(*callback2);
0262   CPPUNIT_ASSERT(handle2.get() == nullptr);
0263   CPPUNIT_ASSERT(handle->value_ == 8);
0264 
0265   callback2->newRecordComing();
0266   call(callback);
0267   call(*callback2);
0268   CPPUNIT_ASSERT(handle2->value_ == 9);
0269   CPPUNIT_ASSERT(handle->value_ == 8);
0270 }
0271 
0272 template <typename P, typename F>
0273 using SharedPtrCallbackT = Callback<P, F, std::shared_ptr<Data>, Record>;
0274 
0275 void testCallback::sharedPtrTest() {
0276   SharedPtrProd prod;
0277 
0278   auto func = [&prod](Record const& rec) { return prod.method(rec); };
0279 
0280   using SharedPtrCallback = SharedPtrCallbackT<SharedPtrProd, decltype(func)>;
0281   SharedPtrCallback callback(&prod, func, 0);
0282   std::shared_ptr<Data> handle;
0283 
0284   callback.holdOntoPointer(&handle);
0285 
0286   callback.newRecordComing();
0287   call(callback);
0288   CPPUNIT_ASSERT(handle.get() == prod.ptr_.get());
0289   CPPUNIT_ASSERT(prod.ptr_->value_ == 1);
0290 
0291   //since haven't cleared, should not have changed
0292   call(callback);
0293   CPPUNIT_ASSERT(handle.get() == prod.ptr_.get());
0294   CPPUNIT_ASSERT(prod.ptr_->value_ == 1);
0295 
0296   handle.reset();
0297   callback.newRecordComing();
0298 
0299   call(callback);
0300   CPPUNIT_ASSERT(handle.get() == prod.ptr_.get());
0301   CPPUNIT_ASSERT(prod.ptr_->value_ == 2);
0302 
0303   // null products
0304   prod.produce_ = false;
0305   callback.newRecordComing();
0306   call(callback);
0307   CPPUNIT_ASSERT(handle.get() == nullptr);
0308 
0309   call(callback);
0310   CPPUNIT_ASSERT(handle.get() == nullptr);
0311 
0312   prod.produce_ = true;
0313   callback.newRecordComing();
0314   call(callback);
0315   CPPUNIT_ASSERT(handle->value_ == 4);
0316 }
0317 
0318 template <typename P, typename F>
0319 using OptionalCallbackT = Callback<P, F, std::optional<Data>, Record>;
0320 
0321 void testCallback::optionalTest() {
0322   OptionalProd prod;
0323 
0324   auto func = [&prod](Record const& rec) { return prod.method(rec); };
0325 
0326   using OptionalCallback = OptionalCallbackT<OptionalProd, decltype(func)>;
0327   OptionalCallback callback(&prod, func, 0);
0328   std::optional<Data> handle;
0329 
0330   callback.holdOntoPointer(&handle);
0331 
0332   callback.newRecordComing();
0333   call(callback);
0334   CPPUNIT_ASSERT(handle.has_value());
0335   CPPUNIT_ASSERT(prod.value_ == 1);
0336   CPPUNIT_ASSERT(prod.value_ == handle->value_);
0337 
0338   //since haven't cleared, should not have changed
0339   call(callback);
0340   CPPUNIT_ASSERT(handle.has_value());
0341   CPPUNIT_ASSERT(prod.value_ == 1);
0342   CPPUNIT_ASSERT(prod.value_ == handle->value_);
0343 
0344   handle.reset();
0345   callback.newRecordComing();
0346 
0347   call(callback);
0348   CPPUNIT_ASSERT(handle.has_value());
0349   CPPUNIT_ASSERT(prod.value_ == 2);
0350   CPPUNIT_ASSERT(prod.value_ == handle->value_);
0351 
0352   // null products
0353   prod.produce_ = false;
0354   callback.newRecordComing();
0355   call(callback);
0356   CPPUNIT_ASSERT(not handle.has_value());
0357 
0358   call(callback);
0359   CPPUNIT_ASSERT(not handle.has_value());
0360 
0361   prod.produce_ = true;
0362   callback.newRecordComing();
0363   call(callback);
0364   CPPUNIT_ASSERT(handle->value_ == 4);
0365 }
0366 
0367 template <typename P, typename F>
0368 using PtrProductsCallbackT = Callback<P, F, edm::ESProducts<std::shared_ptr<Data>, std::shared_ptr<Double>>, Record>;
0369 
0370 void testCallback::ptrProductsTest() {
0371   PtrProductsProd prod;
0372 
0373   auto func = [&prod](Record const& rec) { return prod.method(rec); };
0374 
0375   using PtrProductsCallback = PtrProductsCallbackT<PtrProductsProd, decltype(func)>;
0376   PtrProductsCallback callback(&prod, func, 0);
0377   std::shared_ptr<Data> handle;
0378   std::shared_ptr<Double> doubleHandle;
0379 
0380   callback.holdOntoPointer(&handle);
0381   callback.holdOntoPointer(&doubleHandle);
0382 
0383   callback.newRecordComing();
0384   call(callback);
0385   CPPUNIT_ASSERT(handle.get() == &(prod.data_));
0386   CPPUNIT_ASSERT(prod.data_.value_ == 1);
0387 
0388   //since haven't cleared, should not have changed
0389   call(callback);
0390   CPPUNIT_ASSERT(handle.get() == &(prod.data_));
0391   CPPUNIT_ASSERT(prod.data_.value_ == 1);
0392 
0393   callback.newRecordComing();
0394 
0395   call(callback);
0396   CPPUNIT_ASSERT(handle.get() == &(prod.data_));
0397   CPPUNIT_ASSERT(prod.data_.value_ == 2);
0398 }
0399 
0400 // Lambda tests
0401 
0402 void testCallback::uniquePtrLambdaTest() {
0403   Base prod;
0404 
0405   int value = 0;
0406   auto func = [&value](Record const& rec) mutable { return std::make_unique<Data>(++value); };
0407   using UniquePtrCallback = UniquePtrCallbackT<Base, decltype(func)>;
0408   UniquePtrCallback callback(&prod, func, 0);
0409   std::unique_ptr<Data> handle;
0410   callback.holdOntoPointer(&handle);
0411 
0412   auto callback2 = std::unique_ptr<UniquePtrCallback>(callback.clone());
0413   std::unique_ptr<Data> handle2;
0414   callback2->holdOntoPointer(&handle2);
0415 
0416   callback.newRecordComing();
0417   call(callback);
0418   CPPUNIT_ASSERT(0 != handle.get());
0419   CPPUNIT_ASSERT(value == 1);
0420   CPPUNIT_ASSERT(value == handle->value_);
0421 
0422   //since haven't cleared, should not have changed
0423   call(callback);
0424   CPPUNIT_ASSERT(value == 1);
0425   CPPUNIT_ASSERT(value == handle->value_);
0426 
0427   handle.release();
0428 
0429   callback.newRecordComing();
0430 
0431   call(callback);
0432   CPPUNIT_ASSERT(0 != handle.get());
0433   CPPUNIT_ASSERT(value == 2);
0434   CPPUNIT_ASSERT(value == handle->value_);
0435 
0436   call(*callback2);
0437   CPPUNIT_ASSERT(handle2->value_ == 3);
0438   CPPUNIT_ASSERT(handle->value_ == 2);
0439 
0440   call(callback);
0441   call(*callback2);
0442   CPPUNIT_ASSERT(handle2->value_ == 3);
0443   CPPUNIT_ASSERT(handle->value_ == 2);
0444 
0445   callback2->newRecordComing();
0446   call(callback);
0447   call(*callback2);
0448   CPPUNIT_ASSERT(handle2->value_ == 4);
0449   CPPUNIT_ASSERT(handle->value_ == 2);
0450 
0451   callback.newRecordComing();
0452   call(callback);
0453   call(*callback2);
0454   CPPUNIT_ASSERT(handle2->value_ == 4);
0455   CPPUNIT_ASSERT(handle->value_ == 5);
0456 }
0457 
0458 void testCallback::uniquePtrLambdaCaptureTest() {
0459   // The difference wrt uniquePtrLambdaTest is that the 'value' is
0460   // stored only in the lambda capture
0461   Base prod;
0462 
0463   auto func = [value = int(0)](Record const& rec) mutable { return std::make_unique<Data>(++value); };
0464   using UniquePtrCallback = UniquePtrCallbackT<Base, decltype(func)>;
0465   UniquePtrCallback callback(&prod, func, 0);
0466   std::unique_ptr<Data> handle;
0467   callback.holdOntoPointer(&handle);
0468 
0469   auto callback2 = std::unique_ptr<UniquePtrCallback>(callback.clone());
0470   std::unique_ptr<Data> handle2;
0471   callback2->holdOntoPointer(&handle2);
0472 
0473   callback.newRecordComing();
0474   call(callback);
0475   CPPUNIT_ASSERT(0 != handle.get());
0476   CPPUNIT_ASSERT(handle->value_ == 1);
0477 
0478   //since haven't cleared, should not have changed
0479   call(callback);
0480   CPPUNIT_ASSERT(handle->value_ == 1);
0481 
0482   handle.release();
0483 
0484   callback.newRecordComing();
0485 
0486   call(callback);
0487   CPPUNIT_ASSERT(handle->value_ == 2);
0488 
0489   call(*callback2);
0490   CPPUNIT_ASSERT(handle2->value_ == 3);
0491   CPPUNIT_ASSERT(handle->value_ == 2);
0492 
0493   call(callback);
0494   call(*callback2);
0495   CPPUNIT_ASSERT(handle2->value_ == 3);
0496   CPPUNIT_ASSERT(handle->value_ == 2);
0497 
0498   callback2->newRecordComing();
0499   call(callback);
0500   call(*callback2);
0501   CPPUNIT_ASSERT(handle2->value_ == 4);
0502   CPPUNIT_ASSERT(handle->value_ == 2);
0503 
0504   callback.newRecordComing();
0505   call(callback);
0506   call(*callback2);
0507   CPPUNIT_ASSERT(handle2->value_ == 4);
0508   CPPUNIT_ASSERT(handle->value_ == 5);
0509 }
0510 
0511 void testCallback::sharedPtrLambdaTest() {
0512   Base prod;
0513 
0514   auto ptr = std::make_shared<Data>();
0515   auto func = [ptr](Record const& rec) mutable {
0516     ++ptr->value_;
0517     return ptr;
0518   };
0519 
0520   using SharedPtrCallback = SharedPtrCallbackT<Base, decltype(func)>;
0521   SharedPtrCallback callback(&prod, func, 0);
0522   std::shared_ptr<Data> handle;
0523 
0524   callback.holdOntoPointer(&handle);
0525 
0526   callback.newRecordComing();
0527   call(callback);
0528   CPPUNIT_ASSERT(handle.get() == ptr.get());
0529   CPPUNIT_ASSERT(ptr->value_ == 1);
0530 
0531   //since haven't cleared, should not have changed
0532   call(callback);
0533   CPPUNIT_ASSERT(handle.get() == ptr.get());
0534   CPPUNIT_ASSERT(ptr->value_ == 1);
0535 
0536   handle.reset();
0537   callback.newRecordComing();
0538 
0539   call(callback);
0540   CPPUNIT_ASSERT(handle.get() == ptr.get());
0541   CPPUNIT_ASSERT(ptr->value_ == 2);
0542 }
0543 
0544 void testCallback::optionalLambdaTest() {
0545   Base prod;
0546 
0547   int value = 0;
0548   auto func = [&value](Record const& rec) { return std::optional<Data>(++value); };
0549 
0550   using OptionalCallback = OptionalCallbackT<Base, decltype(func)>;
0551   OptionalCallback callback(&prod, func, 0);
0552   std::optional<Data> handle;
0553 
0554   callback.holdOntoPointer(&handle);
0555 
0556   callback.newRecordComing();
0557   call(callback);
0558   CPPUNIT_ASSERT(handle.has_value());
0559   CPPUNIT_ASSERT(value == 1);
0560   CPPUNIT_ASSERT(value == handle->value_);
0561 
0562   //since haven't cleared, should not have changed
0563   call(callback);
0564   CPPUNIT_ASSERT(handle.has_value());
0565   CPPUNIT_ASSERT(value == 1);
0566   CPPUNIT_ASSERT(value == handle->value_);
0567 
0568   handle.reset();
0569   callback.newRecordComing();
0570 
0571   call(callback);
0572   CPPUNIT_ASSERT(handle.has_value());
0573   CPPUNIT_ASSERT(value == 2);
0574   CPPUNIT_ASSERT(value == handle->value_);
0575 }
0576 
0577 void testCallback::ptrProductsLambdaTest() {
0578   PtrProductsProd prod;
0579 
0580   Data dataValue;
0581   auto func = [&dataValue, doubleValue = Double()](Record const& rec) mutable {
0582     auto dataT = std::shared_ptr<Data>(&dataValue, edm::do_nothing_deleter());
0583     auto doubleT = std::shared_ptr<Double>(&doubleValue, edm::do_nothing_deleter());
0584     ++dataValue.value_;
0585     ++doubleValue.value_;
0586     return edm::es::products(dataT, doubleT);
0587   };
0588 
0589   using PtrProductsCallback = PtrProductsCallbackT<Base, decltype(func)>;
0590   PtrProductsCallback callback(&prod, func, 0);
0591   std::shared_ptr<Data> handle;
0592   std::shared_ptr<Double> doubleHandle;
0593 
0594   callback.holdOntoPointer(&handle);
0595   callback.holdOntoPointer(&doubleHandle);
0596 
0597   callback.newRecordComing();
0598   call(callback);
0599   CPPUNIT_ASSERT(handle.get() == &dataValue);
0600   CPPUNIT_ASSERT(dataValue.value_ == 1);
0601 
0602   //since haven't cleared, should not have changed
0603   call(callback);
0604   CPPUNIT_ASSERT(handle.get() == &dataValue);
0605   CPPUNIT_ASSERT(dataValue.value_ == 1);
0606 
0607   callback.newRecordComing();
0608 
0609   call(callback);
0610   CPPUNIT_ASSERT(handle.get() == &dataValue);
0611   CPPUNIT_ASSERT(dataValue.value_ == 2);
0612 }