CTS测试框架 -- 命令调度

来源:互联网 发布:paint it black 编辑:程序博客网 时间:2024/06/05 16:14

1.CommandScheduler启动

CommandScheduler本身也是一个线程,是在Console线程启动时启动的,作为命令调度的线程,主要作用就是检查本身的CommandQueue中是不是有需要处理的command,进行调度不至于多命令或者多设备时出现混乱,以及启动真正的命令执行线程。

public void run() {    assertStarted();    try {        IDeviceManager manager = getDeviceManager();        startRemoteManager();        // 在启动的第一时间先countDown,此时main线程就会被释放,就退出了        mRunLatch.countDown();        // add a listener that will wake up scheduler when a new avail device is added        manager.addDeviceMonitor(new AvailDeviceMonitor());        // 进入了循环处理命令,直到输入了退出命令        while (!isShutdown()) {            // 每过30s起来看看是不是有需要调度的命令            mCommandProcessWait.waitAndReset(mPollTime);            checkInvocations();            processReadyCommands(manager);            postProcessReadyCommands();        }        // 退出循环之后代表程序即将退出了,进行一些清理工作        mCommandTimer.shutdown();        manager.terminateDeviceRecovery();        manager.terminateDeviceMonitor();        CLog.i("Waiting for invocation threads to complete");        waitForAllInvocationThreads();        closeRemoteClient();        if (mRemoteManager != null) {            mRemoteManager.cancelAndWait();        }        exit(manager);        cleanUp();        CLog.logAndDisplay(LogLevel.INFO, "All done");    } finally {        // Make sure that we don't quit with messages still in the buffers        System.err.flush();        System.out.flush();    }}

2.命令调度

在CommandScheduler的循环中去不断的看是否有新的命令,前面添加命令的时候,有notifyAll,以及上一个命令执行完毕等,当需要有命令被调度时都会有notifyAll去及时唤醒CommandScheduler开始干活。

public synchronized void waitAndReset(long maxWaitTime) {    waitForEvent(maxWaitTime);    reset();}// 这里是等待命令的逻辑// 大致逻辑就是每过30s就看看是不是有需要调度的命令public synchronized boolean waitForEvent(long maxWaitTime) {    if (maxWaitTime == 0) {        return waitForEvent();    }    long startTime = System.currentTimeMillis();    long remainingTime = maxWaitTime;    while (!mEventReceived && remainingTime > 0) {        try {            wait(remainingTime);        } catch (InterruptedException e) {            CLog.w("interrupted");        }        remainingTime = maxWaitTime - (System.currentTimeMillis() - startTime);    }    return mEventReceived;}

调度processReadyCommands

protected void processReadyCommands(IDeviceManager manager) {    Map<ExecutableCommand, IInvocationContext> scheduledCommandMap = new HashMap<>();    // 命令调度是同步的,但是命令执行不是    // 也就是说在调度符合的情况下,是可以开启多个命令执行的线程的    // 即可以有多条命令同时运行    synchronized (this) {        Collections.sort(mReadyCommands, new ExecutableCommandComparator());        Iterator<ExecutableCommand> cmdIter = mReadyCommands.iterator();        while (cmdIter.hasNext()) {            // 遍历mReadyCommands取出命令            ExecutableCommand cmd = cmdIter.next();            IConfiguration config = cmd.getConfiguration();            // 创建命令context            IInvocationContext context = new InvocationContext();            context.setConfigurationDescriptor(config.getConfigurationDescription());            Map<String, ITestDevice> devices =             // 申请设备            allocateDevices(config, manager);            // 如果能申请到设备,说明当前命令是可以执行的            if (!devices.isEmpty()) {                // 从ready Queue中移除                cmdIter.remove();                // 添加到正在执行的queue                mExecutingCommands.add(cmd);                context.addAllocatedDevice(devices);                // 记录正在执行的命令的map                scheduledCommandMap.put(cmd, context);                mUnscheduledWarning.remove(cmd);            } else {                // 没有空闲的设备时提示警告                if (!mUnscheduledWarning.contains(cmd)) {                    CLog.logAndDisplay(LogLevel.DEBUG, "No available device matching all the "                            + "config's requirements for cmd id %d.",                            cmd.getCommandTracker().getId());                    System.out.println(                            String.format(                                    "The command %s will be rescheduled.",                                    Arrays.toString(cmd.getCommandTracker().getArgs())));                    mUnscheduledWarning.add(cmd);                }            }        }    }    // 这里真正开始执行    for (Map.Entry<ExecutableCommand, IInvocationContext> cmdDeviceEntry : scheduledCommandMap            .entrySet()) {        ExecutableCommand cmd = cmdDeviceEntry.getKey();        // 开启命令执行的新线程去执行命令        startInvocation(cmdDeviceEntry.getValue(), cmd,                new FreeDeviceHandler(getDeviceManager()));        // 如果命令是循环模式,则再加入队列进行调度        if (cmd.isLoopMode()) {            addNewExecCommandToQueue(cmd.getCommandTracker());        }    }}

开始执行命令的线程 InvocationThread

private void startInvocation(        IInvocationContext context,        ExecutableCommand cmd,        IScheduledInvocationListener... listeners) {    initInvocation();    // Check if device is not used in another invocation.    throwIfDeviceInInvocationThread(context.getDevices());    CLog.d("starting invocation for command id %d", cmd.getCommandTracker().getId());    final String invocationName = String.format("Invocation-%s",            context.getSerials().get(0));    // 初始化InvocationThread    InvocationThread invocationThread = new InvocationThread(invocationName, context, cmd,            listeners);    logInvocationStartedEvent(cmd.getCommandTracker(), context);    // 开启命令执行的线程    invocationThread.start();    addInvocationThread(invocationThread);}

命令执行 InvocationThread.run
InvocationThread类的run方法开始执行,也就代表一个命令的真正执行就开始了,其实命令执行主要还是把前面封装好的Configuration中的组件拿出来,协作运行,主要还是执行其中最重要的test标签下的类中的模板方法,也就是说,test组件虽然是自己实现的,但是是遵循了模板接口的,命令的执行本质就是讲组件取出来协作运行,然后调用模板方法,也就走到了真正test组件的自定义要执行的方法。
在run方法中,其实做的事情不多,就是调用了初始化时创建的TestInvocation对象的invoke方法

instance.invoke(mInvocationContext, config,        new Rescheduler(mCmd.getCommandTracker()), mListeners);

组件取出运行都在TestInvocation中,这个类功能很多,承担了整个框架的大部分组件的实际运行。

3.总结

命令的调度CommandScheduler的作用主要负责命令以及设备之间的调度,因为一条命令支持多设备运行,也可以多个设备同时运行不同的命令,必须有一个调度的角色去处理,CommandScheduler通过其内部的几个队列以及获取当前的设备状态完成调度,保证每条命令都能有条不紊的执行。

原创粉丝点击