#Semaphore Recycler Pattern

31 messages · Page 1 of 1 (latest)

mighty forge
#

I am using a llvm::ThreadPool to dispatch tasks to process source files for my compiler front-end. The process tasks use buffers of memory that I would like to reuse across tasks. My idea for doing this is to use the recycler pattern with a semaphore for synchronization, but I have never used semaphore before. How would I use a semaphore from the C++ standard library to check out instances of a struct from an std::vector across multiple threads? After a task is finished on one thread, it should give back the resources to the recycler vector, and then when that same thread picks up a new source processing task, it should ask for a new one (possibly the same) resource object from the recycler vector. Obviously, I want no data races. I have never done this kind of thing before. I can initially set the size of the recycler vector to thread_pool.getMaxConcurrency(), which will ensure that the vetor will always have enough resource objects contained within for all active tasks.

steep mesaBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

mighty forge
magic falcon
#

i've only used semaphore on windows..... but i presume it's similar... semaphore is like a bouncer at a nightclub with his little counter in hand.. he uses that to check if he's over or under full.. then permits people (tasks) to enter. your vector is more like the one barman that now has 20 people to serve, he can only serve one at a time.. to access the bar (the vector) you must first acquire the barmans undivided attention (a lock).

#

the bouncer at the door and the barman have nothing to with each other!

#

it's 2 seperate problems

slate field
#

You can implement one with semaphores, but also maybe just use an existing implementation

past zephyr
#

that last snippet just includes some member functions that don't exist in the ring_buffer from the first list, but all it does is tracks all reader/writer threads working with the queue so it knows when it's safe to exit the loop

#

the only part missing is another thread, which would be the queue "writer(s)" pushing the tasks to the buffer to send them to the threadpool

past zephyr
#

the references might be an issue. it seems like that's the data that's planned to be shared across threads (which is what you're synchronizing around in _claimDataAtomic() but at a quick glance i didn't see how it would be atomic. typically a safe way to do it is to transfer ownership of data from thread -> queue -> some other thread. holding onto an unsynchronized reference/pointer into the shared data is probably going to be an issue unless i missed something.

#

i should have a bit more time later so i can take a look at it then

mighty forge
#

it doesn't actually move out of the vector, but no other threads will be accessing the data at that position at the same time

past zephyr
#

that doesn't matter as much as another thread potentially touching that data at the same time

#

ok as long as you can guarantee that you probably don't really even need synchronization if each thread will only every be touching data unique to itself where no other threads can access it

mighty forge
#

the amount of threads in the pool is variable. the amount of tasks is variable. This is a compiler, and at this stage there is one task per source and header file

#

the scratch pool has the same amount of entries as there are threads in the pool, which is what the getMaxConncurrency() call is getting.

past zephyr
#

ok yeah i'll take a closer look at it a bit later then to check out what it's doing. i was only able to take a quick glance

mighty forge
#

so there never should be more running tasks then the scratch data avaliable in the pool. If more tasks are run then getMaxConcurrency, then the extra tasks should wait in line until one of the threads in the pool finishes a different task.

past zephyr
#

you should get in the habit of using managed pointers. there's no reason not to nowadays unless you specifically wanted to work with everything using raw pointers in this project

mighty forge
#

I could possibly use std::ref I guess

#

Done