How to define thread safe array?
Asked Answered
N

4

13

How can I define a thread safe global array with minimal modifications?

I want like every access to it to be accomplished by using mutex and synchronized block.

Something like this as 'T' will be some type (note that 'sync' keyword is not currently defined AFAIK):

sync Array!(T) syncvar;

And every access to it will be simmilar to this:

Mutex __syncvar_mutex;

    //some func scope....
    synchronized(__syncvar_mutex) { /* edits 'syncvar' safely */ }
Nombles answered 22/4, 2015 at 19:4 Comment(6)
Protecting the array itself is easy. You just have to create a wrapper struct with all of the array functions and operators overloaded and where all of the functions are synchronized. The problem is in getting at the elements of the array itself. Protecting those as well gets a lot more complicated, since as soon as you return them from a function like opIndex, they're not protected anymore...Linkwork
I thought this is what shared(T[]) was supposed to do, but apparently not....Esker
@AdamD.Ruppe All shared really does is make it so that the variable is shared across threads rather than being thread-local. There are a few cases where the compiler will complain if you try and do stuff with shared that is guaranteed to be a problem (in some cases related to atomic operations IIRC), but it doesn't do anything with mutexes or synchronization. That's up to you. Synchronized classes is the recommended way to handle shared objects, but they're not fully implemented (just synchronized functions), and anything which escapes that class won't be protected anymore regardless.Linkwork
People if you can help me - please post an answer and don't let some .... to take your bounty. Because at the end (after 6 days) it'll be automatically given to someone who doesn't deserve it.Nombles
I would have answered already if I had a good solution... but I really don't. Best I can think of is like Jonathan said, try doing a wrapper with synchronized methods. But I don't really know.Esker
I think you should read this excellent thread on SO: stackoverflow.com/questions/10395903/… . Especially read the answer to it. :) The answer states a fact: it is really hard make a thread-safe array without it being a concurrency bottleneck.Coadjutress
B
2

My naive attempt was to do something like this:

import std.typecons : Proxy:

synchronized class Array(T)
{
    static import std.array;
    private std.array.Array!T data;
    mixin Proxy!data;
}

Sadly, it doesn't work because of https://issues.dlang.org/show_bug.cgi?id=14509

Can't say I am very surprised though as automagical handling of multi-threading via hidden mutexes is very unidiomatic in modern D and the very concept of synchronized classes is mostly a relict from D1 times.

You can implement same solution manually, of course, by defining own SharedArray class with all necessary methods and adding locks inside the methods before calling internal private plain Array methods. But I presume you want something that work more out of the box.

Can't invent anything better right here and now (will think about it more) but it is worth noting that in general it is encouraged in D to create data structures designed for handling shared access explicitly instead of just protecting normal data structures with mutexes. And, of course, most encouraged approach is to not shared data at all using message passing instead.

I will update the answer if anything better comes to my mind.

Burdett answered 27/4, 2015 at 2:19 Comment(1)
No - I want something that works and is not too long. Thanks anyway - I'll give it a shoot.Nombles
B
0

You can wrap the array inside a struct that locks the access to the array when a thread acquires a token and until it releases it.

The wrapper/locker:

  • acquire(): is called in loop by a thread. As it returns a pointer, the thread knows that it has the token when the method returns a non null value.
  • release(): is called by a thread after processing the data whose access has been acquired previously.

.

shared struct Locker(T)
{
    private:
        T t;
        size_t token;   
    public:  
        shared(T) * acquire() 
        {
            if (token) return null;
            else
            {
                import core.atomic;
                atomicOp!"+="(token, 1);
                return &t;
            }
        }
        void release()
        {
            import core.atomic;
            atomicOp!"-="(token, 1);
        }
}

and a quick test:

alias LockedIntArray = Locker!(size_t[]);
shared LockedIntArray intArr;

void arrayTask(size_t cnt)
{
    import core.thread, std.random;

    // ensure the desynchronization of this job.
    Thread.sleep( dur!"msecs"(uniform(4, 20)));

    shared(size_t[])* arr = null;
    // wait for the token
    while(arr == null) {arr = intArr.acquire;}

    *arr ~= cnt;    
    import std.stdio;
    writeln(*arr);

    // release the token for the waiting threads
    intArr.release;
}

void main(string[] args)
{
    import std.parallelism;
    foreach(immutable i; 0..16)
    {
       auto job = task(&arrayTask, i);
       job.executeInNewThread(); 
    } 
}

With the downside that each block of operation over the array must be surrounded with an acquire/release pair.

Bloater answered 27/4, 2015 at 6:12 Comment(2)
Well - I could achieve the same using 'synchronized' block. Not any real benefit here?Nombles
Everybody has already tells you how to do, there is no simple way (minimal modifications): wrap an array in a struct and overload the operators you're interested by. Михаил Страшун solution would be fine but as he noticed, this doesn't work, which is a very interesting concept for a solution BTW. Do you realize that you could have written it ( i mean the wrapper) since days ? Also don't forget that you could get a better answer on the NG/forum. Not everybody likes/uses SO.Bloater
C
0

It is fairly easy to make a wrapper around array that will make it thread-safe. However, it is extremely difficult to make a thread-safe array that is not a concurrency bottleneck.

The closest thing that comes to mind is Java's CopyOnWriteArrayList class, but even that is not ideal...

Coadjutress answered 27/4, 2015 at 12:39 Comment(1)
I know that but I don't care because it's exactly what I want (concurrency bottleneck). However as I scroll around the answers - it doesn't seem 'fairly easy' to me.Nombles
Y
-1

You have the right idea. As an array, you need to be able to both edit and retrieve information. I suggest you take a look at the read-write mutex and atomic utilities provided by Phobos. A read operation is fairly simple:

  1. synchronize on mutex.readLock
  2. load (with atomicLoad)
  3. copy the item out of the synchronize block
  4. return the copied item

Writing should be almost exactly the same. Just syncronize on mutex.writeLock and do a cas or atomicOp operation.

Note that this will only work if you copy the elements in the array during a read. If you want to get a reference, you need to do additional synchronization on the element every time you access or modify it.

Yepez answered 25/4, 2015 at 13:33 Comment(2)
I trusted the system and I feel it will fail me again. Tell me fairly do you think you deserve '+150' reputation with this answer?Nombles
Does it work? That's really all that matters isn't it? If you think my answer worth it, then yes. You asked the question. I gave you an answer. If you think it insufficient, then we can see how it can be amended.Yepez

© 2022 - 2024 — McMap. All rights reserved.