Jump to content
  • Advertisement
Magogan

C++ Multithreading with mutexes and time constraints

Recommended Posts

Hi everyone,

I am writing a server and I need to generate chunks of the world and access them in other threads. The initialization may take a while (seconds), but I need the other threads to be faster than that (basically realtime).

My idea is to use multiple mutexes per chunk, an InitMutex and a ModifyMutex.

When initializing (pseudocode):

Lock InitMutex;
Initialize the chunk;
Lock ModifyMutex;
Set status "initialized";
Unlock both mutexes;

When reading/modifying:

Lock ModifyMutex;
If status is "initialized":
    Perform operations on chunk data;
Unlock ModifyMutex;

Does this work? Will the thread that only locks the ModifyMutex see the data written by the initialization thread?

In the standard it says:

Quote

All lock and unlock operations on the mutex follow a single total order, with all visible effects synchronized between the lock operations and previous unlock operations on the same object.

Does that mean that the initialization of the chunk data is synchronized when ModifyMutex is locked on the modifying thread because it occured before ModifyMutex was unlocked?

Cheers,

Magogan

Share this post


Link to post
Share on other sites
Advertisement

Yep, this is safe. Because you lock the mutex before writing the initialisation flag (after writing the data), all of that data will be safely visible to the other threads that later lock that mutex.

However, you don't actually need the initialisation mutex here at all, because no other threads will attempt to read the object until they've successfully locked the mutex and read the initialisation flag. The init thread can just write the data, then lock the mutex, write the init flag and then unlock the mutex.

Share this post


Link to post
Share on other sites

Okay, thanks.

The example is a little bit simplified. I need the initialization mutex because multiple threads may try to initialize the chunk at the same time. And the chunks get deleted when they are not used, so I need to make sure to not delete them while they are being initialized (which could in theory happen if the initialization thread is inactive for a while or a player disconnects or whatever).

Share this post


Link to post
Share on other sites
Posted (edited)

Funny, I'm currently working on similar things you are. i.e. generating world chunks in threads. 

Edited by Gnollrunner

Share this post


Link to post
Share on other sites

Sound good, I use multithreading in my game for chunk generation as well

Depending on how the game world is setup you probably want to have good preloading scheme as well, just to make sure the chunks are ready and loaded well in advance for when you need them.

Concerning deleting the chunks, one suggestion (which may/or may not apply depending on you game) is to not delete the unused chunks immidiately. At least of the player could travel back and use them again. Unless they are really huge in memory you would most likely afford to keep a couple on the heap for fast access again

I sorted the unused chunks by accesstime, if the game reaches the maximum allowed buffered chunks not in use, it start replacing the ones that have the longest time since they were accessed. A rough guess that they are more unlikely to be needed again soon

Share this post


Link to post
Share on other sites
Posted (edited)

Depending on the time your initial thread takes to write the chunk, you can use a read/write spin lock or any other form of read/write lock. The lock allowes multiple readers to access the data at the same time while a single writer blocks until all readers have successfully leaved the section and prevents newly readers from entering the section until the writer has left.

WriteMask = 0x7ffffff
ReaderMask = 0x8000000

//Acquire read access
while true
    while (Lock & ReaderMask) != 0
        sleep
    end

    OldLock = (Lock & WriteMask)
    NewLock = OldLock + 1

    if CompareExchange(Lock, NewLock, OldLock) == OldLock
        return
    end
end

//Return read access
Decrement(Lock)

//Aquire write access
while true
    while (Lock & ReaderMask) != 0 //<- waits until writes have finished
        sleep
    end

    OldLock = (Lock & WriteMask)
    NewLock = (OldLock | ReaderMask)

    if CompareExchange(Lock, NewLock, OldLock) == OldLock
        while (Lock & WriteMask) != 0 //<- waits until readers have left the section
            sleep
        end

        return
    end
end
  
//Return write access
Lock = 0

I use this in a database locking data records for read/write access from multiple threads.

Instead of sending threads to sleep you could manage server side tasks in a pool and let spinning threads instead execute those tasks until they got the lock they desired

Edited by Shaarigan

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!