I need to do some simple timezone calculation in mod_perl. DateTime isn't an option. What I need to do is easily accomplished by setting $ENV{TZ} and using localtime and POSIX::mktime, but under a threaded MPM, I'd need to make sure only one thread at a time was mucking with the environment. (I'm not concerned about other uses of localtime, etc.)
我需要在mod_perl中做一些简单的时区计算。 DateTime不是一个选项。我需要做的是通过设置$ ENV {TZ}并使用localtime和POSIX :: mktime轻松完成,但在线程MPM下,我需要确保一次只有一个线程与环境混淆。 (我不关心本地时间的其他用途等)
How can I use a mutex or other locking strategy to serialize (in the non-marshalling sense) access to the environment? The docs I've looked at don't explain well enough how I would create a mutex for just this use. Maybe there's something I'm just not getting about how you create mutexes in general.
如何使用互斥锁或其他锁定策略来序列化(在非编组意义上)对环境的访问?我看过的文档并不能很好地解释如何为这个用途创建一个互斥锁。也许有些东西,我只是没有了解你如何创建互斥体。
Update: yes, I am aware of the need for using Env::C to set TZ.
更新:是的,我知道需要使用Env :: C来设置TZ。
3 个解决方案
#1
3
(repeating what I said over at PerlMonks...)
(重复我在PerlMonks上说的话......)
BEGIN { my $mutex; sub that { $mutex ||= APR::ThreadMutex->new( $r->pool() ); $mutex->lock(); $ENV{TZ}= ...; ... $mutex->unlock(); }}
But, of course, lock() should happen in a c'tor and unlock() should happen in a d'tor except for one-off hacks.
但是,当然,lock()应该在一个c'tor中发生,而unlock()应该在一个de tor中发生,除了一次性黑客攻击。
Update: Note that there is a race condition in how $mutex is initialized in the subroutine (two threads could call that() for the first time nearly simultaneously). You'd most likely want to initialize $mutex before (additional) threads are created but I'm unclear on the details on the 'worker' Apache MPM and how you would accomplish that easily. If there is some code that gets run "early", simply calling that() from there would eliminate the race.
更新:请注意,在子例程中如何初始化$ mutex存在竞争条件(两个线程几乎可以同时第一次调用this())。您很可能希望在创建(附加)线程之前初始化$ mutex,但我不清楚'worker'Apache MPM的详细信息以及如何轻松完成。如果有一些代码“早期”运行,只需从那里调用那个()就可以消除竞争。
Which all suggests a much safer interface to APR::ThreadMutex:
这一切都暗示了APR :: ThreadMutex更安全的界面:
BEGIN { my $mutex; sub that { my $autoLock= APR::ThreadMutex->autoLock( \$mutex ); ... # Mutex automatically released when $autoLock destroyed }}
Note that autoLock() getting a reference to undef would cause it to use a mutex to prevent a race when it initializes $mutex.
请注意,autoLock()获取对undef的引用会导致它在初始化$ mutex时使用互斥锁来阻止竞争。
#2
3
Because of this issue, mod_perl 2 actually deals with the %ENV hash differently than mod_perl 1. In mod_perl 1 %ENV was tied directly to the environ struct, so changing %ENV changed the environment. In mod_perl 2, the %ENV hash is populated from environ, but changes are not passed back.
由于这个问题,mod_perl 2实际上与mod_perl 1不同地处理%ENV哈希。在mod_perl中,1%ENV直接绑定到环境结构,因此更改%ENV会改变环境。在mod_perl 2中,%ENV哈希是从environ填充的,但不会传回更改。
This means you can no longer muck with $ENV{TZ} to adjust the timezone -- particularly in a threaded environment. The Apache2::Localtime module will make it work for the non-threaded case (by using Env::C) but when running in a threaded MPM that will be bad news.
这意味着你不能再使用$ ENV {TZ}来调整时区 - 特别是在线程环境中。 Apache2 :: Localtime模块将使其适用于非线程情况(通过使用Env :: C)但在线程MPM中运行时将是坏消息。
There are some comments in the mod_perl source (src/modules/perl/modperl_env.c) regarding this issue:
关于此问题,mod_perl源(src / modules / perl / modperl_env.c)中有一些注释:
/* * XXX: what we do here might change: * - make it optional for %ENV to be tied to r->subprocess_env * - make it possible to modify environ * - we could allow modification of environ if mpm isn't threaded * - we could allow modification of environ if variable isn't a CGI * variable (still could cause problems) *//* * problems we are trying to solve: * - environ is shared between threads * + Perl does not serialize access to environ * + even if it did, CGI variables cannot be shared between threads! * problems we create by trying to solve above problems: * - a forked process will not inherit the current %ENV * - C libraries might rely on environ, e.g. DBD::Oracle */
#3
1
If you're using apache 1.3, then you shouldn't need to resort to mutexes. Apache 1.3 spawns of a number of worker processes, and each worker executes a single thread. In this case, you can write:
如果您使用的是apache 1.3,那么您不需要使用互斥锁。 Apache 1.3产生了许多工作进程,每个工作程序执行一个线程。在这种情况下,您可以写:
{ local $ENV{TZ} = whatever_I_need_it_to_be(); # Do calculations here.}
Changing the variable with local
means that it reverts back to the previous value at the end of the block, but is still passed into any subroutine calls made from within that block. It's almost certainly what you want. Since each process has its own independent environment, you won't be changing the environment of other processes using this technique.
使用local更改变量意味着它将恢复为块末尾的先前值,但仍会传递到从该块中进行的任何子例程调用。这几乎可以肯定你想要的。由于每个进程都有自己独立的环境,因此您不会使用此技术更改其他进程的环境。
For apache 2, I don't know what model it uses with regards to forks and threads. If it keeps the same approach of forking off processes and having a single thread each, you're fine.
对于apache 2,我不知道它在分叉和线程方面使用的是什么型号。如果它保持相同的方法分离进程并且每个都有一个单独的线程,那么你没问题。
If apache 2 uses honest to goodness real threads, then that's outside my area of detailed knowledge, but I hope another lovely * person can provide assistance.
如果apache 2使用诚实的真实线程,那么这不在我的详细知识领域,但我希望另一个可爱的*人员可以提供帮助。
All the very best,
一切都是最好的,
Paul
#1
3
(repeating what I said over at PerlMonks...)
(重复我在PerlMonks上说的话......)
BEGIN { my $mutex; sub that { $mutex ||= APR::ThreadMutex->new( $r->pool() ); $mutex->lock(); $ENV{TZ}= ...; ... $mutex->unlock(); }}
But, of course, lock() should happen in a c'tor and unlock() should happen in a d'tor except for one-off hacks.
但是,当然,lock()应该在一个c'tor中发生,而unlock()应该在一个de tor中发生,除了一次性黑客攻击。
Update: Note that there is a race condition in how $mutex is initialized in the subroutine (two threads could call that() for the first time nearly simultaneously). You'd most likely want to initialize $mutex before (additional) threads are created but I'm unclear on the details on the 'worker' Apache MPM and how you would accomplish that easily. If there is some code that gets run "early", simply calling that() from there would eliminate the race.
更新:请注意,在子例程中如何初始化$ mutex存在竞争条件(两个线程几乎可以同时第一次调用this())。您很可能希望在创建(附加)线程之前初始化$ mutex,但我不清楚'worker'Apache MPM的详细信息以及如何轻松完成。如果有一些代码“早期”运行,只需从那里调用那个()就可以消除竞争。
Which all suggests a much safer interface to APR::ThreadMutex:
这一切都暗示了APR :: ThreadMutex更安全的界面:
BEGIN { my $mutex; sub that { my $autoLock= APR::ThreadMutex->autoLock( \$mutex ); ... # Mutex automatically released when $autoLock destroyed }}
Note that autoLock() getting a reference to undef would cause it to use a mutex to prevent a race when it initializes $mutex.
请注意,autoLock()获取对undef的引用会导致它在初始化$ mutex时使用互斥锁来阻止竞争。
#2
3
Because of this issue, mod_perl 2 actually deals with the %ENV hash differently than mod_perl 1. In mod_perl 1 %ENV was tied directly to the environ struct, so changing %ENV changed the environment. In mod_perl 2, the %ENV hash is populated from environ, but changes are not passed back.
由于这个问题,mod_perl 2实际上与mod_perl 1不同地处理%ENV哈希。在mod_perl中,1%ENV直接绑定到环境结构,因此更改%ENV会改变环境。在mod_perl 2中,%ENV哈希是从environ填充的,但不会传回更改。
This means you can no longer muck with $ENV{TZ} to adjust the timezone -- particularly in a threaded environment. The Apache2::Localtime module will make it work for the non-threaded case (by using Env::C) but when running in a threaded MPM that will be bad news.
这意味着你不能再使用$ ENV {TZ}来调整时区 - 特别是在线程环境中。 Apache2 :: Localtime模块将使其适用于非线程情况(通过使用Env :: C)但在线程MPM中运行时将是坏消息。
There are some comments in the mod_perl source (src/modules/perl/modperl_env.c) regarding this issue:
关于此问题,mod_perl源(src / modules / perl / modperl_env.c)中有一些注释:
/* * XXX: what we do here might change: * - make it optional for %ENV to be tied to r->subprocess_env * - make it possible to modify environ * - we could allow modification of environ if mpm isn't threaded * - we could allow modification of environ if variable isn't a CGI * variable (still could cause problems) *//* * problems we are trying to solve: * - environ is shared between threads * + Perl does not serialize access to environ * + even if it did, CGI variables cannot be shared between threads! * problems we create by trying to solve above problems: * - a forked process will not inherit the current %ENV * - C libraries might rely on environ, e.g. DBD::Oracle */
#3
1
If you're using apache 1.3, then you shouldn't need to resort to mutexes. Apache 1.3 spawns of a number of worker processes, and each worker executes a single thread. In this case, you can write:
如果您使用的是apache 1.3,那么您不需要使用互斥锁。 Apache 1.3产生了许多工作进程,每个工作程序执行一个线程。在这种情况下,您可以写:
{ local $ENV{TZ} = whatever_I_need_it_to_be(); # Do calculations here.}
Changing the variable with local
means that it reverts back to the previous value at the end of the block, but is still passed into any subroutine calls made from within that block. It's almost certainly what you want. Since each process has its own independent environment, you won't be changing the environment of other processes using this technique.
使用local更改变量意味着它将恢复为块末尾的先前值,但仍会传递到从该块中进行的任何子例程调用。这几乎可以肯定你想要的。由于每个进程都有自己独立的环境,因此您不会使用此技术更改其他进程的环境。
For apache 2, I don't know what model it uses with regards to forks and threads. If it keeps the same approach of forking off processes and having a single thread each, you're fine.
对于apache 2,我不知道它在分叉和线程方面使用的是什么型号。如果它保持相同的方法分离进程并且每个都有一个单独的线程,那么你没问题。
If apache 2 uses honest to goodness real threads, then that's outside my area of detailed knowledge, but I hope another lovely * person can provide assistance.
如果apache 2使用诚实的真实线程,那么这不在我的详细知识领域,但我希望另一个可爱的*人员可以提供帮助。
All the very best,
一切都是最好的,
Paul