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