Docfix
[libcds.git] / cds / intrusive / details / base.h
index 0845569c533b29e38bbeb18067bb01abb1bd756b..511896799463429b4219a168c4d3f755af408b4f 100644 (file)
@@ -1,9 +1,37 @@
-//$$CDS-header$$
+/*
+    This file is a part of libcds - Concurrent Data Structures library
 
-#ifndef __CDS_INTRUSIVE_DETAILS_BASE_H
-#define __CDS_INTRUSIVE_DETAILS_BASE_H
+    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017
 
-#include <cds/intrusive/node_traits.h>
+    Source code repo: http://github.com/khizmax/libcds/
+    Download: http://sourceforge.net/projects/libcds/files/
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this
+      list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef CDSLIB_INTRUSIVE_DETAILS_BASE_H
+#define CDSLIB_INTRUSIVE_DETAILS_BASE_H
+
+#include <cds/intrusive/details/node_traits.h>
 #include <cds/details/allocator.h>
 #include <cds/algo/backoff_strategy.h>
 
@@ -12,18 +40,157 @@ namespace cds {
 /// Intrusive containers
 /**
     @ingroup cds_intrusive_containers
-    The namespace cds::intrusive contains intrusive lock-free containers.
+    The namespace \p cds::intrusive contains intrusive lock-free containers.
     The idea comes from \p boost::intrusive library, see http://boost.org/doc/ as a good introduction to intrusive approach.
-    The intrusive containers of libcds library is developed as close to boost::intrusive
+    The intrusive containers of libcds library is developed as close to \p boost::intrusive
 
     In terms of lock-free approach, the main advantage of intrusive containers is
-    that no memory allocation is performed to maintain container items.
-    However, additional requirements is imposed for types and values that can be stored in intrusive container.
+    that no memory allocation is performed to maintain container elements.
+    However, additional requirements are imposed for types and values that can be stored in intrusive container.
     See the container documentation for details.
 
-    Restriction for Gidenstam's garbage collector cds::gc::HRC:
-    the Gidenstam's garbage collector makes additional requirements to type of item in intrusive container.
-    Therefore, for this GC only \p base_hook is allowed as the value of opt::hook option.
+    \anchor cds_intrusive_hook_tag
+    \par Tags
+    Many hooks and nodes for intrusive containers contain template argument \p Tag.
+    This argument serves as a tag, so you can derive from more than one container's node and hence put an object in multiple intrusive containers
+    at the same time. An incomplete type can serve as a tag. If you specify two hooks, you must specify a different tag for each one.
+    Example:
+    \code
+    struct tag1;
+    cds::intrusive::treiber_stack::node< cds::gc::HP, tag<tag1> >
+    \endcode
+    If no tag is specified the default \p cds::opt::none will be used.
+
+    \anchor cds_intrusive_item_creating
+    \par Inserting items
+    Many intrusive and non-intrusive (standard-like) containers in the library have the member functions
+    that take a functor argument to initialize the inserted item after it has been successfully inserted,
+    for example:
+    \code
+    template <typename Q, typename Func>
+    bool insert( Q& key, Func f );
+
+    template <typename Q, typename Func>
+    std::pair<bool, bool> update( Q& key, Func f, bool bAllowInsert = true );
+    \endcode
+    The first member function calls \p f functor iif a new item has been inserted. The functor takes two parameter: a reference to inserted item and
+    \p key.
+
+    The second member function, \p update(), allows to insert a new item to the container if \p key is not found, or to find the item with \p key and
+    to perform some action with it. The \p f signature is:
+    \code
+    void f( bool bNew, item_type& item, Q& key );
+    \endcode
+    where \p bNew is a flag to indicate whether \p item is a new created node or not.
+
+    Such functions should be used with caution in multi-threaded environment
+    since they can cause races. The library does not synchronize access
+    to container's items, so many threads can access to one item simultaneously.
+    For example, for \p insert member function the following race is possible:
+    \code
+        // Suppose, Foo is a complex structure with int key field
+        SomeContainer<Foo> q;
+
+        Thread 1                                  Thread 2
+
+        q.insert( Foo(5),                         q.find( 5, []( Foo& item ) {
+            []( Foo& item ){                         // access to item fields
+               // complex initialization             ...
+               item.f1 = ...;                     });
+               ...
+            });
+    \endcode
+    Execute sequence:
+    \code
+        Find 5 in the container.
+        Key 5 is not found
+        Create a new item                         Find key 5
+            with calling Foo(5) ctor
+        Insert the new item
+                                                  The key 5 is found -
+                                                     call the functor     (!)
+        Perform complex
+           initialization -
+           call the functor
+    \endcode
+    (!): Thread 2 found the key and call its functor on incomplete initialized item.
+    Simultaneous access to the item also is possible. In this case Thread 1 is
+    initializing the item, thread 2 is reading (or writing) the item's fields.
+    In any case, Thread 2 can read uninitialized or incomplete initialized fields.
+
+    \p update() member function race. Suppose, thread 1 and thread 2 perform
+    the
+    following code:
+    \code
+        q.update( 5, []( bool bNew, Foo& item, int  arg )
+           {
+              // bNew: true if the new element has been created
+              //       false otherwise
+              if ( bNew ) {
+                 // initialize item
+                 item.f1=...;
+                 //...
+              }
+              else {
+                 // do some work
+                 if ( !item.f1 )
+                    item.f1 = ...;
+                 else {
+                   //...
+                 }
+                 //...
+              }
+           }
+        );
+    \endcode
+    Execute sequence:
+    \code
+        Thread 1                                  Thread 2
+        key 5 not found
+        insert new item Foo(5)                    Find 5
+                                                  Key 5 found
+                                                  call the functor with
+                                                     bNew = false        (!)
+        call the functor with
+           bNew = true
+    \endcode
+    (!): Thread 2 executes its functor on incomplete initialized item.
+
+    To protect your code from such races you can use some item-level synchronization,
+    for example:
+    \code
+    struct Foo {
+       spinlock lock;       // item-level lock
+       bool initialized = false;    // initialization flag
+       // other fields
+       // ....
+    };
+
+    q.update( 5, []( bool bNew, Foo& item, int  arg )
+        {
+            // Lock access to the item
+            std::unique_lock( item.lock );
+
+            if ( !item.initialized ) {
+                // initialize item
+                item.f1=...;
+                //...
+                item.initialized = true; // mark the item as initialized
+            }
+            else {
+                // do some work
+                if ( !item.f1 )
+                    item.f1 = ...;
+                else {
+                    //...
+                }
+                //...
+            }
+        }
+    );
+    \endcode
+    If the item-level synchronization is not suitable, you should not use any inserting member function
+    with post-insert functor argument.
 
     \anchor cds_intrusive_item_destroying
     \par Destroying items
@@ -115,17 +282,16 @@ namespace cds {
     If you develop your intrusive container based on <b>libcds</b> library framework, you should
     take in the account the following.
     The main idea of garbage collectors (GC) based on Hazard Pointer schema is protecting a shared pointer
-    by publishing it as a "hazard" one i.e. as a pointer that is changing at the current time and cannot be
-    deleted at this moment. In intrusive container paradigm, the pointer to the node of the container
-    and the pointer to the item stored in the container are not equal in the general case.
-    However, any pointer to the node should be castable to the appropriate pointer to the container's item.
-    In general, any item can be placed to some different intrusive containers simultaneously,
-    and each of those container holds a unique pointer to its node that refers to the same item.
+    by publishing it as a "hazard" i.e. as a pointer that is changing at the current time and cannot be
+    deleted at this moment. In intrusive container paradigm, the pointer to a node of the container
+    and the pointer to a item stored in the container are not equal in the general case.
+    However, any pointer to node should be castable to appropriate pointer to container's item.
+    In general, any item can be placed to two or more intrusive containers simultaneously,
+    and each of those container holds an unique pointer to its node that refers to the same item.
     When we protect a pointer, we want to protect an <b>item</b> pointer that is the invariant
-    for any container stored that item. In your intrusive container, instead of protecting by GC's guard a pointer to an node
-    you should convert it to the pointer to the item and then protect resulting item pointer.
+    for any container stored that item. In your intrusive container, instead of protecting by GC's guard a pointer to node
+    you should cast it to the pointer to item and then protect that item pointer.
     Otherwise an unpredictable result may occur.
-
 */
 namespace intrusive {
 
@@ -154,7 +320,19 @@ namespace intrusive {
     /** @defgroup cds_intrusive_list List
         @ingroup cds_intrusive_containers
     */
+    /** @defgroup cds_intrusive_freelist Free-list
+        @ingroup cds_intrusive_containers
+    */
+
+    //@cond
+    class iterable_list_tag
+    {};
+
+    template <typename List>
+    struct is_iterable_list: public std::is_base_of< iterable_list_tag, List>
+    {};
+    //@endcond
 
 }} // namespace cds::intrusuve
 
-#endif  // #ifndef __CDS_INTRUSIVE_DETAILS_BASE_H
+#endif  // #ifndef CDSLIB_INTRUSIVE_DETAILS_BASE_H