2

I've been playing around with glib, which

  1. utilizes reference counting to manage memory for its objects;
  2. supports multiple threads.

What I can't understand is how they play together. Namely:

  1. In glib each thread doesn't seem to increase refcount of objects passed on its input, AFAIK (I'll call them thread-shared objects). Is it true? (or I've just failed to find the right piece of code?) Is it a common practice not to increase refcounts to thread-shared objects for each thread, that shares them, besides the main thread (responsible for refcounting them)?
  2. Still, each thread increases reference counts for the objects, dynamically created by itself. Should the programmer bother not to give the same names of variables in each thread in order to prevent collision of names and memory leaks? (E.g. on my picture, thread2 shouldn't crate a heap variable called output_object or it will collide with thread1's heap variable of the same name)?

UPDATE: Answer to (question 2) is no, cause the visibility scope of those variables doesn't intersect: Is dynamically allocated memory (heap), local to a function or can all functions in a thread have access to it even without passing pointer as an argument.

An illustration to my questions:

enter image description here

Community
  • 1
  • 1
Boris Burkov
  • 13,420
  • 17
  • 74
  • 109

2 Answers2

2

I think that threads are irrelevant to understanding the use of reference counters. The point is rather ownership and lifetime, and a thread is just one thing that is affected by this. This is a bit difficult to explain, hopefully I'll make this clearer using examples.

Now, let's look at the given example where main() creates an object and starts two threads using that object. The question is, who owns the created object? The simple answer is that main() and both threads share this object, so this is shared ownership. In order to model this, you should increment the refcounter before each call to pthread_create(). If the call fails, you must decrement it again, otherwise it is the responsibility of the started thread to do that when it is done with the object. Then, when main() terminates, it should also release ownership, i.e. decrement the refcounter. The general rule is that when adding an owner, increment the refcounter. When an owner is done with the object, it decrements the refcounter and the last one destroys the object with that.

Now, why does the the code not do this? Firstly, you can get away with adding the first thread as owner and then passing main()'s ownership to the second thread. This will save one increment/decrement operation. This still isn't what's happening though. Instead, no reference counting is done at all, and the simple reason is that it isn't used. The point of refcounting is to coordinate the lifetime of a dynamically allocated object between different owners that are peers. Here though, the object is created and owned by main(), the two threads are not peers but rather slaves of main. Since main() is the master that controls start/stop of the threads, it doesn't have to coordinate the lifetime of the object with them.

Lastly, though that might be due to the example-ness of your code, I think that main simply leaks the reference, relying on the OS to clean up. While this isn't beautiful, it doesn't hurt. In general, you can allocate objects once and then use them forever without any refcounting in some cases. An example for this is the main window of an application, which you only need once and for the whole runtime. You shouldn't repeatedly allocate such objects though, because then you have a significant memory leak that will increase over time. Both cases will be caught by tools like valgrind though.

Concerning your second question, concerning the heap variable name clash you expect, it doesn't exist. Variable names that are function-local can not collide. This is not because they are used by different threads, but even if the same function is called twice by the same thread (think recursion!) the local variables in each call to the function are distinct. Also, variable names are for the human reader. The compiler completely eradicates these.

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
  • Thanks for your clear and detailed answer, doomster. I explained the answer to question 1 to myself through master-slave dependency, too. With question 2 it was just a glitch of my head: although memory, malloc'ed on the heap is shared, the variable output_object is local to the function, where it is created, thus no collision can take place of course. – Boris Burkov Feb 01 '13 at 05:36
1

UPDATE:

As matthias says below, GObject is not thread-safe, only reference counting functions are.

Original content:

GObject is supposed to be thread safe, but I've never played with that myself…

liberforce
  • 11,189
  • 37
  • 48
  • 2
    This is not really true. `g_object_ref` and `g_object_unref` are thread-safe for obvious reasons but anything that goes beyond that is up to the implementation of the object. – matthias Jan 31 '13 at 11:12
  • Thanks for the insight. Seem I read too fast. I had found that weird, as I know GTK+ is not thread-safe and based on GObject, but couldn't find what I had missed. – liberforce Jan 31 '13 at 13:07