1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

debug tests: Add thread compatibility layer and use it

Having a compatibility layer for threading operations, like the one we
have for sleep, will allow removing a lot of platform dependent code in
the test sources, therefore simplifying the tests themselves.

I changed MultiThread.cc and MultiThreadRunControl.cc as examples, but
there are other tests files that could benefit from it.

I also changed MultiThread.cc to remove all the synchronization based on
sleeps. It now works using thread barriers, which should make the tests
less prone to random failure (although I don't think these ones were
particularly flaky) and run faster (since we don't wait for nothing).

The fallouts of that change on the Java part of the tests are taken care
of as well.

Change-Id: I7be86a5727877638c0ff0a489d263ee6bbe84764
Signed-off-by: Simon Marchi <simon.marchi@polymtl.ca>
Reviewed-on: https://git.eclipse.org/r/36814
Reviewed-by: Marc Khouzam <marc.khouzam@ericsson.com>
Tested-by: Hudson CI
Tested-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
Reviewed-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
This commit is contained in:
Simon Marchi 2014-12-11 11:10:24 -05:00 committed by Marc-Andre Laperle
parent eb66a8a2ee
commit 0f94c4a067
9 changed files with 412 additions and 169 deletions

View file

@ -1,66 +1,104 @@
#ifdef __MINGW32__
#include <process.h> // MinGW has no POSIX support; use MSVC runtime
#else
#include <pthread.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "Sleep.h"
#define NUM_THREADS 5
#include "Thread.h"
#ifdef __MINGW32__
typedef unsigned int TID;
#else
typedef pthread_t TID;
#endif
static const int NUM_THREADS = 5;
struct PrintHelloArgs {
int thread_id;
ThreadBarrier *barrier_start;
ThreadBarrier *barrier_finish;
ThreadSemaphore *sem_start;
};
#ifdef __MINGW32__
unsigned int __stdcall PrintHello(void *threadid)
#else
void *PrintHello(void *threadid)
#endif
static ThreadRet THREAD_CALL_CONV PrintHello(void *void_arg)
{
long tid = (long)threadid;
printf("Hello World! It's me, thread #%ld!\n", tid);
SLEEP(2); // keep this thread around for a bit; the tests will check for its existence while the main thread is stopped at a breakpoint
struct PrintHelloArgs *args = (struct PrintHelloArgs *) void_arg;
int thread_id = args->thread_id;
ThreadBarrier *barrier_start = args->barrier_start;
ThreadBarrier *barrier_finish = args->barrier_finish;
ThreadSemaphore *sem_start = args->sem_start;
#ifdef __MINGW32__
return 0;
#else
pthread_exit(NULL);
#endif
/* Indicate to main thread that the thread is started. */
ThreadSemaphorePut(sem_start);
printf("Hello World! It's me, thread #%d!\n", thread_id);
/* Make sure that all threads are started before the breakpoint in main hits. */
ThreadBarrierWait(barrier_start);
printf("Thread %d in the middle\n", thread_id);
/* Make sure that the thread does not finish before the breakpoint in main hits. */
ThreadBarrierWait(barrier_finish);
printf("Goodbye World! From thread #%d\n", thread_id);
return THREAD_DEFAULT_RET;
}
int main(int argc, char *argv[])
{
TID threads[NUM_THREADS];
int t;
for(t=0; t < NUM_THREADS; t++)
ThreadHandle threads[NUM_THREADS];
struct PrintHelloArgs args[NUM_THREADS];
/* Used to make rendez-vous points between all threads. */
ThreadBarrier barrier_start;
ThreadBarrier barrier_finish;
/* Used to tell when a thread is started for real. */
ThreadSemaphore sem_start;
/* + 1 for main thread */
ThreadBarrierInit(&barrier_start, NUM_THREADS + 1);
ThreadBarrierInit(&barrier_finish, NUM_THREADS + 1);
ThreadSemaphoreInit(&sem_start, 0);
for (int t = 0; t < NUM_THREADS; t++)
{
printf("In main: creating thread %d\n", t);
#ifdef __MINGW32__
printf("In main: creating thread #%d\n", t);
args[t].thread_id = t;
args[t].barrier_start = &barrier_start;
args[t].barrier_finish = &barrier_finish;
args[t].sem_start = &sem_start;
int ret = StartThread(PrintHello, &args[t], &threads[t]);
if (!ret)
{
uintptr_t rc = _beginthreadex(NULL, 0, PrintHello, (void*)t, 0, &threads[t]);
SLEEP(1); // debugger should for sure receive thread creation event after stepping over this sleep; not guaranteed to happen simply stepping over the thread creation call
if (rc == 0)
{
printf("ERROR; _beginthreadex() failed. errno = %d\n", errno);
printf("Error: StartThread failed.\n");
exit(-1);
}
}
#else
{
int rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
SLEEP(1); // debugger should for sure receive thread creation event after stepping over this sleep; not guaranteed to happen simply stepping over the thread creation call
if (rc)
{
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
#endif
/* Wait until the thread has really started. */
ThreadSemaphoreTake(&sem_start);
printf("In main: thread #%d has started\n", t); /* Breakpoint LINE_MAIN_AFTER_THREAD_START */
}
/* Let the threads continue to the 'critical' section> */
ThreadBarrierWait(&barrier_start);
printf("In main thread, all threads created.\n"); /* main breakpoint here */
SLEEP(30);
/* Unlock the threads and let the program finish. */
ThreadBarrierWait(&barrier_finish);
for (int t = 0; t < NUM_THREADS; t++)
{
printf("In main, joining thread #%d\n", t);
JoinThread(threads[t], NULL);
}
ThreadBarrierDestroy(&barrier_start);
ThreadBarrierDestroy(&barrier_finish);
ThreadSemaphoreDestroy(&sem_start);
return 0;
}

View file

@ -1,73 +1,48 @@
#ifdef __MINGW32__
#include <process.h> // MinGW has no POSIX support; use MSVC runtime
#else
#include <pthread.h>
#endif
#include "Thread.h"
#include <stdio.h>
#include <stdlib.h>
#include "Sleep.h"
#ifdef __MINGW32__
typedef unsigned int TID;
#else
typedef pthread_t TID;
#endif
// Set a breakpoint here so that both threads stop.
void firstBreakpoint(long id)
{
printf("First breakpoint method from thread %ld\n", id);
}
#ifdef __MINGW32__
unsigned int __stdcall PrintHello(void *threadId)
#else
void *PrintHello(void *threadId)
#endif
{
long tId = (long)threadId;
firstBreakpoint(tId); // Stop a first time
SLEEP(1); // Keep state running a little
firstBreakpoint(tId); // Stop a second time
SLEEP(3); // Resuming past this will give us a running thread
#ifdef __MINGW32__
return 0;
#else
pthread_exit(NULL);
#endif
printf("First breakpoint method from thread %ld\n", id);
}
struct PrintHelloArgs {
int thread_id;
};
static ThreadRet THREAD_CALL_CONV PrintHello(void *void_arg) {
struct PrintHelloArgs *args = (struct PrintHelloArgs *) void_arg;
int thread_id = args->thread_id;
firstBreakpoint(thread_id); // Stop a first time
SLEEP(1); // Keep state running a little
firstBreakpoint(thread_id); // Stop a second time
SLEEP(3); // Resuming past this will give us a running thread
return THREAD_DEFAULT_RET;
}
int main(int argc, char *argv[])
{
TID thread;
int tId = 1; // Break at main will stop here: we have a single thread stopped
ThreadHandle thread;
struct PrintHelloArgs args;
args.thread_id = 1; // Break at main will stop here: we have a single thread stopped
SLEEP(1); // When resuming past here, we have a single thread running
#ifdef __MINGW32__
uintptr_t rc = _beginthreadex(NULL, 0, PrintHello, (void*)tId, 0, &thread);
if (rc == 0)
{
printf("ERROR; _beginthreadex() failed. errno = %d\n", errno);
exit(-1);
}
#else
int rc = pthread_create(&thread, NULL, PrintHello, (void *)tId);
if (rc)
{
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
#endif
firstBreakpoint(0);
int ret = StartThread(PrintHello, &args, &thread);
if (!ret) {
printf("Error: failed to start thread.\n");
return 1;
}
firstBreakpoint(0);
SLEEP(1); // Resuming past this will make this thread run, while we stop the second thread
SLEEP(3); // Resuming past this will make this thread run, while we also run the second thread

View file

@ -0,0 +1,52 @@
#ifndef Thread_h
#define Thread_h
/*
* This file provides a simple and incomplete platform compatibility
* layer for thread-related operations. This should help avoiding
* platform dependent code in tests.
*/
/* First, include type defintions necessary to define the public API. */
#ifndef __MINGW32__
#include "ThreadPthreadTypes.h"
#else // __MINGW32__
#include "ThreadWindowsTypes.h"
#endif
/*
* Type of callback functions for threads
*
* Note: if you don't care about the return value of the thread, you can
* return THREAD_DEFAULT_RET instead, which is defined according to the
* platform type.
*
* Otherwise, you'll need to have some #ifdefs in your test code in order
* to return different value types depending on the platform.
*/
typedef ThreadRet (THREAD_CALL_CONV *ThreadFunc)(void *);
/* Public API */
static int StartThread(ThreadFunc func, void *arg, ThreadHandle *handle);
static int JoinThread(ThreadHandle handle, ThreadRet *ret);
static int ThreadBarrierInit(ThreadBarrier *barrier, unsigned int count);
static int ThreadBarrierDestroy(ThreadBarrier *barrier);
static int ThreadBarrierWait(ThreadBarrier *barrier);
static int ThreadSemaphoreInit(ThreadSemaphore *sem, unsigned int count);
static int ThreadSemaphoreTake(ThreadSemaphore *sem);
static int ThreadSemaphorePut(ThreadSemaphore *sem);
static int ThreadSemaphoreDestroy(ThreadSemaphore *sem);
static int ThreadSetName(const char *name);
/* Then, include the implemention of the API. */
#ifndef __MINGW32__
#include "ThreadPthread.h"
#else // __MINGW32__
#include "ThreadWindows.h"
#endif
#endif // Thread_h

View file

@ -0,0 +1,58 @@
#ifndef THREADPTHREAD_H
#define THREADPTHREAD_H
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
/* Thread functions */
static int StartThread(ThreadFunc func, void *arg, ThreadHandle *handle) {
return pthread_create(handle, NULL, func, arg) == 0;
}
static int JoinThread(ThreadHandle handle, ThreadRet *ret)
{
return pthread_join(handle, ret) == 0;
}
/* Barrier functions */
static int ThreadBarrierInit(ThreadBarrier *barrier, unsigned int count)
{
return pthread_barrier_init(barrier, NULL, count) == 0;
}
static int ThreadBarrierDestroy(ThreadBarrier *barrier)
{
return pthread_barrier_destroy(barrier) == 0;
}
static int ThreadBarrierWait(ThreadBarrier *barrier)
{
int ret = pthread_barrier_wait(barrier);
return ret == 0 || ret == PTHREAD_BARRIER_SERIAL_THREAD;
}
static int ThreadSemaphoreInit(ThreadSemaphore *sem, unsigned int count)
{
return sem_init(sem, 0, count) == 0;
}
static int ThreadSemaphoreTake(ThreadSemaphore *sem)
{
return sem_wait(sem) == 0;
}
static int ThreadSemaphorePut(ThreadSemaphore *sem)
{
return sem_post(sem) == 0;
}
static int ThreadSemaphoreDestroy(ThreadSemaphore *sem)
{
return sem_destroy(sem) == 0;
}
#endif // THREADPTHREAD_H

View file

@ -0,0 +1,16 @@
#ifndef THREADPTHREADTYPES_H
#define THREADPTHREADTYPES_H
#include <pthread.h>
#include <semaphore.h>
/* Type definitions */
typedef pthread_t ThreadHandle;
typedef pthread_barrier_t ThreadBarrier;
typedef sem_t ThreadSemaphore;
typedef void *ThreadRet;
static void *THREAD_DEFAULT_RET = NULL;
#define THREAD_CALL_CONV
#endif // THREADPTHREADTYPES_H

View file

@ -0,0 +1,113 @@
#ifndef THREADWINDOWS_H
#define THREADWINDOWS_H
#include <process.h>
#include <assert.h>
#include <limits.h>
/* Thread functions */
static int StartThread(ThreadFunc func, void *arg, ThreadHandle *handle) {
*handle = (HANDLE) _beginthreadex(NULL, 0, func, arg, 0, NULL);
return *handle != 0;
}
static int JoinThread(ThreadHandle handle, ThreadRet *ret)
{
DWORD result = WaitForSingleObject(handle, INFINITE);
if (result != WAIT_OBJECT_0)
return 0;
BOOL result_b = GetExitCodeThread(handle, (DWORD *) ret);
if (!result)
return 0;
return 1;
}
/* Barrier functions */
struct WindowsThreadBarrier
{
LONG num_threads_to_wait;
// InterlockedIncrement requires the LONG variable to be aligned on a 4-bytes boundary.
LONG num_threads_waiting __attribute__ ((aligned (4)));
HANDLE semaphore;
};
static int ThreadBarrierInit(ThreadBarrier *barrier, unsigned int count)
{
const LONG max_threads = LONG_MAX;
barrier->semaphore = CreateSemaphore(NULL, 0, max_threads, NULL);
if (!barrier->semaphore) {
return 0;
}
barrier->num_threads_to_wait = count;
barrier->num_threads_waiting = 0;
/* Make sure that the 4-bytes alignment directive works properly. */
assert(((intptr_t) &barrier->num_threads_waiting & 0x3) == 0);
return 1;
}
static int ThreadBarrierDestroy(ThreadBarrier *barrier)
{
CloseHandle(barrier->semaphore);
return 1;
}
static int ThreadBarrierWait(ThreadBarrier *barrier)
{
LONG new_value = InterlockedIncrement(&barrier->num_threads_waiting);
if (new_value == barrier->num_threads_to_wait) {
// We are the last thread to hit the barrier, release everybody else (count - 1 threads).
BOOL ret = ReleaseSemaphore(barrier->semaphore, barrier->num_threads_to_wait - 1, NULL);
if (!ret)
return 0;
} else {
// We are not the last thread to hit the barrier, wait to get released.
DWORD ret = WaitForSingleObject(barrier->semaphore, INFINITE);
if (ret != WAIT_OBJECT_0)
return 0;
}
return 1;
}
static int ThreadSemaphoreInit(ThreadSemaphore *sem, unsigned int initial_count)
{
*sem = CreateSemaphore(NULL, initial_count, LONG_MAX, NULL);
return *sem != NULL;
}
static int ThreadSemaphoreTake(ThreadSemaphore *sem)
{
DWORD result = WaitForSingleObject(*sem, INFINITE);
return result == WAIT_OBJECT_0;
}
static int ThreadSemaphorePut(ThreadSemaphore *sem)
{
return ReleaseSemaphore(*sem, 1, NULL) != 0;
}
static int ThreadSemaphoreDestroy(ThreadSemaphore *sem)
{
return CloseHandle(*sem) != 0;
}
static int ThreadSetName(const char *name)
{
/* Not supported> */
return 0;
}
#endif // THREADWINDOWS_H

View file

@ -0,0 +1,17 @@
#ifndef THREADWINDOWSTYPES_H
#define THREADWINDOWSTYPES_H
#include <windows.h>
/* Type definitions */
struct WindowsThreadBarrier;
typedef HANDLE ThreadHandle;
typedef struct WindowsThreadBarrier ThreadBarrier;
typedef HANDLE ThreadSemaphore;
typedef unsigned ThreadRet;
static ThreadRet THREAD_DEFAULT_RET = 0;
#define THREAD_CALL_CONV __stdcall
#endif // THREADWINDOWSTYPES_H

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2014 Ericsson and others.
* Copyright (c) 2009, 2015 Ericsson and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@ -9,6 +9,7 @@
* Ericsson - initial API and implementation
* Alvaro Sanchez-Leon (Ericsson) - Make Registers View specific to a frame (Bug 323552)
* Alvaro Sanchez-Leon (Ericsson) - Allow user to edit the register groups (Bug 235747)
* Simon Marchi (Ericsson) - Adapt test code to thread platform compatibility layer.
*******************************************************************************/
package org.eclipse.cdt.tests.dsf.gdb.tests;
@ -55,7 +56,6 @@ import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType;
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
import org.eclipse.cdt.dsf.gdb.service.GDBRegisters;
import org.eclipse.cdt.dsf.mi.service.IMICommandControl;
import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext;
import org.eclipse.cdt.dsf.mi.service.MIRegisters.MIRegisterDMC;
import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent;
import org.eclipse.cdt.dsf.mi.service.command.output.MIDataListRegisterNamesInfo;
@ -368,19 +368,7 @@ public class MIRegistersTest extends BaseTestCase {
@Test
public void compareRegisterForMultipleExecutionContexts() throws Throwable {
// Run past the line that creates a thread and past the sleep that
// follows it. This is a bit tricky because the code that creates the
// thread is conditional depending on environment. Run to the printf
// before it (which is common), then do step operations over the
// non-common code (but same number of lines)
SyncUtil.runToLine(SRC_NAME, MIRunControlTest.LINE_MAIN_PRINTF);
// Because the program is about to go multi-threaded, we have to select the thread we want to keep stepping. If we don't, we will ask GDB to step
// the entire process which is not what we want. We can fetch the thread from the stopped event but we should do that before the second thread is
// created, to be sure the stopped event is for the main thread.
MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_OVER); // over the printf
SyncUtil.step(stoppedEvent.getDMContext(), StepType.STEP_OVER); // over the create-thread call
stoppedEvent = SyncUtil.step(stoppedEvent.getDMContext(), StepType.STEP_OVER, TestsPlugin.massageTimeout(2000)); // over the one second sleep
MIStoppedEvent stoppedEvent = SyncUtil.runToLine(SRC_NAME, MIRunControlTest.LINE_MAIN_ALL_THREADS_STARTED);
// Get the thread IDs
final IContainerDMContext containerDmc = DMContexts.getAncestorOfType(stoppedEvent.getDMContext(), IContainerDMContext.class);
@ -399,12 +387,8 @@ public class MIRegistersTest extends BaseTestCase {
assertNotNull(ctxts);
assertTrue(ctxts.length > 1);
int tid1 = ((IMIExecutionDMContext) ctxts[0]).getThreadId();
int tid2 = ((IMIExecutionDMContext) ctxts[1]).getThreadId();
// Get execution context to thread 2
IExecutionDMContext execDmc = SyncUtil.createExecutionContext(containerDmc, tid2);
IFrameDMContext frameDmc2 = SyncUtil.getStackFrame(execDmc, 0);
// Get stack frame for thread 2
IFrameDMContext frameDmc2 = SyncUtil.getStackFrame(ctxts[1], 0);
String thread2RegVal0 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 0);
String thread2RegVal1 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 1);
@ -413,9 +397,8 @@ public class MIRegistersTest extends BaseTestCase {
String thread2RegVal4 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 4);
String thread2RegVal5 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 5);
// Get execution context to thread 1
execDmc = SyncUtil.createExecutionContext(containerDmc, tid1);
IFrameDMContext frameDmc1 = SyncUtil.getStackFrame(execDmc, 0);
// Get stack frame for thread 1
IFrameDMContext frameDmc1 = SyncUtil.getStackFrame(ctxts[0], 0);
getModelDataForRegisterDataValue(frameDmc1, IFormattedValues.NATURAL_FORMAT, 0);

View file

@ -1,13 +1,14 @@
/*******************************************************************************
* Copyright (c) 2007, 2014 Ericsson and others.
* Copyright (c) 2007, 2015 Ericsson and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Ericsson AB - Initial implementation of Test cases
* Ericsson AB - Initial implementation of Test cases
* Simon Marchi (Ericsson) - Add and use runningOnWindows().
* Simon Marchi (Ericsson) - Adapt test code to thread platform compatibility layer.
*******************************************************************************/
package org.eclipse.cdt.tests.dsf.gdb.tests;
@ -82,11 +83,13 @@ public class MIRunControlTest extends BaseTestCase {
private IContainerDMContext fContainerDmc;
private IExecutionDMContext fThreadExecDmc;
// line numbers in MultiThread.cc
static final int LINE_MAIN_RETURN = 63;
static final int LINE_MAIN_PRINTF = 41;
static final int LINE_MAIN_BEFORE_THREAD_START = 69; // Just before StartThread
static final int LINE_MAIN_AFTER_THREAD_START = 80; // Just after StartThread, where the thread is guaranteed to be started.
static final int LINE_MAIN_ALL_THREADS_STARTED = 88; // Where all threads are guaranteed to be started.
/*
* Path to executable
*/
@ -270,23 +273,9 @@ public class MIRunControlTest extends BaseTestCase {
new ServiceEventWaitor<IStartedDMEvent>(
getGDBLaunch().getSession(),
IStartedDMEvent.class);
// Run past the line that creates a thread and past the sleep that
// follows it. This is a bit tricky because the code that creates the
// thread is conditional depending on environment. Run to the printf
// before it (which is common), then do step operations over the
// non-common code (but same number of lines)
SyncUtil.runToLine(fContainerDmc, SOURCE_NAME, LINE_MAIN_PRINTF, true);
// Because the program is about to go multi-threaded, we have to select the thread
// we want to keep stepping. If we don't, we will ask GDB to step the entire process
// which is not what we want. We can fetch the thread from the stopped event
// but we should do that before the second thread is created, to be sure the stopped
// event is for the main thread.
MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_OVER); // over the printf
SyncUtil.step(stoppedEvent.getDMContext(), StepType.STEP_OVER); // over the create-thread call
SyncUtil.step(stoppedEvent.getDMContext(), StepType.STEP_OVER, TestsPlugin.massageTimeout(2000)); // over the one second sleep
SyncUtil.runToLine(fContainerDmc, SOURCE_NAME, LINE_MAIN_AFTER_THREAD_START, true);
final IContainerDMContext containerDmc = SyncUtil.getContainerContext();
/*
@ -436,7 +425,7 @@ public class MIRunControlTest extends BaseTestCase {
/*
* Add a breakpoint
*/
SyncUtil.addBreakpoint(SOURCE_NAME + ":" + LINE_MAIN_PRINTF, false);
SyncUtil.addBreakpoint(SOURCE_NAME + ":" + LINE_MAIN_BEFORE_THREAD_START, false);
/*
* Resume till the breakpoint is hit
@ -687,27 +676,25 @@ public class MIRunControlTest extends BaseTestCase {
ISuspendedDMEvent.class);
fRunCtrl.getExecutor().submit(new Runnable() {
@Override
fRunCtrl.getExecutor().submit(new Runnable() {
@Override
public void run() {
fRunCtrl.runToLine(fThreadExecDmc, SOURCE_NAME, LINE_MAIN_RETURN, true,
new RequestMonitor(fRunCtrl.getExecutor(), null) {
@Override
protected void handleCompleted() {
wait.waitFinished(getStatus());
}
});
}
});
fRunCtrl.runToLine(fThreadExecDmc, SOURCE_NAME,
LINE_MAIN_ALL_THREADS_STARTED, true,
new RequestMonitor(fRunCtrl.getExecutor(), null) {
@Override
protected void handleCompleted() {
wait.waitFinished(getStatus());
}
});
}
});
wait.waitUntilDone(TestsPlugin.massageTimeout(1000));
Assert.assertTrue(wait.getMessage(), wait.isOK());
wait.waitReset();
// The program takes five seconds to run. There's five iterations of a
// loop that has a one second sleep in it
suspendedEventWaitor.waitForEvent(TestsPlugin.massageTimeout(10000));
final IContainerDMContext containerDmc = SyncUtil.getContainerContext();
fRunCtrl.getExecutor().submit(new Runnable() {
@ -749,13 +736,16 @@ public class MIRunControlTest extends BaseTestCase {
});
}
});
wait.waitUntilDone(TestsPlugin.massageTimeout(1000));
wait.waitUntilDone(TestsPlugin.massageTimeout(1000));
Assert.assertTrue(wait.getMessage(), wait.isOK());
wait.waitReset();
// The program takes five seconds to run. There's five iterations of a
// loop that has a one second sleep in it. Wait a second then attempt to
// interrupt the target
// Wait one second and attempt to interrupt the target.
// As of gdb 7.8, interrupting execution after a thread exit does not
// work well. This test works around it by interrupting before threads
// exit. Once the bug in gdb is fixed, we should add a test that
// interrupts after the threads exit.
// Ref: https://sourceware.org/bugzilla/show_bug.cgi?id=17627
Thread.sleep(1000);
fRunCtrl.getExecutor().submit(new Runnable() {
@Override
@ -769,6 +759,7 @@ public class MIRunControlTest extends BaseTestCase {
});
}
});
wait.waitUntilDone(TestsPlugin.massageTimeout(1000));
Assert.assertTrue(wait.getMessage(), wait.isOK());
wait.waitReset();