开发者

C99: Restricted Pointers to Document Thread Safety?

This question isn't about the technical usage of restricted, more about the subjective usage. Although I might be mistaken as to how restricted technically works, in which case you should feel free to grill me for basing a question on a false premise.

Here are two examples of how I'm using restricted so far:

If I have a function that takes a pointer to a sequence of immutable chars, I don't say it's restricted, since other people are allowed to access the data via their own pointers at the same time as the function's executing, e.g. from another parallel thread. The data isn't being modified, so no problem.

However, if the function takes a pointer to a sequence of mutable chars that it might modify, I say it's restricted because the data absolutely should not be accessed in anyway from any pointer (bar the argument the function uses, obviously) during the execution of the function due to potentially inconsistent data. It also states the possibility of the data being modified, so the coder knows not to read stale data and that they should use a memory barrier when accessing or whatever开发者_如何转开发...

I don't code much C, so I could easily be wrong about what I'm assuming here. Is this correct usage of restrict? Is it worth doing in this scenario?

I'm also assuming that once the restricted pointer is popped off the stack when the function returns, that the data can then freely be accessed via any other pointer again, and that the restriction only lasts as long as the restricted pointer. I know that this relies on the coder following the rules, since accessing a restricted data via an 'unofficial' pointer is UB.

Have I got all of this right?

EDIT:

I'd just like to make clear that I already know it does absolutely nothing to prevent the users from accessing data via multiple threads, and I also know that C89 has no knowledge of what 'threads' even are.

But given that any context where an argument can be modified via reference, it's clear that it mustn't be accessed as the function is running. This doesn't do anything to enforce thread safety, but it does clearly document that you modify the data through your own pointer during the execution of the function at your own risk.

Even if threading is taken completely out of the equation, you still allow for further optimizations in a scenario where it seems correct to me.

Even so, thanks for all your authoritative answers so far. Do I upvote all the answers that I liked, or just the one that I accept? What if more than one is accepted? Sorry, I'm new here, I'll look through the FAQ more thoroughly now...


restrict has nothing to do with thread safety. In fact, the existing C standards have nothing to say on the topic of threads at all; from the point of view of the spec, there is no such thing as a "thread".

restrict is a way to inform the compiler about aliasing. Pointers often make it hard for the compiler to generate efficient code, because the compiler cannot know at compile time whether two pointers actually refer to the same memory. Toy example:

void foo(int *x, int *y) {
    *x = 5;
    *y = 7;
    printf("%d\n", *x);
}

When the compiler processes this function, it has no idea whether x and y refer to the same memory location. Therefore it does not know whether it will print 5 or 7, and it has to emit code to actually read *x before calling printf.

But if you declare x as int *restrict x, the compiler can prove that this function prints 5, so it can feed a compile-time constant to the printf call.

Many such optimizations become possible with restrict, especially when you are talking about operations on arrays.

But none of this has anything to do with threads. To get multi-treading applications right, you need proper synchronization primitives like mutexes, condition variables, and memory barriers... All of which are specific to your platform, and none of which have anything to do with restrict.

[edit]

To answer your question about using restrict as a form of documentation, I would say "no" to that, also.

You seem to be thinking that you should document when a variable can or cannot be concurrently accessed. But for shared data, the proper discipline is (almost always) to ensure that it is never concurrently accessed.

The relevant documentation for a variable is whether it is shared at all and which mutex protects it. Any code accessing that variable for any reason, even just to read it, needs to hold the mutex. The author should neither know nor care whether some other thread might or might not happen to be accessing the variable concurrently... Because that other thread will be obeying the same discipline, and the mutex guarantees there is no concurrent access.

This discipline is simple, it works, and it scales, which is why it is one of the dominant paradigms for sharing data between threads. (The other is message passing.) If you ever find yourself trying to reason "do I really need to lock the mutex this time?", you are almost certainly doing something wrong. It would be hard to overstate this point.


No, I don't think that this is a good dialect to provide any information about acces from different threads. It is meant as assertions about pointers that a particular peace of code gets for different pointers it handles. Threading is not part of the language, yet. Thread safe acces to data needs much more, restrict is not the right tool. volatile isn't a guarantee, which you sometimes see proposed as well.

  • mutexes or other lock structures
  • memory fences that ensure data integrity
  • atomic operations and types

The upcoming standard C1x is supposed to provide such constructs. C99 isn't.


The canonical example of restrict is memcpy() - which the manpage on OS X 10.6 gives the prototype as:

void* memcpy(void *restrict s1, const void *restrict s2, size_t n);

The source and destination regions in memcpy are not permitted to overlap; therefore by labelling them as restrict this restriction is enforced - the compiler can know that no part of the source array aliases with the destination; can it can do things like read a large chunk of the source and then write it into the destination.

Essentially restrict is about assisting compiler obtimizations by tagging pointers as not aliasing - in and of itself it doeen't help with thread safety - it doesn't automatically cause the object pointed to be to locked while the function is called.

See How to use the restrict qualified in C and the wikipedia article on restrict for a more lengthy discussion.


restrict is a hint to the compiler that the buffer accessed via a pointer is not aliased via another pointer in scope. So if you have a function like:

foo(char *restrict datai, char *restrict dataj)
{
    // we've "promised" the compiler that it should not worry about overlap between 
    // datai and dataj buffers, this gives the compiler the opportunity to generate 
    // "better" code, potentially mitigating load-store issues, amongst other things
}

To not use restrict is not enough to provide guarded access in a multi-threaded application though. If you have a shared buffer simultaneously accessed by multiple threads via char * parameters in a read/write way you would potentially need to use some kind of lock/mutex etc - the absence of restrict does not imply thread safety.

Hope this helps.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜