This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
(perl #124203) avoid a deadlock in DB::sub
authorTony Cook <tony@develop-help.com>
Wed, 27 Feb 2019 01:01:40 +0000 (12:01 +1100)
committerTony Cook <tony@develop-help.com>
Thu, 7 Mar 2019 23:36:13 +0000 (10:36 +1100)
commit609761014c471773184e867d1587daac35036aef
treecae1ae2dbb72c2f906da906dbed4c5bb9661d439
parentd22170b0f355b196776681a081a50e5d7a7520cf
(perl #124203) avoid a deadlock in DB::sub

I don't know how this ever worked.

Previously, DB::sub() would hold a lock on $DB::DBGR for it's entire
body, including the call to the subroutine being called.

This could cause problems in two cases:

a) on creation of a new thread, CLONE() is called in the context of
the new interpreter before the new thread is created.  So you'd have a
sequence like:

  threads->new
  DB::sub for threads::new (lock $DBGR)
  call into threads::new which creates a new interpreter
  Cwd::CLONE() (in the new interpreter)
  DB::sub for Cwd::CLONE (in the new interpreter) (deadlock trying to lock $DBGR)

One workaround I tried for this was to prevent pp_entersub calling
DB::sub if we were cloning (by checking PL_ptr_table).  This did
improve matters, but wasn't needed in the final patch.

Note that the recursive lock on $DBGR would have been fine if the new
code was executing in the same interpreter, since the locking code
simply bumps a reference count if the current interpreter already
holds the lock.

b) when the called subroutine blocks.  For the test case this could
happen with the call to $thr->join.  There would be a sequence like:

  (parent) $thr->join
  (parent) DB::sub for threads::join (lock $DBGR)
  (parent) call threads::join and block
  (child) try to call main::sub1
  (child) DB::sub for main::sub1 (deadlock trying to lock $DBGR)

This isn't limited to threads::join obviously, one thread could be
waiting for input, sleeping, or performing a complex calculation.

The solution I chose here was the obvious one - don't hold the lock
for the actual call.

This required some rearrangement of the code and removed some
duplication too.
MANIFEST
lib/perl5db.pl
lib/perl5db.t
lib/perl5db/t/rt-124203 [new file with mode: 0644]