exception_safety_testing.h 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109
  1. // Copyright 2017 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Utilities for testing exception-safety
  15. #ifndef Y_ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
  16. #define Y_ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
  17. #include "y_absl/base/config.h"
  18. #ifdef Y_ABSL_HAVE_EXCEPTIONS
  19. #include <cstddef>
  20. #include <cstdint>
  21. #include <functional>
  22. #include <initializer_list>
  23. #include <iosfwd>
  24. #include <util/generic/string.h>
  25. #include <tuple>
  26. #include <unordered_map>
  27. #include "gtest/gtest.h"
  28. #include "y_absl/base/internal/pretty_function.h"
  29. #include "y_absl/memory/memory.h"
  30. #include "y_absl/meta/type_traits.h"
  31. #include "y_absl/strings/string_view.h"
  32. #include "y_absl/strings/substitute.h"
  33. #include "y_absl/utility/utility.h"
  34. namespace testing {
  35. enum class TypeSpec;
  36. enum class AllocSpec;
  37. constexpr TypeSpec operator|(TypeSpec a, TypeSpec b) {
  38. using T = y_absl::underlying_type_t<TypeSpec>;
  39. return static_cast<TypeSpec>(static_cast<T>(a) | static_cast<T>(b));
  40. }
  41. constexpr TypeSpec operator&(TypeSpec a, TypeSpec b) {
  42. using T = y_absl::underlying_type_t<TypeSpec>;
  43. return static_cast<TypeSpec>(static_cast<T>(a) & static_cast<T>(b));
  44. }
  45. constexpr AllocSpec operator|(AllocSpec a, AllocSpec b) {
  46. using T = y_absl::underlying_type_t<AllocSpec>;
  47. return static_cast<AllocSpec>(static_cast<T>(a) | static_cast<T>(b));
  48. }
  49. constexpr AllocSpec operator&(AllocSpec a, AllocSpec b) {
  50. using T = y_absl::underlying_type_t<AllocSpec>;
  51. return static_cast<AllocSpec>(static_cast<T>(a) & static_cast<T>(b));
  52. }
  53. namespace exceptions_internal {
  54. TString GetSpecString(TypeSpec);
  55. TString GetSpecString(AllocSpec);
  56. struct NoThrowTag {};
  57. struct StrongGuaranteeTagType {};
  58. // A simple exception class. We throw this so that test code can catch
  59. // exceptions specifically thrown by ThrowingValue.
  60. class TestException {
  61. public:
  62. explicit TestException(y_absl::string_view msg) : msg_(msg) {}
  63. virtual ~TestException() {}
  64. virtual const char* what() const noexcept { return msg_.c_str(); }
  65. private:
  66. TString msg_;
  67. };
  68. // TestBadAllocException exists because allocation functions must throw an
  69. // exception which can be caught by a handler of std::bad_alloc. We use a child
  70. // class of std::bad_alloc so we can customise the error message, and also
  71. // derive from TestException so we don't accidentally end up catching an actual
  72. // bad_alloc exception in TestExceptionSafety.
  73. class TestBadAllocException : public std::bad_alloc, public TestException {
  74. public:
  75. explicit TestBadAllocException(y_absl::string_view msg) : TestException(msg) {}
  76. using TestException::what;
  77. };
  78. extern int countdown;
  79. // Allows the countdown variable to be set manually (defaulting to the initial
  80. // value of 0)
  81. inline void SetCountdown(int i = 0) { countdown = i; }
  82. // Sets the countdown to the terminal value -1
  83. inline void UnsetCountdown() { SetCountdown(-1); }
  84. void MaybeThrow(y_absl::string_view msg, bool throw_bad_alloc = false);
  85. testing::AssertionResult FailureMessage(const TestException& e,
  86. int countdown) noexcept;
  87. struct TrackedAddress {
  88. bool is_alive;
  89. TString description;
  90. };
  91. // Inspects the constructions and destructions of anything inheriting from
  92. // TrackedObject. This allows us to safely "leak" TrackedObjects, as
  93. // ConstructorTracker will destroy everything left over in its destructor.
  94. class ConstructorTracker {
  95. public:
  96. explicit ConstructorTracker(int count) : countdown_(count) {
  97. assert(current_tracker_instance_ == nullptr);
  98. current_tracker_instance_ = this;
  99. }
  100. ~ConstructorTracker() {
  101. assert(current_tracker_instance_ == this);
  102. current_tracker_instance_ = nullptr;
  103. for (auto& it : address_map_) {
  104. void* address = it.first;
  105. TrackedAddress& tracked_address = it.second;
  106. if (tracked_address.is_alive) {
  107. ADD_FAILURE() << ErrorMessage(address, tracked_address.description,
  108. countdown_, "Object was not destroyed.");
  109. }
  110. }
  111. }
  112. static void ObjectConstructed(void* address, TString description) {
  113. if (!CurrentlyTracking()) return;
  114. TrackedAddress& tracked_address =
  115. current_tracker_instance_->address_map_[address];
  116. if (tracked_address.is_alive) {
  117. ADD_FAILURE() << ErrorMessage(
  118. address, tracked_address.description,
  119. current_tracker_instance_->countdown_,
  120. "Object was re-constructed. Current object was constructed by " +
  121. description);
  122. }
  123. tracked_address = {true, std::move(description)};
  124. }
  125. static void ObjectDestructed(void* address) {
  126. if (!CurrentlyTracking()) return;
  127. auto it = current_tracker_instance_->address_map_.find(address);
  128. // Not tracked. Ignore.
  129. if (it == current_tracker_instance_->address_map_.end()) return;
  130. TrackedAddress& tracked_address = it->second;
  131. if (!tracked_address.is_alive) {
  132. ADD_FAILURE() << ErrorMessage(address, tracked_address.description,
  133. current_tracker_instance_->countdown_,
  134. "Object was re-destroyed.");
  135. }
  136. tracked_address.is_alive = false;
  137. }
  138. private:
  139. static bool CurrentlyTracking() {
  140. return current_tracker_instance_ != nullptr;
  141. }
  142. static TString ErrorMessage(void* address,
  143. const TString& address_description,
  144. int countdown,
  145. const TString& error_description) {
  146. return y_absl::Substitute(
  147. "With coundtown at $0:\n"
  148. " $1\n"
  149. " Object originally constructed by $2\n"
  150. " Object address: $3\n",
  151. countdown, error_description, address_description, address);
  152. }
  153. std::unordered_map<void*, TrackedAddress> address_map_;
  154. int countdown_;
  155. static ConstructorTracker* current_tracker_instance_;
  156. };
  157. class TrackedObject {
  158. public:
  159. TrackedObject(const TrackedObject&) = delete;
  160. TrackedObject(TrackedObject&&) = delete;
  161. protected:
  162. explicit TrackedObject(TString description) {
  163. ConstructorTracker::ObjectConstructed(this, std::move(description));
  164. }
  165. ~TrackedObject() noexcept { ConstructorTracker::ObjectDestructed(this); }
  166. };
  167. } // namespace exceptions_internal
  168. extern exceptions_internal::NoThrowTag nothrow_ctor;
  169. extern exceptions_internal::StrongGuaranteeTagType strong_guarantee;
  170. // A test class which is convertible to bool. The conversion can be
  171. // instrumented to throw at a controlled time.
  172. class ThrowingBool {
  173. public:
  174. ThrowingBool(bool b) noexcept : b_(b) {} // NOLINT(runtime/explicit)
  175. operator bool() const { // NOLINT
  176. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  177. return b_;
  178. }
  179. private:
  180. bool b_;
  181. };
  182. /*
  183. * Configuration enum for the ThrowingValue type that defines behavior for the
  184. * lifetime of the instance. Use testing::nothrow_ctor to prevent the integer
  185. * constructor from throwing.
  186. *
  187. * kEverythingThrows: Every operation can throw an exception
  188. * kNoThrowCopy: Copy construction and copy assignment will not throw
  189. * kNoThrowMove: Move construction and move assignment will not throw
  190. * kNoThrowNew: Overloaded operators new and new[] will not throw
  191. */
  192. enum class TypeSpec {
  193. kEverythingThrows = 0,
  194. kNoThrowCopy = 1,
  195. kNoThrowMove = 1 << 1,
  196. kNoThrowNew = 1 << 2,
  197. };
  198. /*
  199. * A testing class instrumented to throw an exception at a controlled time.
  200. *
  201. * ThrowingValue implements a slightly relaxed version of the Regular concept --
  202. * that is it's a value type with the expected semantics. It also implements
  203. * arithmetic operations. It doesn't implement member and pointer operators
  204. * like operator-> or operator[].
  205. *
  206. * ThrowingValue can be instrumented to have certain operations be noexcept by
  207. * using compile-time bitfield template arguments. That is, to make an
  208. * ThrowingValue which has noexcept move construction/assignment and noexcept
  209. * copy construction/assignment, use the following:
  210. * ThrowingValue<testing::kNoThrowMove | testing::kNoThrowCopy> my_thrwr{val};
  211. */
  212. template <TypeSpec Spec = TypeSpec::kEverythingThrows>
  213. class ThrowingValue : private exceptions_internal::TrackedObject {
  214. static constexpr bool IsSpecified(TypeSpec spec) {
  215. return static_cast<bool>(Spec & spec);
  216. }
  217. static constexpr int kDefaultValue = 0;
  218. static constexpr int kBadValue = 938550620;
  219. public:
  220. ThrowingValue() : TrackedObject(GetInstanceString(kDefaultValue)) {
  221. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  222. dummy_ = kDefaultValue;
  223. }
  224. ThrowingValue(const ThrowingValue& other) noexcept(
  225. IsSpecified(TypeSpec::kNoThrowCopy))
  226. : TrackedObject(GetInstanceString(other.dummy_)) {
  227. if (!IsSpecified(TypeSpec::kNoThrowCopy)) {
  228. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  229. }
  230. dummy_ = other.dummy_;
  231. }
  232. ThrowingValue(ThrowingValue&& other) noexcept(
  233. IsSpecified(TypeSpec::kNoThrowMove))
  234. : TrackedObject(GetInstanceString(other.dummy_)) {
  235. if (!IsSpecified(TypeSpec::kNoThrowMove)) {
  236. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  237. }
  238. dummy_ = other.dummy_;
  239. }
  240. explicit ThrowingValue(int i) : TrackedObject(GetInstanceString(i)) {
  241. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  242. dummy_ = i;
  243. }
  244. ThrowingValue(int i, exceptions_internal::NoThrowTag) noexcept
  245. : TrackedObject(GetInstanceString(i)), dummy_(i) {}
  246. // y_absl expects nothrow destructors
  247. ~ThrowingValue() noexcept = default;
  248. ThrowingValue& operator=(const ThrowingValue& other) noexcept(
  249. IsSpecified(TypeSpec::kNoThrowCopy)) {
  250. dummy_ = kBadValue;
  251. if (!IsSpecified(TypeSpec::kNoThrowCopy)) {
  252. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  253. }
  254. dummy_ = other.dummy_;
  255. return *this;
  256. }
  257. ThrowingValue& operator=(ThrowingValue&& other) noexcept(
  258. IsSpecified(TypeSpec::kNoThrowMove)) {
  259. dummy_ = kBadValue;
  260. if (!IsSpecified(TypeSpec::kNoThrowMove)) {
  261. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  262. }
  263. dummy_ = other.dummy_;
  264. return *this;
  265. }
  266. // Arithmetic Operators
  267. ThrowingValue operator+(const ThrowingValue& other) const {
  268. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  269. return ThrowingValue(dummy_ + other.dummy_, nothrow_ctor);
  270. }
  271. ThrowingValue operator+() const {
  272. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  273. return ThrowingValue(dummy_, nothrow_ctor);
  274. }
  275. ThrowingValue operator-(const ThrowingValue& other) const {
  276. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  277. return ThrowingValue(dummy_ - other.dummy_, nothrow_ctor);
  278. }
  279. ThrowingValue operator-() const {
  280. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  281. return ThrowingValue(-dummy_, nothrow_ctor);
  282. }
  283. ThrowingValue& operator++() {
  284. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  285. ++dummy_;
  286. return *this;
  287. }
  288. ThrowingValue operator++(int) {
  289. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  290. auto out = ThrowingValue(dummy_, nothrow_ctor);
  291. ++dummy_;
  292. return out;
  293. }
  294. ThrowingValue& operator--() {
  295. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  296. --dummy_;
  297. return *this;
  298. }
  299. ThrowingValue operator--(int) {
  300. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  301. auto out = ThrowingValue(dummy_, nothrow_ctor);
  302. --dummy_;
  303. return out;
  304. }
  305. ThrowingValue operator*(const ThrowingValue& other) const {
  306. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  307. return ThrowingValue(dummy_ * other.dummy_, nothrow_ctor);
  308. }
  309. ThrowingValue operator/(const ThrowingValue& other) const {
  310. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  311. return ThrowingValue(dummy_ / other.dummy_, nothrow_ctor);
  312. }
  313. ThrowingValue operator%(const ThrowingValue& other) const {
  314. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  315. return ThrowingValue(dummy_ % other.dummy_, nothrow_ctor);
  316. }
  317. ThrowingValue operator<<(int shift) const {
  318. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  319. return ThrowingValue(dummy_ << shift, nothrow_ctor);
  320. }
  321. ThrowingValue operator>>(int shift) const {
  322. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  323. return ThrowingValue(dummy_ >> shift, nothrow_ctor);
  324. }
  325. // Comparison Operators
  326. // NOTE: We use `ThrowingBool` instead of `bool` because most STL
  327. // types/containers requires T to be convertible to bool.
  328. friend ThrowingBool operator==(const ThrowingValue& a,
  329. const ThrowingValue& b) {
  330. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  331. return a.dummy_ == b.dummy_;
  332. }
  333. friend ThrowingBool operator!=(const ThrowingValue& a,
  334. const ThrowingValue& b) {
  335. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  336. return a.dummy_ != b.dummy_;
  337. }
  338. friend ThrowingBool operator<(const ThrowingValue& a,
  339. const ThrowingValue& b) {
  340. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  341. return a.dummy_ < b.dummy_;
  342. }
  343. friend ThrowingBool operator<=(const ThrowingValue& a,
  344. const ThrowingValue& b) {
  345. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  346. return a.dummy_ <= b.dummy_;
  347. }
  348. friend ThrowingBool operator>(const ThrowingValue& a,
  349. const ThrowingValue& b) {
  350. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  351. return a.dummy_ > b.dummy_;
  352. }
  353. friend ThrowingBool operator>=(const ThrowingValue& a,
  354. const ThrowingValue& b) {
  355. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  356. return a.dummy_ >= b.dummy_;
  357. }
  358. // Logical Operators
  359. ThrowingBool operator!() const {
  360. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  361. return !dummy_;
  362. }
  363. ThrowingBool operator&&(const ThrowingValue& other) const {
  364. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  365. return dummy_ && other.dummy_;
  366. }
  367. ThrowingBool operator||(const ThrowingValue& other) const {
  368. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  369. return dummy_ || other.dummy_;
  370. }
  371. // Bitwise Logical Operators
  372. ThrowingValue operator~() const {
  373. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  374. return ThrowingValue(~dummy_, nothrow_ctor);
  375. }
  376. ThrowingValue operator&(const ThrowingValue& other) const {
  377. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  378. return ThrowingValue(dummy_ & other.dummy_, nothrow_ctor);
  379. }
  380. ThrowingValue operator|(const ThrowingValue& other) const {
  381. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  382. return ThrowingValue(dummy_ | other.dummy_, nothrow_ctor);
  383. }
  384. ThrowingValue operator^(const ThrowingValue& other) const {
  385. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  386. return ThrowingValue(dummy_ ^ other.dummy_, nothrow_ctor);
  387. }
  388. // Compound Assignment operators
  389. ThrowingValue& operator+=(const ThrowingValue& other) {
  390. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  391. dummy_ += other.dummy_;
  392. return *this;
  393. }
  394. ThrowingValue& operator-=(const ThrowingValue& other) {
  395. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  396. dummy_ -= other.dummy_;
  397. return *this;
  398. }
  399. ThrowingValue& operator*=(const ThrowingValue& other) {
  400. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  401. dummy_ *= other.dummy_;
  402. return *this;
  403. }
  404. ThrowingValue& operator/=(const ThrowingValue& other) {
  405. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  406. dummy_ /= other.dummy_;
  407. return *this;
  408. }
  409. ThrowingValue& operator%=(const ThrowingValue& other) {
  410. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  411. dummy_ %= other.dummy_;
  412. return *this;
  413. }
  414. ThrowingValue& operator&=(const ThrowingValue& other) {
  415. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  416. dummy_ &= other.dummy_;
  417. return *this;
  418. }
  419. ThrowingValue& operator|=(const ThrowingValue& other) {
  420. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  421. dummy_ |= other.dummy_;
  422. return *this;
  423. }
  424. ThrowingValue& operator^=(const ThrowingValue& other) {
  425. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  426. dummy_ ^= other.dummy_;
  427. return *this;
  428. }
  429. ThrowingValue& operator<<=(int shift) {
  430. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  431. dummy_ <<= shift;
  432. return *this;
  433. }
  434. ThrowingValue& operator>>=(int shift) {
  435. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  436. dummy_ >>= shift;
  437. return *this;
  438. }
  439. // Pointer operators
  440. void operator&() const = delete; // NOLINT(runtime/operator)
  441. // Stream operators
  442. friend std::ostream& operator<<(std::ostream& os, const ThrowingValue& tv) {
  443. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  444. return os << GetInstanceString(tv.dummy_);
  445. }
  446. friend std::istream& operator>>(std::istream& is, const ThrowingValue&) {
  447. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  448. return is;
  449. }
  450. // Memory management operators
  451. static void* operator new(size_t s) noexcept(
  452. IsSpecified(TypeSpec::kNoThrowNew)) {
  453. if (!IsSpecified(TypeSpec::kNoThrowNew)) {
  454. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION, true);
  455. }
  456. return ::operator new(s);
  457. }
  458. static void* operator new[](size_t s) noexcept(
  459. IsSpecified(TypeSpec::kNoThrowNew)) {
  460. if (!IsSpecified(TypeSpec::kNoThrowNew)) {
  461. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION, true);
  462. }
  463. return ::operator new[](s);
  464. }
  465. template <typename... Args>
  466. static void* operator new(size_t s, Args&&... args) noexcept(
  467. IsSpecified(TypeSpec::kNoThrowNew)) {
  468. if (!IsSpecified(TypeSpec::kNoThrowNew)) {
  469. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION, true);
  470. }
  471. return ::operator new(s, std::forward<Args>(args)...);
  472. }
  473. template <typename... Args>
  474. static void* operator new[](size_t s, Args&&... args) noexcept(
  475. IsSpecified(TypeSpec::kNoThrowNew)) {
  476. if (!IsSpecified(TypeSpec::kNoThrowNew)) {
  477. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION, true);
  478. }
  479. return ::operator new[](s, std::forward<Args>(args)...);
  480. }
  481. // Abseil doesn't support throwing overloaded operator delete. These are
  482. // provided so a throwing operator-new can clean up after itself.
  483. void operator delete(void* p) noexcept { ::operator delete(p); }
  484. template <typename... Args>
  485. void operator delete(void* p, Args&&... args) noexcept {
  486. ::operator delete(p, std::forward<Args>(args)...);
  487. }
  488. void operator delete[](void* p) noexcept { return ::operator delete[](p); }
  489. template <typename... Args>
  490. void operator delete[](void* p, Args&&... args) noexcept {
  491. return ::operator delete[](p, std::forward<Args>(args)...);
  492. }
  493. // Non-standard access to the actual contained value. No need for this to
  494. // throw.
  495. int& Get() noexcept { return dummy_; }
  496. const int& Get() const noexcept { return dummy_; }
  497. private:
  498. static TString GetInstanceString(int dummy) {
  499. return y_absl::StrCat("ThrowingValue<",
  500. exceptions_internal::GetSpecString(Spec), ">(", dummy,
  501. ")");
  502. }
  503. int dummy_;
  504. };
  505. // While not having to do with exceptions, explicitly delete comma operator, to
  506. // make sure we don't use it on user-supplied types.
  507. template <TypeSpec Spec, typename T>
  508. void operator,(const ThrowingValue<Spec>&, T&&) = delete;
  509. template <TypeSpec Spec, typename T>
  510. void operator,(T&&, const ThrowingValue<Spec>&) = delete;
  511. /*
  512. * Configuration enum for the ThrowingAllocator type that defines behavior for
  513. * the lifetime of the instance.
  514. *
  515. * kEverythingThrows: Calls to the member functions may throw
  516. * kNoThrowAllocate: Calls to the member functions will not throw
  517. */
  518. enum class AllocSpec {
  519. kEverythingThrows = 0,
  520. kNoThrowAllocate = 1,
  521. };
  522. /*
  523. * An allocator type which is instrumented to throw at a controlled time, or not
  524. * to throw, using AllocSpec. The supported settings are the default of every
  525. * function which is allowed to throw in a conforming allocator possibly
  526. * throwing, or nothing throws, in line with the Y_ABSL_ALLOCATOR_THROWS
  527. * configuration macro.
  528. */
  529. template <typename T, AllocSpec Spec = AllocSpec::kEverythingThrows>
  530. class ThrowingAllocator : private exceptions_internal::TrackedObject {
  531. static constexpr bool IsSpecified(AllocSpec spec) {
  532. return static_cast<bool>(Spec & spec);
  533. }
  534. public:
  535. using pointer = T*;
  536. using const_pointer = const T*;
  537. using reference = T&;
  538. using const_reference = const T&;
  539. using void_pointer = void*;
  540. using const_void_pointer = const void*;
  541. using value_type = T;
  542. using size_type = size_t;
  543. using difference_type = ptrdiff_t;
  544. using is_nothrow =
  545. std::integral_constant<bool, Spec == AllocSpec::kNoThrowAllocate>;
  546. using propagate_on_container_copy_assignment = std::true_type;
  547. using propagate_on_container_move_assignment = std::true_type;
  548. using propagate_on_container_swap = std::true_type;
  549. using is_always_equal = std::false_type;
  550. ThrowingAllocator() : TrackedObject(GetInstanceString(next_id_)) {
  551. exceptions_internal::MaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  552. dummy_ = std::make_shared<const int>(next_id_++);
  553. }
  554. template <typename U>
  555. ThrowingAllocator(const ThrowingAllocator<U, Spec>& other) noexcept // NOLINT
  556. : TrackedObject(GetInstanceString(*other.State())),
  557. dummy_(other.State()) {}
  558. // According to C++11 standard [17.6.3.5], Table 28, the move/copy ctors of
  559. // allocator shall not exit via an exception, thus they are marked noexcept.
  560. ThrowingAllocator(const ThrowingAllocator& other) noexcept
  561. : TrackedObject(GetInstanceString(*other.State())),
  562. dummy_(other.State()) {}
  563. template <typename U>
  564. ThrowingAllocator(ThrowingAllocator<U, Spec>&& other) noexcept // NOLINT
  565. : TrackedObject(GetInstanceString(*other.State())),
  566. dummy_(std::move(other.State())) {}
  567. ThrowingAllocator(ThrowingAllocator&& other) noexcept
  568. : TrackedObject(GetInstanceString(*other.State())),
  569. dummy_(std::move(other.State())) {}
  570. ~ThrowingAllocator() noexcept = default;
  571. ThrowingAllocator& operator=(const ThrowingAllocator& other) noexcept {
  572. dummy_ = other.State();
  573. return *this;
  574. }
  575. template <typename U>
  576. ThrowingAllocator& operator=(
  577. const ThrowingAllocator<U, Spec>& other) noexcept {
  578. dummy_ = other.State();
  579. return *this;
  580. }
  581. template <typename U>
  582. ThrowingAllocator& operator=(ThrowingAllocator<U, Spec>&& other) noexcept {
  583. dummy_ = std::move(other.State());
  584. return *this;
  585. }
  586. template <typename U>
  587. struct rebind {
  588. using other = ThrowingAllocator<U, Spec>;
  589. };
  590. pointer allocate(size_type n) noexcept(
  591. IsSpecified(AllocSpec::kNoThrowAllocate)) {
  592. ReadStateAndMaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  593. return static_cast<pointer>(::operator new(n * sizeof(T)));
  594. }
  595. pointer allocate(size_type n, const_void_pointer) noexcept(
  596. IsSpecified(AllocSpec::kNoThrowAllocate)) {
  597. return allocate(n);
  598. }
  599. void deallocate(pointer ptr, size_type) noexcept {
  600. ReadState();
  601. ::operator delete(static_cast<void*>(ptr));
  602. }
  603. template <typename U, typename... Args>
  604. void construct(U* ptr, Args&&... args) noexcept(
  605. IsSpecified(AllocSpec::kNoThrowAllocate)) {
  606. ReadStateAndMaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  607. ::new (static_cast<void*>(ptr)) U(std::forward<Args>(args)...);
  608. }
  609. template <typename U>
  610. void destroy(U* p) noexcept {
  611. ReadState();
  612. p->~U();
  613. }
  614. size_type max_size() const noexcept {
  615. return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
  616. }
  617. ThrowingAllocator select_on_container_copy_construction() noexcept(
  618. IsSpecified(AllocSpec::kNoThrowAllocate)) {
  619. ReadStateAndMaybeThrow(Y_ABSL_PRETTY_FUNCTION);
  620. return *this;
  621. }
  622. template <typename U>
  623. bool operator==(const ThrowingAllocator<U, Spec>& other) const noexcept {
  624. return dummy_ == other.dummy_;
  625. }
  626. template <typename U>
  627. bool operator!=(const ThrowingAllocator<U, Spec>& other) const noexcept {
  628. return dummy_ != other.dummy_;
  629. }
  630. template <typename, AllocSpec>
  631. friend class ThrowingAllocator;
  632. private:
  633. static TString GetInstanceString(int dummy) {
  634. return y_absl::StrCat("ThrowingAllocator<",
  635. exceptions_internal::GetSpecString(Spec), ">(", dummy,
  636. ")");
  637. }
  638. const std::shared_ptr<const int>& State() const { return dummy_; }
  639. std::shared_ptr<const int>& State() { return dummy_; }
  640. void ReadState() {
  641. // we know that this will never be true, but the compiler doesn't, so this
  642. // should safely force a read of the value.
  643. if (*dummy_ < 0) std::abort();
  644. }
  645. void ReadStateAndMaybeThrow(y_absl::string_view msg) const {
  646. if (!IsSpecified(AllocSpec::kNoThrowAllocate)) {
  647. exceptions_internal::MaybeThrow(
  648. y_absl::Substitute("Allocator id $0 threw from $1", *dummy_, msg));
  649. }
  650. }
  651. static int next_id_;
  652. std::shared_ptr<const int> dummy_;
  653. };
  654. template <typename T, AllocSpec Spec>
  655. int ThrowingAllocator<T, Spec>::next_id_ = 0;
  656. // Tests for resource leaks by attempting to construct a T using args repeatedly
  657. // until successful, using the countdown method. Side effects can then be
  658. // tested for resource leaks.
  659. template <typename T, typename... Args>
  660. void TestThrowingCtor(Args&&... args) {
  661. struct Cleanup {
  662. ~Cleanup() { exceptions_internal::UnsetCountdown(); }
  663. } c;
  664. for (int count = 0;; ++count) {
  665. exceptions_internal::ConstructorTracker ct(count);
  666. exceptions_internal::SetCountdown(count);
  667. try {
  668. T temp(std::forward<Args>(args)...);
  669. static_cast<void>(temp);
  670. break;
  671. } catch (const exceptions_internal::TestException&) {
  672. }
  673. }
  674. }
  675. // Tests the nothrow guarantee of the provided nullary operation. If the an
  676. // exception is thrown, the result will be AssertionFailure(). Otherwise, it
  677. // will be AssertionSuccess().
  678. template <typename Operation>
  679. testing::AssertionResult TestNothrowOp(const Operation& operation) {
  680. struct Cleanup {
  681. Cleanup() { exceptions_internal::SetCountdown(); }
  682. ~Cleanup() { exceptions_internal::UnsetCountdown(); }
  683. } c;
  684. try {
  685. operation();
  686. return testing::AssertionSuccess();
  687. } catch (const exceptions_internal::TestException&) {
  688. return testing::AssertionFailure()
  689. << "TestException thrown during call to operation() when nothrow "
  690. "guarantee was expected.";
  691. } catch (...) {
  692. return testing::AssertionFailure()
  693. << "Unknown exception thrown during call to operation() when "
  694. "nothrow guarantee was expected.";
  695. }
  696. }
  697. namespace exceptions_internal {
  698. // Dummy struct for ExceptionSafetyTestBuilder<> partial state.
  699. struct UninitializedT {};
  700. template <typename T>
  701. class DefaultFactory {
  702. public:
  703. explicit DefaultFactory(const T& t) : t_(t) {}
  704. std::unique_ptr<T> operator()() const { return y_absl::make_unique<T>(t_); }
  705. private:
  706. T t_;
  707. };
  708. template <size_t LazyContractsCount, typename LazyFactory,
  709. typename LazyOperation>
  710. using EnableIfTestable = typename y_absl::enable_if_t<
  711. LazyContractsCount != 0 &&
  712. !std::is_same<LazyFactory, UninitializedT>::value &&
  713. !std::is_same<LazyOperation, UninitializedT>::value>;
  714. template <typename Factory = UninitializedT,
  715. typename Operation = UninitializedT, typename... Contracts>
  716. class ExceptionSafetyTestBuilder;
  717. } // namespace exceptions_internal
  718. /*
  719. * Constructs an empty ExceptionSafetyTestBuilder. All
  720. * ExceptionSafetyTestBuilder objects are immutable and all With[thing] mutation
  721. * methods return new instances of ExceptionSafetyTestBuilder.
  722. *
  723. * In order to test a T for exception safety, a factory for that T, a testable
  724. * operation, and at least one contract callback returning an assertion
  725. * result must be applied using the respective methods.
  726. */
  727. exceptions_internal::ExceptionSafetyTestBuilder<> MakeExceptionSafetyTester();
  728. namespace exceptions_internal {
  729. template <typename T>
  730. struct IsUniquePtr : std::false_type {};
  731. template <typename T, typename D>
  732. struct IsUniquePtr<std::unique_ptr<T, D>> : std::true_type {};
  733. template <typename Factory>
  734. struct FactoryPtrTypeHelper {
  735. using type = decltype(std::declval<const Factory&>()());
  736. static_assert(IsUniquePtr<type>::value, "Factories must return a unique_ptr");
  737. };
  738. template <typename Factory>
  739. using FactoryPtrType = typename FactoryPtrTypeHelper<Factory>::type;
  740. template <typename Factory>
  741. using FactoryElementType = typename FactoryPtrType<Factory>::element_type;
  742. template <typename T>
  743. class ExceptionSafetyTest {
  744. using Factory = std::function<std::unique_ptr<T>()>;
  745. using Operation = std::function<void(T*)>;
  746. using Contract = std::function<AssertionResult(T*)>;
  747. public:
  748. template <typename... Contracts>
  749. explicit ExceptionSafetyTest(const Factory& f, const Operation& op,
  750. const Contracts&... contracts)
  751. : factory_(f), operation_(op), contracts_{WrapContract(contracts)...} {}
  752. AssertionResult Test() const {
  753. for (int count = 0;; ++count) {
  754. exceptions_internal::ConstructorTracker ct(count);
  755. for (const auto& contract : contracts_) {
  756. auto t_ptr = factory_();
  757. try {
  758. SetCountdown(count);
  759. operation_(t_ptr.get());
  760. // Unset for the case that the operation throws no exceptions, which
  761. // would leave the countdown set and break the *next* exception safety
  762. // test after this one.
  763. UnsetCountdown();
  764. return AssertionSuccess();
  765. } catch (const exceptions_internal::TestException& e) {
  766. if (!contract(t_ptr.get())) {
  767. return AssertionFailure() << e.what() << " failed contract check";
  768. }
  769. }
  770. }
  771. }
  772. }
  773. private:
  774. template <typename ContractFn>
  775. Contract WrapContract(const ContractFn& contract) {
  776. return [contract](T* t_ptr) { return AssertionResult(contract(t_ptr)); };
  777. }
  778. Contract WrapContract(StrongGuaranteeTagType) {
  779. return [this](T* t_ptr) { return AssertionResult(*factory_() == *t_ptr); };
  780. }
  781. Factory factory_;
  782. Operation operation_;
  783. std::vector<Contract> contracts_;
  784. };
  785. /*
  786. * Builds a tester object that tests if performing a operation on a T follows
  787. * exception safety guarantees. Verification is done via contract assertion
  788. * callbacks applied to T instances post-throw.
  789. *
  790. * Template parameters for ExceptionSafetyTestBuilder:
  791. *
  792. * - Factory: The factory object (passed in via tester.WithFactory(...) or
  793. * tester.WithInitialValue(...)) must be invocable with the signature
  794. * `std::unique_ptr<T> operator()() const` where T is the type being tested.
  795. * It is used for reliably creating identical T instances to test on.
  796. *
  797. * - Operation: The operation object (passed in via tester.WithOperation(...)
  798. * or tester.Test(...)) must be invocable with the signature
  799. * `void operator()(T*) const` where T is the type being tested. It is used
  800. * for performing steps on a T instance that may throw and that need to be
  801. * checked for exception safety. Each call to the operation will receive a
  802. * fresh T instance so it's free to modify and destroy the T instances as it
  803. * pleases.
  804. *
  805. * - Contracts...: The contract assertion callback objects (passed in via
  806. * tester.WithContracts(...)) must be invocable with the signature
  807. * `testing::AssertionResult operator()(T*) const` where T is the type being
  808. * tested. Contract assertion callbacks are provided T instances post-throw.
  809. * They must return testing::AssertionSuccess when the type contracts of the
  810. * provided T instance hold. If the type contracts of the T instance do not
  811. * hold, they must return testing::AssertionFailure. Execution order of
  812. * Contracts... is unspecified. They will each individually get a fresh T
  813. * instance so they are free to modify and destroy the T instances as they
  814. * please.
  815. */
  816. template <typename Factory, typename Operation, typename... Contracts>
  817. class ExceptionSafetyTestBuilder {
  818. public:
  819. /*
  820. * Returns a new ExceptionSafetyTestBuilder with an included T factory based
  821. * on the provided T instance. The existing factory will not be included in
  822. * the newly created tester instance. The created factory returns a new T
  823. * instance by copy-constructing the provided const T& t.
  824. *
  825. * Preconditions for tester.WithInitialValue(const T& t):
  826. *
  827. * - The const T& t object must be copy-constructible where T is the type
  828. * being tested. For non-copy-constructible objects, use the method
  829. * tester.WithFactory(...).
  830. */
  831. template <typename T>
  832. ExceptionSafetyTestBuilder<DefaultFactory<T>, Operation, Contracts...>
  833. WithInitialValue(const T& t) const {
  834. return WithFactory(DefaultFactory<T>(t));
  835. }
  836. /*
  837. * Returns a new ExceptionSafetyTestBuilder with the provided T factory
  838. * included. The existing factory will not be included in the newly-created
  839. * tester instance. This method is intended for use with types lacking a copy
  840. * constructor. Types that can be copy-constructed should instead use the
  841. * method tester.WithInitialValue(...).
  842. */
  843. template <typename NewFactory>
  844. ExceptionSafetyTestBuilder<y_absl::decay_t<NewFactory>, Operation, Contracts...>
  845. WithFactory(const NewFactory& new_factory) const {
  846. return {new_factory, operation_, contracts_};
  847. }
  848. /*
  849. * Returns a new ExceptionSafetyTestBuilder with the provided testable
  850. * operation included. The existing operation will not be included in the
  851. * newly created tester.
  852. */
  853. template <typename NewOperation>
  854. ExceptionSafetyTestBuilder<Factory, y_absl::decay_t<NewOperation>, Contracts...>
  855. WithOperation(const NewOperation& new_operation) const {
  856. return {factory_, new_operation, contracts_};
  857. }
  858. /*
  859. * Returns a new ExceptionSafetyTestBuilder with the provided MoreContracts...
  860. * combined with the Contracts... that were already included in the instance
  861. * on which the method was called. Contracts... cannot be removed or replaced
  862. * once added to an ExceptionSafetyTestBuilder instance. A fresh object must
  863. * be created in order to get an empty Contracts... list.
  864. *
  865. * In addition to passing in custom contract assertion callbacks, this method
  866. * accepts `testing::strong_guarantee` as an argument which checks T instances
  867. * post-throw against freshly created T instances via operator== to verify
  868. * that any state changes made during the execution of the operation were
  869. * properly rolled back.
  870. */
  871. template <typename... MoreContracts>
  872. ExceptionSafetyTestBuilder<Factory, Operation, Contracts...,
  873. y_absl::decay_t<MoreContracts>...>
  874. WithContracts(const MoreContracts&... more_contracts) const {
  875. return {
  876. factory_, operation_,
  877. std::tuple_cat(contracts_, std::tuple<y_absl::decay_t<MoreContracts>...>(
  878. more_contracts...))};
  879. }
  880. /*
  881. * Returns a testing::AssertionResult that is the reduced result of the
  882. * exception safety algorithm. The algorithm short circuits and returns
  883. * AssertionFailure after the first contract callback returns an
  884. * AssertionFailure. Otherwise, if all contract callbacks return an
  885. * AssertionSuccess, the reduced result is AssertionSuccess.
  886. *
  887. * The passed-in testable operation will not be saved in a new tester instance
  888. * nor will it modify/replace the existing tester instance. This is useful
  889. * when each operation being tested is unique and does not need to be reused.
  890. *
  891. * Preconditions for tester.Test(const NewOperation& new_operation):
  892. *
  893. * - May only be called after at least one contract assertion callback and a
  894. * factory or initial value have been provided.
  895. */
  896. template <
  897. typename NewOperation,
  898. typename = EnableIfTestable<sizeof...(Contracts), Factory, NewOperation>>
  899. testing::AssertionResult Test(const NewOperation& new_operation) const {
  900. return TestImpl(new_operation, y_absl::index_sequence_for<Contracts...>());
  901. }
  902. /*
  903. * Returns a testing::AssertionResult that is the reduced result of the
  904. * exception safety algorithm. The algorithm short circuits and returns
  905. * AssertionFailure after the first contract callback returns an
  906. * AssertionFailure. Otherwise, if all contract callbacks return an
  907. * AssertionSuccess, the reduced result is AssertionSuccess.
  908. *
  909. * Preconditions for tester.Test():
  910. *
  911. * - May only be called after at least one contract assertion callback, a
  912. * factory or initial value and a testable operation have been provided.
  913. */
  914. template <
  915. typename LazyOperation = Operation,
  916. typename = EnableIfTestable<sizeof...(Contracts), Factory, LazyOperation>>
  917. testing::AssertionResult Test() const {
  918. return Test(operation_);
  919. }
  920. private:
  921. template <typename, typename, typename...>
  922. friend class ExceptionSafetyTestBuilder;
  923. friend ExceptionSafetyTestBuilder<> testing::MakeExceptionSafetyTester();
  924. ExceptionSafetyTestBuilder() {}
  925. ExceptionSafetyTestBuilder(const Factory& f, const Operation& o,
  926. const std::tuple<Contracts...>& i)
  927. : factory_(f), operation_(o), contracts_(i) {}
  928. template <typename SelectedOperation, size_t... Indices>
  929. testing::AssertionResult TestImpl(SelectedOperation selected_operation,
  930. y_absl::index_sequence<Indices...>) const {
  931. return ExceptionSafetyTest<FactoryElementType<Factory>>(
  932. factory_, selected_operation, std::get<Indices>(contracts_)...)
  933. .Test();
  934. }
  935. Factory factory_;
  936. Operation operation_;
  937. std::tuple<Contracts...> contracts_;
  938. };
  939. } // namespace exceptions_internal
  940. } // namespace testing
  941. #endif // Y_ABSL_HAVE_EXCEPTIONS
  942. #endif // Y_ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_