1 /*
  2  * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 #include "precompiled.hpp"
 25 #include "runtime/atomic.hpp"
 26 #include "runtime/os.hpp"
 27 #include "runtime/thread.hpp"
 28 #include "runtime/interfaceSupport.inline.hpp"
 29 #include "threadHelper.inline.hpp"
 30 #include "unittest.hpp"
 31 
 32 // Timing controller, might need bigger barriers
 33 class Control : public AllStatic {
 34   static bool _suspend_done;
 35   static bool _block_done;
 36  public:
 37   static bool suspend_done() { return Atomic::load(&_suspend_done); }
 38   static bool block_done() { return Atomic::load(&_block_done); }
 39   static void set_suspend_done() { Atomic::store(&_suspend_done, true); }
 40   static void set_block_done() { Atomic::store(&_block_done, true); }
 41 };
 42 
 43 bool Control::_suspend_done = false;
 44 bool Control::_block_done = false;
 45 
 46 class BlockeeThread : public JavaTestThread {
 47   public:
 48   BlockeeThread(Semaphore* post) : JavaTestThread(post) {}
 49   virtual ~BlockeeThread() {}
 50   void main_run() {
 51     while (!Control::suspend_done()) {
 52       ThreadBlockInVM tbivm(this);
 53     }
 54   }
 55 };
 56 
 57 class BlockingThread : public JavaTestThread {
 58   JavaThread* _target;
 59   public:
 60   BlockingThread(Semaphore* post, JavaThread* target) : JavaTestThread(post), _target(target) {}
 61   virtual ~BlockingThread() {}
 62   void main_run() {
 63     int print_count = 0;
 64     // Suspend the target thread and check its state
 65     while (!Control::block_done()) {
 66       ASSERT_LT(print_count++, 100) << "Blocking thread - never suspended";
 67       if (_target->block_suspend(this)) {
 68         tty->print_cr("Block succeeded");
 69         Control::set_block_done();
 70         os::naked_short_sleep(10);
 71          while (!Control::suspend_done()) {
 72            ASSERT_EQ(_target->thread_state(), _thread_blocked) << "should be blocked";
 73         }
 74         _target->continue_resume(this);
 75         tty->print_cr("Release succeeded");
 76       }
 77     }
 78   }
 79 };
 80 
 81 class SuspendingThread : public JavaTestThread {
 82   JavaThread* _target;
 83   public:
 84   SuspendingThread(Semaphore* post, JavaThread* target) : JavaTestThread(post), _target(target) {}
 85   virtual ~SuspendingThread() {}
 86   void main_run() {
 87     int print_count = 0;
 88     int test_count = 0;
 89     // Suspend the target thread and resume it
 90     while (test_count < 100) {
 91       ASSERT_LT(print_count++, 100) << "Suspending thread - never suspended";
 92       if (_target->java_suspend()) {
 93         ASSERT_EQ(_target->thread_state(), _thread_blocked) << "should be blocked";
 94         _target->java_resume();
 95         test_count++;
 96       }
 97     }
 98     // Still blocked until Blocking thread resumes the thread
 99     ASSERT_EQ(_target->thread_state(), _thread_blocked) << "should still be blocked";
100     Control::set_suspend_done();
101   }
102 };
103 
104 // This guy should fail, then pass.
105 class AnotherBlockingThread : public JavaTestThread {
106   JavaThread* _target;
107   public:
108   AnotherBlockingThread(Semaphore* post, JavaThread* target) : JavaTestThread(post), _target(target) {}
109   virtual ~AnotherBlockingThread() {}
110   void main_run() {
111     bool done = false;
112     // Suspend the target thread and check its state
113     while (!Control::block_done()) {
114       os::naked_short_sleep(10);
115     }
116     while (!done) {
117       if (_target->block_suspend(this)) {
118         ASSERT_EQ(Control::suspend_done(), true) << "should only pass if Blocking thread releases the block";
119         tty->print_cr("Other Block succeeded");
120         _target->continue_resume(this);
121         tty->print_cr("Other Release succeeded");
122         done = true;
123       }
124     }
125   }
126 };
127 
128 #define TEST_THREAD_COUNT 4
129 
130 class DriverSuspendThread : public JavaTestThread {
131 public:
132   Semaphore _done;
133   DriverSuspendThread(Semaphore* post) : JavaTestThread(post) { };
134   virtual ~DriverSuspendThread(){}
135 
136   void main_run() {
137     Semaphore done(0);
138 
139     BlockeeThread* target = new BlockeeThread(&done);
140     BlockingThread* bt = new BlockingThread(&done, target);
141     SuspendingThread* st = new SuspendingThread(&done, target);
142     AnotherBlockingThread* obt = new AnotherBlockingThread(&done, target);
143 
144     target->doit();
145     bt->doit();
146     st->doit();
147     obt->doit();
148 
149     for (int i = 0; i < TEST_THREAD_COUNT; i++) {
150       done.wait();
151     }
152   }
153 };
154 
155 TEST_VM(ThreadSuspend, test_thread_suspend) {
156   mt_test_doer<DriverSuspendThread>();
157 }