[vm/concurrency] Make SafepointMutexLocker inherit from StackResource

The SafepointMutexLocker class already has a virtual destructor and was
therefore probably intended to be called when a longjmp() crosses it.

Issue https://github.com/dart-lang/sdk/issues/36097

TEST=Added vm/cc tests in the CL.

Change-Id: Ifcfe51db733c4451be5be688df0c34004b98c3cc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/174644
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
diff --git a/runtime/vm/lockers.cc b/runtime/vm/lockers.cc
index 7ad731d..cb04006 100644
--- a/runtime/vm/lockers.cc
+++ b/runtime/vm/lockers.cc
@@ -40,7 +40,8 @@
   return result;
 }
 
-SafepointMutexLocker::SafepointMutexLocker(Mutex* mutex) : mutex_(mutex) {
+SafepointMutexLocker::SafepointMutexLocker(ThreadState* thread, Mutex* mutex)
+    : StackResource(thread), mutex_(mutex) {
   ASSERT(mutex != NULL);
   if (!mutex_->TryLock()) {
     // We did not get the lock and could potentially block, so transition
diff --git a/runtime/vm/lockers.h b/runtime/vm/lockers.h
index 94a8070..d5b2576 100644
--- a/runtime/vm/lockers.h
+++ b/runtime/vm/lockers.h
@@ -226,9 +226,11 @@
  *      ...
  *    }
  */
-class SafepointMutexLocker : public ValueObject {
+class SafepointMutexLocker : public StackResource {
  public:
-  explicit SafepointMutexLocker(Mutex* mutex);
+  explicit SafepointMutexLocker(Mutex* mutex)
+      : SafepointMutexLocker(ThreadState::Current(), mutex) {}
+  SafepointMutexLocker(ThreadState* thread, Mutex* mutex);
   virtual ~SafepointMutexLocker() { mutex_->Unlock(); }
 
  private:
diff --git a/runtime/vm/thread_test.cc b/runtime/vm/thread_test.cc
index aaedbb7..95a0839 100644
--- a/runtime/vm/thread_test.cc
+++ b/runtime/vm/thread_test.cc
@@ -1029,6 +1029,38 @@
   EXPECT(!lock.IsCurrentThreadWriter());
 }
 
+template <typename LockType, typename LockerType>
+static void RunLockerWithLongJumpTest() {
+  const intptr_t kNumIterations = 5;
+  intptr_t execution_count = 0;
+  intptr_t thrown_count = 0;
+  LockType lock;
+  for (intptr_t i = 0; i < kNumIterations; ++i) {
+    LongJumpScope jump;
+    if (setjmp(*jump.Set()) == 0) {
+      LockerType locker(Thread::Current(), &lock);
+      execution_count++;
+      Thread::Current()->long_jump_base()->Jump(
+          1, Object::background_compilation_error());
+    } else {
+      thrown_count++;
+    }
+  }
+  EXPECT_EQ(kNumIterations, execution_count);
+  EXPECT_EQ(kNumIterations, thrown_count);
+}
+ISOLATE_UNIT_TEST_CASE(SafepointRwLockWriteWithLongJmp) {
+  RunLockerWithLongJumpTest<SafepointRwLock, SafepointWriteRwLocker>();
+}
+
+ISOLATE_UNIT_TEST_CASE(SafepointRwLockReadWithLongJmp) {
+  RunLockerWithLongJumpTest<SafepointRwLock, SafepointReadRwLocker>();
+}
+
+ISOLATE_UNIT_TEST_CASE(SafepointMutexLockerWithLongJmp) {
+  RunLockerWithLongJumpTest<Mutex, SafepointMutexLocker>();
+}
+
 struct ReaderThreadState {
   ThreadJoinId reader_id = OSThread::kInvalidThreadJoinId;
   SafepointRwLock* rw_lock = nullptr;