[NativeProcessLinux] Get rid of the thread state coordinator thread

Summary:
This change removes the thread state coordinator thread by making all the operations it was
performing synchronous. In order to prevent deadlock, NativeProcessLinux must now always call
m_monitor->DoOperation with the m_threads_mutex released. This is needed because HandleWait
callbacks lock the mutex (which means the monitor thread will block waiting on whoever holds the
lock). If the other thread now requests a monitor operation, it will wait for the monitor thread
do process it, creating a deadlock.

To preserve this invariant I have introduced two new Monitor commands: "begin operation block"
and "end operation block". They begin command blocks the monitor from processing waitpid
events until the corresponding end command, thereby assuring the monitor does not attempt to
acquire the mutex.

Test Plan: Run the test suite locally, verify no tests fail.

Reviewers: vharron, chaoren

Subscribers: lldb-commits

Differential Revision: http://reviews.llvm.org/D9227

llvm-svn: 236501
This commit is contained in:
Pavel Labath 2015-05-05 15:05:50 +00:00
parent 941b743fc9
commit 45f5cb31dc
5 changed files with 176 additions and 405 deletions

View File

@ -1069,7 +1069,6 @@ namespace
{
PTRACE(PTRACE_DETACH, m_tid, nullptr, 0, 0, m_error);
}
} // end of anonymous namespace
// Simple helper function to ensure flags are enabled on the given file
@ -1105,7 +1104,8 @@ EnsureFDFlags(int fd, int flags)
// pipe, and the completion of the operation is signalled over the semaphore.
// - thread exit event: this is signaled from the Monitor destructor by closing the write end
// of the command pipe.
class NativeProcessLinux::Monitor {
class NativeProcessLinux::Monitor
{
private:
// The initial monitor operation (launch or attach). It returns a inferior process id.
std::unique_ptr<InitialOperation> m_initial_operation_up;
@ -1124,7 +1124,11 @@ private:
sem_t m_operation_sem;
Error m_operation_error;
static constexpr char operation_command = 'o';
unsigned m_operation_nesting_level = 0;
static constexpr char operation_command = 'o';
static constexpr char begin_block_command = '{';
static constexpr char end_block_command = '}';
void
HandleSignals();
@ -1143,7 +1147,22 @@ private:
RunMonitor(void *arg);
Error
WaitForOperation();
WaitForAck();
void
BeginOperationBlock()
{
write(m_pipefd[WRITE], &begin_block_command, sizeof operation_command);
WaitForAck();
}
void
EndOperationBlock()
{
write(m_pipefd[WRITE], &end_block_command, sizeof operation_command);
WaitForAck();
}
public:
Monitor(const InitialOperation &initial_operation,
NativeProcessLinux *native_process)
@ -1158,10 +1177,27 @@ public:
Error
Initialize();
void
Terminate();
void
DoOperation(Operation *op);
class ScopedOperationLock {
Monitor &m_monitor;
public:
ScopedOperationLock(Monitor &monitor)
: m_monitor(monitor)
{ m_monitor.BeginOperationBlock(); }
~ScopedOperationLock()
{ m_monitor.EndOperationBlock(); }
};
};
constexpr char NativeProcessLinux::Monitor::operation_command;
constexpr char NativeProcessLinux::Monitor::begin_block_command;
constexpr char NativeProcessLinux::Monitor::end_block_command;
Error
NativeProcessLinux::Monitor::Initialize()
@ -1198,7 +1234,7 @@ NativeProcessLinux::Monitor::Initialize()
return Error("Failed to create monitor thread for NativeProcessLinux.");
// Wait for initial operation to complete.
return WaitForOperation();
return WaitForAck();
}
void
@ -1218,15 +1254,24 @@ NativeProcessLinux::Monitor::DoOperation(Operation *op)
// notify the thread that an operation is ready to be processed
write(m_pipefd[WRITE], &operation_command, sizeof operation_command);
WaitForOperation();
WaitForAck();
}
void
NativeProcessLinux::Monitor::Terminate()
{
if (m_pipefd[WRITE] >= 0)
{
close(m_pipefd[WRITE]);
m_pipefd[WRITE] = -1;
}
if (m_thread.IsJoinable())
m_thread.Join(nullptr);
}
NativeProcessLinux::Monitor::~Monitor()
{
if (m_pipefd[WRITE] >= 0)
close(m_pipefd[WRITE]);
if (m_thread.IsJoinable())
m_thread.Join(nullptr);
Terminate();
if (m_pipefd[READ] >= 0)
close(m_pipefd[READ]);
if (m_signal_fd >= 0)
@ -1356,6 +1401,7 @@ NativeProcessLinux::Monitor::HandleCommands()
{
if (log)
log->Printf("NativeProcessLinux::Monitor::%s exit command received, exiting...", __FUNCTION__);
assert(m_operation_nesting_level == 0 && "Unbalanced begin/end block commands detected");
return true; // We are done.
}
@ -1363,15 +1409,22 @@ NativeProcessLinux::Monitor::HandleCommands()
{
case operation_command:
m_operation->Execute(m_native_process);
// notify calling thread that operation is complete
sem_post(&m_operation_sem);
break;
case begin_block_command:
++m_operation_nesting_level;
break;
case end_block_command:
assert(m_operation_nesting_level > 0);
--m_operation_nesting_level;
break;
default:
if (log)
log->Printf("NativeProcessLinux::Monitor::%s received unknown command '%c'",
__FUNCTION__, command);
}
// notify calling thread that the command has been processed
sem_post(&m_operation_sem);
}
}
@ -1387,7 +1440,10 @@ NativeProcessLinux::Monitor::MainLoop()
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(m_signal_fd, &fds);
// Only process waitpid events if we are outside of an operation block. Any pending
// events will be processed after we leave the block.
if (m_operation_nesting_level == 0)
FD_SET(m_signal_fd, &fds);
FD_SET(m_pipefd[READ], &fds);
int max_fd = std::max(m_signal_fd, m_pipefd[READ]) + 1;
@ -1416,7 +1472,7 @@ NativeProcessLinux::Monitor::MainLoop()
}
Error
NativeProcessLinux::Monitor::WaitForOperation()
NativeProcessLinux::Monitor::WaitForAck()
{
Error error;
while (sem_wait(&m_operation_sem) != 0)
@ -1617,8 +1673,7 @@ NativeProcessLinux::NativeProcessLinux () :
m_supports_mem_region (eLazyBoolCalculate),
m_mem_region_cache (),
m_mem_region_cache_mutex (),
m_coordinator_up (new ThreadStateCoordinator (GetThreadLoggerFunction ())),
m_coordinator_thread ()
m_coordinator_up (new ThreadStateCoordinator (GetThreadLoggerFunction ()))
{
}
@ -1652,10 +1707,6 @@ NativeProcessLinux::LaunchInferior (
StartMonitorThread ([&] (Error &e) { return Launch(args.get(), e); }, error);
if (!error.Success ())
return;
error = StartCoordinatorThread ();
if (!error.Success ())
return;
}
void
@ -1705,16 +1756,12 @@ NativeProcessLinux::AttachToInferior (lldb::pid_t pid, Error &error)
StartMonitorThread ([=] (Error &e) { return Attach(pid, e); }, error);
if (!error.Success ())
return;
error = StartCoordinatorThread ();
if (!error.Success ())
return;
}
void
NativeProcessLinux::Terminate ()
{
StopMonitor();
m_monitor_up->Terminate();
}
::pid_t
@ -2997,6 +3044,7 @@ NativeProcessLinux::Resume (const ResumeActionList &resume_actions)
bool stepping = false;
bool software_single_step = !SupportHardwareSingleStepping();
Monitor::ScopedOperationLock monitor_lock(*m_monitor_up);
Mutex::Locker locker (m_threads_mutex);
if (software_single_step)
@ -3144,7 +3192,7 @@ NativeProcessLinux::Detach ()
error = Detach (GetID ());
// Stop monitoring the inferior.
StopMonitor ();
m_monitor_up->Terminate();
// No error.
return error;
@ -3179,6 +3227,7 @@ NativeProcessLinux::Interrupt ()
if (log)
log->Printf ("NativeProcessLinux::%s selecting running thread for interrupt target", __FUNCTION__);
Monitor::ScopedOperationLock monitor_lock(*m_monitor_up);
Mutex::Locker locker (m_threads_mutex);
for (auto thread_sp : m_threads)
@ -3233,6 +3282,7 @@ NativeProcessLinux::Interrupt ()
// Tell the process delegate that the process is in a stopped state.
SetState (StateType::eStateStopped, true);
});
return Error();
}
@ -3834,7 +3884,25 @@ NativeProcessLinux::GetCrashReasonForSIGBUS(const siginfo_t *info)
#endif
Error
NativeProcessLinux::ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read)
NativeProcessLinux::SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware)
{
// The base SetWatchpoint will end up executing monitor operations. Let's lock the monitor
// for it.
Monitor::ScopedOperationLock monitor_lock(*m_monitor_up);
return NativeProcessProtocol::SetWatchpoint(addr, size, watch_flags, hardware);
}
Error
NativeProcessLinux::RemoveWatchpoint (lldb::addr_t addr)
{
// The base RemoveWatchpoint will end up executing monitor operations. Let's lock the monitor
// for it.
Monitor::ScopedOperationLock monitor_lock(*m_monitor_up);
return NativeProcessProtocol::RemoveWatchpoint(addr);
}
Error
NativeProcessLinux::ReadMemory (lldb::addr_t addr, void *buf, lldb::addr_t size, lldb::addr_t &bytes_read)
{
ReadOperation op(addr, buf, size, bytes_read);
m_monitor_up->DoOperation(&op);
@ -3997,77 +4065,6 @@ NativeProcessLinux::StartMonitorThread(const InitialOperation &initial_operation
}
}
void
NativeProcessLinux::StopMonitor()
{
StopCoordinatorThread ();
m_monitor_up.reset();
}
Error
NativeProcessLinux::StartCoordinatorThread ()
{
Error error;
static const char *g_thread_name = "lldb.process.linux.ts_coordinator";
Log *const log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
// Skip if thread is already running
if (m_coordinator_thread.IsJoinable())
{
error.SetErrorString ("ThreadStateCoordinator's run loop is already running");
if (log)
log->Printf ("NativeProcessLinux::%s %s", __FUNCTION__, error.AsCString ());
return error;
}
// Enable verbose logging if lldb thread logging is enabled.
m_coordinator_up->LogEnableEventProcessing (log != nullptr);
if (log)
log->Printf ("NativeProcessLinux::%s launching ThreadStateCoordinator thread for pid %" PRIu64, __FUNCTION__, GetID ());
m_coordinator_thread = ThreadLauncher::LaunchThread(g_thread_name, CoordinatorThread, this, &error);
return error;
}
void *
NativeProcessLinux::CoordinatorThread (void *arg)
{
Log *const log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
NativeProcessLinux *const process = static_cast<NativeProcessLinux*> (arg);
assert (process && "null process passed to CoordinatorThread");
if (!process)
{
if (log)
log->Printf ("NativeProcessLinux::%s null process, exiting ThreadStateCoordinator processing loop", __FUNCTION__);
return nullptr;
}
// Run the thread state coordinator loop until it is done. This call uses
// efficient waiting for an event to be ready.
while (process->m_coordinator_up->ProcessNextEvent () == ThreadStateCoordinator::eventLoopResultContinue)
{
}
if (log)
log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " exiting ThreadStateCoordinator processing loop due to coordinator indicating completion", __FUNCTION__, process->GetID ());
return nullptr;
}
void
NativeProcessLinux::StopCoordinatorThread()
{
Log *const log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
if (log)
log->Printf ("NativeProcessLinux::%s requesting ThreadStateCoordinator stop for pid %" PRIu64, __FUNCTION__, GetID ());
// Tell the coordinator we're done. This will cause the coordinator
// run loop thread to exit when the processing queue hits this message.
m_coordinator_up->StopCoordinator ();
m_coordinator_thread.Join (nullptr);
}
bool
NativeProcessLinux::HasThreadNoLock (lldb::tid_t thread_id)
{

View File

@ -110,6 +110,12 @@ namespace process_linux {
Error
SetBreakpoint (lldb::addr_t addr, uint32_t size, bool hardware) override;
Error
SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) override;
Error
RemoveWatchpoint (lldb::addr_t addr) override;
void
DoStopIDBumped (uint32_t newBumpId) override;
@ -185,7 +191,6 @@ namespace process_linux {
Mutex m_mem_region_cache_mutex;
std::unique_ptr<ThreadStateCoordinator> m_coordinator_up;
HostThread m_coordinator_thread;
// List of thread ids stepping with a breakpoint with the address of
// the relevan breakpoint
@ -303,19 +308,6 @@ namespace process_linux {
GetCrashReasonForSIGBUS(const siginfo_t *info);
#endif
Error
StartCoordinatorThread ();
static void*
CoordinatorThread (void *arg);
void
StopCoordinatorThread ();
/// Stops monitoring the child process thread.
void
StopMonitor();
bool
HasThreadNoLock (lldb::tid_t thread_id);

View File

@ -46,29 +46,6 @@ public:
//===----------------------------------------------------------------------===//
class ThreadStateCoordinator::EventStopCoordinator : public ThreadStateCoordinator::EventBase
{
public:
EventStopCoordinator ():
EventBase ()
{
}
EventLoopResult
ProcessEvent(ThreadStateCoordinator &coordinator) override
{
return eventLoopResultStop;
}
std::string
GetDescription () override
{
return "EventStopCoordinator";
}
};
//===----------------------------------------------------------------------===//
class ThreadStateCoordinator::EventCallAfterThreadsStop : public ThreadStateCoordinator::EventBase
{
public:
@ -588,41 +565,11 @@ private:
ThreadStateCoordinator::ThreadStateCoordinator (const LogFunction &log_function) :
m_log_function (log_function),
m_event_queue (),
m_queue_condition (),
m_queue_mutex (),
m_tid_map (),
m_log_event_processing (false)
{
}
void
ThreadStateCoordinator::EnqueueEvent (EventBaseSP event_sp)
{
std::lock_guard<std::mutex> lock (m_queue_mutex);
m_event_queue.push (event_sp);
if (m_log_event_processing)
Log ("ThreadStateCoordinator::%s enqueued event: %s", __FUNCTION__, event_sp->GetDescription ().c_str ());
m_queue_condition.notify_one ();
}
ThreadStateCoordinator::EventBaseSP
ThreadStateCoordinator::DequeueEventWithWait ()
{
// Wait for an event to be present.
std::unique_lock<std::mutex> lock (m_queue_mutex);
m_queue_condition.wait (lock,
[this] { return !m_event_queue.empty (); });
// Grab the event and pop it off the queue.
EventBaseSP event_sp = m_event_queue.front ();
m_event_queue.pop ();
return event_sp;
}
void
ThreadStateCoordinator::SetPendingNotification (const EventBaseSP &event_sp)
{
@ -653,11 +600,11 @@ ThreadStateCoordinator::CallAfterThreadsStop (const lldb::tid_t triggering_tid,
const ThreadIDFunction &call_after_function,
const ErrorFunction &error_function)
{
EnqueueEvent (EventBaseSP (new EventCallAfterThreadsStop (triggering_tid,
wait_for_stop_tids,
request_thread_stop_function,
call_after_function,
error_function)));
ProcessEvent(EventBaseSP(new EventCallAfterThreadsStop (triggering_tid,
wait_for_stop_tids,
request_thread_stop_function,
call_after_function,
error_function)));
}
void
@ -666,10 +613,10 @@ ThreadStateCoordinator::CallAfterRunningThreadsStop (const lldb::tid_t triggerin
const ThreadIDFunction &call_after_function,
const ErrorFunction &error_function)
{
EnqueueEvent (EventBaseSP (new EventCallAfterThreadsStop (triggering_tid,
request_thread_stop_function,
call_after_function,
error_function)));
ProcessEvent(EventBaseSP(new EventCallAfterThreadsStop (triggering_tid,
request_thread_stop_function,
call_after_function,
error_function)));
}
void
@ -679,11 +626,11 @@ ThreadStateCoordinator::CallAfterRunningThreadsStopWithSkipTIDs (lldb::tid_t tri
const ThreadIDFunction &call_after_function,
const ErrorFunction &error_function)
{
EnqueueEvent (EventBaseSP (new EventCallAfterThreadsStop (triggering_tid,
request_thread_stop_function,
call_after_function,
skip_stop_request_tids,
error_function)));
ProcessEvent(EventBaseSP(new EventCallAfterThreadsStop (triggering_tid,
request_thread_stop_function,
call_after_function,
skip_stop_request_tids,
error_function)));
}
@ -826,7 +773,7 @@ ThreadStateCoordinator::NotifyThreadStop (lldb::tid_t tid,
bool initiated_by_llgs,
const ErrorFunction &error_function)
{
EnqueueEvent (EventBaseSP (new EventThreadStopped (tid, initiated_by_llgs, error_function)));
ProcessEvent(EventBaseSP(new EventThreadStopped (tid, initiated_by_llgs, error_function)));
}
void
@ -834,7 +781,7 @@ ThreadStateCoordinator::RequestThreadResume (lldb::tid_t tid,
const ResumeThreadFunction &request_thread_resume_function,
const ErrorFunction &error_function)
{
EnqueueEvent (EventBaseSP (new EventRequestResume (tid, request_thread_resume_function, error_function, true)));
ProcessEvent(EventBaseSP(new EventRequestResume (tid, request_thread_resume_function, error_function, true)));
}
void
@ -842,7 +789,7 @@ ThreadStateCoordinator::RequestThreadResumeAsNeeded (lldb::tid_t tid,
const ResumeThreadFunction &request_thread_resume_function,
const ErrorFunction &error_function)
{
EnqueueEvent (EventBaseSP (new EventRequestResume (tid, request_thread_resume_function, error_function, false)));
ProcessEvent(EventBaseSP(new EventRequestResume (tid, request_thread_resume_function, error_function, false)));
}
void
@ -850,56 +797,26 @@ ThreadStateCoordinator::NotifyThreadCreate (lldb::tid_t tid,
bool is_stopped,
const ErrorFunction &error_function)
{
EnqueueEvent (EventBaseSP (new EventThreadCreate (tid, is_stopped, error_function)));
ProcessEvent(EventBaseSP(new EventThreadCreate (tid, is_stopped, error_function)));
}
void
ThreadStateCoordinator::NotifyThreadDeath (lldb::tid_t tid,
const ErrorFunction &error_function)
{
EnqueueEvent (EventBaseSP (new EventThreadDeath (tid, error_function)));
ProcessEvent(EventBaseSP(new EventThreadDeath (tid, error_function)));
}
void
ThreadStateCoordinator::ResetForExec ()
{
std::lock_guard<std::mutex> lock (m_queue_mutex);
// Remove everything from the queue. This is the only
// state mutation that takes place outside the processing
// loop.
QueueType empty_queue;
m_event_queue.swap (empty_queue);
// Do the real clear behavior on the the queue to eliminate
// the chance that processing of a dequeued earlier event is
// overlapping with the clearing of state here. Push it
// directly because we need to have this happen with the lock,
// and so far I only have this one place that needs a no-lock
// variant.
m_event_queue.push (EventBaseSP (new EventReset ()));
ProcessEvent(EventBaseSP(new EventReset()));
}
void
ThreadStateCoordinator::StopCoordinator ()
ThreadStateCoordinator::ProcessEvent (const EventBaseSP &event_sp)
{
EnqueueEvent (EventBaseSP (new EventStopCoordinator ()));
}
ThreadStateCoordinator::EventLoopResult
ThreadStateCoordinator::ProcessNextEvent ()
{
// Dequeue the next event, synchronous.
if (m_log_event_processing)
Log ("ThreadStateCoordinator::%s about to dequeue next event in blocking mode", __FUNCTION__);
EventBaseSP event_sp = DequeueEventWithWait();
assert (event_sp && "event should never be null");
if (!event_sp)
{
Log ("ThreadStateCoordinator::%s error: event_sp was null, signaling exit of event loop.", __FUNCTION__);
return eventLoopResultStop;
}
std::lock_guard<std::mutex> lock(m_event_mutex);
if (m_log_event_processing)
{
@ -907,15 +824,12 @@ ThreadStateCoordinator::ProcessNextEvent ()
}
// Process the event.
const EventLoopResult result = event_sp->ProcessEvent (*this);
event_sp->ProcessEvent (*this);
if (m_log_event_processing)
{
Log ("ThreadStateCoordinator::%s event processing returned value %s", __FUNCTION__,
result == eventLoopResultContinue ? "eventLoopResultContinue" : "eventLoopResultStop");
Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__);
}
return result;
}
void

View File

@ -10,10 +10,8 @@
#ifndef lldb_ThreadStateCoordinator_h
#define lldb_ThreadStateCoordinator_h
#include <condition_variable>
#include <functional>
#include <mutex>
#include <queue>
#include <unordered_map>
#include <unordered_set>
@ -126,24 +124,9 @@ namespace process_linux {
// Indicate the calling process did an exec and that the thread state
// should be 100% cleared.
//
// Note this will clear out any pending notifications, but will not stop
// a notification currently in progress via ProcessNextEvent().
void
ResetForExec ();
// Indicate when the coordinator should shut down.
void
StopCoordinator ();
// Process the next event, returning false when the coordinator is all done.
// This call is synchronous and blocks when there are no events pending.
// Expected usage is to run this in a separate thread until the function
// returns false. Always call this from the same thread. The processing
// logic assumes the execution of this is implicitly serialized.
EventLoopResult
ProcessNextEvent ();
// Enable/disable verbose logging of event processing.
void
LogEnableEventProcessing (bool enabled);
@ -159,13 +142,10 @@ namespace process_linux {
class EventThreadDeath;
class EventRequestResume;
class EventStopCoordinator;
class EventReset;
typedef std::shared_ptr<EventBase> EventBaseSP;
typedef std::queue<EventBaseSP> QueueType;
enum class ThreadState
{
Running,
@ -181,12 +161,10 @@ namespace process_linux {
typedef std::unordered_map<lldb::tid_t, ThreadContext> TIDContextMap;
// Private member functions.
void
EnqueueEvent (EventBaseSP event_sp);
std::mutex m_event_mutex; // Serializes execution of ProcessEvent.
EventBaseSP
DequeueEventWithWait ();
void
ProcessEvent (const EventBaseSP &event_sp);
void
SetPendingNotification (const EventBaseSP &event_sp);
@ -215,14 +193,6 @@ namespace process_linux {
// Member variables.
LogFunction m_log_function;
QueueType m_event_queue;
// For now we do simple read/write lock strategy with efficient wait-for-data.
// We can replace with an entirely non-blocking queue later but we still want the
// reader to sleep when nothing is available - this will be a bursty but infrequent
// event mechanism.
std::condition_variable m_queue_condition;
std::mutex m_queue_mutex;
EventBaseSP m_pending_notification_sp;
// Maps known TIDs to ThreadContext.

View File

@ -22,12 +22,11 @@ namespace
// These are single-line macros so that IDE integration of gtest results puts
// the error markers on the correct failure point within the gtest.
#define ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS() ASSERT_EQ (ThreadStateCoordinator::eventLoopResultContinue, m_coordinator.ProcessNextEvent ()); \
#define ASSERT_PROCESS_EVENT_SUCCEEDED() do { \
if (HasError ()) { printf ("unexpected error in processing of event, error: %s\n", m_error_string.c_str ()); } \
ASSERT_EQ (false, HasError ());
ASSERT_EQ (false, HasError ()); } while(0)
#define ASSERT_PROCESS_NEXT_EVENT_FAILS() ASSERT_EQ (ThreadStateCoordinator::eventLoopResultContinue, m_coordinator.ProcessNextEvent ()); \
ASSERT_EQ (true, HasError ());
#define ASSERT_PROCESS_EVENT_FAILED() ASSERT_EQ (true, HasError ())
class ThreadStateCoordinatorTest: public ::testing::Test
{
@ -142,14 +141,14 @@ ASSERT_EQ (true, HasError ());
SetupKnownRunningThread (lldb::tid_t tid)
{
NotifyThreadCreate (tid, false);
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
}
void
SetupKnownStoppedThread (lldb::tid_t tid)
{
NotifyThreadCreate (tid, true);
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
}
// Convenience wrappers for ThreadStateCoordinator, using defaults for expected arguments
@ -194,13 +193,6 @@ ASSERT_EQ (true, HasError ());
};
}
TEST_F (ThreadStateCoordinatorTest, StopCoordinatorWorksNoPriorEvents)
{
m_coordinator.StopCoordinator ();
ASSERT_EQ (ThreadStateCoordinator::eventLoopResultStop, m_coordinator.ProcessNextEvent ());
ASSERT_EQ (false, HasError ());
}
TEST_F (ThreadStateCoordinatorTest, NotifyThreadCreateSignalsErrorOnAlreadyKnownThread)
{
// Let the coordinator know about our thread.
@ -210,7 +202,7 @@ TEST_F (ThreadStateCoordinatorTest, NotifyThreadCreateSignalsErrorOnAlreadyKnown
NotifyThreadCreate (TRIGGERING_TID, true);
// This should error out.
ASSERT_PROCESS_NEXT_EVENT_FAILS ();
ASSERT_PROCESS_EVENT_FAILED ();
}
@ -222,7 +214,7 @@ TEST_F (ThreadStateCoordinatorTest, NotifyThreadDeathSignalsErrorOnUnknownThread
NotifyThreadDeath (UNKNOWN_TID);
// This should error out.
ASSERT_PROCESS_NEXT_EVENT_FAILS ();
ASSERT_PROCESS_EVENT_FAILED ();
}
TEST_F (ThreadStateCoordinatorTest, NotifyThreadStopSignalsErrorOnUnknownThread)
@ -231,7 +223,7 @@ TEST_F (ThreadStateCoordinatorTest, NotifyThreadStopSignalsErrorOnUnknownThread)
// Notify an unknown thread has stopped.
NotifyThreadStop (UNKNOWN_TID);
ASSERT_PROCESS_NEXT_EVENT_FAILS ();
ASSERT_PROCESS_EVENT_FAILED ();
}
TEST_F (ThreadStateCoordinatorTest, CallAfterTheadsStopSignalsErrorOnUnknownDeferredThread)
@ -246,7 +238,7 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterTheadsStopSignalsErrorOnUnknownDefe
ASSERT_EQ (false, DidFireDeferredNotification ());
// Event should fail because trigger tid is unknown.
ASSERT_PROCESS_NEXT_EVENT_FAILS ();
ASSERT_PROCESS_EVENT_FAILED ();
// Shouldn't have fired due to error.
ASSERT_EQ (false, DidFireDeferredNotification ());
@ -268,7 +260,7 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterTheadsStopSignalsErrorOnUnknownPend
ASSERT_EQ (false, DidFireDeferredNotification ());
// Event should fail because trigger tid is unknown.
ASSERT_PROCESS_NEXT_EVENT_FAILS ();
ASSERT_PROCESS_EVENT_FAILED ();
// Shouldn't have triggered deferred notification due to error.
ASSERT_EQ (false, DidFireDeferredNotification ());
@ -285,14 +277,9 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenNoPendingStops)
// Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
CallAfterThreadsStop (TRIGGERING_TID,
EMPTY_THREAD_ID_SET);
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Notification trigger shouldn't go off yet.
ASSERT_EQ (false, DidFireDeferredNotification ());
// Process next event. This will pick up the call after threads stop event.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
// Now the trigger should have fired, since there were no threads that needed to first stop.
// The trigger should have fired, since there were no threads that needed to first stop.
ASSERT_EQ (true, DidFireDeferredNotification ());
ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
}
@ -310,15 +297,9 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingStop)
// Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
CallAfterThreadsStop (TRIGGERING_TID,
pending_stop_tids);
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Neither trigger should have gone off yet.
ASSERT_EQ (false, DidFireDeferredNotification ());
ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID));
// Process next event.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
// Now the request thread stop should have been called for the pending stop.
// The request thread stop should have been called for the pending stop.
ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
// But we still shouldn't have the deferred signal call go off yet. Need to wait for the stop to be reported.
@ -326,12 +307,7 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingStop)
// Now report the that the pending stop occurred.
NotifyThreadStop (PENDING_STOP_TID);
// Shouldn't take effect until after next processing step.
ASSERT_EQ (false, DidFireDeferredNotification ());
// Process next event.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Deferred signal notification should have fired now.
ASSERT_EQ (true, DidFireDeferredNotification ());
@ -350,15 +326,9 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenTwoPendingStops
// Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
CallAfterThreadsStop (TRIGGERING_TID,
pending_stop_tids);
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Neither trigger should have gone off yet.
ASSERT_EQ (false, DidFireDeferredNotification ());
ASSERT_EQ (0u, GetRequestedStopCount ());
// Process next event.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
// Now the request thread stops should have been called for the pending stop tids.
// The request thread stops should have been called for the pending stop tids.
ASSERT_EQ (2u, GetRequestedStopCount ());
ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID_02));
@ -368,15 +338,14 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenTwoPendingStops
// Report the that the first pending stop occurred.
NotifyThreadStop (PENDING_STOP_TID);
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Shouldn't take effect until after both pending threads are notified.
ASSERT_EQ (false, DidFireDeferredNotification ());
// Report the that the first pending stop occurred.
NotifyThreadStop (PENDING_STOP_TID_02);
ASSERT_EQ (false, DidFireDeferredNotification ());
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Deferred signal notification should have fired now.
ASSERT_EQ (true, DidFireDeferredNotification ());
@ -393,18 +362,12 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenPendingAlreadyS
// Tell m_coordinator the pending stop tid is already stopped.
NotifyThreadStop (PENDING_STOP_TID);
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
CallAfterThreadsStop (TRIGGERING_TID,
pending_stop_tids);
// Neither trigger should have gone off yet.
ASSERT_EQ (false, DidFireDeferredNotification ());
ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID));
// Process next event.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// The pending stop should *not* fire because the m_coordinator knows it has already stopped.
ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID));
@ -424,18 +387,12 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenTwoPendingOneAl
// Tell coordinator the pending stop tid is already stopped.
NotifyThreadStop (PENDING_STOP_TID);
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
CallAfterThreadsStop (TRIGGERING_TID,
pending_stop_tids);
// Neither trigger should have gone off yet.
ASSERT_EQ (false, DidFireDeferredNotification ());
ASSERT_EQ (0u, GetRequestedStopCount ());
// Process next event.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// The pending stop should only fire for one of the threads, the one that wasn't already stopped.
ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID_02));
@ -446,7 +403,7 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenTwoPendingOneAl
// Notify final thread has stopped.
NotifyThreadStop (PENDING_STOP_TID_02);
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// The deferred signal notification should have fired since all requirements were met.
ASSERT_EQ (true, DidFireDeferredNotification ());
@ -463,15 +420,9 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingThrea
// Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
CallAfterThreadsStop (TRIGGERING_TID,
pending_stop_tids);
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Neither trigger should have gone off yet.
ASSERT_EQ (false, DidFireDeferredNotification ());
ASSERT_EQ (0u, GetRequestedStopCount ());
// Process next event.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
// Now the request thread stop should have been called for the pending stop.
// The request thread stop should have been called for the pending stop.
ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
// But we still shouldn't have the deferred signal call go off yet. Need to wait for the death to be reported.
@ -479,12 +430,7 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingThrea
// Now report the that the thread with pending stop dies.
NotifyThreadDeath (PENDING_STOP_TID);
// Shouldn't take effect until after next processing step.
ASSERT_EQ (false, DidFireDeferredNotification ());
// Process next event.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Deferred signal notification should have fired now.
ASSERT_EQ (true, DidFireDeferredNotification ());
@ -501,13 +447,7 @@ TEST_F (ThreadStateCoordinatorTest, ExistingPendingNotificationRequiresStopFromN
// Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
CallAfterThreadsStop (TRIGGERING_TID,
pending_stop_tids);
// Neither trigger should have gone off yet.
ASSERT_EQ (false, DidFireDeferredNotification ());
ASSERT_EQ (0u, GetRequestedStopCount ());
// Process next event.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Now the request thread stop should have been called for the pending stop.
ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
@ -526,39 +466,18 @@ TEST_F (ThreadStateCoordinatorTest, ExistingPendingNotificationRequiresStopFromN
// trigger the pending notification because we should now require the
// new thread to stop too.
NotifyThreadStop (PENDING_STOP_TID);
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
ASSERT_EQ (false, DidFireDeferredNotification ());
// Now notify the new thread stopped.
NotifyThreadStop (NEW_THREAD_TID);
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Deferred signal notification should have fired now.
ASSERT_EQ (true, DidFireDeferredNotification ());
ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
}
TEST_F (ThreadStateCoordinatorTest, DeferredNotificationRemovedByResetForExec)
{
SetupKnownStoppedThread (TRIGGERING_TID);
// Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
CallAfterThreadsStop (TRIGGERING_TID,
EMPTY_THREAD_ID_SET);
// Notification trigger shouldn't go off yet.
ASSERT_EQ (false, DidFireDeferredNotification ());
// Now indicate an exec occurred, which will invalidate all state about the process and threads.
m_coordinator.ResetForExec ();
// Verify the deferred stop notification does *not* fire with the next
// process. It will handle the reset and not the deferred signaling, which
// should now be removed.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_EQ (false, DidFireDeferredNotification ());
}
TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeSignalsErrorOnUnknownThread)
{
const lldb::tid_t UNKNOWN_TID = 411;
@ -574,7 +493,7 @@ TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeSignalsErrorOnUnknownThre
ASSERT_EQ (0, resume_call_count);
// Process next event. This should fail since the coordinator doesn't know about the thread.
ASSERT_PROCESS_NEXT_EVENT_FAILS ();
ASSERT_PROCESS_EVENT_FAILED ();
ASSERT_EQ (0, resume_call_count);
}
@ -590,11 +509,7 @@ TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeCallsCallbackWhenThreadIs
m_coordinator.RequestThreadResume (NEW_THREAD_TID,
GetResumeThreadFunction(resumed_tid, resume_call_count),
GetErrorFunction ());
// Shouldn't be called yet.
ASSERT_EQ (0, resume_call_count);
// Process next event. After that, the resume request call should have fired.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
ASSERT_EQ (1, resume_call_count);
ASSERT_EQ (NEW_THREAD_TID, resumed_tid);
}
@ -611,11 +526,7 @@ TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeSkipsCallbackOnSecondResu
m_coordinator.RequestThreadResume (NEW_THREAD_TID,
GetResumeThreadFunction(resumed_tid, resume_call_count),
GetErrorFunction ());
// Shouldn't be called yet.
ASSERT_EQ (0, resume_call_count);
// Process next event. After that, the resume request call should have fired.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
ASSERT_EQ (1, resume_call_count);
ASSERT_EQ (NEW_THREAD_TID, resumed_tid);
@ -625,8 +536,8 @@ TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeSkipsCallbackOnSecondResu
GetResumeThreadFunction(resumed_tid, resume_call_count),
GetErrorFunction ());
// Process next event. This should fail since the thread should already be running.
ASSERT_PROCESS_NEXT_EVENT_FAILS ();
// This should fail since the thread should already be running.
ASSERT_PROCESS_EVENT_FAILED ();
// And the resume count should not have increased.
ASSERT_EQ (initial_resume_call_count, resume_call_count);
@ -649,7 +560,7 @@ TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeSignalsErrorOnAlreadyRunn
ASSERT_EQ (0, resume_call_count);
// Process next event. Should be an error.
ASSERT_PROCESS_NEXT_EVENT_FAILS ();
ASSERT_PROCESS_EVENT_FAILED ();
// The resume request should not have gone off because we think it is already running.
ASSERT_EQ (0, resume_call_count);
@ -680,13 +591,7 @@ TEST_F (ThreadStateCoordinatorTest, ResumedThreadAlreadyMarkedDoesNotHoldUpPendi
// Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
CallAfterThreadsStop (TRIGGERING_TID,
pending_stop_tids);
// Neither trigger should have gone off yet.
ASSERT_EQ (false, DidFireDeferredNotification ());
ASSERT_EQ (0u, GetRequestedStopCount ());
// Execute CallAfterThreadsStop.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Both TID A and TID B should have had stop requests made.
ASSERT_EQ (2u, GetRequestedStopCount ());
@ -698,7 +603,7 @@ TEST_F (ThreadStateCoordinatorTest, ResumedThreadAlreadyMarkedDoesNotHoldUpPendi
// Report thread A stopped.
NotifyThreadStop (PENDING_TID_A);
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
ASSERT_EQ (false, DidFireDeferredNotification ());
// Now report thread A is resuming. Ensure the resume is called.
@ -707,15 +612,13 @@ TEST_F (ThreadStateCoordinatorTest, ResumedThreadAlreadyMarkedDoesNotHoldUpPendi
m_coordinator.RequestThreadResume (PENDING_TID_A,
GetResumeThreadFunction(resumed_tid, resume_call_count),
GetErrorFunction ());
ASSERT_EQ (0, resume_call_count);
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
ASSERT_EQ (1, resume_call_count);
ASSERT_EQ (PENDING_TID_A, resumed_tid);
// Report thread B stopped.
NotifyThreadStop (PENDING_TID_B);
ASSERT_EQ (false, DidFireDeferredNotification ());
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// After notifying thread b stopped, we now have thread a resumed but thread b stopped.
// However, since thread a had stopped, we now have had both requirements stopped at some point.
@ -731,14 +634,9 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterRunningThreadsStopFiresWhenNoRunnin
// Notify we have a trigger that needs to be fired when all running threads have stopped.
CallAfterRunningThreadsStop (TRIGGERING_TID);
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Notification trigger shouldn't go off yet.
ASSERT_EQ (false, DidFireDeferredNotification ());
// Process next event. This will pick up the call after threads stop event.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
// Now the trigger should have fired, since there were no threads that needed to first stop.
// The trigger should have fired, since there were no threads that needed to first stop.
ASSERT_EQ (true, DidFireDeferredNotification ());
ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
@ -760,7 +658,7 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterRunningThreadsStopRequestsTwoPendin
ASSERT_EQ (false, DidFireDeferredNotification ());
// Process next event. This will pick up the call after threads stop event.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// We should have two stop requests for the two threads currently running.
ASSERT_EQ (2u, GetRequestedStopCount ());
@ -772,11 +670,11 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterRunningThreadsStopRequestsTwoPendin
// Now notify the two threads stopped.
NotifyThreadStop (PENDING_STOP_TID);
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
ASSERT_EQ (false, DidFireDeferredNotification ());
NotifyThreadStop (PENDING_STOP_TID_02);
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Now the trigger should have fired, since there were no threads that needed to first stop.
ASSERT_EQ (true, DidFireDeferredNotification ());
@ -797,7 +695,7 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterRunningThreadsStopRequestsStopTwoOt
ASSERT_EQ (false, DidFireDeferredNotification ());
// Process next event. This will pick up the call after threads stop event.
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// We should have two stop requests for the two threads currently running.
ASSERT_EQ (1u, GetRequestedStopCount ());
@ -808,7 +706,7 @@ TEST_F (ThreadStateCoordinatorTest, CallAfterRunningThreadsStopRequestsStopTwoOt
// Now notify the two threads stopped.
NotifyThreadStop (PENDING_STOP_TID);
ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
ASSERT_PROCESS_EVENT_SUCCEEDED ();
// Now the trigger should have fired, since there were no threads that needed to first stop.
ASSERT_EQ (true, DidFireDeferredNotification ());