dainis/node-gcstats

Assertion failure while changing priority of worker thread during GC

tlbtlbtlb opened this issue · 0 comments

Doing this causes occasional assertion failures deep inside v8:

  var gc = (require('gc-stats'))();
  gc.on('stats', function (stats) {
    console.log('GC ' +
      ((stats.gctype&1) ? 'scavenge ' : '') +
      ((stats.gctype&2) ? 'marksweepcompact ' : '') +
      ((stats.gctype&4) ? 'incrementalmarking ' : '') +
      ((stats.gctype&8) ? 'weakcallbacks ' : '') +
      ((stats.gctype&0xfffffff0) ? ('0x' + stats.gctype.toString(16) + ' ') : '') +
      (stats.pause/1000000.0).toFixed(1) + 'mS ' +
      (stats.diff.usedHeapSize/1048576).toFixed(1) + 'M ' +
      (stats.after.usedHeapSize/1048576).toFixed(1) + 'M');
  });
  • Version:
    Node v8.1.0
  • Platform:
    Linux london 4.4.0-78-generic #99-Ubuntu SMP Thu Apr 27 15:29:09 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

I get the following assertion failure:

node: tpp.c:84: __pthread_tpp_change_priority: Assertion `new_prio == -1 || (new_prio >= fifo_min_prio && new_prio <= fifo_max_prio)' failed.

and gdb gives this backtrace:

Thread 5 "V8 WorkerThread" received signal SIGABRT, Aborted.
[Switching to Thread 0x7ffff5340700 (LWP 20368)]
0x00007ffff6b79428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
54	../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  0x00007ffff6b79428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007ffff6b7b02a in __GI_abort () at abort.c:89
#2  0x00007ffff6b71bd7 in __assert_fail_base (fmt=<optimized out>, assertion=assertion@entry=0x7ffff6f20598 "new_prio == -1 || (new_prio >= fifo_min_prio && new_prio <= fifo_max_prio)",
    file=file@entry=0x7ffff6f2058c "tpp.c", line=line@entry=84, function=function@entry=0x7ffff6f20650 <__PRETTY_FUNCTION__.7703> "__pthread_tpp_change_priority") at assert.c:92
#3  0x00007ffff6b71c82 in __GI___assert_fail (assertion=assertion@entry=0x7ffff6f20598 "new_prio == -1 || (new_prio >= fifo_min_prio && new_prio <= fifo_max_prio)",
    file=file@entry=0x7ffff6f2058c "tpp.c", line=line@entry=84, function=function@entry=0x7ffff6f20650 <__PRETTY_FUNCTION__.7703> "__pthread_tpp_change_priority") at assert.c:101
#4  0x00007ffff6f1f029 in __pthread_tpp_change_priority (previous_prio=previous_prio@entry=-1, new_prio=new_prio@entry=0) at tpp.c:82
#5  0x00007ffff6f1687c in __pthread_mutex_lock_full (mutex=0xe19ec80) at ../nptl/pthread_mutex_lock.c:456
#6  0x0000000000f11b7f in v8::internal::MemoryChunk::ReleaseTypedOldToOldSlots() ()
#7  0x0000000000ef3422 in v8::internal::PageParallelJob<v8::internal::PointerUpdateJobTraits<(v8::internal::PointerDirection)0> >::Task::RunInternal() ()
#8  0x0000000000b7130d in v8::internal::CancelableTask::Run() ()
#9  0x000000000148a479 in v8::platform::WorkerThread::Run() ()
#10 0x00000000016136a0 in ?? ()
#11 0x00007ffff6f146ba in start_thread (arg=0x7ffff5340700) at pthread_create.c:333
#12 0x00007ffff6c4a82d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109

MemoryChunk::ReleaseTypedOldToOldSlots() references a base::AtomicValue<TypedSlotSet*>, which is why pthread_mutex gets involved.

I believe the root cause is calling uv_queue_work from the GC callback. uv_queue_work isn't safe to use from outside the main thread. You really want the uv_async system, which is intended for triggering work from a non-main thread (like the GC thread) and having it executed on the main thread.