├── CMakeLists.txt ├── LICENSE ├── README.md ├── TODO.txt ├── src ├── cc │ └── sync_ptr.h ├── main.cpp └── mem │ ├── sync_ptr.h │ └── sync_ptr_policy.h ├── talks └── sync_ptr_c++_london_08032017.pdf └── tests ├── cc_sync_ptr.cpp ├── cc_sync_ptr.h ├── mem_sync_ptr.cpp └── mem_sync_ptr.h /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.5) 3 | 4 | project( sync_ptr ) 5 | 6 | # Concurrency. 7 | set(SRCS 8 | ${CMAKE_CURRENT_SOURCE_DIR}/src/cc/sync_ptr.h 9 | ) 10 | source_group( "Concurrency" FILES ${SRCS} ) 11 | set( SOURCE_FILES ${SOURCE_FILES} ${SRCS} ) 12 | 13 | # Memory. 14 | set(SRCS 15 | ${CMAKE_CURRENT_SOURCE_DIR}/src/mem/sync_ptr.h 16 | ${CMAKE_CURRENT_SOURCE_DIR}/src/mem/sync_ptr_policy.h 17 | ) 18 | source_group( "Memory" FILES ${SRCS} ) 19 | set( SOURCE_FILES ${SOURCE_FILES} ${SRCS} ) 20 | 21 | # Tests. 22 | set(SRCS 23 | ${CMAKE_CURRENT_SOURCE_DIR}/tests/cc_sync_ptr.cpp 24 | ${CMAKE_CURRENT_SOURCE_DIR}/tests/cc_sync_ptr.h 25 | ${CMAKE_CURRENT_SOURCE_DIR}/tests/mem_sync_ptr.cpp 26 | ${CMAKE_CURRENT_SOURCE_DIR}/tests/mem_sync_ptr.h 27 | ) 28 | source_group( "Tests" FILES ${SRCS} ) 29 | set( SOURCE_FILES ${SOURCE_FILES} ${SRCS} ) 30 | 31 | # Main. 32 | set(SRCS 33 | ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp) 34 | set( SOURCE_FILES ${SOURCE_FILES} ${SRCS} ) 35 | 36 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 37 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) 38 | add_executable( sync_ptr ${SOURCE_FILES} ) 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Romain Cheminade 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **sync_ptr** 2 | 3 | Instance management (and propagation) involves multiple combined patterns and requires us to keep track of the code managing creation and reclamation of such resources. 4 | 5 | Chained references are complex to maintain, update, swap, steal, reclaim. 6 | These operation often require costly traversals generating performance penalty. 7 | 8 | Providing an object managing these simplifies the design, 9 | ease the development process and guaranties execution safety. 10 | 11 | *** 12 | `sync_ptr` uses RAII technique by binding the life cycle and propagation of a managed object. 13 | 14 | The object management is populated on all chained `sync_ptr` instances in a single call. 15 | 16 | `sync_ptr` automatically release the resource once it is no more in use. 17 | 18 | *** 19 | 20 | `sync_ptr` stands for **synchronized pointer**. 21 | 22 | `sync_ptr` objects that are **copy constructed** or **copy assigned** point to the same underlying raw pointer. 23 | 24 | **When the original `sync_ptr` or one of its copy underlying raw pointer changes, all `sync_ptr` and copies point to the updated raw pointer.** 25 | 26 | `sync_ptr` and underlying raw pointer are **reference counted**. 27 | 28 | The underlying pointer memory is returned when the reference count drops to zero or another raw pointer is assigned. 29 | 30 | *** 31 | 32 | **`sync_ptr` come in 2 different flavors** 33 | - **policy**, offers strong execution guarantee and thread safety using default policies, and is easily extensible using the provided policies or any desired one. 34 | - **atomic**, offers faster concurrent environment execution (lock free and wait free) but weaker execution guarantee, all operation return their success state leaving the programmer the choice in the response strategy. 35 | 36 | *** 37 | 38 | ### Policy sync_ptr 39 | Header only implementation for easy integration. 40 | ~~~cpp 41 | #include 42 | ~~~ 43 | 44 | Call **copy assignment operator** or **copy constructor** to link objects to the same underlying raw pointer. 45 | 46 | ~~~cpp 47 | struct Obj 48 | { 49 | void do_something(); 50 | }; 51 | 52 | mem::sync_ptr ptr1 = mem::make_sync(); 53 | mem::sync_ptr ptr2(ptr1); 54 | mem::sync_ptr ptr3 = ptr2; 55 | ptr3->do_something(); // same as ptr1->do_something() and ptr2->do_something(). 56 | ~~~ 57 | 58 | Call **reset()** to update raw pointer on all linked references. 59 | ~~~cpp 60 | mem::sync_ptr ptr1 = mem::make_sync(); 61 | mem::sync_ptr ptr2(ptr1); 62 | 63 | // Update underlying raw pointer 64 | Obj * obj = new Obj(); 65 | ptr1.reset(obj); // ptr1 and ptr2 point to obj. 66 | ~~~ 67 | 68 | Call **release()** to steal the raw pointer from a `sync_ptr` chain. 69 | ~~~cpp 70 | mem::sync_ptr ptr = mem::make_sync(); 71 | 72 | // Stealing. 73 | Obj * raw = ptr.release(); 74 | 75 | // Note that First underlying raw pointer is now null. 76 | assert(ptr.get() == nullptr); 77 | ~~~ 78 | 79 | Call **exchange()** to steal the raw pointer from a `sync_ptr` chain and store a target one in the chain. 80 | ~~~cpp 81 | mem::sync_ptr ptr = mem::make_sync(); 82 | 83 | // Stealing. 84 | Obj * ptr_add = ptr.get(); 85 | Obj * raw = new Obj(); 86 | std::unique_ptr ptr_xc(ptr.exchange(raw)); 87 | 88 | // "ptr_xc" now contains original underlying pointer from "ptr". 89 | assert(ptr_xc); 90 | assert(ptr_xc.get() == ptr_add); 91 | 92 | // "ptr" now contains "raw". 93 | assert(ptr); 94 | assert(ptr.get() == raw); 95 | ~~~ 96 | 97 | Helpers. 98 | ~~~cpp 99 | mem::make_sync() 100 | mem::make_sync_with_allocator() 101 | ~~~ 102 | 103 | For convenience, relational operators are provided. 104 | 105 | *** 106 | 107 | ### Atomic sync_ptr 108 | 109 | The same methods as the policy based one are provided. 110 | The main difference is that they return their execution success state. 111 | 112 | See `cc/sync_ptr.h` and `tests/cc_sync_ptr.h .cpp` for usage example. 113 | 114 | *** 115 | Please note that `sync_ptr` behavior is different from `std::shared_ptr`. 116 | ~~~cpp 117 | struct Obj 118 | {}; 119 | std::shared_ptr ptr1 = std::make_shared(); 120 | std::shared_ptr ptr2 = ptr1; 121 | assert(ptr1 == ptr2); // same behavior 122 | ptr1.reset(new Obj); 123 | assert(ptr1 != ptr2); // different behavior 124 | 125 | mem::sync_ptr sptr1 = mem::make_sync(); 126 | mem::sync_ptr sptr2 = sptr1; 127 | assert(sptr1 == sptr2); // same behavior 128 | sptr1.reset(new Obj); 129 | assert(sptr1 == sptr2); // different behavior 130 | ~~~ 131 | After calling **reset()** the two `sync_ptr` point to the **same** underlying raw pointer whereas the `std::shared_ptr` point to **different** ones. 132 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | 2 | - Linked Pointer 3 | -------------------------------------------------------------------------------- /src/cc/sync_ptr.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __CC_SYNC_PTR_H__ 3 | #define __CC_SYNC_PTR_H__ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifndef __MEMORY_SYNC_PTR_POLICY_H__ 11 | #include "mem/sync_ptr_policy.h" 12 | #endif 13 | 14 | 15 | namespace cc 16 | { 17 | template 18 | using sync_ptr_allocator = mem::default_allocator; 19 | 20 | template 21 | using sync_ptr_deleter = mem::default_deleter; 22 | 23 | /** 24 | * \class cc::sync_ptr 25 | * 26 | * \brief Reference counted synchronized pointer. 27 | * Used to avoid cross module cycle. 28 | * When the original sync_ptr or one of its copy underlying raw pointer changes, 29 | * all sync_ptr and copies point to the updated raw pointer. 30 | */ 31 | template < 32 | class TPtr, 33 | template class TDeleter = sync_ptr_deleter > 34 | class sync_ptr final 35 | { 36 | 37 | public: 38 | typedef typename TPtr pointer_type; 39 | typedef typename TDeleter deleter_type; 40 | 41 | 42 | private: 43 | /** 44 | * \class cc::sync_ptr::body 45 | * 46 | * \brief Reference counted template type pointer. 47 | * Deletes pointer when reference count drops to zero. 48 | * Reference count and pointer are atomic. 49 | */ 50 | template< 51 | class TPtr, 52 | template class TDeleter> 53 | class body final 54 | : private TDeleter 55 | { 56 | 57 | ////////////////////////////////////// 58 | // MEMBERS // 59 | ////////////////////////////////////// 60 | 61 | private: 62 | std::atomic ref_count_; 63 | std::atomic ref_count_ptr_; 64 | std::atomic ptr_; 65 | 66 | 67 | ////////////////////////////////////// 68 | // METHODS // 69 | ////////////////////////////////////// 70 | 71 | public: 72 | body(body const & p_other) = delete; 73 | body(body && p_other) = delete; 74 | void operator=(body & p_arg) = delete; 75 | void operator=(body && p_arg) = delete; 76 | 77 | public: 78 | body( 79 | void) 80 | noexcept 81 | // Members. 82 | : ref_count_(0) 83 | , ref_count_ptr_(0) 84 | , ptr_(nullptr) 85 | {} 86 | 87 | template< 88 | class TPtrCompatible> 89 | body( 90 | TPtrCompatible * p_ptr) 91 | noexcept 92 | // Members. 93 | : ref_count_(1U) 94 | , ref_count_ptr_(1U) 95 | , ptr_(p_ptr) 96 | { 97 | assert(p_ptr); 98 | } 99 | 100 | private: 101 | ~body( 102 | void) 103 | noexcept 104 | {} 105 | 106 | 107 | private: 108 | /** 109 | * \brief Delete this. 110 | * Called when this reference count drops to zero. 111 | */ 112 | inline void release_this( 113 | void) 114 | noexcept 115 | { 116 | delete this; 117 | } 118 | /** 119 | * \brief Delete contained pointer and store target one using CAS. 120 | */ 121 | inline bool release_ptr_cas( 122 | TPtr * p_ptr) 123 | noexcept 124 | { 125 | auto ptr = ptr_.load(); 126 | if (ptr_.compare_exchange_strong( 127 | ptr, 128 | p_ptr)) 129 | { 130 | if (ptr) 131 | { 132 | free(ptr); 133 | } 134 | return true; 135 | } 136 | return false; 137 | } 138 | 139 | 140 | public: 141 | /** 142 | * \brief Increments this reference count. 143 | */ 144 | inline void ref( 145 | void) 146 | noexcept 147 | { 148 | ref_count_.fetch_add(1U); 149 | } 150 | /** 151 | * \brief Decrements this reference count, 152 | * release this if reference count drops to zero. 153 | */ 154 | inline void unref( 155 | void) 156 | noexcept 157 | { 158 | if (ref_count_.fetch_sub(1U) == 1U) 159 | { 160 | release_this(); 161 | } 162 | } 163 | 164 | /** 165 | * \brief Increments pointer reference count. 166 | */ 167 | inline void ref_ptr( 168 | void) 169 | noexcept 170 | { 171 | if (ptr_.load()) 172 | { 173 | ref_count_ptr_.fetch_add(1U); 174 | } 175 | } 176 | /** 177 | * \brief Decrements pointer reference count, 178 | * release pointer if reference count drops to zero. 179 | */ 180 | inline void unref_ptr( 181 | void) 182 | noexcept 183 | { 184 | if (ptr_.load()) 185 | { 186 | if (ref_count_ptr_.fetch_sub(1U) == 1U) 187 | { 188 | release_ptr_cas(nullptr); 189 | } 190 | } 191 | } 192 | 193 | 194 | /////////////////////////////////////////////////////////////////////////////////////// 195 | // GET / SET 196 | /////////////////////////////////////////////////////////////////////////////////////// 197 | 198 | public: 199 | inline size_t get_ref_count( 200 | void) 201 | const noexcept 202 | { 203 | return ref_count_.load(); 204 | } 205 | inline size_t get_ref_count_ptr( 206 | void) 207 | const noexcept 208 | { 209 | return ref_count_ptr_.load(); 210 | } 211 | 212 | 213 | public: 214 | inline TPtr * get_ptr( 215 | void) 216 | const noexcept 217 | { 218 | return ptr_.load(); 219 | } 220 | 221 | template< 222 | class TPtrCompatible> 223 | inline bool set_ptr( 224 | TPtrCompatible * p_ptr) 225 | noexcept 226 | { 227 | assert(p_ptr); 228 | assert(p_ptr != ptr_.load()); 229 | return release_ptr_cas(p_ptr); 230 | } 231 | 232 | inline bool reset_ptr( 233 | void) 234 | noexcept 235 | { 236 | return release_ptr_cas(nullptr); 237 | } 238 | 239 | template < 240 | class TPtrCompatible> 241 | inline bool release( 242 | TPtrCompatible ** p_out) 243 | noexcept 244 | { 245 | assert(*p_out != get_ptr()); 246 | *p_out = ptr_.load(); 247 | return ptr_.compare_exchange_strong( 248 | *p_out, 249 | nullptr); 250 | } 251 | 252 | template< 253 | class TPtrCompatible> 254 | inline bool exchange( 255 | TPtr ** p_out, 256 | TPtrCompatible * p_ptr) 257 | noexcept 258 | { 259 | assert(*p_out != get_ptr()); 260 | assert(p_ptr); 261 | assert(p_ptr != get_ptr()); 262 | *p_out = ptr_.load(); 263 | return ptr_.compare_exchange_strong( 264 | *p_out, 265 | p_ptr); 266 | } 267 | 268 | }; // class body 269 | 270 | 271 | ////////////////////////////////////// 272 | // MEMBERS // 273 | ////////////////////////////////////// 274 | 275 | private: 276 | typedef typename sync_ptr< 277 | TPtr, 278 | TDeleter> sync_ptr_t; 279 | 280 | typedef typename body< 281 | TPtr, 282 | TDeleter> body_t; 283 | 284 | 285 | private: 286 | body_t * body_; 287 | 288 | 289 | ////////////////////////////////////// 290 | // METHODS // 291 | ////////////////////////////////////// 292 | 293 | public: 294 | sync_ptr( 295 | void) 296 | noexcept 297 | // Members. 298 | : body_(new body_t()) 299 | {} 300 | 301 | template< 302 | class TPtrCompatible> 303 | sync_ptr( 304 | TPtrCompatible * p_ptr) 305 | noexcept 306 | // Members. 307 | : body_(new body_t(p_ptr)) 308 | {} 309 | 310 | sync_ptr( 311 | sync_ptr_t && p_other) 312 | noexcept 313 | // Members. 314 | : body_(p_other.body_) 315 | { 316 | p_other.body_ = nullptr; 317 | } 318 | 319 | sync_ptr( 320 | sync_ptr_t const & p_other) 321 | noexcept 322 | // Members. 323 | : body_(p_other.body_) 324 | { 325 | body_->ref(); 326 | body_->ref_ptr(); 327 | } 328 | 329 | ~sync_ptr( 330 | void) 331 | noexcept 332 | { 333 | if (body_) 334 | { 335 | body_->unref_ptr(); 336 | body_->unref(); 337 | } 338 | } 339 | 340 | inline sync_ptr_t & operator=( 341 | sync_ptr_t && p_other) 342 | noexcept 343 | { 344 | auto * tmp = p_other.body_; 345 | if (tmp != body_) 346 | { 347 | body_ = tmp; 348 | p_other.body_ = nullptr; 349 | } 350 | return *this; 351 | } 352 | 353 | inline sync_ptr_t & operator=( 354 | sync_ptr_t const & p_other) 355 | & noexcept 356 | { 357 | auto * tmp = p_other.body_; 358 | if (tmp != body_) 359 | { 360 | body_->unref_ptr(); 361 | body_->unref(); 362 | 363 | body_ = tmp; 364 | body_->ref(); 365 | body_->ref_ptr(); 366 | } 367 | return *this; 368 | } 369 | 370 | void swap( 371 | sync_ptr_t & p_rhs) 372 | noexcept 373 | { 374 | auto tmp = body_; 375 | body_ = p_rhs.body_; 376 | p_rhs.body_ = tmp; 377 | } 378 | 379 | 380 | public: 381 | inline size_t count( 382 | void) 383 | const noexcept 384 | { 385 | return body_->get_ref_count_ptr(); 386 | } 387 | 388 | 389 | public: 390 | /** 391 | * \brief Set underlying pointer. 392 | * Free previous pointer on success. 393 | * Return true on success false otherwise. 394 | */ 395 | template < 396 | class TPtrCompatible> 397 | inline bool reset( 398 | TPtrCompatible * p_ptr) 399 | noexcept 400 | { 401 | assert(p_ptr); 402 | return body_->set_ptr(p_ptr); 403 | } 404 | /** 405 | * \brief Set underlying pointer to null. 406 | * Free previous pointer on success. 407 | * Return true on success false otherwise. 408 | */ 409 | inline bool reset( 410 | void) 411 | noexcept 412 | { 413 | return body_->reset_ptr(); 414 | } 415 | 416 | 417 | public: 418 | /** 419 | * \brief Releases the ownership of the managed object if any. 420 | * Return the previously owned pointer and set the current to null. 421 | * Return true on success, false otherwise. 422 | */ 423 | inline bool release( 424 | TPtr ** p_out) 425 | noexcept 426 | { 427 | return body_->release(p_out); 428 | } 429 | /** 430 | * \brief Set managed object and return previous one. 431 | * Return true on success, false otherwise. 432 | * On failure current object is unchanged and returned one is undefined. 433 | */ 434 | template < 435 | class TPtrCompatible> 436 | inline bool exchange( 437 | TPtr ** p_out, 438 | TPtrCompatible * p_ptr) 439 | noexcept 440 | { 441 | return body_->exchange(p_out, p_ptr); 442 | } 443 | 444 | 445 | public: 446 | inline TPtr * get( 447 | void) 448 | const noexcept 449 | { 450 | return body_->get_ptr(); 451 | } 452 | 453 | 454 | public: 455 | inline TPtr & operator*( 456 | void) 457 | const noexcept 458 | { 459 | return *get(); 460 | } 461 | 462 | inline TPtr * operator->( 463 | void) 464 | const noexcept 465 | { 466 | return get(); 467 | } 468 | 469 | 470 | public: 471 | inline bool valid( 472 | void) 473 | const noexcept 474 | { 475 | return (get() != nullptr); 476 | } 477 | 478 | inline operator bool( 479 | void) 480 | const noexcept 481 | { 482 | return valid(); 483 | } 484 | 485 | }; // class sync_ptr 486 | 487 | 488 | /////////////////////////////////////////////////////////////////////////////////////////// 489 | // MAKE 490 | /////////////////////////////////////////////////////////////////////////////////////////// 491 | 492 | template < 493 | class TPtr, 494 | template class TDeleter = sync_ptr_deleter, 495 | class... TArgs> 496 | inline typename std::enable_if< 497 | !std::is_array::value, 498 | cc::sync_ptr>::type 499 | make_sync( 500 | TArgs&&... p_args) 501 | { 502 | typedef typename sync_ptr< 503 | TPtr, 504 | TDeleter> sync_ptr_t; 505 | return (sync_ptr_t(new TPtr(std::forward(p_args)...))); 506 | } 507 | 508 | template < 509 | class TPtr, 510 | template class TDeleter = sync_ptr_deleter, 511 | class... TArgs> 512 | typename std::enable_if::value != 0, void>::type make_sync( 513 | TArgs&&...) 514 | = delete; 515 | 516 | 517 | /////////////////////////////////////////////////////////////////////////////////////////// 518 | // MAKE WITH ALLOCATOR 519 | /////////////////////////////////////////////////////////////////////////////////////////// 520 | 521 | template < 522 | class TPtr, 523 | template class TAllocator, 524 | template class TDeleter = sync_ptr_deleter, 525 | class... TArgs> 526 | inline typename std::enable_if< 527 | !std::is_array::value, 528 | cc::sync_ptr>::type 529 | make_sync_with_allocator( 530 | TAllocator const & p_allocator, 531 | TArgs&&... p_args) 532 | { 533 | typedef typename sync_ptr< 534 | TPtr, 535 | TDeleter> sync_ptr_t; 536 | return (sync_ptr_t(p_allocator.allocate(std::forward(p_args)...))); 537 | } 538 | 539 | template< 540 | class TPtr, 541 | template class TAllocator, 542 | template class TDeleter, 543 | class... TArgs> 544 | typename std::enable_if::value != 0, void>::type make_sync_with_allocator( 545 | TAllocator const & p_allocator, 546 | TArgs&&...) 547 | = delete; 548 | 549 | } // namespace cc 550 | 551 | 552 | namespace std 553 | { 554 | 555 | template < 556 | class TPtr, 557 | template class TDeleter> 558 | inline void swap( 559 | cc::sync_ptr & p_lhs, 560 | cc::sync_ptr & p_rhs) 561 | noexcept(noexcept(p_lhs.swap(p_rhs))) 562 | { 563 | p_lhs.swap(p_rhs); 564 | } 565 | 566 | } // namespace std 567 | 568 | 569 | /////////////////////////////////////////////////////////////////////////////////////////////////// 570 | // RELATIONAL OPERATORS 571 | /////////////////////////////////////////////////////////////////////////////////////////////////// 572 | 573 | template< 574 | class TPtr1, 575 | template class TDeleter1, 576 | class TPtr2, 577 | template class TDeleter2> 578 | inline bool operator==( 579 | cc::sync_ptr const & p_lhs, 580 | cc::sync_ptr const & p_rhs) 581 | noexcept 582 | { 583 | return (p_lhs.get() == p_rhs.get()); 584 | } 585 | 586 | template< 587 | class TPtr1, 588 | template class TDeleter1, 589 | class TPtr2, 590 | template class TDeleter2> 591 | inline bool operator!=( 592 | cc::sync_ptr const & p_lhs, 593 | cc::sync_ptr const & p_rhs) 594 | noexcept 595 | { 596 | return (!(p_lhs == p_rhs)); 597 | } 598 | 599 | template< 600 | class TPtr1, 601 | template class TDeleter1, 602 | class TPtr2, 603 | template class TDeleter2> 604 | inline bool operator<( 605 | cc::sync_ptr const & p_lhs, 606 | cc::sync_ptr const & p_rhs) 607 | { 608 | typedef typename cc::sync_ptr::pointer ptr1_t; 609 | typedef typename cc::sync_ptr::pointer ptr2_t; 610 | typedef typename std::common_type::type common_t; 611 | return (std::less()(p_lhs.get(), p_rhs.get())); 612 | } 613 | 614 | template< 615 | class TPtr1, 616 | template class TDeleter1, 617 | class TPtr2, 618 | template class TDeleter2> 619 | inline bool operator>=( 620 | cc::sync_ptr const & p_lhs, 621 | cc::sync_ptr const & p_rhs) 622 | { 623 | return (!(p_lhs < p_rhs)); 624 | } 625 | 626 | template< 627 | class TPtr1, 628 | template class TDeleter1, 629 | class TPtr2, 630 | template class TDeleter2> 631 | inline bool operator>( 632 | cc::sync_ptr const & p_lhs, 633 | cc::sync_ptr const & p_rhs) 634 | { 635 | return (p_rhs < p_lhs); 636 | } 637 | 638 | template< 639 | class TPtr1, 640 | template class TDeleter1, 641 | class TPtr2, 642 | template class TDeleter2> 643 | inline bool operator<=( 644 | cc::sync_ptr const & p_lhs, 645 | cc::sync_ptr const & p_rhs) 646 | { 647 | return (!(p_rhs < p_lhs)); 648 | } 649 | 650 | 651 | 652 | template< 653 | class TPtr, 654 | template class TDeleter > 655 | inline bool operator==( 656 | cc::sync_ptr const & p_lhs, 657 | std::nullptr_t) 658 | noexcept 659 | { 660 | return (!p_lhs); 661 | } 662 | 663 | template< 664 | class TPtr, 665 | template class TDeleter > 666 | inline bool operator==( 667 | std::nullptr_t, 668 | cc::sync_ptr const & p_rhs) 669 | noexcept 670 | { 671 | return (!p_rhs); 672 | } 673 | 674 | template< 675 | class TPtr, 676 | template class TDeleter > 677 | inline bool operator!=( 678 | cc::sync_ptr const & p_lhs, 679 | std::nullptr_t p_rhs) 680 | noexcept 681 | { 682 | return (!(p_lhs == p_rhs)); 683 | } 684 | 685 | template< 686 | class TPtr, 687 | template class TDeleter > 688 | inline bool operator!=( 689 | std::nullptr_t p_lhs, 690 | cc::sync_ptr const & p_rhs) 691 | noexcept 692 | { 693 | return (!(p_lhs == p_rhs)); 694 | } 695 | 696 | template< 697 | class TPtr, 698 | template class TDeleter > 699 | inline bool operator<( 700 | cc::sync_ptr const & p_lhs, 701 | std::nullptr_t p_rhs) 702 | { 703 | typedef typename cc::sync_ptr::pointer _Ptr; 704 | return (std::less<_Ptr>()(p_lhs.get(), p_rhs)); 705 | } 706 | 707 | template< 708 | class TPtr, 709 | template class TDeleter > 710 | inline bool operator<( 711 | std::nullptr_t p_lhs, 712 | cc::sync_ptr const & p_rhs) 713 | { 714 | typedef typename cc::sync_ptr::pointer _Ptr; 715 | return (std::less<_Ptr>()(p_lhs, p_rhs.get())); 716 | } 717 | 718 | template< 719 | class TPtr, 720 | template class TDeleter > 721 | inline bool operator>=( 722 | cc::sync_ptr const & p_lhs, 723 | std::nullptr_t p_rhs) 724 | { 725 | return (!(p_lhs < p_rhs)); 726 | } 727 | 728 | template< 729 | class TPtr, 730 | template class TDeleter > 731 | inline bool operator>=( 732 | std::nullptr_t p_lhs, 733 | cc::sync_ptr const & p_rhs) 734 | { 735 | return (!(p_lhs < p_rhs)); 736 | } 737 | 738 | template< 739 | class TPtr, 740 | template class TDeleter > 741 | inline bool operator>( 742 | cc::sync_ptr const & p_lhs, 743 | std::nullptr_t p_rhs) 744 | { 745 | return (p_rhs < p_lhs); 746 | } 747 | 748 | template< 749 | class TPtr, 750 | template class TDeleter > 751 | inline bool operator>( 752 | std::nullptr_t p_lhs, 753 | cc::sync_ptr const & p_rhs) 754 | { 755 | return (p_rhs < p_lhs); 756 | } 757 | 758 | template< 759 | class TPtr, 760 | template class TDeleter > 761 | inline bool operator<=( 762 | cc::sync_ptr const & p_lhs, 763 | std::nullptr_t p_rhs) 764 | { 765 | return (!(p_rhs < p_lhs)); 766 | } 767 | 768 | template< 769 | class TPtr, 770 | template class TDeleter > 771 | inline bool operator<=( 772 | std::nullptr_t p_lhs, 773 | cc::sync_ptr const & p_rhs) 774 | { 775 | return (!(p_rhs < p_lhs)); 776 | } 777 | 778 | #endif // __CC_SYNC_PTR_H__ 779 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "tests/cc_sync_ptr.h" 3 | #include "tests/mem_sync_ptr.h" 4 | 5 | 6 | int main( 7 | int argc, char *argv[]) 8 | try 9 | { 10 | tests::cc_sync_ptr_synchro(); 11 | tests::cc_sync_ptr_release(); 12 | tests::cc_sync_ptr_exchange(); 13 | tests::cc_sync_ptr_allocator(); 14 | 15 | tests::mem_sync_ptr_synchro(); 16 | tests::mem_sync_ptr_release(); 17 | tests::mem_sync_ptr_exchange(); 18 | tests::mem_sync_ptr_allocator(); 19 | 20 | return 0; 21 | } 22 | catch (...) 23 | { 24 | return 1; 25 | } 26 | -------------------------------------------------------------------------------- /src/mem/sync_ptr.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __MEMORY_SYNC_PTR_H__ 3 | #define __MEMORY_SYNC_PTR_H__ 4 | 5 | #include 6 | 7 | #ifndef __MEMORY_SYNC_PTR_POLICY_H__ 8 | #include "mem/sync_ptr_policy.h" 9 | #endif 10 | 11 | 12 | namespace mem 13 | { 14 | 15 | template 16 | using sync_ptr_allocator = default_allocator; 17 | 18 | template 19 | using sync_ptr_deleter = default_deleter; 20 | 21 | template 22 | using sync_ptr_holder = ptr_holder_ts; 23 | 24 | using sync_ptr_ref_counter = atomic_ref_counter; 25 | 26 | 27 | /** 28 | * \class mem::sync_ptr 29 | * 30 | * \brief Reference counted synchronized pointer. 31 | * Used to avoid cross module cycle. 32 | * When the original sync_ptr or one of its copy underlying raw pointer changes, 33 | * all sync_ptr and copies point to the updated raw pointer. 34 | */ 35 | template < 36 | class TPtr, 37 | template class TDeleter = sync_ptr_deleter, 38 | template class THolder = sync_ptr_holder, 39 | class TRefCounter = sync_ptr_ref_counter> 40 | class sync_ptr final 41 | { 42 | 43 | public: 44 | typedef typename TPtr pointer_type; 45 | typedef typename TDeleter deleter_type; 46 | typedef typename THolder holder_type; 47 | typedef typename TRefCounter reference_counter_type; 48 | 49 | 50 | private: 51 | /** 52 | * \class mem::sync_ptr::body 53 | * 54 | * \brief Reference counted template type pointer. 55 | * Deletes pointer when reference count drops to zero. 56 | */ 57 | template< 58 | class TPtr, 59 | template class TDeleter, 60 | template class THolder, 61 | class TRefCounter> 62 | class body final 63 | : private TDeleter 64 | , private THolder 65 | , private TRefCounter 66 | { 67 | 68 | ////////////////////////////////////// 69 | // METHODS // 70 | ////////////////////////////////////// 71 | 72 | public: 73 | body(body const & p_other) = delete; 74 | body(body && p_other) = delete; 75 | void operator=(body & p_arg) = delete; 76 | void operator=(body && p_arg) = delete; 77 | 78 | public: 79 | /** 80 | * \brief Construct default empty object. 81 | */ 82 | body( 83 | void) 84 | noexcept 85 | {} 86 | 87 | /** 88 | * \brief Construct with compatible pointer. 89 | */ 90 | template< 91 | class TPtrCompatible> 92 | body( 93 | TPtrCompatible * p_ptr) 94 | noexcept 95 | // Inheritance. 96 | : THolder(p_ptr) 97 | { 98 | assert(p_ptr); 99 | increment_ptr(); 100 | } 101 | 102 | private: 103 | ~body( 104 | void) 105 | noexcept 106 | {} 107 | 108 | 109 | private: 110 | inline void release_this( 111 | void) 112 | noexcept 113 | { 114 | delete this; 115 | } 116 | 117 | inline void release_ptr( 118 | TPtr * p_ptr) 119 | noexcept 120 | { 121 | static_assert( 122 | noexcept(free(p_ptr)), 123 | "Deleter policy must offer no-throw guarantee."); 124 | 125 | static_assert( 126 | noexcept(set(p_ptr)), 127 | "Pointer holder policy must offer no-throw guarantee."); 128 | 129 | auto p = set(p_ptr); 130 | if (p) 131 | { 132 | free(p); 133 | } 134 | } 135 | 136 | 137 | public: 138 | /** 139 | * \brief Increments this reference count. 140 | */ 141 | inline void ref( 142 | void) 143 | noexcept 144 | { 145 | static_assert( 146 | noexcept(increment()), 147 | "Reference counter policy must offer no-throw guarantee."); 148 | 149 | increment(); 150 | } 151 | /** 152 | * \brief Decrements this reference count. 153 | * Release this if reference count drops to zero. 154 | */ 155 | inline void unref( 156 | void) 157 | noexcept 158 | { 159 | static_assert( 160 | noexcept(decrement()), 161 | "Reference counter policy must offer no-throw guarantee."); 162 | 163 | if (decrement() == 1U) 164 | { 165 | release_this(); 166 | } 167 | } 168 | 169 | /** 170 | * \brief Increments pointer reference count. 171 | */ 172 | inline void ref_ptr( 173 | void) 174 | noexcept 175 | { 176 | static_assert( 177 | noexcept(increment_ptr()), 178 | "Reference counter policy must offer no-throw guarantee."); 179 | 180 | if (get_ptr()) 181 | { 182 | increment_ptr(); 183 | } 184 | } 185 | /** 186 | * \brief Decrements pointer reference count. 187 | * Release pointer if reference count drops to zero. 188 | */ 189 | inline void unref_ptr( 190 | void) 191 | noexcept 192 | { 193 | static_assert( 194 | noexcept(decrement_ptr()), 195 | "Reference counter policy must offer no-throw guarantee."); 196 | 197 | if (get_ptr()) 198 | { 199 | if (decrement_ptr() == 1U) 200 | { 201 | release_ptr(nullptr); 202 | } 203 | } 204 | } 205 | 206 | 207 | public: 208 | template< 209 | class TPtrCompatible> 210 | inline void reset_ptr( 211 | TPtrCompatible * p_ptr) 212 | noexcept 213 | { 214 | assert(p_ptr); 215 | assert(p_ptr != get_ptr()); 216 | release_ptr(p_ptr); 217 | } 218 | 219 | inline void reset_ptr( 220 | void) 221 | noexcept 222 | { 223 | release_ptr(nullptr); 224 | } 225 | 226 | 227 | public: 228 | inline TPtr * release( 229 | void) 230 | noexcept 231 | { 232 | static_assert( 233 | noexcept(set(nullptr)), 234 | "Pointer holder policy must offer no-throw guarantee."); 235 | 236 | return set(nullptr); 237 | } 238 | 239 | template< 240 | class TPtrCompatible> 241 | inline TPtr * exchange( 242 | TPtrCompatible * p_ptr) 243 | noexcept 244 | { 245 | static_assert( 246 | noexcept(set(p_ptr)), 247 | "Pointer holder policy must offer no-throw guarantee."); 248 | 249 | assert(p_ptr); 250 | assert(p_ptr != get_ptr()); 251 | return set(p_ptr); 252 | } 253 | 254 | 255 | /////////////////////////////////////////////////////////////////////////////////////// 256 | // GET / SET 257 | /////////////////////////////////////////////////////////////////////////////////////// 258 | 259 | public: 260 | inline size_t get_ref_count( 261 | void) 262 | const noexcept 263 | { 264 | static_assert( 265 | noexcept(count()), 266 | "Reference counter policy must offer no-throw guarantee."); 267 | 268 | return count(); 269 | } 270 | 271 | inline size_t get_ref_count_ptr( 272 | void) 273 | const noexcept 274 | { 275 | static_assert( 276 | noexcept(count_ptr()), 277 | "Reference counter policy must offer no-throw guarantee."); 278 | 279 | return count_ptr(); 280 | } 281 | 282 | inline TPtr * get_ptr( 283 | void) 284 | const noexcept 285 | { 286 | static_assert( 287 | noexcept(get()), 288 | "Pointer holder policy must offer no-throw guarantee."); 289 | 290 | return get(); 291 | } 292 | 293 | }; // class body 294 | 295 | 296 | ////////////////////////////////////// 297 | // MEMBERS // 298 | ////////////////////////////////////// 299 | 300 | private: 301 | typedef typename sync_ptr< 302 | TPtr, 303 | TDeleter, 304 | THolder, 305 | TRefCounter> sync_ptr_t; 306 | 307 | typedef typename body< 308 | TPtr, 309 | TDeleter, 310 | THolder, 311 | TRefCounter> body_t; 312 | 313 | private: 314 | body_t * body_; 315 | 316 | 317 | ////////////////////////////////////// 318 | // METHODS // 319 | ////////////////////////////////////// 320 | 321 | public: 322 | /** 323 | * \brief Construct default empty object. 324 | */ 325 | sync_ptr( 326 | void) 327 | noexcept 328 | // Members. 329 | : body_(new body_t()) 330 | {} 331 | 332 | /** 333 | * \brief Construct with compatible pointer. 334 | */ 335 | template< 336 | class TPtrCompatible> 337 | sync_ptr( 338 | TPtrCompatible * p_ptr) 339 | noexcept 340 | // Members. 341 | : body_(new body_t(p_ptr)) 342 | {} 343 | 344 | sync_ptr( 345 | sync_ptr && p_other) 346 | noexcept 347 | // Members. 348 | : body_(p_other.body_) 349 | { 350 | p_other.body_ = nullptr; 351 | } 352 | 353 | sync_ptr( 354 | sync_ptr const & p_other) 355 | noexcept 356 | // Members. 357 | : body_(p_other.body_) 358 | { 359 | body_->ref(); 360 | body_->ref_ptr(); 361 | } 362 | 363 | ~sync_ptr( 364 | void) 365 | noexcept 366 | { 367 | if (body_) 368 | { 369 | body_->unref_ptr(); 370 | body_->unref(); 371 | } 372 | } 373 | 374 | inline sync_ptr_t & operator=( 375 | sync_ptr_t && p_other) 376 | noexcept 377 | { 378 | auto * tmp = p_other.body_; 379 | if (tmp != body_) 380 | { 381 | body_ = tmp; 382 | p_other.body_ = nullptr; 383 | } 384 | return *this; 385 | } 386 | 387 | inline sync_ptr_t & operator=( 388 | sync_ptr_t const & p_other) 389 | & noexcept 390 | { 391 | auto * tmp = p_other.body_; 392 | if (tmp != body_) 393 | { 394 | body_->unref_ptr(); 395 | body_->unref(); 396 | 397 | body_ = tmp; 398 | body_->ref(); 399 | body_->ref_ptr(); 400 | } 401 | return *this; 402 | } 403 | 404 | void swap( 405 | sync_ptr & p_rhs) 406 | noexcept 407 | { 408 | auto tmp = body_; 409 | body_ = p_rhs.body_; 410 | p_rhs.body_ = tmp; 411 | } 412 | 413 | 414 | public: 415 | inline size_t count( 416 | void) 417 | const noexcept 418 | { 419 | return body_->get_ref_count_ptr(); 420 | } 421 | 422 | 423 | public: 424 | /** 425 | * \brief Set underlying pointer. 426 | * Free previous pointer. 427 | */ 428 | template < 429 | class TPtrCompatible> 430 | inline void reset( 431 | TPtrCompatible * p_ptr) 432 | noexcept 433 | { 434 | assert(p_ptr); 435 | body_->reset_ptr(p_ptr); 436 | } 437 | /** 438 | * \brief Set underlying pointer to null. 439 | * Free previous pointer. 440 | */ 441 | inline void reset( 442 | void) 443 | noexcept 444 | { 445 | body_->reset_ptr(); 446 | } 447 | 448 | 449 | public: 450 | /** 451 | * \brief Releases the ownership of the managed object if any. 452 | * Return the previously owned pointer and set the current to null. 453 | */ 454 | inline TPtr * release( 455 | void) 456 | noexcept 457 | { 458 | return body_->release(); 459 | } 460 | /** 461 | * \brief Set managed object and return previous one. 462 | */ 463 | template < 464 | class TPtrCompatible> 465 | inline TPtr * exchange( 466 | TPtrCompatible * p_ptr) 467 | noexcept 468 | { 469 | return body_->exchange(p_ptr); 470 | } 471 | 472 | 473 | public: 474 | inline TPtr * get( 475 | void) 476 | const noexcept 477 | { 478 | return body_->get_ptr(); 479 | } 480 | 481 | inline TPtr & operator*( 482 | void) 483 | const noexcept 484 | { 485 | return *get(); 486 | } 487 | 488 | inline TPtr * operator->( 489 | void) 490 | const noexcept 491 | { 492 | return get(); 493 | } 494 | 495 | 496 | public: 497 | inline bool valid( 498 | void) 499 | const noexcept 500 | { 501 | return (get() != nullptr); 502 | } 503 | 504 | inline operator bool( 505 | void) 506 | const noexcept 507 | { 508 | return valid(); 509 | } 510 | 511 | }; // class sync_ptr 512 | 513 | 514 | /////////////////////////////////////////////////////////////////////////////////////////// 515 | // MAKE 516 | /////////////////////////////////////////////////////////////////////////////////////////// 517 | 518 | template < 519 | class TPtr, 520 | template class TDeleter = sync_ptr_deleter, 521 | template class THolder = sync_ptr_holder, 522 | class TRefCounter = sync_ptr_ref_counter, 523 | class... TArgs> 524 | inline typename std::enable_if< 525 | !std::is_array::value, 526 | mem::sync_ptr>::type 527 | make_sync( 528 | TArgs&&... p_args) 529 | { 530 | typedef typename sync_ptr< 531 | TPtr, 532 | TDeleter, 533 | THolder, 534 | TRefCounter> sync_ptr_t; 535 | return (sync_ptr_t(new TPtr(std::forward(p_args)...))); 536 | } 537 | 538 | template< 539 | class TPtr, 540 | template class TDeleter, 541 | template class THolder, 542 | class TRefCounter, 543 | class... TArgs> 544 | typename std::enable_if::value != 0, void>::type make_sync( 545 | TArgs&&...) 546 | = delete; 547 | 548 | 549 | /////////////////////////////////////////////////////////////////////////////////////////// 550 | // MAKE WITH ALLOCATOR 551 | /////////////////////////////////////////////////////////////////////////////////////////// 552 | 553 | template < 554 | class TPtr, 555 | template class TAllocator, 556 | template class TDeleter = sync_ptr_deleter, 557 | template class THolder = sync_ptr_holder, 558 | class TRefCounter = sync_ptr_ref_counter, 559 | class... TArgs> 560 | inline typename std::enable_if< 561 | !std::is_array::value, 562 | mem::sync_ptr>::type 563 | make_sync_with_allocator( 564 | TAllocator const & p_allocator, 565 | TArgs&&... p_args) 566 | { 567 | typedef typename sync_ptr< 568 | TPtr, 569 | TDeleter, 570 | THolder, 571 | TRefCounter> sync_ptr_t; 572 | return (sync_ptr_t(p_allocator.allocate(std::forward(p_args)...))); 573 | } 574 | 575 | template< 576 | class TPtr, 577 | template class TAllocator, 578 | template class TDeleter, 579 | template class THolder, 580 | class TRefCounter, 581 | class... TArgs> 582 | typename std::enable_if::value != 0, void>::type make_sync_with_allocator( 583 | TAllocator const & p_allocator, 584 | TArgs&&...) 585 | = delete; 586 | 587 | } // namespace mem 588 | 589 | 590 | namespace std 591 | { 592 | 593 | template < 594 | class TPtr, 595 | template class TDeleter, 596 | template class THolder, 597 | class TRefCounter> 598 | inline void swap( 599 | mem::sync_ptr & p_lhs, 600 | mem::sync_ptr & p_rhs) 601 | noexcept(noexcept(p_lhs.swap(p_rhs))) 602 | { 603 | p_lhs.swap(p_rhs); 604 | } 605 | 606 | } // namespace std 607 | 608 | 609 | /////////////////////////////////////////////////////////////////////////////////////////////////// 610 | // RELATIONAL OPERATORS 611 | /////////////////////////////////////////////////////////////////////////////////////////////////// 612 | 613 | template< 614 | class TPtr1, 615 | template class TDeleter1, 616 | template class THolder1, 617 | class TRefCounter1, 618 | class TPtr2, 619 | template class TDeleter2, 620 | template class THolder2, 621 | class TRefCounter2 > 622 | inline bool operator==( 623 | mem::sync_ptr const & p_lhs, 624 | mem::sync_ptr const & p_rhs) 625 | noexcept 626 | { 627 | return (p_lhs.get() == p_rhs.get()); 628 | } 629 | 630 | template< 631 | class TPtr1, 632 | template class TDeleter1, 633 | template class THolder1, 634 | class TRefCounter1, 635 | class TPtr2, 636 | template class TDeleter2, 637 | template class THolder2, 638 | class TRefCounter2 > 639 | inline bool operator!=( 640 | mem::sync_ptr const & p_lhs, 641 | mem::sync_ptr const & p_rhs) 642 | noexcept 643 | { 644 | return (!(p_lhs == p_rhs)); 645 | } 646 | 647 | template< 648 | class TPtr1, 649 | template class TDeleter1, 650 | template class THolder1, 651 | class TRefCounter1, 652 | class TPtr2, 653 | template class TDeleter2, 654 | template class THolder2, 655 | class TRefCounter2 > 656 | inline bool operator<( 657 | mem::sync_ptr const & p_lhs, 658 | mem::sync_ptr const & p_rhs) 659 | { 660 | typedef typename mem::sync_ptr::pointer ptr1_t; 661 | typedef typename mem::sync_ptr::pointer ptr2_t; 662 | typedef typename std::common_type::type common_t; 663 | return (std::less()(p_lhs.get(), p_rhs.get())); 664 | } 665 | 666 | template< 667 | class TPtr1, 668 | template class TDeleter1, 669 | template class THolder1, 670 | class TRefCounter1, 671 | class TPtr2, 672 | template class TDeleter2, 673 | template class THolder2, 674 | class TRefCounter2 > 675 | inline bool operator>=( 676 | mem::sync_ptr const & p_lhs, 677 | mem::sync_ptr const & p_rhs) 678 | { 679 | return (!(p_lhs < p_rhs)); 680 | } 681 | 682 | template< 683 | class TPtr1, 684 | template class TDeleter1, 685 | template class THolder1, 686 | class TRefCounter1, 687 | class TPtr2, 688 | template class TDeleter2, 689 | template class THolder2, 690 | class TRefCounter2 > 691 | inline bool operator>( 692 | mem::sync_ptr const & p_lhs, 693 | mem::sync_ptr const & p_rhs) 694 | { 695 | return (p_rhs < p_lhs); 696 | } 697 | 698 | template< 699 | class TPtr1, 700 | template class TDeleter1, 701 | template class THolder1, 702 | class TRefCounter1, 703 | class TPtr2, 704 | template class TDeleter2, 705 | template class THolder2, 706 | class TRefCounter2 > 707 | inline bool operator<=( 708 | mem::sync_ptr const & p_lhs, 709 | mem::sync_ptr const & p_rhs) 710 | { 711 | return (!(p_rhs < p_lhs)); 712 | } 713 | 714 | 715 | 716 | template< 717 | class TPtr, 718 | template class TDeleter, 719 | template class THolder, 720 | class TRefCounter > 721 | inline bool operator==( 722 | mem::sync_ptr const & p_lhs, 723 | std::nullptr_t) 724 | noexcept 725 | { 726 | return (!p_lhs); 727 | } 728 | 729 | template< 730 | class TPtr, 731 | template class TDeleter, 732 | template class THolder, 733 | class TRefCounter > 734 | inline bool operator==( 735 | std::nullptr_t, 736 | mem::sync_ptr const & p_rhs) 737 | noexcept 738 | { 739 | return (!p_rhs); 740 | } 741 | 742 | template< 743 | class TPtr, 744 | template class TDeleter, 745 | template class THolder, 746 | class TRefCounter > 747 | inline bool operator!=( 748 | mem::sync_ptr const & p_lhs, 749 | std::nullptr_t p_rhs) 750 | noexcept 751 | { 752 | return (!(p_lhs == p_rhs)); 753 | } 754 | 755 | template< 756 | class TPtr, 757 | template class TDeleter, 758 | template class THolder, 759 | class TRefCounter > 760 | inline bool operator!=( 761 | std::nullptr_t p_lhs, 762 | mem::sync_ptr const & p_rhs) 763 | noexcept 764 | { 765 | return (!(p_lhs == p_rhs)); 766 | } 767 | 768 | template< 769 | class TPtr, 770 | template class TDeleter, 771 | template class THolder, 772 | class TRefCounter > 773 | inline bool operator<( 774 | mem::sync_ptr const & p_lhs, 775 | std::nullptr_t p_rhs) 776 | { 777 | typedef typename mem::sync_ptr::pointer _Ptr; 778 | return (std::less<_Ptr>()(p_lhs.get(), p_rhs)); 779 | } 780 | 781 | template< 782 | class TPtr, 783 | template class TDeleter, 784 | template class THolder, 785 | class TRefCounter > 786 | inline bool operator<( 787 | std::nullptr_t p_lhs, 788 | mem::sync_ptr const & p_rhs) 789 | { 790 | typedef typename mem::sync_ptr::pointer _Ptr; 791 | return (std::less<_Ptr>()(p_lhs, p_rhs.get())); 792 | } 793 | 794 | template< 795 | class TPtr, 796 | template class TDeleter, 797 | template class THolder, 798 | class TRefCounter > 799 | inline bool operator>=( 800 | mem::sync_ptr const & p_lhs, 801 | std::nullptr_t p_rhs) 802 | { 803 | return (!(p_lhs < p_rhs)); 804 | } 805 | 806 | template< 807 | class TPtr, 808 | template class TDeleter, 809 | template class THolder, 810 | class TRefCounter > 811 | inline bool operator>=( 812 | std::nullptr_t p_lhs, 813 | mem::sync_ptr const & p_rhs) 814 | { 815 | return (!(p_lhs < p_rhs)); 816 | } 817 | 818 | template< 819 | class TPtr, 820 | template class TDeleter, 821 | template class THolder, 822 | class TRefCounter > 823 | inline bool operator>( 824 | mem::sync_ptr const & p_lhs, 825 | std::nullptr_t p_rhs) 826 | { 827 | return (p_rhs < p_lhs); 828 | } 829 | 830 | template< 831 | class TPtr, 832 | template class TDeleter, 833 | template class THolder, 834 | class TRefCounter > 835 | inline bool operator>( 836 | std::nullptr_t p_lhs, 837 | mem::sync_ptr const & p_rhs) 838 | { 839 | return (p_rhs < p_lhs); 840 | } 841 | 842 | template< 843 | class TPtr, 844 | template class TDeleter, 845 | template class THolder, 846 | class TRefCounter > 847 | inline bool operator<=( 848 | mem::sync_ptr const & p_lhs, 849 | std::nullptr_t p_rhs) 850 | { 851 | return (!(p_rhs < p_lhs)); 852 | } 853 | 854 | template< 855 | class TPtr, 856 | template class TDeleter, 857 | template class THolder, 858 | class TRefCounter > 859 | inline bool operator<=( 860 | std::nullptr_t p_lhs, 861 | mem::sync_ptr const & p_rhs) 862 | { 863 | return (!(p_rhs < p_lhs)); 864 | } 865 | 866 | #endif // __MEMORY_SYNC_PTR_H__ 867 | -------------------------------------------------------------------------------- /src/mem/sync_ptr_policy.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __MEMORY_SYNC_PTR_POLICY_H__ 3 | #define __MEMORY_SYNC_PTR_POLICY_H__ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | namespace mem 13 | { 14 | 15 | /** 16 | * \brief Default allocator used by smart pointer(s). 17 | * Calls "new" on template type. 18 | */ 19 | template< 20 | class TType> 21 | struct default_allocator 22 | { 23 | constexpr default_allocator( 24 | void) 25 | noexcept = default; 26 | 27 | template< 28 | class TType2, 29 | class = typename std::enable_if::value, void>::type> 30 | default_allocator(default_allocator const &) 31 | noexcept 32 | {} 33 | 34 | template 35 | TType * allocate( 36 | TArg && ...p_args) 37 | const 38 | { 39 | static_assert( 40 | 0 < sizeof(TType), 41 | "can't allocate an incomplete type"); 42 | return new TType(std::forward(p_args)...); 43 | } 44 | 45 | }; // struct default_allocator 46 | 47 | 48 | /** 49 | * \brief Default deleter used by smart pointer(s). 50 | * Calls "delete" on target template type pointer. 51 | */ 52 | template< 53 | class TType> 54 | struct default_deleter 55 | { 56 | constexpr default_deleter( 57 | void) 58 | noexcept = default; 59 | 60 | template< 61 | class TType2, 62 | class = typename std::enable_if::value, void>::type> 63 | default_deleter(default_deleter const &) 64 | noexcept 65 | {} 66 | 67 | void free( 68 | TType * p_ptr) 69 | const noexcept 70 | { 71 | static_assert( 72 | 0 < sizeof(TType), 73 | "can't delete an incomplete type"); 74 | delete p_ptr; 75 | } 76 | 77 | }; // struct default_deleter 78 | 79 | /** 80 | * \brief No op deleter used by smart pointer(s). 81 | * Does NOT free memory, no operation performed. 82 | */ 83 | template< 84 | class TType> 85 | struct noop_deleter 86 | { 87 | constexpr noop_deleter( 88 | void) 89 | noexcept = default; 90 | 91 | template< 92 | class TType2, 93 | class = typename std::enable_if::value, void>::type> 94 | noop_deleter(noop_deleter const &) 95 | noexcept 96 | {} 97 | 98 | void free( 99 | TType * p_ptr) 100 | const noexcept 101 | {} 102 | 103 | }; // struct noop_deleter 104 | 105 | 106 | 107 | /** 108 | * \brief Reference counter. 109 | */ 110 | class ref_counter 111 | { 112 | 113 | private: 114 | size_t ref_count_; 115 | size_t ref_count_ptr_; 116 | 117 | public: 118 | ref_counter( 119 | void) 120 | noexcept 121 | : ref_count_(1U) 122 | , ref_count_ptr_(0) 123 | {} 124 | 125 | inline void increment( 126 | void) 127 | noexcept 128 | { 129 | ref_count_++; 130 | } 131 | 132 | inline size_t decrement( 133 | void) 134 | noexcept 135 | { 136 | size_t ret = ref_count_; 137 | ref_count_--; 138 | return ret; 139 | } 140 | 141 | inline void increment_ptr( 142 | void) 143 | noexcept 144 | { 145 | ref_count_ptr_++; 146 | } 147 | 148 | inline size_t decrement_ptr( 149 | void) 150 | noexcept 151 | { 152 | size_t ret = ref_count_ptr_; 153 | ref_count_ptr_--; 154 | return ret; 155 | } 156 | 157 | inline size_t count( 158 | void) 159 | const noexcept 160 | { 161 | return ref_count_; 162 | } 163 | 164 | inline size_t count_ptr( 165 | void) 166 | const noexcept 167 | { 168 | return ref_count_ptr_; 169 | } 170 | 171 | }; // class ref_counter 172 | 173 | /** 174 | * \brief Atomic reference counter. 175 | */ 176 | class atomic_ref_counter 177 | { 178 | 179 | private: 180 | std::atomic ref_count_; 181 | std::atomic ref_count_ptr_; 182 | 183 | public: 184 | inline atomic_ref_counter( 185 | void) 186 | noexcept 187 | : ref_count_(1U) 188 | , ref_count_ptr_(0) 189 | {} 190 | 191 | inline void increment( 192 | void) 193 | noexcept 194 | { 195 | ref_count_.fetch_add(1U); 196 | } 197 | 198 | inline size_t decrement( 199 | void) 200 | noexcept 201 | { 202 | return ref_count_.fetch_sub(1U); 203 | } 204 | 205 | inline void increment_ptr( 206 | void) 207 | noexcept 208 | { 209 | ref_count_ptr_.fetch_add(1U); 210 | } 211 | 212 | inline size_t decrement_ptr( 213 | void) 214 | noexcept 215 | { 216 | return ref_count_ptr_.fetch_sub(1U); 217 | } 218 | 219 | inline size_t count( 220 | void) 221 | const noexcept 222 | { 223 | return ref_count_.load(); 224 | } 225 | 226 | inline size_t count_ptr( 227 | void) 228 | const noexcept 229 | { 230 | return ref_count_ptr_.load(); 231 | } 232 | 233 | }; // class atomic_ref_counter 234 | 235 | 236 | 237 | /** 238 | * \brief Pointer holder. 239 | */ 240 | template 241 | class ptr_holder 242 | { 243 | 244 | private: 245 | TPtr * ptr_; 246 | 247 | public: 248 | inline ptr_holder( 249 | void) 250 | noexcept 251 | : ptr_(nullptr) 252 | {} 253 | 254 | inline explicit ptr_holder( 255 | TPtr * p_ptr) 256 | noexcept 257 | : ptr_(p_ptr) 258 | {} 259 | 260 | inline TPtr * set( 261 | TPtr * p_ptr) 262 | noexcept 263 | { 264 | auto p = ptr_; 265 | ptr_ = p_ptr; 266 | return p; 267 | } 268 | 269 | inline TPtr * get( 270 | void) 271 | const noexcept 272 | { 273 | return ptr_; 274 | } 275 | 276 | }; // class ptr_holder 277 | 278 | /** 279 | * \brief Recursive mutex protected pointer holder. 280 | */ 281 | template 282 | class ptr_holder_ts 283 | { 284 | 285 | private: 286 | TPtr * ptr_; 287 | mutable std::recursive_mutex mtx_; 288 | 289 | public: 290 | inline ptr_holder_ts( 291 | void) 292 | noexcept 293 | : ptr_(nullptr) 294 | , mtx_() 295 | {} 296 | 297 | inline explicit ptr_holder_ts( 298 | TPtr * p_ptr) 299 | noexcept 300 | : ptr_(p_ptr) 301 | , mtx_() 302 | {} 303 | 304 | inline TPtr * set( 305 | TPtr * p_ptr) 306 | noexcept 307 | { 308 | std::lock_guard l(mtx_); 309 | auto p = ptr_; 310 | ptr_ = p_ptr; 311 | return p; 312 | } 313 | 314 | inline TPtr * get( 315 | void) 316 | const noexcept 317 | { 318 | std::lock_guard l(mtx_); 319 | return ptr_; 320 | } 321 | 322 | }; // class ptr_holder_ts 323 | 324 | } // namespace mem 325 | 326 | #endif // __MEMORY_SYNC_PTR_POLICY_H__ 327 | -------------------------------------------------------------------------------- /talks/sync_ptr_c++_london_08032017.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romaincheminade/sync_ptr/29d6d3b808235d894883acae1b584ef4fca4e526/talks/sync_ptr_c++_london_08032017.pdf -------------------------------------------------------------------------------- /tests/cc_sync_ptr.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Main header. 3 | #include "cc_sync_ptr.h" 4 | 5 | #include 6 | 7 | 8 | void tests::cc_sync_ptr_synchro(void) 9 | { 10 | class Obj 11 | {}; 12 | cc::sync_ptr obj; 13 | 14 | cc::sync_ptr obj1 = cc::make_sync(); 15 | cc::sync_ptr obj2(obj1); 16 | cc::sync_ptr obj3 = cc::make_sync(); 17 | assert(obj1); 18 | assert(obj2); 19 | assert(obj3); 20 | 21 | assert(obj1.get() == obj2.get()); 22 | assert(obj1.count() == obj2.count()); 23 | assert(obj1 != obj3); 24 | 25 | obj1.reset(new Obj()); 26 | assert(obj1 == obj2); 27 | assert(obj1.count() == obj2.count()); 28 | 29 | obj1 = obj3; 30 | assert(obj1 == obj3); 31 | assert(obj1.count() == obj3.count()); 32 | assert(obj1 != obj2); 33 | 34 | obj1.reset(new Obj()); 35 | assert(obj1.get() != nullptr); 36 | assert(obj1 == obj3); 37 | assert(obj1.count() == obj3.count()); 38 | } 39 | 40 | void tests::cc_sync_ptr_release(void) 41 | { 42 | struct Obj 43 | {}; 44 | 45 | cc::sync_ptr ptr = cc::make_sync(); 46 | assert(ptr); 47 | 48 | Obj * ptr_out = nullptr; 49 | bool ret = ptr.release(&ptr_out); 50 | assert(ptr_out); 51 | assert(!ptr); 52 | 53 | delete ptr_out; 54 | } 55 | 56 | void tests::cc_sync_ptr_exchange(void) 57 | { 58 | struct Obj 59 | {}; 60 | 61 | cc::sync_ptr ptr = cc::make_sync(); 62 | assert(ptr); 63 | 64 | Obj * ptr_add = ptr.get(); 65 | Obj * ptr_out = nullptr; 66 | Obj * ptr_in = new Obj(); 67 | bool ret = ptr.exchange(&ptr_out, ptr_in); 68 | 69 | assert(ret); 70 | assert(ptr_out == ptr_add); 71 | 72 | assert(ptr); 73 | assert(ptr.get() == ptr_in); 74 | 75 | delete ptr_out; 76 | } 77 | 78 | 79 | 80 | static bool test_allocator_called = false; 81 | 82 | template 83 | struct test_allocator 84 | { 85 | constexpr test_allocator( 86 | void) 87 | noexcept = default; 88 | 89 | template 90 | TType * allocate( 91 | TArg && ...p_args) 92 | const 93 | { 94 | static_assert(0 < sizeof(TType), 95 | "can't allocate an incomplete type"); 96 | 97 | test_allocator_called = true; 98 | return new TType(std::forward(p_args)...); 99 | } 100 | }; 101 | 102 | void tests::cc_sync_ptr_allocator(void) 103 | { 104 | class Obj 105 | {}; 106 | 107 | // User defined template. 108 | 109 | test_allocator_called = false; 110 | { 111 | test_allocator allocator_; 112 | 113 | auto obj( 114 | cc::make_sync_with_allocator< 115 | Obj> ( 116 | allocator_)); 117 | } 118 | assert(test_allocator_called); 119 | } 120 | -------------------------------------------------------------------------------- /tests/cc_sync_ptr.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __TESTS_CC_SYNC_PTR_H__ 3 | #define __TESTS_CC_SYNC_PTR_H__ 4 | 5 | #ifndef __CC_SYNC_PTR_H__ 6 | #include "cc/sync_ptr.h" 7 | #endif 8 | 9 | 10 | namespace tests 11 | { 12 | /** 13 | * \brief Test sync_ptr synchronization. 14 | * \note Result: Synchronized on all expected path. 15 | */ 16 | void cc_sync_ptr_synchro(void); 17 | 18 | /** 19 | * \brief Test sync_ptr release. 20 | * \note Result: Pointer gives ownership and is set to null. 21 | */ 22 | void cc_sync_ptr_release(void); 23 | 24 | /** 25 | * \brief Test sync_ptr exchange. 26 | * \note Result: Pointer gives ownership and is set to target one. 27 | */ 28 | void cc_sync_ptr_exchange(void); 29 | 30 | /** 31 | * \brief Test sync_ptr allocator. 32 | * \note Result: Pointer is allocated. 33 | */ 34 | void cc_sync_ptr_allocator(void); 35 | 36 | } // namespace tests 37 | 38 | #endif // __TESTS_CC_SYNC_PTR_H__ 39 | -------------------------------------------------------------------------------- /tests/mem_sync_ptr.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Main header. 3 | #include "mem_sync_ptr.h" 4 | 5 | #include 6 | 7 | 8 | void tests::mem_sync_ptr_synchro(void) 9 | { 10 | class Obj 11 | {}; 12 | mem::sync_ptr obj; 13 | 14 | mem::sync_ptr obj1 = mem::make_sync(); 15 | mem::sync_ptr obj2(obj1); 16 | mem::sync_ptr obj3 = mem::make_sync(); 17 | assert(obj1); 18 | assert(obj2); 19 | assert(obj3); 20 | 21 | assert(obj1.get() == obj2.get()); 22 | assert(obj1.count() == obj2.count()); 23 | assert(obj1 != obj3); 24 | 25 | obj1.reset(new Obj()); 26 | assert(obj1 == obj2); 27 | assert(obj1.count() == obj2.count()); 28 | 29 | obj1 = obj3; 30 | assert(obj1 == obj3); 31 | assert(obj1.count() == obj3.count()); 32 | assert(obj1 != obj2); 33 | 34 | obj1.reset(new Obj()); 35 | assert(obj1.get() != nullptr); 36 | assert(obj1 == obj3); 37 | assert(obj1.count() == obj3.count()); 38 | } 39 | 40 | void tests::mem_sync_ptr_release(void) 41 | { 42 | struct Obj 43 | {}; 44 | 45 | mem::sync_ptr ptr = mem::make_sync(); 46 | assert(ptr); 47 | 48 | Obj * raw = ptr.release(); 49 | assert(raw); 50 | assert(!ptr); 51 | 52 | delete raw; 53 | } 54 | 55 | void tests::mem_sync_ptr_exchange(void) 56 | { 57 | struct Obj 58 | {}; 59 | 60 | mem::sync_ptr ptr = mem::make_sync(); 61 | assert(ptr); 62 | 63 | Obj * ptr_add = ptr.get(); 64 | Obj * raw = new Obj(); 65 | std::unique_ptr ptr_xc(ptr.exchange(raw)); 66 | 67 | assert(ptr_xc); 68 | assert(ptr_xc.get() == ptr_add); 69 | 70 | assert(ptr); 71 | assert(ptr.get() == raw); 72 | } 73 | 74 | 75 | 76 | static bool test_allocator_called = false; 77 | 78 | template 79 | struct test_allocator 80 | { 81 | constexpr test_allocator( 82 | void) 83 | noexcept = default; 84 | 85 | template 86 | TType * allocate( 87 | TArg && ...p_args) 88 | const 89 | { 90 | static_assert(0 < sizeof(TType), 91 | "can't allocate an incomplete type"); 92 | 93 | test_allocator_called = true; 94 | return new TType(std::forward(p_args)...); 95 | } 96 | }; 97 | 98 | void tests::mem_sync_ptr_allocator(void) 99 | { 100 | class Obj 101 | {}; 102 | 103 | // User defined template. 104 | 105 | test_allocator_called = false; 106 | { 107 | test_allocator allocator_; 108 | 109 | auto obj( 110 | mem::make_sync_with_allocator< 111 | Obj> ( 112 | allocator_)); 113 | } 114 | assert(test_allocator_called); 115 | } 116 | -------------------------------------------------------------------------------- /tests/mem_sync_ptr.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __TESTS_MEM_SYNC_PTR_H__ 3 | #define __TESTS_MEM_SYNC_PTR_H__ 4 | 5 | #ifndef __MEMORY_SYNC_PTR_H__ 6 | #include "mem/sync_ptr.h" 7 | #endif 8 | 9 | 10 | namespace tests 11 | { 12 | /** 13 | * \brief Test sync_ptr synchronization. 14 | * \note Result: Synchronized on all expected path. 15 | */ 16 | void mem_sync_ptr_synchro(void); 17 | 18 | /** 19 | * \brief Test sync_ptr release. 20 | * \note Result: Pointer gives ownership and is set to null. 21 | */ 22 | void mem_sync_ptr_release(void); 23 | 24 | /** 25 | * \brief Test sync_ptr exchange. 26 | * \note Result: Pointer gives ownership and is set to target one. 27 | */ 28 | void mem_sync_ptr_exchange(void); 29 | 30 | /** 31 | * \brief Test sync_ptr allocator. 32 | * \note Result: Pointer is allocated. 33 | */ 34 | void mem_sync_ptr_allocator(void); 35 | 36 | } // namespace tests 37 | 38 | #endif // __TESTS_MEM_SYNC_PTR_H__ 39 | --------------------------------------------------------------------------------