Exemple #1
0
    /// Forks and executes a test case cleanup routine asynchronously.
    ///
    /// \param test_program The container test program.
    /// \param test_case_name The name of the test case to run.
    /// \param user_config User-provided configuration variables.
    /// \param body_handle The exit handle of the test case's corresponding
    ///     body.  The cleanup will be executed in the same context.
    /// \param body_result The result of the test case's corresponding body.
    ///
    /// \return A handle for the background operation.  Used to match the result
    /// of the execution returned by wait_any() with this invocation.
    executor::exec_handle
    spawn_cleanup(const model::test_program_ptr test_program,
                  const std::string& test_case_name,
                  const config::tree& user_config,
                  const executor::exit_handle& body_handle,
                  const model::test_result& body_result)
    {
        generic.check_interrupt();

        const std::shared_ptr< scheduler::interface > interface =
            find_interface(test_program->interface_name());

        LI(F("Spawning %s:%s (cleanup)") % test_program->absolute_path() %
           test_case_name);

        const executor::exec_handle handle = generic.spawn_followup(
            run_test_cleanup(interface, test_program, test_case_name,
                             user_config),
            body_handle, cleanup_timeout);

        const exec_data_ptr data(new cleanup_exec_data(
            test_program, test_case_name, body_handle, body_result));
        all_exec_data.insert(exec_data_map::value_type(handle.pid(), data));

        return handle;
    }
Exemple #2
0
    /// Cleans up the executor state.
    void
    cleanup(void)
    {
        PRE(!cleaned);

        for (exec_data_map::const_iterator iter = all_exec_data.begin();
             iter != all_exec_data.end(); ++iter) {
            const exec_handle& pid = (*iter).first;
            const exec_data_ptr& data = (*iter).second;

            process::terminate_group(pid);
            int status;
            if (::waitpid(pid, &status, 0) == -1) {
                // Should not happen.
                LW(F("Failed to wait for PID %s") % pid);
            }

            try {
                fs::rm_r(data->control_directory);
            } catch (const fs::error& e) {
                LE(F("Failed to clean up subprocess work directory %s: %s") %
                   data->control_directory % e.what());
            }
        }
        all_exec_data.clear();

        try {
            // The following only causes the work directory to be deleted, not
            // any of its contents, so we expect this to always succeed.  This
            // *should* be sufficient because, in the loop above, we have
            // individually wiped the subdirectories of any still-unclean
            // subprocesses.
            root_work_directory->cleanup();
        } catch (const fs::error& e) {
            LE(F("Failed to clean up executor work directory %s: %s; this is "
                 "an internal error") % root_work_directory->directory()
               % e.what());
        }
        root_work_directory.reset(NULL);

        interrupts_handler->unprogram();
        interrupts_handler.reset(NULL);
    }
Exemple #3
0
    /// Finds any pending exec_datas that correspond to tests needing cleanup.
    ///
    /// \return The collection of test_exec_data objects that have their
    /// needs_cleanup property set to true.
    test_exec_data_vector
    tests_needing_cleanup(void)
    {
        test_exec_data_vector tests_data;

        for (exec_data_map::const_iterator iter = all_exec_data.begin();
             iter != all_exec_data.end(); ++iter) {
            const exec_data_ptr data = (*iter).second;

            try {
                test_exec_data* test_data = &dynamic_cast< test_exec_data& >(
                    *data.get());
                if (test_data->needs_cleanup) {
                    tests_data.push_back(test_data);
                    test_data->needs_cleanup = false;
                }
            } catch (const std::bad_cast& e) {
                // Do nothing for cleanup_exec_data objects.
            }
        }

        return tests_data;
    }
Exemple #4
0
    /// Common code to run after any of the wait calls.
    ///
    /// \param handle The exec_handle of the terminated subprocess.
    /// \param status The exit status of the terminated subprocess.
    ///
    /// \return A pointer to an object describing the waited-for subprocess.
    executor::exit_handle
    post_wait(const executor::exec_handle handle, const process::status& status)
    {
        PRE(handle == status.dead_pid());
        LI(F("Waited for subprocess with exec_handle %s") % handle);

        process::terminate_group(status.dead_pid());

        const exec_data_map::iterator iter = all_exec_data.find(handle);
        exec_data_ptr& data = (*iter).second;
        data->timer.unprogram();

        // It is tempting to assert here (and old code did) that, if the timer
        // has fired, the process has been forcibly killed by us.  This is not
        // always the case though: for short-lived processes and with very short
        // timeouts (think 1ms), it is possible for scheduling decisions to
        // allow the subprocess to finish while at the same time cause the timer
        // to fire.  So we do not assert this any longer and just rely on the
        // timer expiration to check if the process timed out or not.  If the
        // process did finish but the timer expired... oh well, we do not detect
        // this correctly but we don't care because this should not really
        // happen.

        if (!fs::exists(data->stdout_file)) {
            std::ofstream new_stdout(data->stdout_file.c_str());
        }
        if (!fs::exists(data->stderr_file)) {
            std::ofstream new_stderr(data->stderr_file.c_str());
        }

        return exit_handle(std::shared_ptr< exit_handle::impl >(
            new exit_handle::impl(
                handle,
                data->timer.fired() ? none : utils::make_optional(status),
                data->unprivileged_user,
                data->start_time, datetime::timestamp::now(),
                data->control_directory,
                data->stdout_file,
                data->stderr_file,
                data->state_owners,
                all_exec_data)));
    }