forked from OSchip/llvm-project
[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:
parent
941b743fc9
commit
45f5cb31dc
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 ());
|
||||
|
|
Loading…
Reference in New Issue