Description:
I have multiple threads (4-32). These threads can all access an array: int resources[1024]. Resources array contains different values (0-1023). There can only be one instance of a single resource(int). Each thread requires different number of resources, which are at some point returned back to the array. Threads can ask for resources more than once and return only portion of acquired resources at once. Each thread is accessing this array via 2 methods: GetElement(), ReturnElement(int element)
GetElement(): this method locks section, deletes last element from resources array and returns it to the calling thread. Each thread is calling the method in loop for n resources.
ReturnElement(int element): this method locks section, appends resource element in parameter after last one in array. Each thread is calling the method in loop for n resources.
Current implementation is flawed in a way that when multiple threads are acquiring resources at once, none of them might get required amount. I was thinking about locking the access to the array for a single thread while it gets or returns the resources and then unlocking it. This approach is blocking all the other threads which might be an issue.
Is there a more efficient approach?
This problem is a variant of the dining philosophers problem, which is an area of active research. There are some solutions, but none are perfect and generally in such situations avoiding a deadlock in a generic and constant-time way is problematic.
Some general solution directions:
Use an arbitrator. This can be as simple as locking the entire resource allocation process globally or per-node (in case of NUMA), but can also be done in a lock-free way with prioritizing workers with a lower ID, for example.
Implement work stealing: reclaim partially allocated resources from another waiting worker, but with some unfairness built-in (e.g. never take from worker 0). This requires keeping track of partially allocated resources and some inter-thread (inter-process) communication.
Try-and-release: try to acquire resources, and if that fails, release the partially acquired ones and yield execution (wait a short period of time). Howard Hinnant did some research on this which can be found here.
Given your problem description, I would recommend a simple solution with central locking, or, better yet, try to avoid the problem in the first place. Given that you're developing a virtual memory allocator, there's some research specific to that, e.g. this one from from jemalloc.