library of assembled shared sources

http://lass.cocamware.com

lock_free_queue.inl

Go to the documentation of this file.
00001 /** @file
00002  *  @author Bram de Greve (bramz@users.sourceforge.net)
00003  *  @author Tom De Muer (tomdemuer@users.sourceforge.net)
00004  *
00005  *  *** BEGIN LICENSE INFORMATION ***
00006  *  
00007  *  The contents of this file are subject to the Common Public Attribution License 
00008  *  Version 1.0 (the "License"); you may not use this file except in compliance with 
00009  *  the License. You may obtain a copy of the License at 
00010  *  http://lass.sourceforge.net/cpal-license. The License is based on the 
00011  *  Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover 
00012  *  use of software over a computer network and provide for limited attribution for 
00013  *  the Original Developer. In addition, Exhibit A has been modified to be consistent 
00014  *  with Exhibit B.
00015  *  
00016  *  Software distributed under the License is distributed on an "AS IS" basis, WITHOUT 
00017  *  WARRANTY OF ANY KIND, either express or implied. See the License for the specific 
00018  *  language governing rights and limitations under the License.
00019  *  
00020  *  The Original Code is LASS - Library of Assembled Shared Sources.
00021  *  
00022  *  The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
00023  *  The Original Developer is the Initial Developer.
00024  *  
00025  *  All portions of the code written by the Initial Developer are:
00026  *  Copyright (C) 2004-2007 the Initial Developer.
00027  *  All Rights Reserved.
00028  *  
00029  *  Contributor(s):
00030  *
00031  *  Alternatively, the contents of this file may be used under the terms of the 
00032  *  GNU General Public License Version 2 or later (the GPL), in which case the 
00033  *  provisions of GPL are applicable instead of those above.  If you wish to allow use
00034  *  of your version of this file only under the terms of the GPL and not to allow 
00035  *  others to use your version of this file under the CPAL, indicate your decision by 
00036  *  deleting the provisions above and replace them with the notice and other 
00037  *  provisions required by the GPL License. If you do not delete the provisions above,
00038  *  a recipient may use your version of this file under either the CPAL or the GPL.
00039  *  
00040  *  *** END LICENSE INFORMATION ***
00041  */
00042 
00043 namespace lass
00044 {
00045 namespace stde
00046 {
00047 
00048 // --- public --------------------------------------------------------------------------------------
00049 
00050 template <typename T, typename A>
00051 lock_free_queue<T, A>::lock_free_queue():
00052     node_allocator_(sizeof(node_t)),
00053     value_allocator_(sizeof(value_type))
00054 {
00055     pointer_t tail(make_node(0), 0);
00056     
00057     // Mentally equivalent to: head_ = tail_ = tail;
00058     //
00059     // the funny way to assign head_ and tail_ is because we don't support
00060     // operator= on volatile TaggedPtrs 
00061     // [Bramz]
00062     //
00063     pointer_t expected = head_;
00064     head_.atomicCompareAndSwap(expected, tail);
00065     expected = tail_;
00066     tail_.atomicCompareAndSwap(expected, tail);
00067     LASS_ASSERT(tail == head_ && tail == tail_);    
00068 }
00069 
00070 
00071 
00072 template <typename T, typename A>
00073 lock_free_queue<T, A>::~lock_free_queue()
00074 {
00075     pointer_t head = head_;
00076     while (head)
00077     {
00078         pointer_t next = head->next;
00079         free_node(head.get());
00080         head = next;
00081     }
00082 }
00083 
00084 
00085 
00086 /** push a value in the back
00087  *  @arg exception safe: if no node of could be allocatoed, or if copy constructor of x throws, 
00088  *      it fails gracefully.
00089  */
00090 template <typename T, typename A>
00091 void lock_free_queue<T, A>::push(const value_type& x)
00092 {
00093     value_type* const value = make_value(x);
00094     node_t* node = 0;
00095     try
00096     {
00097         node = make_node(value);
00098     }
00099     catch (...)
00100     {
00101         free_value(value);
00102         throw;
00103     }
00104     pointer_t tail;
00105     while (true)
00106     {
00107         tail = tail_;
00108         pointer_t next = tail->next;
00109         if (tail == tail_)
00110         {
00111             if (!next)
00112             {
00113                 pointer_t new_next(node, next.tag() + 1);
00114                 if (tail->next.atomicCompareAndSwap(next, new_next))
00115                 {
00116                     break;
00117                 }
00118             }
00119             else
00120             {
00121                 pointer_t new_tail(next.get(), tail.tag() + 1);
00122                 tail_.atomicCompareAndSwap(tail, new_tail);
00123             }
00124         }
00125     }
00126     pointer_t new_tail(node, tail.tag() + 1);
00127     tail_.atomicCompareAndSwap(tail, new_tail);
00128 }
00129 
00130 
00131 
00132 /** Try to pop a value from the front and store it in x.
00133  *  @return false if no element could be popped.
00134  *  @arg EXCEPTION UNSAFE: if the copy constructor of x can throw, things can go miserably wrong!
00135  */
00136 template <typename T, typename A>
00137 bool lock_free_queue<T, A>::pop(value_type& x)
00138 {
00139     pointer_t head;
00140     value_type* value;
00141     while (true)
00142     {
00143         head = head_;
00144         pointer_t tail = tail_;
00145         pointer_t next = head->next;
00146         if (head == head_)
00147         {
00148             if (head.get() == tail.get())
00149             {
00150                 if (!next)
00151                 {
00152                     return false;
00153                 }
00154                 pointer_t new_tail(next.get(), tail.tag() + 1);
00155                 tail_.atomicCompareAndSwap(tail, new_tail);
00156             }
00157             else
00158             {
00159                 // 'next' might be reclaimed here, but that's not really a problem:
00160                 // a) its memory will still exist, so at most value will contain rubbish but that's
00161                 //      ok to copy because it's just used as a pointer
00162                 // b) if it is reclaimed, the following CAS will fail so that value will never be
00163                 //      used so we don't care if it contains rubbish.
00164                 // SEE ALSO: lock_free_stack::pop_node, and why the above is not entirely true ...              
00165                 // [Bramz]
00166                 //
00167                 value = next->value; 
00168 
00169                 pointer_t new_head(next.get(), head.tag() + 1);
00170                 if (head_.atomicCompareAndSwap(head, new_head))
00171                 {
00172                     x = *value;
00173                     free_value(value);
00174                     free_node(head.get());
00175                     return true;
00176                 }
00177             }
00178         }
00179     }
00180 }
00181 
00182 // --- private -------------------------------------------------------------------------------------
00183 
00184 template <typename T, typename A>
00185 typename lock_free_queue<T, A>::value_type* const
00186 lock_free_queue<T, A>::make_value(const value_type& x)
00187 {
00188     value_type* value = static_cast<value_type*>(value_allocator_.allocate());
00189     try
00190     {
00191         new (value) value_type(x);
00192     }
00193     catch (...)
00194     {
00195         value_allocator_.deallocate(value);
00196         throw;
00197     }
00198     return value;
00199 }
00200 
00201 
00202 
00203 template <typename T, typename A>
00204 void lock_free_queue<T, A>::free_value(value_type* value)
00205 {
00206     value->~value_type();
00207     value_allocator_.deallocate(value);
00208 }
00209 
00210 
00211 
00212 template <typename T, typename A>
00213 typename lock_free_queue<T, A>::node_t* const
00214 lock_free_queue<T, A>::make_node(value_type* value)
00215 {
00216     node_t* node = static_cast<node_t*>(node_allocator_.allocate());
00217     try
00218     {
00219         new (&node->next) pointer_t();
00220     }
00221     catch (...)
00222     {
00223         node_allocator_.deallocate(node);
00224         throw;
00225     }
00226     node->value = value;
00227     return node;
00228 }
00229     
00230 
00231 
00232 template <typename T, typename A>
00233 void lock_free_queue<T, A>::free_node(node_t* node)
00234 {
00235     node->next.~pointer_t();
00236     node_allocator_.deallocate(node);
00237 }
00238 
00239 }
00240 
00241 }
00242 
00243 // EOF

Generated on Mon Nov 10 14:20:30 2008 for Library of Assembled Shared Sources by doxygen 1.5.7.1
SourceForge.net Logo