/******************************************************************************* * Copyright (c) 2011, 2015 Wind River Systems, Inc. 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: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.internal.debug.tests; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Random; import java.util.Set; import org.eclipse.tcf.protocol.IChannel; import org.eclipse.tcf.protocol.IToken; import org.eclipse.tcf.protocol.Protocol; import org.eclipse.tcf.services.IDiagnostics; import org.eclipse.tcf.services.IRunControl; import org.eclipse.tcf.services.IRunControl.RunControlContext; class RunControl { public interface DiagnosticTestDone { void testDone(String id); } private final TCFTestSuite test_suite; private final IChannel channel; private int channel_id; private final IRunControl rc_service; private final HashSet<String> suspended_ctx_ids = new HashSet<String>(); private final HashSet<IToken> get_state_cmds = new HashSet<IToken>(); private final HashMap<String,IToken> resume_cmds = new HashMap<String,IToken>(); private final HashSet<String> pending_resume_ids = new HashSet<String>(); private final HashMap<String,IRunControl.RunControlContext> ctx_map = new HashMap<String,IRunControl.RunControlContext>(); private final Random rnd = new Random(); private boolean enable_trace; private boolean sync_pending; private final IRunControl.RunControlListener listener = new IRunControl.RunControlListener() { public void contextAdded(RunControlContext[] contexts) { for (IRunControl.RunControlContext ctx : contexts) { ctx_map.put(ctx.getID(), ctx); } } public void contextChanged(RunControlContext[] contexts) { for (IRunControl.RunControlContext ctx : contexts) { RunControlContext prv = ctx_map.put(ctx.getID(), ctx); if (prv != null && prv.getProperties().get("DiagnosticTestProcess") != null && ctx.getProperties().get("DiagnosticTestProcess") == null) { test_suite.getCanceledTests().remove(ctx.getID()); ITCFTest test = test_suite.getActiveTest(channel); if (test instanceof DiagnosticTestDone) { ((DiagnosticTestDone)test).testDone(ctx.getID()); } } } } public void contextRemoved(String[] context_ids) { for (String id : context_ids) { ctx_map.remove(id); test_suite.getCanceledTests().remove(id); suspended_ctx_ids.remove(id); } } public void contextSuspended(final String id, String pc, String reason, Map<String,Object> params) { if (enable_trace) System.out.println("" + channel_id + " suspended " + id); suspended_ctx_ids.add(id); Protocol.invokeLater(new Runnable() { public void run() { resume(); } }); } public void contextResumed(String id) { if (enable_trace) System.out.println("" + channel_id + " resumed " + id); suspended_ctx_ids.remove(id); pending_resume_ids.remove(id); } public void containerSuspended(String context, String pc, String reason, Map<String, Object> params, String[] suspended_ids) { if (enable_trace) { StringBuffer bf = new StringBuffer(); for (String id : suspended_ids) { if (bf.length() > 0) bf.append(','); bf.append(id); } System.out.println("" + channel_id + " suspended " + bf); } for (String id : suspended_ids) { suspended_ctx_ids.add(id); } Protocol.invokeLater(new Runnable() { public void run() { resume(); } }); } public void containerResumed(String[] context_ids) { if (enable_trace) { StringBuffer bf = new StringBuffer(); for (String id : context_ids) { if (bf.length() > 0) bf.append(','); bf.append(id); } System.out.println("" + channel_id + " resumed " + bf); } for (String id : context_ids) { suspended_ctx_ids.remove(id); pending_resume_ids.remove(id); } } public void contextException(String context, String msg) { } }; RunControl(TCFTestSuite test_suite, IChannel channel, int channel_id) { this.test_suite = test_suite; this.channel = channel; this.channel_id = channel_id; rc_service = channel.getRemoteService(IRunControl.class); if (rc_service != null) { rc_service.addListener(listener); getState(); startTimer(); } enable_trace = System.getProperty("org.eclipse.tcf.debug.tracing.tests.runcontrol") != null; } private void getState() { get_state_cmds.add(rc_service.getChildren(null, new IRunControl.DoneGetChildren() { public void doneGetChildren(IToken token, Exception error, String[] context_ids) { get_state_cmds.remove(token); if (error != null) { exit(error); } else { for (final String id : context_ids) { get_state_cmds.add(rc_service.getChildren(id, this)); get_state_cmds.add(rc_service.getContext(id, new IRunControl.DoneGetContext() { public void doneGetContext(IToken token, Exception error, RunControlContext context) { get_state_cmds.remove(token); if (error != null) { exit(error); } else { ctx_map.put(id, context); if (context.hasState()) { get_state_cmds.add(context.getState(new IRunControl.DoneGetState() { public void doneGetState(IToken token, Exception error, boolean suspended, String pc, String reason, Map<String, Object> params) { get_state_cmds.remove(token); if (error != null) { if (ctx_map.get(id) != null) exit(new Exception( "Cannot get context state", error)); } else { if (suspended) suspended_ctx_ids.add(id); } } })); } } } })); } } } })); } private void startTimer() { Protocol.invokeLater(50, new Runnable() { public void run() { if (channel.getState() != IChannel.STATE_OPEN) return; if (test_suite.cancel) return; Protocol.invokeLater(50, this); resume(); } }); } private void resume() { Set<String> s = test_suite.getCanceledTests().keySet(); if (s.size() > 0 || suspended_ctx_ids.size() > 0) { Set<String> ids = new HashSet<String>(s); ids.addAll(suspended_ctx_ids); String[] arr = ids.toArray(new String[ids.size()]); resume(arr[rnd.nextInt(arr.length)], IRunControl.RM_RESUME, 1); } } private void exit(Throwable error) { Collection<ITCFTest> c = test_suite.getActiveTests(); ITCFTest[] arr = c.toArray(new ITCFTest[c.size()]); for (ITCFTest t : arr) test_suite.done(t, error); } IRunControl.RunControlContext getContext(String id) { return ctx_map.get(id); } boolean canResume(String id) { if (sync_pending) return false; if (get_state_cmds.size() > 0) return false; if (resume_cmds.get(id) != null) return false; if (test_suite.getCanceledTests().get(id) == null && !suspended_ctx_ids.contains(id)) return false; IRunControl.RunControlContext ctx = ctx_map.get(id); if (ctx == null) return false; String grp = ctx.getRCGroup(); if (grp != null) { for (String s : resume_cmds.keySet()) { IRunControl.RunControlContext c = ctx_map.get(s); if (c == null) return false; if (grp.equals(c.getRCGroup())) return false; } } return true; } void resume(final String id, final int mode, final int cnt) { if (!test_suite.canResume(id)) return; assert !sync_pending; sync_pending = true; Protocol.sync(new Runnable() { public void run() { sync_pending = false; if (test_suite.canResume(id)) { assert resume_cmds.get(id) == null; final String test_id = test_suite.getCanceledTests().get(id); if (test_id != null) { boolean ok = false; if (enable_trace) System.out.println("" + channel_id + " cancel " + id); if (rnd.nextInt(4) == 0) { IRunControl.RunControlContext ctx = ctx_map.get(test_id); if (ctx != null && ctx.canTerminate()) { resume_cmds.put(id, ctx.terminate(new IRunControl.DoneCommand() { public void doneCommand(IToken token, Exception error) { assert resume_cmds.get(id) == token; resume_cmds.remove(id); if (enable_trace) System.out.println("" + channel_id + " done cancel " + error); if (error != null && ctx_map.get(test_id) != null) exit(error); } })); ok = true; } } if (!ok && rnd.nextInt(4) == 0) { IRunControl.RunControlContext ctx = ctx_map.get(test_id); if (ctx != null && ctx.canDetach()) { resume_cmds.put(id, ctx.detach(new IRunControl.DoneCommand() { public void doneCommand(IToken token, Exception error) { assert resume_cmds.get(id) == token; resume_cmds.remove(id); if (enable_trace) System.out.println("" + channel_id + " done detach " + error); if (error != null && ctx_map.get(test_id) != null) exit(error); } })); ok = true; } } if (!ok) { IDiagnostics diag = channel.getRemoteService(IDiagnostics.class); resume_cmds.put(id, diag.cancelTest(test_id, new IDiagnostics.DoneCancelTest() { public void doneCancelTest(IToken token, Throwable error) { assert resume_cmds.get(id) == token; resume_cmds.remove(id); if (enable_trace) System.out.println("" + channel_id + " done cancel " + error); if (error != null && ctx_map.get(test_id) != null) exit(error); } })); } } else { final IRunControl.RunControlContext ctx = ctx_map.get(id); if (ctx != null) { pending_resume_ids.add(id); if (enable_trace) System.out.println("" + channel_id + " resume " + mode + " " + cnt + " " + id); resume_cmds.put(id, ctx.resume(mode, cnt, new IRunControl.DoneCommand() { int retry = 0; public void doneCommand(IToken token, Exception error) { assert resume_cmds.get(id) == token; resume_cmds.remove(id); if (enable_trace) System.out.println("" + channel_id + " done resume " + error); if (error != null) { if (retry == 0 && cnt > 1 && suspended_ctx_ids.contains(id) && pending_resume_ids.contains(id)) { // Older agent that does not support resume count resume_cmds.put(id, ctx.resume(mode, 1, this)); retry++; } else { pending_resume_ids.remove(id); if (suspended_ctx_ids.contains(id)) exit(error); } } else if (pending_resume_ids.contains(id)) { exit(new Exception("Missing contextResumed event")); } } })); } } } } }); } void cancel(String test_id) { for (IRunControl.RunControlContext ctx : ctx_map.values()) { if (ctx.hasState()) { if (test_id.equals(ctx.getID()) || test_id.equals(ctx.getParentID()) || test_id.equals(ctx.getCreatorID())) { String thread_id = ctx.getID(); test_suite.getCanceledTests().put(thread_id, test_id); resume(thread_id, IRunControl.RM_RESUME, 1); } } } } }