0

I have written some data structures in C like task queue used by thread pool. It is synchronized with mutex and conditional variable objects. I make it like this:

struct task_queue { 
  // some fields
};

typedef struct task_queue task_queue_t; 

static pthread_mutex_t mutex; 
static pthread_cond_t cond; 

task_queue_init(task_queue_t **tq);
task_queue_destroy(task_queue_t *tq); 

But now I think that such approach is rather wrong as when I create with task_queue_init() several instances of task_queue struct in my program than they will be synchronized by the same pthread_mutex_t, pthread_cond_t objects. I think that I have seen such static synchronization objects declarations somewhere previously and I have used it in my code.

My question is to make sure myself that I am planning to do it right way i.e put this synchronization objects always inside this struct task_queue, or other synchronized data structure struct like below:

struct task_queue { 
   // hitherto fields
   pthread_mutex_t mutex;
   pthread_cond_t cond; 
}
typedef struct task_queue task_queue_t; 

//and initialize/destroy them in 
task_queue_init(task_queue_t **tq); 
task_queue_destroy(task_queue_t *tq); 

Maybe I should use pointers to mutex, cond in this struct?

Michał Ziobro
  • 10,759
  • 11
  • 88
  • 143
  • I recomend use [Semaphores](http://stackoverflow.com/questions/15182328/semaphore-implementation) insted of mutex to syncronize threads – Raskayu Aug 10 '16 at 08:25
  • In order to evaluate a solution, it's necessary to understand what the problem is. To me this seems a XY problem. See http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem – Support Ukraine Aug 10 '16 at 08:53
  • The problem is that I create code like task queue, thread pool, threads manager, that all use global synchronization objects. Than I realize what if I now need to create too such globally synchronized threads managers or threads pools? I will have separated thread pools/thread managers 1) to receive and handle requests from client and 2) to send server events notifications. And such thread pools/thread managers will reuse the same global mutex which seems to me odd. So I ask whether associating this mutexes, cond vars, with instances (structs) is a good and safe idea. – Michał Ziobro Aug 10 '16 at 09:08

2 Answers2

1

(Beforehand I should mention I am coming from OpenMP and WinAPI, not POSIX.)

The optimal solution is depending on the structure, information passing between and runtime-behaviour of the tasks.

If my understanding of the question is correct, it is intended to allocate the task queues and their necessary infrastructure at runtime. In this case placing the infrastructure in the structs is not incorrect. To my knowledge in most cases allocation at runtime is done as heap allocation which in turn means read/write access from all tasks of the process to the same locations in the heap memory of the process. Depending on the frequency of accesses this may or may not cause performance issues ("false sharing", as a rule of thumb try to avoid process-global data).

If(!) on the other hand ...

a) more than one (master) tasks will exist, each using it's own task queues and their necessary infrastructure

b.1) either an enqueued (worker) task does not need to access the infrastructure of it's task queue

b.2) or information about the task queue's infrastructure is passed between an enqueued (worker) task and it's using (master) task (task-/thread-private copies in and out)

... it is possible to avoid some of the memory access issues by allocation on the stack memory of the using (master) task. In this case I would use arrays with a predefined, sufficient size. This could be a single one-dimensional array of structs as you propose. It would be also possible to use various one-dimensional arrays, each with the same number of elements. Each of the arrays holds elements of one type of information: one array for the task queues, one array for the task queue's mutexes and so on. Then the same task queue and it's infrastructure is accessed by accessing the various arrays with the same array index. Depending on circumstance this can be of advantage in passing information about the task queue in and out of the enqueued (worker) tasks.

F. Anklam
  • 11
  • 3
0

If you plan to use task queue from places in the code where you can't guarantee task queue still exists then mutex and condition variable can't be a member variables - you must be sure at least they exist so you can touch them.

4pie0
  • 29,204
  • 9
  • 82
  • 118
  • But if i use them while enqueueing/dequeueing task on the queue enqueue()/dequeue() operations how the queue could not exists. Yeah I think mutex should be global only if there isn't possible do get access to struct object. But if I have functions like: task_queue_init(), task_queue_destroy(), task_queue_enqueue(), task_queue_dequeue(), etc that takes as it's first param task queue object than inside this method synchronization object is always available from task_queue struct – Michał Ziobro Aug 10 '16 at 08:45
  • I think that problem with this mutex, cond as fields of struct will be only i I would like to use signals and signal_handlers as there isn't possible to pass arguments – Michał Ziobro Aug 10 '16 at 08:46
  • @MichałZiobro you can delete queue while there will be still pointers in your code to deleted queue, you can't then use these pointers to acquire the mutex – 4pie0 Aug 10 '16 at 08:49
  • yeah but yo think that using mutex as global variable is better approach? If I create 2 separate task queues and will use them simultanously than mutex will be reused between both task queues. I have also thread pool synchornized with mutex so I use in my app 2 thread pools and they reuse 1 global mutex it seems odd, I think it is more logical when given instance of data structure have it's own mutex or cond variable. – Michał Ziobro Aug 10 '16 at 08:59
  • each queue can have it's own mutex, you can store them in global table – 4pie0 Aug 10 '16 at 09:47
  • Global table is better than storing them inside struct? – Michał Ziobro Aug 10 '16 at 09:52
  • "still pointers in your code to deleted queue" when I deleted queue it means I don't want it to be used anymore in program? So what's the problem here? If queue is deallocated what is the aim of accessing it somewhere else? Mutex will be associated with this queue so when it doesn't exists why I need to use it to sync access to deallocated queue? – Michał Ziobro Aug 10 '16 at 10:06
  • This wasn't answer to what is better but a hint to address your problem about having one mutex per all queues - you can have table of mutexes, each per queue. Addressing your last comment: the need to access queue would be the same as usually, but problem is you may not know that queue has been deleted, when you delete from one thread while other threads keep pointers to that queue. – 4pie0 Aug 10 '16 at 10:07
  • Nonetheless I think Global Table is also good concept that I will think more about but It seems this solution needs more code. Such table should be variable length so reallocation in C needed, etc. Some association needed between struct and global table. Here what If struct is deallocated? Than index of mutex in Global Table is also lost. So I need to check if struct is null or not! – Michał Ziobro Aug 10 '16 at 10:09