Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pthreads and opaque types [closed]

I was reading the header files of the pthreads library and found this particular definition of the mutex (and other types) in bits/pthreadtypes.h:

typedef union
{
  struct __pthread_mutex_s
  {
    int __lock;
    unsigned int __count;
    int __owner;
    /* KIND must stay at this position in the structure to maintain
       binary compatibility.  */
    int __kind;
    unsigned int __nusers;
    __extension__ union
    {
      int __spins;
      __pthread_slist_t __list;
    };
  } __data;
  char __size[__SIZEOF_PTHREAD_MUTEX_T];
  long int __align;
} pthread_mutex_t;

It's not exactly like this but I've simplified it for clarity. Creating a struct with two different definitions in the header and in the implementation file, being the implementation the real struct definition and the header just a character buffer of the size of the real struct, is used as a technique to hide the implementation (opaque type) but still allocate the correct amount of memory when calling malloc or allocating an object in the stack.

This particular implementation is using a union and still exposing both the definition of the struct and the character buffer, but doesn't seem to provide any benefits in terms of hiding the type as the struct is still exposed and the binary compatibility is still dependent on the structures being unchanged.

  1. Why are the types defined in pthreads following this pattern?
  2. What are the benefits of having opaque types if you're not providing binary compatibility (as in the opaque pointer pattern)? I understand security is one of them as you aren't allowing the user to tamper with the fields of the struct, but is there anything else?
  3. Are pthread types exposed mostly to allow static initializations or is there any other specific reason for this?
  4. Would it be feasible a pthreads implementation following the opaque pointer pattern (i.e. not exposing any types at all and not allowing static initializations)? or more specifically, is there any situation where a problem can only be solved with static initializations?
  5. And totally unrelated, are there "before main" threads in C?
like image 202
AnilM3 Avatar asked Jun 05 '14 18:06

AnilM3


1 Answers

My take is that __size and __align fields specify (guess what :-) ) the size and alignment of the structure independently of the __data structure. So, the data can be of a less size and have less alignment requirements, it can be modified freely without breaking these basic assumptions about it. And vice-versa, these basic characteristics can be changed without altering the data structure, like here.

It is important to note that if the size of the __data becomes bigger than specified by __SIZEOF_PTHREAD_MUTEX_T, an assertion fails in __pthread_mutex_init():

assert (sizeof (pthread_mutex_t) <= __SIZEOF_PTHREAD_MUTEX_T);

Consider this assertion as an essential part of this approach.

So, the conclusion is that this was done not to hide the implementation details, but to make the data structure more predictable and manageable. It is very important for a widely-used library which should care a lot about backward compatibility and performance impact to other codes from the changes which can be made to this structure.

like image 99
Anton Avatar answered Oct 21 '22 15:10

Anton