and threads Zend Thread Safety
Hello everybody  Julien PAULI  Programming in PHP since early 2000s  PHP Internals hacker and trainer  PHP 5.5/5.6 Release Manager  Working at SensioLabs in Paris - Blackfire  Writing PHP tech articles and books  http://phpinternalsbook.com  @julienpauli - github.com/jpauli - jpauli@php.net  Like working on OSS such as PHP :-)
TOC  Recall on computer parallel programming  What are threads ?  Threads VS process  The PHP multi-tasking model  Zend Thread Safe mode  What - why - how ?  Parallel programming with PHP  pcntl  pthread
Computer architecture  Nowadays we own machines  With several CPUs  With several-core CPUs  Multitasking  Time division of one CPU frequency  Concurrency  Using several CPUs (or Core) at the same time  Parallelism  Both above solutions mixed
Concurrency
Parallelism
OS Process  The lowest level representation of "a task"  The OS scheduler does the job
Processes are heavy  Processes are heavy guys  Creating a process means a lot of work for the Kernel  Lots of CPU instructions involved  Many memory movements  A process eats some memory  For its own structures
Threads
Thread infos
Threads are light process  Threads have been designed like processes  But their goal  is to be lighter  is to be userland programmable  They thus share some data between them  They are lighter to create  Less structure involved  Less memory access / movement on management
Thread model
Threads share memory  Threads leave in processes  If the process dies, all its threads die  Threads share their process'  heap  data  sigmask  FDs  Threads got their own  stack  sigmask  CPU registers
Threads dangers  Threads share the heap and the data segment  Thus while programming, threads share the global state  Accessing the global state with several threads  Needs extra care  Memory barriers  Semaphores and locks
Threads VS processes  Threads  Will by default share memory  Will share file descriptors  Will share filesystem context  Will share signal handling  Processes  Will by default not share memory  Most file descriptors not shared  Don't share filesystem context  Don't share signal handling
PHP Multitasking  Since 1995 ...  Rely on the webserver to process several requests at the same time  Rely on FastCGI users
Multitasking PHP  To treat several requests  The webserver embedding PHP forks itself  Apache  The webserver forks itself and pass the web request to PHP CGI processes  Using FastCGI : PHP forks itself  php-fpm
PHP in a threaded env  PHP could leave in a threaded environment  If the webserver uses threads to handle concurrency  Apache with mpm_worker  IIS under Windows  Windows heavily makes use of threads  Unix tend to prefer using processes
ZTS
Zend Thread Safety  PHP will never itself make use threads  PHP's code is NOT threaded  But PHP could, despite him, live in threads  PHP thus needs to be programmed  So that it can access thread shared memory safely  This is called the Zend Thread Safe mode : ZTS
ZTS goals  ZTS is a way of programming PHP core and extensions , in C  A layer that eases access to globals and turn them thread-safe using one OS supported thread lib  Let's see how
ZTS, abstract thread model  Several thread library exists  They all got their own behavior / API / performances  ZTS provides macros and automation to abstract that  ZTS supports  GNU Pth  POSIX Thread (pthread)  SGI's State Thread  BeOS Threads  Choose at compile time (--with-tsrm-???)
TSRM  TSRM stands for Thread Safe Resource Manager  This is the C code layer behind ZTS mode  TSRM/ in PHP source code  Activate using --enable-maintainer-zts  This will build a ZTS PHP
Example program static int val; /* true global */ PHP_MINIT(wow_ext) /* PHP Module initialization */ { if (something()) { val = 3; /* writing to a true global */ } } PHP_RINIT(wow_ext) /* PHP Request initialization */ { if (something()) { WOW_G(val) = 3; /* writing to a thread global */ } }
TSRM macros  Accessing globals while in a thread must be done using TSRM macros #ifndef ZTS #define WOW_G(v) wow_globals.v #else #define WOW_G(v) (((wow_globals *) (*((void ***) tsrm_get_ls_cache()))[((wow_globals_id)-1)])->v) #endif
How all this stuff work ?
Pthread keys  In pthread, each thread is given a key.  That key is used to access a TLS : Thread Local Storage  A piece of memory owned by each thread  The compiler takes care of the hard job  This is where we'll store our "globals"
Extensions compilation  At compilation, each extension declares  a pointer to some TLS space  __thread is used  An id (integer) which will retain this extension specific storage BF_DECLARE_ZEND_GLOBALS ts_rsrc_id blackfire_globals_id; __thread void *_tsrm_ls_cache = ((void *)0);;
TLS storage detail TLS #1 TLS #2 TLS #3 ... void *storage
TLS storage detail TLS #1 ext storage id ext/core ext/xml ext/pcre ext/gz ext/blackfire TLS #2 ext/core ext/xml ext/pcre ext/gz ext/blackfire TLS #3 ext/core ext/xml ext/pcre ext/gz ext/blackfire void *storage
Allocating resources per thread  This is done at every new request  ts_resource_ex() checks if this thread's got data  If not, it calls for allocate_new_resource()  This will set the thread storage using the current thread key
Allocating thread resources static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id) { int i; (*thread_resources_ptr) = (tsrm_tls_entry *) malloc(sizeof(tsrm_tls_entry)); (*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count); (*thread_resources_ptr)->count = id_count; (*thread_resources_ptr)->thread_id = thread_id; (*thread_resources_ptr)->next = NULL; /* Set thread local storage to this new thread resources structure */ tsrm_tls_set(*thread_resources_ptr); (*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size); if (resource_types_table[i].ctor) { resource_types_table[i].ctor((*thread_resources_ptr)->storage[i]); } tsrm_mutex_unlock(tsmm_mutex); }
Extensions thread startup  For each new thread, extensions should read the local storage ... PHP_GINIT_FUNCTION(blackfire) { #ifdef ZTS ZEND_TSRMLS_CACHE_UPDATE(); #endif /* ... ... */ } _tsrm_ls_cache = tsrm_get_ls_cache(); _tsrm_ls_cache = pthread_getspecific(tls_key);
Accessing resources per thread  For each new thread, extensions should read the local storage ...  To better read their own memory part after #define BF_G(v) (((zend_blackfire_globals *) (*((void ***) _tsrm_ls_cache))[((blackfire_globals_id)-1)])->(v)) Thread Safe Resource Mana _ Local Storage _ cache extension storage id
ZTS ?  A nice layer, that eases and abstract all the thread and TLS management  If you use the cache, then it is fully performant  Compile PHP with -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1  However, it slows down PHP's startup  We don't care  It slows down request creation for every new thread  That is however clearly acceptable
When to use ZTS ?  If you run Windows with Apache  Threads are used , so compile with ZTS  If you run Unix and need ZTS  You use an extension that needs the interpreter to be built with ZTS  Like the pthread extension  You use some kind of webserver that embed PHP and makes use of threads  Like Apache with mpm_worker  In a HUGE majorty of cases, you'll use Unix, and you won't need ZTS
Check if ZTS is used  Use the PHP_ZTS constant in PHP  See the output of php -v  See the output of phpinfo()  As a PHP developer, you shouldn't care about ZTS in your code  As a PHP extension developer, you should be aware of how it works, and use dedicated macros
ZTS ABI  Obviously, ZTS ABI is not compatible with NTS ABI  Thus extensions must be rebuilt  Check their install dir
Using threads in PHP Land  This is possible  You need ext/pthread for that  http://php.net/manual/fr/book.pthreads.php  http://pthreads.org/  This is experimental  You must really be used to thread programming to use ext/pthread
Using threads in PHP Land  Not really a good idea  At least, there exists better languages for that  C , C++ or Java  Those are languages designed to provide thread usage  PHP is not !  You should not need to parallelize tasks when using the PHP language  If so, then probably you should use another language
Using processes in PHP land  ext/pcntl  I guess you already know it  It shadows Unix processes  Know your machine and your OS  Obviously not available for Windows  fork(), wait(), waitpid(), signal() ...  All those calls are syscalls available using C  Consider using C for true performances and to finely master what you do  Even if PHP adds a really thin layer on top of that stuf
ZTS and threads : concluding  PHP is thread-safe  Compile with --enable-maintainer-zts  You'll activate Zend Thread Safety  You only need ZTS is some uncommon specific cases  Take care of "exotic" extensions  They may not be thread-safe themselves  Analyze before using them  Please don't blindly blame PHP
Thank you for listening

Php and threads ZTS

  • 1.
  • 2.
    Hello everybody  JulienPAULI  Programming in PHP since early 2000s  PHP Internals hacker and trainer  PHP 5.5/5.6 Release Manager  Working at SensioLabs in Paris - Blackfire  Writing PHP tech articles and books  http://phpinternalsbook.com  @julienpauli - github.com/jpauli - jpauli@php.net  Like working on OSS such as PHP :-)
  • 3.
    TOC  Recall oncomputer parallel programming  What are threads ?  Threads VS process  The PHP multi-tasking model  Zend Thread Safe mode  What - why - how ?  Parallel programming with PHP  pcntl  pthread
  • 4.
    Computer architecture  Nowadayswe own machines  With several CPUs  With several-core CPUs  Multitasking  Time division of one CPU frequency  Concurrency  Using several CPUs (or Core) at the same time  Parallelism  Both above solutions mixed
  • 5.
  • 6.
  • 7.
    OS Process  Thelowest level representation of "a task"  The OS scheduler does the job
  • 8.
    Processes are heavy Processes are heavy guys  Creating a process means a lot of work for the Kernel  Lots of CPU instructions involved  Many memory movements  A process eats some memory  For its own structures
  • 9.
  • 10.
  • 11.
    Threads are lightprocess  Threads have been designed like processes  But their goal  is to be lighter  is to be userland programmable  They thus share some data between them  They are lighter to create  Less structure involved  Less memory access / movement on management
  • 12.
  • 13.
    Threads share memory Threads leave in processes  If the process dies, all its threads die  Threads share their process'  heap  data  sigmask  FDs  Threads got their own  stack  sigmask  CPU registers
  • 14.
    Threads dangers  Threadsshare the heap and the data segment  Thus while programming, threads share the global state  Accessing the global state with several threads  Needs extra care  Memory barriers  Semaphores and locks
  • 15.
    Threads VS processes Threads  Will by default share memory  Will share file descriptors  Will share filesystem context  Will share signal handling  Processes  Will by default not share memory  Most file descriptors not shared  Don't share filesystem context  Don't share signal handling
  • 16.
    PHP Multitasking  Since1995 ...  Rely on the webserver to process several requests at the same time  Rely on FastCGI users
  • 17.
    Multitasking PHP  Totreat several requests  The webserver embedding PHP forks itself  Apache  The webserver forks itself and pass the web request to PHP CGI processes  Using FastCGI : PHP forks itself  php-fpm
  • 18.
    PHP in athreaded env  PHP could leave in a threaded environment  If the webserver uses threads to handle concurrency  Apache with mpm_worker  IIS under Windows  Windows heavily makes use of threads  Unix tend to prefer using processes
  • 19.
  • 20.
    Zend Thread Safety PHP will never itself make use threads  PHP's code is NOT threaded  But PHP could, despite him, live in threads  PHP thus needs to be programmed  So that it can access thread shared memory safely  This is called the Zend Thread Safe mode : ZTS
  • 21.
    ZTS goals  ZTSis a way of programming PHP core and extensions , in C  A layer that eases access to globals and turn them thread-safe using one OS supported thread lib  Let's see how
  • 22.
    ZTS, abstract threadmodel  Several thread library exists  They all got their own behavior / API / performances  ZTS provides macros and automation to abstract that  ZTS supports  GNU Pth  POSIX Thread (pthread)  SGI's State Thread  BeOS Threads  Choose at compile time (--with-tsrm-???)
  • 23.
    TSRM  TSRM standsfor Thread Safe Resource Manager  This is the C code layer behind ZTS mode  TSRM/ in PHP source code  Activate using --enable-maintainer-zts  This will build a ZTS PHP
  • 24.
    Example program static intval; /* true global */ PHP_MINIT(wow_ext) /* PHP Module initialization */ { if (something()) { val = 3; /* writing to a true global */ } } PHP_RINIT(wow_ext) /* PHP Request initialization */ { if (something()) { WOW_G(val) = 3; /* writing to a thread global */ } }
  • 25.
    TSRM macros  Accessingglobals while in a thread must be done using TSRM macros #ifndef ZTS #define WOW_G(v) wow_globals.v #else #define WOW_G(v) (((wow_globals *) (*((void ***) tsrm_get_ls_cache()))[((wow_globals_id)-1)])->v) #endif
  • 26.
    How all thisstuff work ?
  • 27.
    Pthread keys  Inpthread, each thread is given a key.  That key is used to access a TLS : Thread Local Storage  A piece of memory owned by each thread  The compiler takes care of the hard job  This is where we'll store our "globals"
  • 28.
    Extensions compilation  Atcompilation, each extension declares  a pointer to some TLS space  __thread is used  An id (integer) which will retain this extension specific storage BF_DECLARE_ZEND_GLOBALS ts_rsrc_id blackfire_globals_id; __thread void *_tsrm_ls_cache = ((void *)0);;
  • 29.
    TLS storage detail TLS#1 TLS #2 TLS #3 ... void *storage
  • 30.
    TLS storage detail TLS#1 ext storage id ext/core ext/xml ext/pcre ext/gz ext/blackfire TLS #2 ext/core ext/xml ext/pcre ext/gz ext/blackfire TLS #3 ext/core ext/xml ext/pcre ext/gz ext/blackfire void *storage
  • 31.
    Allocating resources perthread  This is done at every new request  ts_resource_ex() checks if this thread's got data  If not, it calls for allocate_new_resource()  This will set the thread storage using the current thread key
  • 32.
    Allocating thread resources staticvoid allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id) { int i; (*thread_resources_ptr) = (tsrm_tls_entry *) malloc(sizeof(tsrm_tls_entry)); (*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count); (*thread_resources_ptr)->count = id_count; (*thread_resources_ptr)->thread_id = thread_id; (*thread_resources_ptr)->next = NULL; /* Set thread local storage to this new thread resources structure */ tsrm_tls_set(*thread_resources_ptr); (*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size); if (resource_types_table[i].ctor) { resource_types_table[i].ctor((*thread_resources_ptr)->storage[i]); } tsrm_mutex_unlock(tsmm_mutex); }
  • 33.
    Extensions thread startup For each new thread, extensions should read the local storage ... PHP_GINIT_FUNCTION(blackfire) { #ifdef ZTS ZEND_TSRMLS_CACHE_UPDATE(); #endif /* ... ... */ } _tsrm_ls_cache = tsrm_get_ls_cache(); _tsrm_ls_cache = pthread_getspecific(tls_key);
  • 34.
    Accessing resources perthread  For each new thread, extensions should read the local storage ...  To better read their own memory part after #define BF_G(v) (((zend_blackfire_globals *) (*((void ***) _tsrm_ls_cache))[((blackfire_globals_id)-1)])->(v)) Thread Safe Resource Mana _ Local Storage _ cache extension storage id
  • 35.
    ZTS ?  Anice layer, that eases and abstract all the thread and TLS management  If you use the cache, then it is fully performant  Compile PHP with -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1  However, it slows down PHP's startup  We don't care  It slows down request creation for every new thread  That is however clearly acceptable
  • 36.
    When to useZTS ?  If you run Windows with Apache  Threads are used , so compile with ZTS  If you run Unix and need ZTS  You use an extension that needs the interpreter to be built with ZTS  Like the pthread extension  You use some kind of webserver that embed PHP and makes use of threads  Like Apache with mpm_worker  In a HUGE majorty of cases, you'll use Unix, and you won't need ZTS
  • 37.
    Check if ZTSis used  Use the PHP_ZTS constant in PHP  See the output of php -v  See the output of phpinfo()  As a PHP developer, you shouldn't care about ZTS in your code  As a PHP extension developer, you should be aware of how it works, and use dedicated macros
  • 38.
    ZTS ABI  Obviously,ZTS ABI is not compatible with NTS ABI  Thus extensions must be rebuilt  Check their install dir
  • 39.
    Using threads inPHP Land  This is possible  You need ext/pthread for that  http://php.net/manual/fr/book.pthreads.php  http://pthreads.org/  This is experimental  You must really be used to thread programming to use ext/pthread
  • 40.
    Using threads inPHP Land  Not really a good idea  At least, there exists better languages for that  C , C++ or Java  Those are languages designed to provide thread usage  PHP is not !  You should not need to parallelize tasks when using the PHP language  If so, then probably you should use another language
  • 41.
    Using processes inPHP land  ext/pcntl  I guess you already know it  It shadows Unix processes  Know your machine and your OS  Obviously not available for Windows  fork(), wait(), waitpid(), signal() ...  All those calls are syscalls available using C  Consider using C for true performances and to finely master what you do  Even if PHP adds a really thin layer on top of that stuf
  • 42.
    ZTS and threads: concluding  PHP is thread-safe  Compile with --enable-maintainer-zts  You'll activate Zend Thread Safety  You only need ZTS is some uncommon specific cases  Take care of "exotic" extensions  They may not be thread-safe themselves  Analyze before using them  Please don't blindly blame PHP
  • 43.
    Thank you forlistening