【发布时间】:2016-08-20 23:51:27
【问题描述】:
在 Glibc 的 pthread.h 中,pthread_self 函数使用 const 属性声明:
extern pthread_t pthread_self (void) __THROW __attribute__ ((__const__));
在 GCC 中 that attribute means:
许多函数除了参数之外不检查任何值,并且除了返回值之外没有任何效果。基本上这只是比下面的纯属性稍微严格的类,因为不允许函数读取全局内存。
我想知道那应该是怎样的?由于它不接受任何参数,因此pthread_self 只允许总是返回相同的值,显然情况并非如此。也就是说,我本来希望pthread_self 读取全局内存,因此最终被标记为pure:
许多函数除了返回值之外没有任何影响,它们的返回值仅取决于参数和/或全局变量。这样的函数可以像算术运算符一样进行公共子表达式消除和循环优化。这些函数应该用pure属性声明。
x86-64 上的实现似乎实际上是在读取全局内存:
# define THREAD_SELF \
({ struct pthread *__self; \
asm ("mov %%fs:%c1,%0" : "=r" (__self) \
: "i" (offsetof (struct pthread, header.self))); \
__self;})
pthread_t
__pthread_self (void)
{
return (pthread_t) THREAD_SELF;
}
strong_alias (__pthread_self, pthread_self)
这是一个错误还是我没有看到什么?
【问题讨论】:
-
它总是会返回相同的值在给定的线程中。如果你调用一个纯函数,改变一个全局变量,然后再次调用它,你可能会得到不同的返回值。如果您之间不更改全局变量并且参数相同,则编译器可以假设返回值相同。这不会发生在像
pthread_self()这样的__const__函数中,因为返回值不依赖于任何全局变量的值。它会在不同的线程中返回不同的值这一事实与这里发生的事情并不真正相关。 -
该内存实际上并不是全局的,它位于
%fs段中,这是一个通常专用于线程本地存储的特殊段。所以它实际上是在访问线程本地内存。 -
@PaulGriffiths:IOW 与
pure-ness 和const-ness 以及多线程有交互。很公平,但这并不能解释如何在不读取任何全局内存的情况下实现pthread_self? -
@peppe:没关系。
pthread_self()返回调用线程的线程 ID,并且在线程的生命周期内永远不会改变。它是这样定义的。如果它确实发生了变化,那么你的实现就会被破坏,所以所有的赌注都没有了。如果你做了一些奇怪的事情来打破它,那么你就会调用未定义的行为,所以所有的赌注都是一样的。您个人无法创建一个全局变量来更改该值,而不做一些奇怪的事情以使您无权依赖该保证。即使它确实读取了全局内存,它的读取方式也不会产生明显的影响。 -
好吧,如果您使用一个属性然后违反允许使用该属性的条件,那么它确实很重要。这几乎就是这里的问题......