PHP Internals: How does TSRMLS_FETCH Work?

人盡茶涼 提交于 2020-02-24 00:42:12

问题


How does the PHP Internals TSRMLS_FETCH macro do its job?

Per the PHP Manual

While developing extensions, build errors that contain "tsrm_ls is undefined" or errors to that effect stem from the fact that TSRMLS is undefined in the current scope, to fix this, declare the function to accept TSRMLS with the appropriate macro, if the prototype of the function in question cannot be changed, you must call TSRMLS_FETCH within the function body.

I understand that declaring the function to accept TSRMLS with the appropriate macros means using TSRMLS_C, TSRMLS_D, TSRMLS_CC, and TSRMLS_DC to either define of call a function with extra parameters/arguments.

However, if the prototype of the function in question cannot be changed, you must call TSRMLS_FETCH within the function body confuses me a bit. If I look at the php-src both here and here the TSRMLS_FETCH seems to be an empty macro.

So that leaves me with the question -- how does TSRMLS_FETCH even work? Is something else populating this macro at compile time?


回答1:


Firstly, I would not pay too much attention to what the manual says on PHP's internals. It is very outdated, and there's a good chance it is going to be removed from the manual in the near future. There are two websites currently dedicated to PHP's internals: PHPInternalsBook.com and PHPInternals.net (I author content for the latter). There's also a couple of good blogs to follow, including Nikita's and Julien's.

The TSRM in PHP's 5.x series was quite invasive. When wanting to access any Zend globals from within a function, the choice was between either fetching the TLS memory pointer from a function call (such as pthread_getspecific, which was relatively expensive) or propagating the TLS memory pointer through function parameters (a messy and error-prone affair, but the faster way). The TSRMLS_FETCH macro you mentioned was used for the former approach.

In PHP 7.x, propagating the TLS memory pointer (via the TSRMLS_[D|C]C? macros) has been removed completely (though their macros are still defined for backwards compatibility - they just won't do anything). The preferred way to access the TSRM's TLS now is via its static cache. This is basically just a thread local global variable used to hold the current TLS memory pointer.

Here are the relevant macros:

#define TSRMLS_CACHE _tsrm_ls_cache // the TLS global variable
#define TSRMLS_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE = NULL; // define it
#define TSRMLS_CACHE_UPDATE() TSRMLS_CACHE = tsrm_get_ls_cache() // update it - i.e. calls pthread_getspecific()
#define TSRMLS_CACHE_RESET()  TSRMLS_CACHE = NULL // reset it

Using the above macros does require special care to update the static cache appropriately (usually during the GINIT, and sometimes RINIT, phases of an extension). However, it is a cleaner way to provide access to the TLS memory pointer without the mess of propagating it via function parameters or the performance hit of always fetching it (via pthread_getspecific and similar).

Extra reading:

  • Native TLS (the RFC that introduced this change in PHP 7.0)
  • Threads and PHP (Julien's blog post on the TSRM)



回答2:


Take a look at older versions of that file:

#define TSRMLS_FETCH()            void ***tsrm_ls = (void ***) ts_resource_ex(0, NULL)
#define TSRMG(id, type, element)  (((type) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
#define TSRMLS_D  void ***tsrm_ls
#define TSRMLS_DC , TSRMLS_D
#define TSRMLS_C  tsrm_ls
#define TSRMLS_CC , TSRMLS_C

It seems at some point PHP removed support for those macros but kept them empty in order to avoid having to split external code in two versions, one for the new PHP and one for the old PHP.




回答3:


This piece of code

#if ZEND_DEBUG
...
#else
#define TSRMLS_FETCH()
...
#endif

Is doing the following:

If you are not in debug mode, change each call to the macro TSRMLS_FETCH() with nothing.

In this example:

#if 0
#define TSRMLS_FETCH() printf("Bla");
#else
#define TSRMLS_FETCH()
#endif

int main(void) 
{
    TSRMLS_FETCH()
    return 0;
}

cpp demo.c (preprocessor output) returns:

int main(void) 
{
    return 0;
}


来源:https://stackoverflow.com/questions/49539849/php-internals-how-does-tsrmls-fetch-work

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!