Is this thread safe?

Koroskin Denis 2korden+dmd at gmail.com
Sun Jul 13 15:07:32 PDT 2008


On Sat, 12 Jul 2008 04:52:15 +0400, dsimcha <dsimcha at yahoo.com> wrote:

> Never mind, I see at least one reason why my last post would be  
> incorrect.  The
> length variable could be set out-of-order before all of the factorials  
> up to
> length are calculated.  Is there any simple way to write this function  
> so it's
> thread-safe without using synchronized just to access cached results?

The following approach would be easier, I think:

1) Have an array that reallocates without invalidating old data, so that
all the pointers and iterators remain valid after array resize took place

2) When need to access Nth element do the following:
    a) if size > N, return array[N]
    b) increase the capacity (atomically)
    c) set the array[N] to the calculated value
    d) use compareAndSwap (atomicStoreIf in Tango) to increase array.length
    e) return value

Using my SafeGrowingArray from here -  
http://www.everfall.com/paste/id.php?tmjp3qzeo622
your code could be simplified to the following:

// Important invariant assumption:
// if the size is N, then values 0..N-1 are evaluated and correct.
//
// The steps are taken in the following order:
// 1) increase capacity
// 2) calculate and set the value
// 3) increase size
//
// As a result, if another thread changes the array size to some K then it  
means that it is guarantied that values 0..K-1 are correct

class FactorialTable : SafeGrowingArray!(real,  
ThreadSafetyPolicy.ThreadSafe)
{
     real opIndex(size_t index)
     {
         size_t size = _size;
         if (size > index) {
             return super.opIndex(index);
         }

         reserve(index + 1); // step 1, ensure the capacity

         for (size_t i = size; i <= index; ++i) {
             real value = calcFactorialValue(i);   // step 2 a, calculate  
the value
             this.opIndexAssign(index, value);     // step 2 b, set the  
value. It is safe
                                                   // because capacity is  
large and faction is deterministic
         }

         while (true) {
             if (atomicStoreIf(_size, size, index + 1)) { // step 3, the  
most important one
                 break; // succeeded                      // if succeeded,  
the size is updated
             }

             // failed                                    // if failed ->  
size is changed in another thread
             size = _size;
             if (size > index) {                          // check whether  
the size is large enough
                 break;
             }

             // try again
         }

         return super.opIndex(index);
     }
}

FactorialTable factorialTable;

static this() {
     factorialTable = new FactorialTable();
}

real logFactorial(uint n) {
    return factorialTable[n];
}

Use anything at your will.
NOTE: not tested



More information about the Digitalmars-d mailing list