<commit_before>#pragma once
#ifndef CUDA_API_WRAPPERS_ERROR_HPP_
#define CUDA_API_WRAPPERS_ERROR_HPP_

#include "cuda/api/types.h"

#ifdef HAVE_KERNEL_TESTER_UTILS
// This wrapper exception will include a stack trace;
// also, it can take multiple arguments in the ctor which will
// be piped into a stringstream; but we won't make use of that here
// to ensure compatibility with the single-string ctor of an
// std::runtime_error
#include "util/exception.h"
using cuda_inspecific_runtime_error = util::runtime_error;
#else
#include <stdexcept>
using cuda_inspecific_runtime_error = std::runtime_error;
#endif

#include <cuda_runtime_api.h>
#include <type_traits>
#include <string>

namespace cuda {

namespace error {

// We can't just 'inherit' from status_t, unfortunately,
// so we're creating an "unrelated" enum; see also the comparison
// operators below which help us avoid the warnings we would
// get from comparing values of the two enums
enum code_t : std::underlying_type<status_t>::type {
	success                         = cudaSuccess,
	missing_configuration           = cudaErrorMissingConfiguration,
	memory_allocation               = cudaErrorMemoryAllocation,
	initialization_error            = cudaErrorInitializationError,
	launch_failure                  = cudaErrorLaunchFailure,
	prior_launch_failure            = cudaErrorPriorLaunchFailure,
	launch_timeout                  = cudaErrorLaunchTimeout,
	launch_out_of_resources         = cudaErrorLaunchOutOfResources,
	invalid_device_function         = cudaErrorInvalidDeviceFunction,
	invalid_configuration           = cudaErrorInvalidConfiguration,
	invalid_device                  = cudaErrorInvalidDevice,
	invalid_value                   = cudaErrorInvalidValue,
	invalid_pitch_value             = cudaErrorInvalidPitchValue,
	invalid_symbol                  = cudaErrorInvalidSymbol,
	map_buffer_object_failed        = cudaErrorMapBufferObjectFailed,
	unmap_buffer_object_failed      = cudaErrorUnmapBufferObjectFailed,
	invalid_host_pointer            = cudaErrorInvalidHostPointer,
	invalid_device_pointer          = cudaErrorInvalidDevicePointer,
	invalid_texture                 = cudaErrorInvalidTexture,
	invalid_texture_binding         = cudaErrorInvalidTextureBinding,
	invalid_channel_descriptor      = cudaErrorInvalidChannelDescriptor,
	invalid_memcpy_direction        = cudaErrorInvalidMemcpyDirection,
	address_of_constant             = cudaErrorAddressOfConstant,
	texture_fetch_failed            = cudaErrorTextureFetchFailed,
	texture_not_bound               = cudaErrorTextureNotBound,
	synchronization_error           = cudaErrorSynchronizationError,
	invalid_filter_setting          = cudaErrorInvalidFilterSetting,
	invalid_norm_setting            = cudaErrorInvalidNormSetting,
	mixed_device_execution          = cudaErrorMixedDeviceExecution,
	cudart_unloading                = cudaErrorCudartUnloading,
	unknown                         = cudaErrorUnknown,
	not_yet_implemented             = cudaErrorNotYetImplemented,
	memory_value_too_large          = cudaErrorMemoryValueTooLarge,
	invalid_resource_handle         = cudaErrorInvalidResourceHandle,
	not_ready                       = cudaErrorNotReady,
	insufficient_driver             = cudaErrorInsufficientDriver,
	set_on_active_process           = cudaErrorSetOnActiveProcess,
	invalid_surface                 = cudaErrorInvalidSurface,
	no_device                       = cudaErrorNoDevice,
	ecc_uncorrectable               = cudaErrorECCUncorrectable,
	shared_object_symbol_not_found  = cudaErrorSharedObjectSymbolNotFound,
	shared_object_init_failed       = cudaErrorSharedObjectInitFailed,
	unsupported_limit               = cudaErrorUnsupportedLimit,
	duplicate_variable_name         = cudaErrorDuplicateVariableName,
	duplicate_texture_name          = cudaErrorDuplicateTextureName,
	duplicate_surface_name          = cudaErrorDuplicateSurfaceName,
	devices_unavailable             = cudaErrorDevicesUnavailable,
	invalid_kernel_image            = cudaErrorInvalidKernelImage,
	no_kernel_image_for_device      = cudaErrorNoKernelImageForDevice,
	incompatible_driver_context     = cudaErrorIncompatibleDriverContext,
	peer_access_already_enabled     = cudaErrorPeerAccessAlreadyEnabled,
	peer_access_not_enabled         = cudaErrorPeerAccessNotEnabled,
	device_already_in_use           = cudaErrorDeviceAlreadyInUse,
	profiler_disabled               = cudaErrorProfilerDisabled,
	profiler_not_initialized        = cudaErrorProfilerNotInitialized,
	profiler_already_started        = cudaErrorProfilerAlreadyStarted,
	profiler_already_stopped        = cudaErrorProfilerAlreadyStopped,
	assert                          = cudaErrorAssert,
	too_many_peers                  = cudaErrorTooManyPeers,
	host_memory_already_registered  = cudaErrorHostMemoryAlreadyRegistered,
	host_memory_not_registered      = cudaErrorHostMemoryNotRegistered,
	operating_system                = cudaErrorOperatingSystem,
	peer_access_unsupported         = cudaErrorPeerAccessUnsupported,
	launch_max_depth_exceeded       = cudaErrorLaunchMaxDepthExceeded,
	launch_file_scoped_tex          = cudaErrorLaunchFileScopedTex,
	launch_file_scoped_surf         = cudaErrorLaunchFileScopedSurf,
	sync_depth_exceeded             = cudaErrorSyncDepthExceeded,
	launch_pending_count_exceeded   = cudaErrorLaunchPendingCountExceeded,
	not_permitted                   = cudaErrorNotPermitted,
	not_supported                   = cudaErrorNotSupported,
	hardware_stack_error            = cudaErrorHardwareStackError,
	illegal_instruction             = cudaErrorIllegalInstruction,
	misaligned_address              = cudaErrorMisalignedAddress,
	invalid_address_space           = cudaErrorInvalidAddressSpace,
	invalid_pc                      = cudaErrorInvalidPc,
	illegal_address                 = cudaErrorIllegalAddress,
	invalid_ptx                     = cudaErrorInvalidPtx,
	invalid_graphics_context        = cudaErrorInvalidGraphicsContext,
	nvlink_uncorrectable            = cudaErrorNvlinkUncorrectable,
	startup_failure                 = cudaErrorStartupFailure,
	api_failure_base                = cudaErrorApiFailureBase

};

bool operator==(const status_t& lhs, const code_t& rhs) { return lhs == (status_t) rhs;}
bool operator!=(const status_t& lhs, const code_t& rhs) { return lhs != (status_t) rhs;}

} // namespace error

inline bool is_success(status_t status)  { return status == error::success; }
inline bool is_failure(status_t status)  { return status != error::success; }

namespace detail {

template <typename I, bool UpperCase = false>
std::string as_hex(I x, unsigned hex_string_length = 2*sizeof(I) )
{
	static_assert(std::is_unsigned<I>::value, "only signed representations are supported");
	enum { bits_per_hex_digit = 4 }; // = log_2 of 16
	static const char* digit_characters =
		UpperCase ? "0123456789ABCDEF" : "0123456789abcdef" ;

	std::string result(hex_string_length,'0');
	for (unsigned digit_index = 0; digit_index < hex_string_length ; digit_index++)
	{
		size_t bit_offset = (hex_string_length - 1 - digit_index) * bits_per_hex_digit;
		auto hexadecimal_digit = (x >> bit_offset) & 0xF;
		result[digit_index] = digit_characters[hexadecimal_digit];
	}
    return result;
}

// TODO: Perhaps find a way to avoid the extra function, so that as_hex() can
// be called for pointer types as well? Would be easier with boost's uint<T>...
template <typename I, bool UpperCase = false>
inline std::string ptr_as_hex(const I* ptr, unsigned hex_string_length = 2*sizeof(I*) )
{
	return as_hex((size_t) ptr, hex_string_length);
}

} // namespace detail

/**
 * A (base?) class for exceptions raised by CUDA code
 */
class runtime_error : public cuda_inspecific_runtime_error {
public:
	// TODO: Constructor chaining; and perhaps allow for more construction mechanisms?
	runtime_error(cuda::status_t error_code) :
		cuda_inspecific_runtime_error(cudaGetErrorString(error_code)),
		error_code_(error_code)
	{ }
	// I wonder if I should do this the other way around
	runtime_error(cuda::status_t error_code, const std::string& what_arg) :
		cuda_inspecific_runtime_error(what_arg + ": " + cudaGetErrorString(error_code)),
		error_code_(error_code)
	{ }

	status_t error_code() const { return error_code_; }

private:
	status_t error_code_;
};

// TODO: The following could use std::optiomal arguments - which would
// prevent the need for dual versions of the functions - but we're
// not writing C++17 here

inline void throw_if_error(
	cuda::status_t error_code, std::string message) noexcept(false)
{
	if (is_failure(error_code)) { throw runtime_error(error_code, message); }
}

inline void throw_if_error(cuda::status_t error_code) noexcept(false)
{
	if (is_failure(error_code)) { throw runtime_error(error_code); }
}

namespace errors {
enum : bool {
	dont_clear = false,
	clear = true
};
} // namespace errors

inline void ensure_no_outstanding_error(
	std::string message, bool clear_any_error = errors::clear) noexcept(false)
{
	auto last_status = clear_any_error ? cudaGetLastError() : cudaPeekAtLastError();
	throw_if_error(last_status, message);
}

inline void ensure_no_outstanding_error(bool clear_any_error = errors::clear) noexcept(false)
{
	auto last_status = clear_any_error ? cudaGetLastError() : cudaPeekAtLastError();
	throw_if_error(last_status);
}

/**
 * Reset the CUDA status to cuda::error::success.
 */
inline void clear_outstanding_errors() { cudaGetLastError(); }

} // namespace cuda

#endif /* CUDA_API_WRAPPERS_ERROR_HPP_ */
<commit_msg>Added missing `inline` qualifiers for:<commit_after>#pragma once
#ifndef CUDA_API_WRAPPERS_ERROR_HPP_
#define CUDA_API_WRAPPERS_ERROR_HPP_

#include "cuda/api/types.h"

#ifdef HAVE_KERNEL_TESTER_UTILS
// This wrapper exception will include a stack trace;
// also, it can take multiple arguments in the ctor which will
// be piped into a stringstream; but we won't make use of that here
// to ensure compatibility with the single-string ctor of an
// std::runtime_error
#include "util/exception.h"
using cuda_inspecific_runtime_error = util::runtime_error;
#else
#include <stdexcept>
using cuda_inspecific_runtime_error = std::runtime_error;
#endif

#include <cuda_runtime_api.h>
#include <type_traits>
#include <string>

namespace cuda {

namespace error {

// We can't just 'inherit' from status_t, unfortunately,
// so we're creating an "unrelated" enum; see also the comparison
// operators below which help us avoid the warnings we would
// get from comparing values of the two enums
enum code_t : std::underlying_type<status_t>::type {
	success                         = cudaSuccess,
	missing_configuration           = cudaErrorMissingConfiguration,
	memory_allocation               = cudaErrorMemoryAllocation,
	initialization_error            = cudaErrorInitializationError,
	launch_failure                  = cudaErrorLaunchFailure,
	prior_launch_failure            = cudaErrorPriorLaunchFailure,
	launch_timeout                  = cudaErrorLaunchTimeout,
	launch_out_of_resources         = cudaErrorLaunchOutOfResources,
	invalid_device_function         = cudaErrorInvalidDeviceFunction,
	invalid_configuration           = cudaErrorInvalidConfiguration,
	invalid_device                  = cudaErrorInvalidDevice,
	invalid_value                   = cudaErrorInvalidValue,
	invalid_pitch_value             = cudaErrorInvalidPitchValue,
	invalid_symbol                  = cudaErrorInvalidSymbol,
	map_buffer_object_failed        = cudaErrorMapBufferObjectFailed,
	unmap_buffer_object_failed      = cudaErrorUnmapBufferObjectFailed,
	invalid_host_pointer            = cudaErrorInvalidHostPointer,
	invalid_device_pointer          = cudaErrorInvalidDevicePointer,
	invalid_texture                 = cudaErrorInvalidTexture,
	invalid_texture_binding         = cudaErrorInvalidTextureBinding,
	invalid_channel_descriptor      = cudaErrorInvalidChannelDescriptor,
	invalid_memcpy_direction        = cudaErrorInvalidMemcpyDirection,
	address_of_constant             = cudaErrorAddressOfConstant,
	texture_fetch_failed            = cudaErrorTextureFetchFailed,
	texture_not_bound               = cudaErrorTextureNotBound,
	synchronization_error           = cudaErrorSynchronizationError,
	invalid_filter_setting          = cudaErrorInvalidFilterSetting,
	invalid_norm_setting            = cudaErrorInvalidNormSetting,
	mixed_device_execution          = cudaErrorMixedDeviceExecution,
	cudart_unloading                = cudaErrorCudartUnloading,
	unknown                         = cudaErrorUnknown,
	not_yet_implemented             = cudaErrorNotYetImplemented,
	memory_value_too_large          = cudaErrorMemoryValueTooLarge,
	invalid_resource_handle         = cudaErrorInvalidResourceHandle,
	not_ready                       = cudaErrorNotReady,
	insufficient_driver             = cudaErrorInsufficientDriver,
	set_on_active_process           = cudaErrorSetOnActiveProcess,
	invalid_surface                 = cudaErrorInvalidSurface,
	no_device                       = cudaErrorNoDevice,
	ecc_uncorrectable               = cudaErrorECCUncorrectable,
	shared_object_symbol_not_found  = cudaErrorSharedObjectSymbolNotFound,
	shared_object_init_failed       = cudaErrorSharedObjectInitFailed,
	unsupported_limit               = cudaErrorUnsupportedLimit,
	duplicate_variable_name         = cudaErrorDuplicateVariableName,
	duplicate_texture_name          = cudaErrorDuplicateTextureName,
	duplicate_surface_name          = cudaErrorDuplicateSurfaceName,
	devices_unavailable             = cudaErrorDevicesUnavailable,
	invalid_kernel_image            = cudaErrorInvalidKernelImage,
	no_kernel_image_for_device      = cudaErrorNoKernelImageForDevice,
	incompatible_driver_context     = cudaErrorIncompatibleDriverContext,
	peer_access_already_enabled     = cudaErrorPeerAccessAlreadyEnabled,
	peer_access_not_enabled         = cudaErrorPeerAccessNotEnabled,
	device_already_in_use           = cudaErrorDeviceAlreadyInUse,
	profiler_disabled               = cudaErrorProfilerDisabled,
	profiler_not_initialized        = cudaErrorProfilerNotInitialized,
	profiler_already_started        = cudaErrorProfilerAlreadyStarted,
	profiler_already_stopped        = cudaErrorProfilerAlreadyStopped,
	assert                          = cudaErrorAssert,
	too_many_peers                  = cudaErrorTooManyPeers,
	host_memory_already_registered  = cudaErrorHostMemoryAlreadyRegistered,
	host_memory_not_registered      = cudaErrorHostMemoryNotRegistered,
	operating_system                = cudaErrorOperatingSystem,
	peer_access_unsupported         = cudaErrorPeerAccessUnsupported,
	launch_max_depth_exceeded       = cudaErrorLaunchMaxDepthExceeded,
	launch_file_scoped_tex          = cudaErrorLaunchFileScopedTex,
	launch_file_scoped_surf         = cudaErrorLaunchFileScopedSurf,
	sync_depth_exceeded             = cudaErrorSyncDepthExceeded,
	launch_pending_count_exceeded   = cudaErrorLaunchPendingCountExceeded,
	not_permitted                   = cudaErrorNotPermitted,
	not_supported                   = cudaErrorNotSupported,
	hardware_stack_error            = cudaErrorHardwareStackError,
	illegal_instruction             = cudaErrorIllegalInstruction,
	misaligned_address              = cudaErrorMisalignedAddress,
	invalid_address_space           = cudaErrorInvalidAddressSpace,
	invalid_pc                      = cudaErrorInvalidPc,
	illegal_address                 = cudaErrorIllegalAddress,
	invalid_ptx                     = cudaErrorInvalidPtx,
	invalid_graphics_context        = cudaErrorInvalidGraphicsContext,
	nvlink_uncorrectable            = cudaErrorNvlinkUncorrectable,
	startup_failure                 = cudaErrorStartupFailure,
	api_failure_base                = cudaErrorApiFailureBase

};

inline bool operator==(const status_t& lhs, const code_t& rhs) { return lhs == (status_t) rhs;}
inline bool operator!=(const status_t& lhs, const code_t& rhs) { return lhs != (status_t) rhs;}

} // namespace error

inline bool is_success(status_t status)  { return status == error::success; }
inline bool is_failure(status_t status)  { return status != error::success; }

namespace detail {

template <typename I, bool UpperCase = false>
std::string as_hex(I x, unsigned hex_string_length = 2*sizeof(I) )
{
	static_assert(std::is_unsigned<I>::value, "only signed representations are supported");
	enum { bits_per_hex_digit = 4 }; // = log_2 of 16
	static const char* digit_characters =
		UpperCase ? "0123456789ABCDEF" : "0123456789abcdef" ;

	std::string result(hex_string_length,'0');
	for (unsigned digit_index = 0; digit_index < hex_string_length ; digit_index++)
	{
		size_t bit_offset = (hex_string_length - 1 - digit_index) * bits_per_hex_digit;
		auto hexadecimal_digit = (x >> bit_offset) & 0xF;
		result[digit_index] = digit_characters[hexadecimal_digit];
	}
    return result;
}

// TODO: Perhaps find a way to avoid the extra function, so that as_hex() can
// be called for pointer types as well? Would be easier with boost's uint<T>...
template <typename I, bool UpperCase = false>
inline std::string ptr_as_hex(const I* ptr, unsigned hex_string_length = 2*sizeof(I*) )
{
	return as_hex((size_t) ptr, hex_string_length);
}

} // namespace detail

/**
 * A (base?) class for exceptions raised by CUDA code
 */
class runtime_error : public cuda_inspecific_runtime_error {
public:
	// TODO: Constructor chaining; and perhaps allow for more construction mechanisms?
	runtime_error(cuda::status_t error_code) :
		cuda_inspecific_runtime_error(cudaGetErrorString(error_code)),
		error_code_(error_code)
	{ }
	// I wonder if I should do this the other way around
	runtime_error(cuda::status_t error_code, const std::string& what_arg) :
		cuda_inspecific_runtime_error(what_arg + ": " + cudaGetErrorString(error_code)),
		error_code_(error_code)
	{ }

	status_t error_code() const { return error_code_; }

private:
	status_t error_code_;
};

// TODO: The following could use std::optiomal arguments - which would
// prevent the need for dual versions of the functions - but we're
// not writing C++17 here

inline void throw_if_error(
	cuda::status_t error_code, std::string message) noexcept(false)
{
	if (is_failure(error_code)) { throw runtime_error(error_code, message); }
}

inline void throw_if_error(cuda::status_t error_code) noexcept(false)
{
	if (is_failure(error_code)) { throw runtime_error(error_code); }
}

namespace errors {
enum : bool {
	dont_clear = false,
	clear = true
};
} // namespace errors

inline void ensure_no_outstanding_error(
	std::string message, bool clear_any_error = errors::clear) noexcept(false)
{
	auto last_status = clear_any_error ? cudaGetLastError() : cudaPeekAtLastError();
	throw_if_error(last_status, message);
}

inline void ensure_no_outstanding_error(bool clear_any_error = errors::clear) noexcept(false)
{
	auto last_status = clear_any_error ? cudaGetLastError() : cudaPeekAtLastError();
	throw_if_error(last_status);
}

/**
 * Reset the CUDA status to cuda::error::success.
 */
inline void clear_outstanding_errors() { cudaGetLastError(); }

} // namespace cuda

#endif /* CUDA_API_WRAPPERS_ERROR_HPP_ */
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <signal.h> // For strsignal.
#include <stdio.h>  // For freopen.

#include <sys/wait.h> // For wait (and associated macros).

#include <string>

#include <stout/check.hpp>
#include <stout/os.hpp>
#include <stout/path.hpp>
#include <stout/stringify.hpp>
#include <stout/strings.hpp>
#include <stout/uri.hpp>

#include <stout/os/realpath.hpp>

#include <stout/os/constants.hpp>

#include "common/status_utils.hpp"

#include "mesos/mesos.hpp"

#include "tests/environment.hpp"
#include "tests/flags.hpp"
#include "tests/mesos.hpp"
#include "tests/script.hpp"
#include "tests/utils.hpp"

using std::string;

namespace mesos {
namespace internal {
namespace tests {

void execute(const string& script)
{
  // Create a temporary directory for the test.
  Try<string> directory = environment->mkdtemp();

  CHECK_SOME(directory) << "Failed to create temporary directory";

  if (flags.verbose) {
    std::cerr << "Using temporary directory '"
              << directory.get() << "'" << std::endl;
  }

  // Determine the path for the script.
  Result<string> path = os::realpath(getTestScriptPath(script));

  if (!path.isSome()) {
    FAIL() << "Failed to locate script "
           << script << ": "
           << (path.isError() ? path.error() : "No such file or directory");
  }

  // Fork a process to change directory and run the test.
  pid_t pid;
  if ((pid = fork()) == -1) {
    FAIL() << "Failed to fork to launch script";
  }

  if (pid > 0) {
    // In parent process.
    int status;
    while (wait(&status) != pid || WIFSTOPPED(status));
    CHECK(WIFEXITED(status) || WIFSIGNALED(status))
      << "Unexpected wait status " << status;

    if (!WSUCCEEDED(status)) {
      FAIL() << script << " " << WSTRINGIFY(status);
    }
  } else {
    // In child process. DO NOT USE GLOG!

    // Start by cd'ing into the temporary directory.
    Try<Nothing> chdir = os::chdir(directory.get());
    if (chdir.isError()) {
      std::cerr << "Failed to chdir to '" << directory.get() << "': "
                << chdir.error() << std::endl;
      abort();
    }

    // Redirect output to /dev/null unless the test is verbose.
    if (!flags.verbose) {
      if (freopen(os::DEV_NULL, "w", stdout) == nullptr ||
          freopen(os::DEV_NULL, "w", stderr) == nullptr) {
        std::cerr << "Failed to redirect stdout/stderr to /dev/null:"
                  << os::strerror(errno) << std::endl;
        abort();
      }
    }

    // Set up the environment for executing the script. We might be running from
    // the Mesos source tree or from an installed version of the tests. In the
    // latter case, all of the variables below are swizzled to point to the
    // installed locations, except MESOS_SOURCE_DIR. Scripts that make use of
    // MESOS_SOURCE_DIR are expected to gracefully degrade if the Mesos source
    // is no longer present.
    os::setenv("MESOS_BUILD_DIR", flags.build_dir);
    os::setenv("MESOS_HELPER_DIR", getTestHelperDir());
    os::setenv("MESOS_LAUNCHER_DIR", getLauncherDir());
    os::setenv("MESOS_SBIN_DIR", getSbinDir());
    os::setenv("MESOS_SOURCE_DIR", flags.source_dir);
    os::setenv("MESOS_WEBUI_DIR", getWebUIDir());

    // Enable replicated log based registry.
    os::setenv("MESOS_REGISTRY", "replicated_log");

    // Create test credentials.
    const string& credentials =
      DEFAULT_CREDENTIAL.principal() + " " + DEFAULT_CREDENTIAL.secret();

    const string& credentialsPath =
      path::join(directory.get(), "credentials");

    CHECK_SOME(os::write(credentialsPath, credentials))
      << "Failed to write credentials to '" << credentialsPath << "'";

    os::setenv("MESOS_CREDENTIALS", uri::from_path(credentialsPath));

    // Enable framework authentication on the master.
    os::setenv("MESOS_AUTHENTICATE_FRAMEWORKS", "true");

    // Enable authentication on the test framework.
    os::setenv("MESOS_EXAMPLE_AUTHENTICATE", "true");

    // We set test credentials here for example frameworks to use.
    os::setenv("MESOS_EXAMPLE_PRINCIPAL", DEFAULT_CREDENTIAL.principal());
    os::setenv("MESOS_EXAMPLE_SECRET", DEFAULT_CREDENTIAL.secret());

    // Create test ACLs.
    ACLs acls;
    acls.set_permissive(false);

    mesos::ACL::RunTask* run = acls.add_run_tasks();
    run->mutable_principals()->add_values(DEFAULT_CREDENTIAL.principal());

    Result<string> user = os::user();
    CHECK_SOME(user) << "Failed to get current user name";
    run->mutable_users()->add_values(user.get());

    mesos::ACL::RegisterFramework* register_ = acls.add_register_frameworks();
    register_->mutable_principals()->add_values(DEFAULT_CREDENTIAL.principal());
    register_->mutable_roles()->add_values("*");

    // Allow agents with any principal or no principal to register.
    // Currently the agents in the example tests don't have authentication
    // enabled so the agent's principal would be none.
    // TODO(xujyan): Enable agent authN and authZ by default in example tests.
    mesos::ACL::RegisterAgent* registerAgent = acls.add_register_agents();
    registerAgent->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
    registerAgent->mutable_agents()->set_type(mesos::ACL::Entity::ANY);

    const string& aclsPath = path::join(directory.get(), "acls");

    CHECK_SOME(os::write(aclsPath, stringify(JSON::protobuf(acls))))
      << "Failed to write ACLs to '" << aclsPath << "'";

    os::setenv("MESOS_ACLS", uri::from_path(aclsPath));

    // Now execute the script.
    execl(path->c_str(), path->c_str(), (char*) nullptr);

    std::cerr << "Failed to execute '" << script << "': "
              << os::strerror(errno) << std::endl;
    abort();
  }
}

} // namespace tests {
} // namespace internal {
} // namespace mesos {
<commit_msg>Replaced a set of single '*' role in SCRIPT_TEST ACL with ANY.<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <signal.h> // For strsignal.
#include <stdio.h>  // For freopen.

#include <sys/wait.h> // For wait (and associated macros).

#include <string>

#include <stout/check.hpp>
#include <stout/os.hpp>
#include <stout/path.hpp>
#include <stout/stringify.hpp>
#include <stout/strings.hpp>
#include <stout/uri.hpp>

#include <stout/os/realpath.hpp>

#include <stout/os/constants.hpp>

#include "common/status_utils.hpp"

#include "mesos/mesos.hpp"

#include "tests/environment.hpp"
#include "tests/flags.hpp"
#include "tests/mesos.hpp"
#include "tests/script.hpp"
#include "tests/utils.hpp"

using std::string;

namespace mesos {
namespace internal {
namespace tests {

void execute(const string& script)
{
  // Create a temporary directory for the test.
  Try<string> directory = environment->mkdtemp();

  CHECK_SOME(directory) << "Failed to create temporary directory";

  if (flags.verbose) {
    std::cerr << "Using temporary directory '"
              << directory.get() << "'" << std::endl;
  }

  // Determine the path for the script.
  Result<string> path = os::realpath(getTestScriptPath(script));

  if (!path.isSome()) {
    FAIL() << "Failed to locate script "
           << script << ": "
           << (path.isError() ? path.error() : "No such file or directory");
  }

  // Fork a process to change directory and run the test.
  pid_t pid;
  if ((pid = fork()) == -1) {
    FAIL() << "Failed to fork to launch script";
  }

  if (pid > 0) {
    // In parent process.
    int status;
    while (wait(&status) != pid || WIFSTOPPED(status));
    CHECK(WIFEXITED(status) || WIFSIGNALED(status))
      << "Unexpected wait status " << status;

    if (!WSUCCEEDED(status)) {
      FAIL() << script << " " << WSTRINGIFY(status);
    }
  } else {
    // In child process. DO NOT USE GLOG!

    // Start by cd'ing into the temporary directory.
    Try<Nothing> chdir = os::chdir(directory.get());
    if (chdir.isError()) {
      std::cerr << "Failed to chdir to '" << directory.get() << "': "
                << chdir.error() << std::endl;
      abort();
    }

    // Redirect output to /dev/null unless the test is verbose.
    if (!flags.verbose) {
      if (freopen(os::DEV_NULL, "w", stdout) == nullptr ||
          freopen(os::DEV_NULL, "w", stderr) == nullptr) {
        std::cerr << "Failed to redirect stdout/stderr to /dev/null:"
                  << os::strerror(errno) << std::endl;
        abort();
      }
    }

    // Set up the environment for executing the script. We might be running from
    // the Mesos source tree or from an installed version of the tests. In the
    // latter case, all of the variables below are swizzled to point to the
    // installed locations, except MESOS_SOURCE_DIR. Scripts that make use of
    // MESOS_SOURCE_DIR are expected to gracefully degrade if the Mesos source
    // is no longer present.
    os::setenv("MESOS_BUILD_DIR", flags.build_dir);
    os::setenv("MESOS_HELPER_DIR", getTestHelperDir());
    os::setenv("MESOS_LAUNCHER_DIR", getLauncherDir());
    os::setenv("MESOS_SBIN_DIR", getSbinDir());
    os::setenv("MESOS_SOURCE_DIR", flags.source_dir);
    os::setenv("MESOS_WEBUI_DIR", getWebUIDir());

    // Enable replicated log based registry.
    os::setenv("MESOS_REGISTRY", "replicated_log");

    // Create test credentials.
    const string& credentials =
      DEFAULT_CREDENTIAL.principal() + " " + DEFAULT_CREDENTIAL.secret();

    const string& credentialsPath =
      path::join(directory.get(), "credentials");

    CHECK_SOME(os::write(credentialsPath, credentials))
      << "Failed to write credentials to '" << credentialsPath << "'";

    os::setenv("MESOS_CREDENTIALS", uri::from_path(credentialsPath));

    // Enable framework authentication on the master.
    os::setenv("MESOS_AUTHENTICATE_FRAMEWORKS", "true");

    // Enable authentication on the test framework.
    os::setenv("MESOS_EXAMPLE_AUTHENTICATE", "true");

    // We set test credentials here for example frameworks to use.
    os::setenv("MESOS_EXAMPLE_PRINCIPAL", DEFAULT_CREDENTIAL.principal());
    os::setenv("MESOS_EXAMPLE_SECRET", DEFAULT_CREDENTIAL.secret());

    // Create test ACLs.
    ACLs acls;
    acls.set_permissive(false);

    mesos::ACL::RunTask* run = acls.add_run_tasks();
    run->mutable_principals()->add_values(DEFAULT_CREDENTIAL.principal());

    Result<string> user = os::user();
    CHECK_SOME(user) << "Failed to get current user name";
    run->mutable_users()->add_values(user.get());

    mesos::ACL::RegisterFramework* register_ = acls.add_register_frameworks();
    register_->mutable_principals()->add_values(DEFAULT_CREDENTIAL.principal());
    register_->mutable_roles()->set_type(mesos::ACL::Entity::ANY);

    // Allow agents with any principal or no principal to register.
    // Currently the agents in the example tests don't have authentication
    // enabled so the agent's principal would be none.
    // TODO(xujyan): Enable agent authN and authZ by default in example tests.
    mesos::ACL::RegisterAgent* registerAgent = acls.add_register_agents();
    registerAgent->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
    registerAgent->mutable_agents()->set_type(mesos::ACL::Entity::ANY);

    const string& aclsPath = path::join(directory.get(), "acls");

    CHECK_SOME(os::write(aclsPath, stringify(JSON::protobuf(acls))))
      << "Failed to write ACLs to '" << aclsPath << "'";

    os::setenv("MESOS_ACLS", uri::from_path(aclsPath));

    // Now execute the script.
    execl(path->c_str(), path->c_str(), (char*) nullptr);

    std::cerr << "Failed to execute '" << script << "': "
              << os::strerror(errno) << std::endl;
    abort();
  }
}

} // namespace tests {
} // namespace internal {
} // namespace mesos {
<|endoftext|>


<commit_before>#include <bind/binder.hh>

namespace bind
{
    Binder::Binder()
        : scope_map_(misc::ScopedMap<std::string, ast::Ast*>(nullptr))
        , declaration_(false)
        , in_class_(false)
    {}

    Binder::~Binder()
    {}

    misc::Error& Binder::error_get()
    {
        return error_;
    }

    void Binder::operator()(ast::WhileStmt& ast)
    {
        loop_stack_.push(&ast);

        scope_map_.scope_begin();

        // Parser already avoid break/continue in condition
        ast.cond_get()->accept(*this);
        ast.loop_get()->accept(*this);

        scope_map_.scope_end();

        loop_stack_.pop();
    }

    void Binder::operator()(ast::BreakStmt& ast)
    {
        if (loop_stack_.empty())
            error_ << misc::Error::BIND
                   << ast.location_get() << ": break outside any loop"
                   << std::endl;
        else
            ast.in_loop_set(loop_stack_.top());
    }

    void Binder::operator()(ast::ContinueStmt& ast)
    {
        if (loop_stack_.empty())
            error_ << misc::Error::BIND
                   << ast.location_get() << ": continue outside any loop"
                   << std::endl;
        else
            ast.in_loop_set(loop_stack_.top());
    }

    void Binder::operator()(ast::ClassDec& ast)
    {
        bool old = in_class_;

        in_class_ = true;
        ast.def_get()->accept(*this);
        in_class_ = old;
    }

    void Binder::operator()(ast::FunctionDec& s)
    {
        if (!in_class_)
            scope_map_.add(s.name_get(), &s);

        scope_map_.scope_begin();

        if (s.args_get())
        {
            for (auto arg : s.args_get()->list_get())
            {
                ast::IdVar* var = dynamic_cast<ast::IdVar*> (arg);

                if (var)
                    scope_map_.add(var->id_get(), var);
                else
                {
                    declaration_ = true;
                    arg->accept(*this);
                    declaration_ = false;
                }
            }
        }

        s.body_get()->accept(*this);

        scope_map_.scope_end();
    }

    void Binder::operator()(ast::FunctionVar& ast)
    {
        ast::IdVar* v = dynamic_cast<ast::IdVar*> (ast.var_get());

        if (v)
        {
            ast::FunctionDec* d = nullptr;

            d = dynamic_cast<ast::FunctionDec*> (scope_map_.get(v->id_get()));

            if (d)
                ast.def_set(d);
            else if (!builtin::BuiltinLibrary::instance().is_builtin(v->id_get()))
            {
                error_ << misc::Error::BIND
                       << ast.location_get() << ": undeclared function "
                       << v->id_get() << std::endl;
            }
        }
        else
            assert(false
                   && "Function call from function result not yet supported");

        if (ast.params_get())
            ast.params_get()->accept(*this);
    }

    void Binder::operator()(ast::StarExpr& ast)
    {
        ast::IdVar* var = dynamic_cast<ast::IdVar*> (ast.expr_get());

        if (!var)
            error_ << misc::Error::BIND
                   << ast.location_get() << ": Invalid double star expr "
                   << var->id_get() << std::endl;
        else if (declaration_)
            scope_map_.add(var->id_get(), &ast);
        else if (!scope_map_.get(var->id_get()))
            error_ << misc::Error::BIND
                   << ast.location_get() << ": unknown identifier "
                   << var->id_get() << std::endl;
        else
        {
            ast.def_set(scope_map_.get(var->id_get()));
            var->def_set(scope_map_.get(var->id_get()));
        }
    }

    void Binder::operator()(ast::DoubleStarExpr& ast)
    {
        ast::IdVar* var = dynamic_cast<ast::IdVar*> (ast.expr_get());

        if (!var)
            error_ << misc::Error::BIND
                   << ast.location_get() << ": Invalid double star expr "
                   << var->id_get() << std::endl;
        else if (declaration_)
            scope_map_.add(var->id_get(), &ast);
        else if (!scope_map_.get(var->id_get()))
            error_ << misc::Error::BIND
                   << ast.location_get() << ": unknown identifier "
                   << var->id_get() << std::endl;
        else
        {
            ast.def_set(scope_map_.get(var->id_get()));
            var->def_set(scope_map_.get(var->id_get()));
        }
    }

    void Binder::operator()(ast::AssignExpr& e)
    {
        e.rvalue_get()->accept(*this);

        ast::IdVar* var = dynamic_cast<ast::IdVar*> (e.lvalue_get());

        if (!var)
            e.lvalue_get()->accept(*this);
        else
        {
            if (!scope_map_.get(var->id_get()))
                scope_map_.add(var->id_get(), &e);
            else
            {
                var->def_set(scope_map_.get(var->id_get()));
                e.def_set(scope_map_.get(var->id_get()));
            }
        }
    }

    void Binder::operator()(ast::IdVar& ast)
    {
        if (in_class_ && ast.id_get() == "self")
            return;

        if (!scope_map_.get(ast.id_get()))
            error_ << misc::Error::BIND
                   << ast.location_get() << ": unknown identifier "
                   << ast.id_get() << std::endl;
        else
            ast.def_set(scope_map_.get(ast.id_get()));
    }
} // namespace bind
<commit_msg>[BIND] Correct binding of constructors<commit_after>#include <bind/binder.hh>

namespace bind
{
    Binder::Binder()
        : scope_map_(misc::ScopedMap<std::string, ast::Ast*>(nullptr))
        , declaration_(false)
        , in_class_(false)
    {}

    Binder::~Binder()
    {}

    misc::Error& Binder::error_get()
    {
        return error_;
    }

    void Binder::operator()(ast::WhileStmt& ast)
    {
        loop_stack_.push(&ast);

        scope_map_.scope_begin();

        // Parser already avoid break/continue in condition
        ast.cond_get()->accept(*this);
        ast.loop_get()->accept(*this);

        scope_map_.scope_end();

        loop_stack_.pop();
    }

    void Binder::operator()(ast::BreakStmt& ast)
    {
        if (loop_stack_.empty())
            error_ << misc::Error::BIND
                   << ast.location_get() << ": break outside any loop"
                   << std::endl;
        else
            ast.in_loop_set(loop_stack_.top());
    }

    void Binder::operator()(ast::ContinueStmt& ast)
    {
        if (loop_stack_.empty())
            error_ << misc::Error::BIND
                   << ast.location_get() << ": continue outside any loop"
                   << std::endl;
        else
            ast.in_loop_set(loop_stack_.top());
    }

    void Binder::operator()(ast::ClassDec& ast)
    {
        bool old = in_class_;

        scope_map_.add(ast.name_get(), &ast);

        in_class_ = true;
        ast.def_get()->accept(*this);
        in_class_ = old;
    }

    void Binder::operator()(ast::FunctionDec& s)
    {
        if (!in_class_)
            scope_map_.add(s.name_get(), &s);

        scope_map_.scope_begin();

        if (s.args_get())
        {
            for (auto arg : s.args_get()->list_get())
            {
                ast::IdVar* var = dynamic_cast<ast::IdVar*> (arg);

                if (var)
                    scope_map_.add(var->id_get(), var);
                else
                {
                    declaration_ = true;
                    arg->accept(*this);
                    declaration_ = false;
                }
            }
        }

        s.body_get()->accept(*this);

        scope_map_.scope_end();
    }

    void Binder::operator()(ast::FunctionVar& ast)
    {
        ast::IdVar* v = dynamic_cast<ast::IdVar*> (ast.var_get());

        if (v)
        {
            ast::FunctionDec* d = nullptr;

            d = dynamic_cast<ast::FunctionDec*> (scope_map_.get(v->id_get()));

            if (d)
                ast.def_set(d);
            else if (!builtin::BuiltinLibrary::instance().is_builtin(v->id_get()))
            {
                // Check if the name match a class dec
                ast::ClassDec* c = dynamic_cast<ast::ClassDec*> (scope_map_.get(v->id_get()));

                if (!c)
                    error_ << misc::Error::BIND
                           << ast.location_get() << ": undeclared function "
                           << v->id_get() << std::endl;
            }
        }
        else
            assert(false
                   && "Function call from function result not yet supported");

        if (ast.params_get())
            ast.params_get()->accept(*this);
    }

    void Binder::operator()(ast::StarExpr& ast)
    {
        ast::IdVar* var = dynamic_cast<ast::IdVar*> (ast.expr_get());

        if (!var)
            error_ << misc::Error::BIND
                   << ast.location_get() << ": Invalid double star expr "
                   << var->id_get() << std::endl;
        else if (declaration_)
            scope_map_.add(var->id_get(), &ast);
        else if (!scope_map_.get(var->id_get()))
            error_ << misc::Error::BIND
                   << ast.location_get() << ": unknown identifier "
                   << var->id_get() << std::endl;
        else
        {
            ast.def_set(scope_map_.get(var->id_get()));
            var->def_set(scope_map_.get(var->id_get()));
        }
    }

    void Binder::operator()(ast::DoubleStarExpr& ast)
    {
        ast::IdVar* var = dynamic_cast<ast::IdVar*> (ast.expr_get());

        if (!var)
            error_ << misc::Error::BIND
                   << ast.location_get() << ": Invalid double star expr "
                   << var->id_get() << std::endl;
        else if (declaration_)
            scope_map_.add(var->id_get(), &ast);
        else if (!scope_map_.get(var->id_get()))
            error_ << misc::Error::BIND
                   << ast.location_get() << ": unknown identifier "
                   << var->id_get() << std::endl;
        else
        {
            ast.def_set(scope_map_.get(var->id_get()));
            var->def_set(scope_map_.get(var->id_get()));
        }
    }

    void Binder::operator()(ast::AssignExpr& e)
    {
        e.rvalue_get()->accept(*this);

        ast::IdVar* var = dynamic_cast<ast::IdVar*> (e.lvalue_get());

        if (!var)
            e.lvalue_get()->accept(*this);
        else
        {
            if (!scope_map_.get(var->id_get()))
                scope_map_.add(var->id_get(), &e);
            else
            {
                var->def_set(scope_map_.get(var->id_get()));
                e.def_set(scope_map_.get(var->id_get()));
            }
        }
    }

    void Binder::operator()(ast::IdVar& ast)
    {
        if (in_class_ && ast.id_get() == "self")
            return;

        if (!scope_map_.get(ast.id_get()))
            error_ << misc::Error::BIND
                   << ast.location_get() << ": unknown identifier "
                   << ast.id_get() << std::endl;
        else
        {
            ast.def_set(scope_map_.get(ast.id_get()));
        }
    }
} // namespace bind
<|endoftext|>


<commit_before>/*
 * A queue that manages work for worker threads.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "thread_queue.hxx"
#include "thread_job.hxx"
#include "notify.hxx"

#include <glib.h>

#include <pthread.h>
#include <assert.h>
#include <stdio.h>

class ThreadQueue {
public:
    pthread_mutex_t mutex;
    pthread_cond_t cond;

    bool alive;

    /**
     * Was the #wakeup_event triggered?  This avoids duplicate events.
     */
    bool pending;

    struct list_head waiting, busy, done;

    Notify *notify;
};

static void
thread_queue_wakeup_callback(void *ctx)
{
    ThreadQueue *q = (ThreadQueue *)ctx;
    pthread_mutex_lock(&q->mutex);

    q->pending = false;

    while (!list_empty(&q->done)) {
        struct thread_job *job = (struct thread_job *)q->done.next;
        assert(job->state == THREAD_JOB_DONE);

        list_remove(&job->siblings);

        if (job->again) {
            /* schedule this job again */
            job->state = THREAD_JOB_WAITING;
            job->again = false;
            list_add(&job->siblings, &q->waiting);
            pthread_cond_signal(&q->cond);
        } else {
            job->state = THREAD_JOB_NULL;
            pthread_mutex_unlock(&q->mutex);
            job->done(job);
            pthread_mutex_lock(&q->mutex);
        }
    }

    pthread_mutex_unlock(&q->mutex);

    if (list_empty(&q->waiting) && list_empty(&q->busy) &&
        list_empty(&q->done))
        notify_disable(q->notify);
}

ThreadQueue *
thread_queue_new()
{
    auto q = new ThreadQueue();

    pthread_mutex_init(&q->mutex, nullptr);
    pthread_cond_init(&q->cond, nullptr);

    q->alive = true;
    q->pending = false;

    list_init(&q->waiting);
    list_init(&q->busy);
    list_init(&q->done);

    GError *error = nullptr;
    q->notify = notify_new(thread_queue_wakeup_callback, q, &error);
    if (q->notify == nullptr)
        fprintf(stderr, "%s\n", error->message);

    return q;
}

void
thread_queue_stop(ThreadQueue *q)
{
    pthread_mutex_lock(&q->mutex);
    q->alive = false;
    pthread_cond_broadcast(&q->cond);
    pthread_mutex_unlock(&q->mutex);
}

void
thread_queue_free(ThreadQueue *q)
{
    assert(!q->alive);

    notify_free(q->notify);

    pthread_mutex_destroy(&q->mutex);
    pthread_cond_destroy(&q->cond);
    delete q;
}

void
thread_queue_add(ThreadQueue *q, struct thread_job *job)
{
    pthread_mutex_lock(&q->mutex);
    assert(q->alive);

    if (job->state == THREAD_JOB_NULL) {
        job->state = THREAD_JOB_WAITING;
        job->again = false;
        list_add(&job->siblings, &q->waiting);
        pthread_cond_signal(&q->cond);
    } else if (job->state != THREAD_JOB_WAITING) {
        job->again = true;
    }

    pthread_mutex_unlock(&q->mutex);

    notify_enable(q->notify);
}

struct thread_job *
thread_queue_wait(ThreadQueue *q)
{
    pthread_mutex_lock(&q->mutex);

    while (q->alive && list_empty(&q->waiting))
        /* queue is empty, wait for a new job to be added */
        pthread_cond_wait(&q->cond, &q->mutex);

    struct thread_job *job = nullptr;
    if (q->alive && !list_empty(&q->waiting)) {
        job = (struct thread_job *)q->waiting.next;
        assert(job->state == THREAD_JOB_WAITING);

        job->state = THREAD_JOB_BUSY;
        list_remove(&job->siblings);
        list_add(&job->siblings, &q->busy);
    }

    pthread_mutex_unlock(&q->mutex);

    return job;
}

void
thread_queue_done(ThreadQueue *q, struct thread_job *job)
{
    assert(job->state == THREAD_JOB_BUSY);

    pthread_mutex_lock(&q->mutex);

    job->state = THREAD_JOB_DONE;
    list_remove(&job->siblings);
    list_add(&job->siblings, &q->done);

    q->pending = true;

    pthread_mutex_unlock(&q->mutex);

    notify_signal(q->notify);
}

bool
thread_queue_cancel(ThreadQueue *q, struct thread_job *job)
{
    pthread_mutex_lock(&q->mutex);

    bool result = false;
    switch (job->state) {
    case THREAD_JOB_NULL:
        /* already idle */
        result = true;
        break;

    case THREAD_JOB_WAITING:
        /* cancel it */
        list_remove(&job->siblings);
        job->state = THREAD_JOB_NULL;
        result = true;
        break;

    case THREAD_JOB_BUSY:
        /* no chance */
        break;

    case THREAD_JOB_DONE:
        /* TODO: the callback hasn't been invoked yet - do that now?
           anyway, with this pending state, we can't return success */
        break;
    }

    pthread_mutex_unlock(&q->mutex);
    return result;
}
<commit_msg>thread_queue: use std::mutex and std::condition_variable<commit_after>/*
 * A queue that manages work for worker threads.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "thread_queue.hxx"
#include "thread_job.hxx"
#include "notify.hxx"

#include <glib.h>

#include <mutex>
#include <condition_variable>

#include <assert.h>
#include <stdio.h>

class ThreadQueue {
public:
    std::mutex mutex;
    std::condition_variable cond;

    bool alive;

    /**
     * Was the #wakeup_event triggered?  This avoids duplicate events.
     */
    bool pending;

    struct list_head waiting, busy, done;

    Notify *notify;
};

static void
thread_queue_wakeup_callback(void *ctx)
{
    ThreadQueue *q = (ThreadQueue *)ctx;
    q->mutex.lock();

    q->pending = false;

    while (!list_empty(&q->done)) {
        struct thread_job *job = (struct thread_job *)q->done.next;
        assert(job->state == THREAD_JOB_DONE);

        list_remove(&job->siblings);

        if (job->again) {
            /* schedule this job again */
            job->state = THREAD_JOB_WAITING;
            job->again = false;
            list_add(&job->siblings, &q->waiting);
            q->cond.notify_one();
        } else {
            job->state = THREAD_JOB_NULL;
            q->mutex.unlock();
            job->done(job);
            q->mutex.lock();
        }
    }

    q->mutex.unlock();

    if (list_empty(&q->waiting) && list_empty(&q->busy) &&
        list_empty(&q->done))
        notify_disable(q->notify);
}

ThreadQueue *
thread_queue_new()
{
    auto q = new ThreadQueue();

    q->alive = true;
    q->pending = false;

    list_init(&q->waiting);
    list_init(&q->busy);
    list_init(&q->done);

    GError *error = nullptr;
    q->notify = notify_new(thread_queue_wakeup_callback, q, &error);
    if (q->notify == nullptr)
        fprintf(stderr, "%s\n", error->message);

    return q;
}

void
thread_queue_stop(ThreadQueue *q)
{
    std::unique_lock<std::mutex> lock(q->mutex);
    q->alive = false;
    q->cond.notify_all();
}

void
thread_queue_free(ThreadQueue *q)
{
    assert(!q->alive);

    notify_free(q->notify);

    delete q;
}

void
thread_queue_add(ThreadQueue *q, struct thread_job *job)
{
    q->mutex.lock();
    assert(q->alive);

    if (job->state == THREAD_JOB_NULL) {
        job->state = THREAD_JOB_WAITING;
        job->again = false;
        list_add(&job->siblings, &q->waiting);
        q->cond.notify_one();
    } else if (job->state != THREAD_JOB_WAITING) {
        job->again = true;
    }

    q->mutex.unlock();

    notify_enable(q->notify);
}

struct thread_job *
thread_queue_wait(ThreadQueue *q)
{
    std::unique_lock<std::mutex> lock(q->mutex);

    /* queue is empty, wait for a new job to be added */
    q->cond.wait(lock, [q](){ return !q->alive || !list_empty(&q->waiting); });

    struct thread_job *job = nullptr;
    if (q->alive && !list_empty(&q->waiting)) {
        job = (struct thread_job *)q->waiting.next;
        assert(job->state == THREAD_JOB_WAITING);

        job->state = THREAD_JOB_BUSY;
        list_remove(&job->siblings);
        list_add(&job->siblings, &q->busy);
    }

    return job;
}

void
thread_queue_done(ThreadQueue *q, struct thread_job *job)
{
    assert(job->state == THREAD_JOB_BUSY);

    q->mutex.lock();

    job->state = THREAD_JOB_DONE;
    list_remove(&job->siblings);
    list_add(&job->siblings, &q->done);

    q->pending = true;

    q->mutex.unlock();

    notify_signal(q->notify);
}

bool
thread_queue_cancel(ThreadQueue *q, struct thread_job *job)
{
    std::unique_lock<std::mutex> lock(q->mutex);

    switch (job->state) {
    case THREAD_JOB_NULL:
        /* already idle */
        return true;

    case THREAD_JOB_WAITING:
        /* cancel it */
        list_remove(&job->siblings);
        job->state = THREAD_JOB_NULL;
        return true;

    case THREAD_JOB_BUSY:
        /* no chance */
        return false;

    case THREAD_JOB_DONE:
        /* TODO: the callback hasn't been invoked yet - do that now?
           anyway, with this pending state, we can't return success */
        return false;
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2009-2011 The VOTCA Development Team (http://www.votca.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <stdlib.h>
#include <votca/ctp/qmapplication.h>
#include <votca/ctp/calculatorfactory.h>
#include <string>

using namespace votca::ctp;

/*
 *
 */
class QMAppRun : public QMApplication {
public:
    void HelpText() {}

    string ProgramName() { return "ctp_run"; }

    void HelpText(std::ostream &out) {
        out << "Runs specified calculators." << endl;
    }

    void Initialize() {
        QMApplication::Initialize();
        AddProgramOptions("Calculators")
            ("execute,e", boost::program_options::value<string>(), "list of calculators separated by commas or spaces")
	    ("list,l", "lists all available calculators")
            ("description,d", boost::program_options::value<string>(), "detailed description of a calculator");
    }

    // outputs options from the XML file
    bool EvaluateOptions() {

        if(OptionsMap().count("list")) {
            cout << "Available calculators: \n";
            for(CalculatorFactory::assoc_map::const_iterator iter=Calculators().getObjects().begin();
                    iter != Calculators().getObjects().end(); ++iter) {
                PrintDescription( (iter->first).c_str(), _short );
            }
            StopExecution();
            return true;
        }

         if(OptionsMap().count("description")) {
            CheckRequired("description", "no calculator is given");
 	    Tokenizer tok(OptionsMap()["description"].as<string>(), " ,\n\t");
            // loop over the names in the description string
            for (Tokenizer::iterator n = tok.begin(); n != tok.end(); ++n) {
                // loop over calculators
                bool printerror = true;
                for(CalculatorFactory::assoc_map::const_iterator iter=Calculators().getObjects().begin();
                        iter != Calculators().getObjects().end(); ++iter) {

                    if ( (*n).compare( (iter->first).c_str() ) == 0 ) {
                         PrintDescription( (iter->first).c_str(), _long );
                        printerror = false;
                        break;
                    }
                 }
                 if ( printerror ) cout << "Calculator " << *n << " does not exist\n";
            }
            StopExecution();
            return true;
         }

        QMApplication::EvaluateOptions();
        CheckRequired("execute", "no calculator is given");

        Tokenizer tok(OptionsMap()["execute"].as<string>(), " ,\n\t");
        for (Tokenizer::iterator n = tok.begin(); n != tok.end(); ++n)
            AddCalculator(Calculators().Create((*n).c_str()));
        return true;
    }

    void PrintDescription(const char *name, const bool length) {
        // loading the documentation xml file from VOTCASHARE
        char *votca_share = getenv("VOTCASHARE");
        if(votca_share == NULL) throw std::runtime_error("VOTCASHARE not set, cannot open help files.");
        string xmlFile = string(getenv("VOTCASHARE")) + string("/ctp/xml/")+name+string(".xml");
        try {
            Property options;
            load_property_from_xml(options, xmlFile);

           if ( length ) { // short description of the calculator

                 cout << string("  ") << _fwstring(string(name),14);
                 cout << options.get(name+string(".description")).as<string>();

            } else { // long description of the calculator
                cout << " " << _fwstring(string(name),18);
                cout << options.get(name+string(".description")).as<string>() << endl;

                list<Property *> items = options.Select(name+string(".item"));

                for(list<Property*>::iterator iter = items.begin(); iter!=items.end(); ++iter) {
                    //cout << "Long description" << endl;
                    Property *pname=&( (*iter)->get( string("name") ) );
                    Property *pdesc=&( (*iter)->get( string("description") ) );
                    //Property *pdflt=&( (*iter)->get( string("default") ) );
                    if ( ! (pname->value()).empty() ) {
                        cout << string("  -") << _fwstring(pname->value(), 14);
                        cout << pdesc->value() << endl;
                    }
                 }
            }
            cout << endl;
        } catch(std::exception &error) {
            cout << string("XML file or description tag missing: ") << xmlFile;
        }
    }

private:
    static const bool _short = true;
    static const bool _long = false;

    string _fwstring(string original, size_t charCount ) {
        original.resize( charCount, ' ' );
        return original;
    }

};

int main(int argc, char** argv) {
    QMAppRun qmapprun;
    return qmapprun.Exec(argc, argv);
}
<commit_msg>ctp run had merge issues<commit_after>/*
 * Copyright 2009-2011 The VOTCA Development Team (http://www.votca.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <stdlib.h>
#include <votca/ctp/qmapplication.h>
#include <votca/ctp/calculatorfactory.h>
#include <string>

using namespace votca::ctp;

class QMAppRun : public QMApplication {
public:
    void HelpText() {}

    string ProgramName() { return "ctp_run"; }

    void HelpText(std::ostream &out) {
        out << "Runs specified calculators." << endl;
    }

    void Initialize() {
        QMApplication::Initialize();
        AddProgramOptions("Calculators")
            ("execute,e", boost::program_options::value<string>(), "list of calculators separated by commas or spaces")
	    ("list,l", "lists all available calculators")
            ("description,d", boost::program_options::value<string>(), "detailed description of a calculator");
    }

    // outputs options from the XML file
    bool EvaluateOptions() {

        if(OptionsMap().count("list")) {
            cout << "Available calculators: \n";
            for(CalculatorFactory::assoc_map::const_iterator iter=Calculators().getObjects().begin();
                    iter != Calculators().getObjects().end(); ++iter) {
                PrintDescription( (iter->first).c_str(), _short );
            }
            StopExecution();
            return true;
        }

         if(OptionsMap().count("description")) {
            CheckRequired("description", "no calculator is given");
 	    Tokenizer tok(OptionsMap()["description"].as<string>(), " ,\n\t");
            // loop over the names in the description string
            for (Tokenizer::iterator n = tok.begin(); n != tok.end(); ++n) {
                // loop over calculators
                bool printerror = true;
                for(CalculatorFactory::assoc_map::const_iterator iter=Calculators().getObjects().begin();
                        iter != Calculators().getObjects().end(); ++iter) {

                    if ( (*n).compare( (iter->first).c_str() ) == 0 ) {
                         PrintDescription( (iter->first).c_str(), _long );
                        printerror = false;
                        break;
                    }
                 }
                 if ( printerror ) cout << "Calculator " << *n << " does not exist\n";
            }
            StopExecution();
            return true;
         }

        QMApplication::EvaluateOptions();
        CheckRequired("execute", "no calculator is given");

        Tokenizer tok(OptionsMap()["execute"].as<string>(), " ,\n\t");
        for (Tokenizer::iterator n = tok.begin(); n != tok.end(); ++n)
            AddCalculator(Calculators().Create((*n).c_str()));
        return true;
    }

    void PrintDescription(const char *name, const bool length) {
        // loading the documentation xml file from VOTCASHARE
        char *votca_share = getenv("VOTCASHARE");
        if(votca_share == NULL) throw std::runtime_error("VOTCASHARE not set, cannot open help files.");
        string xmlFile = string(getenv("VOTCASHARE")) + string("/ctp/xml/")+name+string(".xml");
        try {
            Property options;
            load_property_from_xml(options, xmlFile);

           if ( length ) { // short description of the calculator

                 cout << string("  ") << _fwstring(string(name),14);
                 cout << options.get(name+string(".description")).as<string>();

            } else { // long description of the calculator
                cout << " " << _fwstring(string(name),18);
                cout << options.get(name+string(".description")).as<string>() << endl;

                list<Property *> items = options.Select(name+string(".item"));

                for(list<Property*>::iterator iter = items.begin(); iter!=items.end(); ++iter) {
                    //cout << "Long description" << endl;
                    Property *pname=&( (*iter)->get( string("name") ) );
                    Property *pdesc=&( (*iter)->get( string("description") ) );
                    //Property *pdflt=&( (*iter)->get( string("default") ) );
                    if ( ! (pname->value()).empty() ) {
                        cout << string("  -") << _fwstring(pname->value(), 14);
                        cout << pdesc->value() << endl;
                    }
                 }
            }
            cout << endl;
        } catch(std::exception &error) {
            cout << string("XML file or description tag missing: ") << xmlFile;
        }
    }

private:
    static const bool _short = true;
    static const bool _long = false;

    string _fwstring(string original, size_t charCount ) {
        original.resize( charCount, ' ' );
        return original;
    }

};

int main(int argc, char** argv) {
    QMAppRun qmapprun;
    return qmapprun.Exec(argc, argv);
}
<|endoftext|>


<commit_before>#include "bufferAgent.h"
#include "helpers/storageHelperFactory.h"
#include "glog/logging.h"

using boost::shared_ptr;
using boost::thread;
using boost::bind;

using std::string;

namespace veil {
namespace helpers {

boost::recursive_mutex BufferAgent::m_bufferSizeMutex;
volatile size_t         BufferAgent::m_rdBufferTotalSize;
volatile size_t         BufferAgent::m_wrBufferTotalSize;
rdbuf_size_mem_t        BufferAgent::m_rdBufferSizeMem;
wrbuf_size_mem_t        BufferAgent::m_wrBufferSizeMem;

BufferAgent::BufferAgent(write_fun w, read_fun r)
  : m_agentActive(false),
    doWrite(w),
    doRead(r)
{
    agentStart();
}

BufferAgent::~BufferAgent()
{
    agentStop();
}

void BufferAgent::updateWrBufferSize(fd_type key, size_t size)
{
    unique_lock guard(m_bufferSizeMutex);
    wrbuf_size_mem_t::iterator it = m_wrBufferSizeMem.find(key);

    // If entry for this key doesn't exist, just create one
    if(it == m_wrBufferSizeMem.end()) {
        m_wrBufferSizeMem[key] = size;
        m_rdBufferTotalSize += size;
    } else {
    // If key exists, update total bytes size according to diff between last size and current
        if(size < it->second && (it->second - size) >= m_wrBufferTotalSize) {
            m_wrBufferTotalSize = 0;
        } else {
            m_wrBufferTotalSize += (size - it->second);
        }

        if(size == 0) {
            m_wrBufferSizeMem.erase(key);
        } else {
            m_wrBufferSizeMem[key] = size;
        }
    }
}

void BufferAgent::updateRdBufferSize(std::string key, size_t size)
{
    unique_lock guard(m_bufferSizeMutex);
    rdbuf_size_mem_t::iterator it = m_rdBufferSizeMem.find(key);

    if(it == m_rdBufferSizeMem.end()) {
        m_rdBufferSizeMem[key] = size;
        m_rdBufferTotalSize += size;
    // If key exists, update total bytes size according to diff between last size and current
    } else {
        if(size < it->second && (it->second - size) >= m_rdBufferTotalSize) {
            m_rdBufferTotalSize = 0;
        } else {
            m_rdBufferTotalSize += (size - it->second);
        }

        if(size == 0) {
            m_rdBufferSizeMem.erase(key);
        } else {
            m_rdBufferSizeMem[key] = size;
        }
    }
}

size_t BufferAgent::getWriteBufferSize()
{
    unique_lock guard(m_bufferSizeMutex);
    return m_wrBufferTotalSize;
}

size_t BufferAgent::getReadBufferSize()
{
    unique_lock guard(m_bufferSizeMutex);
    return m_rdBufferTotalSize;
}

int BufferAgent::onOpen(std::string path, ffi_type ffi)
{
    DLOG(INFO) << "BufferAgent::onOpen(" << path << ")";
    // Initialize write buffer's holder
    {
        unique_lock guard(m_wrMutex);
        m_wrCacheMap.erase(ffi->fh);

        write_buffer_ptr lCache(new WriteCache());
        lCache->fileName = path;
        lCache->buffer = newFileCache(true);
        lCache->ffi = *ffi;

        m_wrCacheMap[ffi->fh] = lCache;
    }

    {   // Initialize read buffer's holder
        unique_lock guard(m_rdMutex);
        read_cache_map_t::iterator it;
        if(( it = m_rdCacheMap.find(path) ) != m_rdCacheMap.end()) {
            it->second->openCount++;
        } else {
            read_buffer_ptr lCache(new ReadCache());
            lCache->fileName = path;
            lCache->openCount = 1;
            lCache->ffi = *ffi;
            lCache->buffer = newFileCache(false);

            m_rdCacheMap[path] = lCache;
        }

        // Prefetch first 512B block
        m_rdJobQueue.insert(PrefetchJob(path, 0, 512, ffi->fh));
        m_rdCond.notify_one();
    }

    return 0;
}

int BufferAgent::onWrite(std::string path, const std::string &buf, size_t size, off_t offset, ffi_type ffi)
{
    DLOG(INFO) << "BufferAgent::onWrite(path: " << path << ", size: " << size << ", offset: " << offset <<")";
    unique_lock guard(m_wrMutex);
        write_buffer_ptr wrapper = m_wrCacheMap[ffi->fh];

        // If there was an error while sending cached data, return this error
        if(wrapper->lastError < 0) {
            wrapper->lastError = 0;
            return wrapper->lastError;
        }

    guard.unlock();

    {
        // If memory limit is exceeded, force flush
        if(wrapper->buffer->byteSize() > config::buffers::writeBufferPerFileSizeLimit ||
           getWriteBufferSize() > config::buffers::writeBufferGlobalSizeLimit) {
            DLOG(INFO) << "Write Buffer memory limit exceeded, force flush";
            if(int fRet = onFlush(path, ffi)) {
                return fRet;
            }
        }

        // If memory limit is still exceeded, send the block without using buffer
        if(wrapper->buffer->byteSize() > config::buffers::writeBufferPerFileSizeLimit ||
           getWriteBufferSize() > config::buffers::writeBufferGlobalSizeLimit) {
            return doWrite(path, buf, size, offset, ffi);
        }

        // Save the block in write buffer
        wrapper->buffer->writeData(offset, buf);
        updateWrBufferSize(ffi->fh, wrapper->buffer->byteSize());
    }

    unique_lock buffGuard(wrapper->mutex);
    guard.lock();
    if(!wrapper->opPending)
    {
        // Notify workers if needed
        wrapper->opPending = true;
        m_wrJobQueue.push_back(ffi->fh);
        m_wrCond.notify_one();
    }

    return size;
}

int BufferAgent::onRead(std::string path, std::string &buf, size_t size, off_t offset, ffi_type ffi)
{
    unique_lock guard(m_rdMutex);
        read_buffer_ptr wrapper = m_rdCacheMap[path];
    guard.unlock();

    {
        unique_lock buffGuard(wrapper->mutex);

        wrapper->lastBlock[ffi->fh] = offset;
        wrapper->blockSize = std::min((size_t) config::buffers::preferedBlockSize, (size_t) std::max(size, 2*wrapper->blockSize));
    }

    wrapper->buffer->readData(offset, size, buf);
    updateRdBufferSize(path, wrapper->buffer->byteSize());

    // If cached file block is not complete, read it from server and save to cache
    if(buf.size() < size) {

        string buf2;
        int ret = doRead(path, buf2, size - buf.size(), offset + buf.size(), &wrapper->ffi);
        if(ret < 0)
            return ret;

        wrapper->buffer->writeData(offset + buf.size(), buf2);

        buf += buf2;

        guard.lock();
            m_rdJobQueue.insert(PrefetchJob(wrapper->fileName, offset + buf.size() + config::buffers::preferedBlockSize, wrapper->blockSize, ffi->fh));
        guard.unlock();
    } else {
        string tmp;
        size_t prefSize = std::max(2*size, wrapper->blockSize);
        wrapper->buffer->readData(offset + size, prefSize, tmp);

        if(tmp.size() != prefSize) {
            guard.lock();
                m_rdJobQueue.insert(PrefetchJob(wrapper->fileName, offset + size + tmp.size(), wrapper->blockSize, ffi->fh));
                m_rdJobQueue.insert(PrefetchJob(wrapper->fileName, offset + size + tmp.size() + wrapper->blockSize, wrapper->blockSize, ffi->fh));
            guard.unlock();
        }
    }

    {
        unique_lock buffGuard(wrapper->mutex);
        if(offset + buf.size() > wrapper->endOfFile)
            wrapper->endOfFile = 0;
    }

    m_rdCond.notify_one();

    return buf.size();
}

int BufferAgent::onFlush(std::string path, ffi_type ffi)
{
    unique_lock guard(m_wrMutex);
        write_buffer_ptr wrapper = m_wrCacheMap[ffi->fh];
    guard.unlock();

    unique_lock sendGuard(wrapper->sendMutex);
    unique_lock buff_guard(wrapper->mutex);

    // Send all pending blocks to server
    while(wrapper->buffer->blockCount() > 0)
    {
        block_ptr block = wrapper->buffer->removeOldestBlock();
        uint64_t start = utils::mtime<uint64_t>();
        int res = doWrite(wrapper->fileName, block->data, block->data.size(), block->offset, &wrapper->ffi);
        uint64_t end = utils::mtime<uint64_t>();

        if(res < 0)
        {
            // Skip all blocks after receiving error
            while(wrapper->buffer->blockCount() > 0)
            {
                (void) wrapper->buffer->removeOldestBlock();
            }
            return res;
        } else if(res < block->data.size()) {
            // Send wasn't complete
            block->offset += res;
            block->data = block->data.substr(res);
            wrapper->buffer->insertBlock(*block);
        }
    }

    updateWrBufferSize(ffi->fh, wrapper->buffer->byteSize());

    guard.lock();
    m_wrJobQueue.remove(ffi->fh);

    return 0;
}

int BufferAgent::onRelease(std::string path, ffi_type ffi)
{
    {
        unique_lock guard(m_wrMutex);

        m_wrCacheMap.erase(ffi->fh);
        m_wrJobQueue.remove(ffi->fh);
    }

    {
        unique_lock guard(m_rdMutex);

        read_cache_map_t::iterator it;
        if(( it = m_rdCacheMap.find(path) ) != m_rdCacheMap.end()) {
            it->second->openCount--;
            if(it->second->openCount <= 0) {
                m_rdCacheMap.erase(it);
            }
        }
    }

    return 0;
}

void BufferAgent::agentStart(int worker_count)
{
    m_workers.clear();
    m_agentActive = true;

    while(worker_count--)
    {
        m_workers.push_back(shared_ptr<thread>(new thread(bind(&BufferAgent::writerLoop, this))));
        m_workers.push_back(shared_ptr<thread>(new thread(bind(&BufferAgent::readerLoop, this))));
    }
}

void BufferAgent::agentStop()
{
    m_agentActive = false;
    m_wrCond.notify_all();
    m_rdCond.notify_all();

    while(m_workers.size() > 0)
    {
        m_workers.back()->join();
        m_workers.pop_back();
    }
}

void BufferAgent::readerLoop()
{
    unique_lock guard(m_rdMutex);
    while(m_agentActive)
    {
        while(m_rdJobQueue.empty() && m_agentActive)
            m_rdCond.wait(guard);

        if(!m_agentActive)
            return;

        PrefetchJob job = *m_rdJobQueue.begin();
        read_buffer_ptr wrapper = m_rdCacheMap[job.fileName];
        m_rdJobQueue.erase(m_rdJobQueue.begin());
        m_rdCond.notify_one();

        if(!wrapper || wrapper->lastBlock[job.fh] + config::buffers::preferedBlockSize >= job.offset + job.size || (wrapper->endOfFile > 0 && wrapper->endOfFile <= job.offset))
            continue;

        guard.unlock();

        {

            string buff;
            wrapper->buffer->readData(job.offset, job.size, buff);
            if(buff.size() < job.size)
            {
                string tmp;
                off_t effectiveOffset = job.offset + buff.size();
                int ret = doRead(wrapper->fileName, tmp, job.size, effectiveOffset, &wrapper->ffi);
                LOG(INFO) << "Job: offset: " << job.offset << " size: " << job.size << " ret: " << ret;

                guard.lock();
                unique_lock buffGuard(wrapper->mutex);

                if(ret > 0 && tmp.size() >= ret) {
                    wrapper->buffer->writeData(effectiveOffset, tmp);
                    updateRdBufferSize(job.fileName, wrapper->buffer->byteSize());
                    //m_rdJobQueue.insert(PrefetchJob(job.fileName, effectiveOffset + ret, wrapper->blockSize, job.fh));
                } else if(ret == 0) {
                    wrapper->endOfFile = std::max(wrapper->endOfFile, effectiveOffset);
                }

                wrapper->cond.notify_all();

                guard.unlock();
            }
        }

        guard.lock();
    }
}

void BufferAgent::writerLoop()
{
    unique_lock guard(m_wrMutex);
    while(m_agentActive)
    {
        while(m_wrJobQueue.empty() && m_agentActive)
            m_wrCond.wait(guard);

        if(!m_agentActive)
            return;

        std::multiset<PrefetchJob, PrefetchJobCompare>::iterator it;

        fd_type file = m_wrJobQueue.front();
        write_buffer_ptr wrapper = m_wrCacheMap[file];
        m_wrJobQueue.pop_front();
        m_wrCond.notify_one();

        if(!wrapper)
            continue;

        guard.unlock();

        {
            unique_lock sendGuard(wrapper->sendMutex);

            block_ptr block;
            {
                unique_lock buff_guard(wrapper->mutex);
                block = wrapper->buffer->removeOldestBlock();
            }

            int writeRes;
            if(block)
            {
                uint64_t start = utils::mtime<uint64_t>();
                writeRes = doWrite(wrapper->fileName, block->data, block->data.size(), block->offset, &wrapper->ffi);
                uint64_t end = utils::mtime<uint64_t>();

                //LOG(INFO) << "Roundtrip: " << (end - start) << " for " << block->data.size() << " bytes";

                wrapper->cond.notify_all();
            }

            {
                unique_lock buff_guard(wrapper->mutex);
                guard.lock();

                if(block)
                {
                    if(writeRes < 0)
                    {
                        while(wrapper->buffer->blockCount() > 0)
                        {
                            wrapper->buffer->removeOldestBlock();
                        }

                        wrapper->lastError = writeRes;
                    }
                    else if(writeRes < block->data.size())
                    {
                        block->offset += writeRes;
                        block->data = block->data.substr(writeRes);
                        wrapper->buffer->insertBlock(*block);
                    }
                }

                if(wrapper->buffer->blockCount() > 0)
                {
                    m_wrJobQueue.push_back(file);
                }
                else
                {
                    wrapper->opPending = false;
                }

                updateWrBufferSize(file, wrapper->buffer->byteSize());
            }
        }

        wrapper->cond.notify_all();
    }
}

boost::shared_ptr<FileCache> BufferAgent::newFileCache(bool isBuffer)
{
    return boost::shared_ptr<FileCache>(new FileCache(config::buffers::preferedBlockSize, isBuffer));
}

} // namespace helpers
} // namespace veil

<commit_msg>VFS-292: docs<commit_after>#include "bufferAgent.h"
#include "helpers/storageHelperFactory.h"
#include "glog/logging.h"

using boost::shared_ptr;
using boost::thread;
using boost::bind;

using std::string;

namespace veil {
namespace helpers {

boost::recursive_mutex BufferAgent::m_bufferSizeMutex;
volatile size_t         BufferAgent::m_rdBufferTotalSize;
volatile size_t         BufferAgent::m_wrBufferTotalSize;
rdbuf_size_mem_t        BufferAgent::m_rdBufferSizeMem;
wrbuf_size_mem_t        BufferAgent::m_wrBufferSizeMem;

BufferAgent::BufferAgent(write_fun w, read_fun r)
  : m_agentActive(false),
    doWrite(w),
    doRead(r)
{
    agentStart(1);
}

BufferAgent::~BufferAgent()
{
    agentStop();
}

void BufferAgent::updateWrBufferSize(fd_type key, size_t size)
{
    unique_lock guard(m_bufferSizeMutex);
    wrbuf_size_mem_t::iterator it = m_wrBufferSizeMem.find(key);

    // If entry for this key doesn't exist, just create one
    if(it == m_wrBufferSizeMem.end()) {
        m_wrBufferSizeMem[key] = size;
        m_rdBufferTotalSize += size;
    } else {
    // If key exists, update total bytes size according to diff between last size and current
        if(size < it->second && (it->second - size) >= m_wrBufferTotalSize) {
            m_wrBufferTotalSize = 0;
        } else {
            m_wrBufferTotalSize += (size - it->second);
        }

        if(size == 0) {
            m_wrBufferSizeMem.erase(key);
        } else {
            m_wrBufferSizeMem[key] = size;
        }
    }
}

void BufferAgent::updateRdBufferSize(std::string key, size_t size)
{
    unique_lock guard(m_bufferSizeMutex);
    rdbuf_size_mem_t::iterator it = m_rdBufferSizeMem.find(key);

    if(it == m_rdBufferSizeMem.end()) {
        m_rdBufferSizeMem[key] = size;
        m_rdBufferTotalSize += size;
    // If key exists, update total bytes size according to diff between last size and current
    } else {
        if(size < it->second && (it->second - size) >= m_rdBufferTotalSize) {
            m_rdBufferTotalSize = 0;
        } else {
            m_rdBufferTotalSize += (size - it->second);
        }

        if(size == 0) {
            m_rdBufferSizeMem.erase(key);
        } else {
            m_rdBufferSizeMem[key] = size;
        }
    }
}

size_t BufferAgent::getWriteBufferSize()
{
    unique_lock guard(m_bufferSizeMutex);
    return m_wrBufferTotalSize;
}

size_t BufferAgent::getReadBufferSize()
{
    unique_lock guard(m_bufferSizeMutex);
    return m_rdBufferTotalSize;
}

int BufferAgent::onOpen(std::string path, ffi_type ffi)
{
    DLOG(INFO) << "BufferAgent::onOpen(" << path << ")";
    // Initialize write buffer's holder
    {
        unique_lock guard(m_wrMutex);
        m_wrCacheMap.erase(ffi->fh);

        write_buffer_ptr lCache(new WriteCache());
        lCache->fileName = path;
        lCache->buffer = newFileCache(true);
        lCache->ffi = *ffi;

        m_wrCacheMap[ffi->fh] = lCache;
    }

    {   // Initialize read buffer's holder
        unique_lock guard(m_rdMutex);
        read_cache_map_t::iterator it;
        if(( it = m_rdCacheMap.find(path) ) != m_rdCacheMap.end()) {
            it->second->openCount++;
        } else {
            read_buffer_ptr lCache(new ReadCache());
            lCache->fileName = path;
            lCache->openCount = 1;
            lCache->ffi = *ffi;
            lCache->buffer = newFileCache(false);

            m_rdCacheMap[path] = lCache;
        }

        // Prefetch first 512B block
        m_rdJobQueue.insert(PrefetchJob(path, 0, 512, ffi->fh));
        m_rdCond.notify_one();
    }

    return 0;
}

int BufferAgent::onWrite(std::string path, const std::string &buf, size_t size, off_t offset, ffi_type ffi)
{
    DLOG(INFO) << "BufferAgent::onWrite(path: " << path << ", size: " << size << ", offset: " << offset <<")";
    unique_lock guard(m_wrMutex);
        write_buffer_ptr wrapper = m_wrCacheMap[ffi->fh];

        // If there was an error while sending cached data, return this error
        if(wrapper->lastError < 0) {
            wrapper->lastError = 0;
            return wrapper->lastError;
        }

    guard.unlock();

    {
        // If memory limit is exceeded, force flush
        if(wrapper->buffer->byteSize() > config::buffers::writeBufferPerFileSizeLimit ||
           getWriteBufferSize() > config::buffers::writeBufferGlobalSizeLimit) {
            DLOG(INFO) << "Write Buffer memory limit exceeded, force flush";
            if(int fRet = onFlush(path, ffi)) {
                return fRet;
            }
        }

        // If memory limit is still exceeded, send the block without using buffer
        if(wrapper->buffer->byteSize() > config::buffers::writeBufferPerFileSizeLimit ||
           getWriteBufferSize() > config::buffers::writeBufferGlobalSizeLimit) {
            return doWrite(path, buf, size, offset, ffi);
        }

        // Save the block in write buffer
        wrapper->buffer->writeData(offset, buf);
        updateWrBufferSize(ffi->fh, wrapper->buffer->byteSize());
    }

    unique_lock buffGuard(wrapper->mutex);
    guard.lock();
    if(!wrapper->opPending)
    {
        // Notify workers if needed
        wrapper->opPending = true;
        m_wrJobQueue.push_back(ffi->fh);
        m_wrCond.notify_one();
    }

    return size;
}

int BufferAgent::onRead(std::string path, std::string &buf, size_t size, off_t offset, ffi_type ffi)
{
    unique_lock guard(m_rdMutex);
        read_buffer_ptr wrapper = m_rdCacheMap[path];
    guard.unlock();

    {
        unique_lock buffGuard(wrapper->mutex);

        wrapper->lastBlock[ffi->fh] = offset;
        wrapper->blockSize = std::min((size_t) config::buffers::preferedBlockSize, (size_t) std::max(size, 2*wrapper->blockSize));
    }

    wrapper->buffer->readData(offset, size, buf);
    updateRdBufferSize(path, wrapper->buffer->byteSize());

    // If cached file block is not complete, read it from server and save to cache
    if(buf.size() < size) {

        string buf2;
        int ret = doRead(path, buf2, size - buf.size(), offset + buf.size(), &wrapper->ffi);
        if(ret < 0)
            return ret;

        wrapper->buffer->writeData(offset + buf.size(), buf2);

        buf += buf2;

        guard.lock();
            m_rdJobQueue.insert(PrefetchJob(wrapper->fileName, offset + buf.size() + config::buffers::preferedBlockSize, wrapper->blockSize, ffi->fh));
        guard.unlock();
    } else {
        string tmp;
        size_t prefSize = std::max(2*size, wrapper->blockSize);
        wrapper->buffer->readData(offset + size, prefSize, tmp);

        if(tmp.size() != prefSize) {
            guard.lock();
                m_rdJobQueue.insert(PrefetchJob(wrapper->fileName, offset + size + tmp.size(), wrapper->blockSize, ffi->fh));
                m_rdJobQueue.insert(PrefetchJob(wrapper->fileName, offset + size + tmp.size() + wrapper->blockSize, wrapper->blockSize, ffi->fh));
            guard.unlock();
        }
    }

    {
        unique_lock buffGuard(wrapper->mutex);
        if(offset + buf.size() > wrapper->endOfFile)
            wrapper->endOfFile = 0;
    }

    m_rdCond.notify_one();

    return buf.size();
}

int BufferAgent::onFlush(std::string path, ffi_type ffi)
{
    unique_lock guard(m_wrMutex);
        write_buffer_ptr wrapper = m_wrCacheMap[ffi->fh];
    guard.unlock();

    unique_lock sendGuard(wrapper->sendMutex);
    unique_lock buff_guard(wrapper->mutex);

    // Send all pending blocks to server
    while(wrapper->buffer->blockCount() > 0)
    {
        block_ptr block = wrapper->buffer->removeOldestBlock();
        uint64_t start = utils::mtime<uint64_t>();
        int res = doWrite(wrapper->fileName, block->data, block->data.size(), block->offset, &wrapper->ffi);
        uint64_t end = utils::mtime<uint64_t>();

        if(res < 0)
        {
            // Skip all blocks after receiving error
            while(wrapper->buffer->blockCount() > 0)
            {
                (void) wrapper->buffer->removeOldestBlock();
            }
            return res;
        } else if(res < block->data.size()) {
            // Send wasn't complete
            block->offset += res;
            block->data = block->data.substr(res);
            wrapper->buffer->insertBlock(*block);
        }
    }

    updateWrBufferSize(ffi->fh, wrapper->buffer->byteSize());

    guard.lock();
    m_wrJobQueue.remove(ffi->fh);

    return 0;
}

int BufferAgent::onRelease(std::string path, ffi_type ffi)
{
    {
        unique_lock guard(m_wrMutex);

        m_wrCacheMap.erase(ffi->fh);
        m_wrJobQueue.remove(ffi->fh);
    }

    {
        unique_lock guard(m_rdMutex);

        read_cache_map_t::iterator it;
        if(( it = m_rdCacheMap.find(path) ) != m_rdCacheMap.end()) {
            it->second->openCount--;
            if(it->second->openCount <= 0) {
                m_rdCacheMap.erase(it);
            }
        }
    }

    return 0;
}

void BufferAgent::agentStart(int worker_count)
{
    m_workers.clear();
    m_agentActive = true;

    while(worker_count--)
    {
        m_workers.push_back(shared_ptr<thread>(new thread(bind(&BufferAgent::writerLoop, this))));
        m_workers.push_back(shared_ptr<thread>(new thread(bind(&BufferAgent::readerLoop, this))));
    }
}

void BufferAgent::agentStop()
{
    m_agentActive = false;
    m_wrCond.notify_all();
    m_rdCond.notify_all();

    while(m_workers.size() > 0)
    {
        m_workers.back()->join();
        m_workers.pop_back();
    }
}

void BufferAgent::readerLoop()
{
    unique_lock guard(m_rdMutex);
    while(m_agentActive)
    {
        while(m_rdJobQueue.empty() && m_agentActive)
            m_rdCond.wait(guard);

        if(!m_agentActive)
            return;

        PrefetchJob job = *m_rdJobQueue.begin();
        read_buffer_ptr wrapper = m_rdCacheMap[job.fileName];
        m_rdJobQueue.erase(m_rdJobQueue.begin());
        m_rdCond.notify_one();

        if(!wrapper || wrapper->lastBlock[job.fh] + config::buffers::preferedBlockSize >= job.offset + job.size || (wrapper->endOfFile > 0 && wrapper->endOfFile <= job.offset))
            continue;

        guard.unlock();

        {

            string buff;
            wrapper->buffer->readData(job.offset, job.size, buff);
            if(buff.size() < job.size)
            {
                string tmp;
                off_t effectiveOffset = job.offset + buff.size();
                int ret = doRead(wrapper->fileName, tmp, job.size, effectiveOffset, &wrapper->ffi);
                LOG(INFO) << "Job: offset: " << job.offset << " size: " << job.size << " ret: " << ret;

                guard.lock();
                unique_lock buffGuard(wrapper->mutex);

                if(ret > 0 && tmp.size() >= ret) {
                    wrapper->buffer->writeData(effectiveOffset, tmp);
                    updateRdBufferSize(job.fileName, wrapper->buffer->byteSize());
                    //m_rdJobQueue.insert(PrefetchJob(job.fileName, effectiveOffset + ret, wrapper->blockSize, job.fh));
                } else if(ret == 0) {
                    wrapper->endOfFile = std::max(wrapper->endOfFile, effectiveOffset);
                }

                wrapper->cond.notify_all();

                guard.unlock();
            }
        }

        guard.lock();
    }
}

void BufferAgent::writerLoop()
{
    unique_lock guard(m_wrMutex);
    while(m_agentActive)
    {
        while(m_wrJobQueue.empty() && m_agentActive)
            m_wrCond.wait(guard);

        if(!m_agentActive)
            return;

        std::multiset<PrefetchJob, PrefetchJobCompare>::iterator it;

        fd_type file = m_wrJobQueue.front();
        write_buffer_ptr wrapper = m_wrCacheMap[file];
        m_wrJobQueue.pop_front();
        m_wrCond.notify_one();

        if(!wrapper)
            continue;

        guard.unlock();

        {
            unique_lock sendGuard(wrapper->sendMutex);

            block_ptr block;
            {
                unique_lock buff_guard(wrapper->mutex);
                block = wrapper->buffer->removeOldestBlock();
            }

            int writeRes;
            if(block)
            {
                uint64_t start = utils::mtime<uint64_t>();
                writeRes = doWrite(wrapper->fileName, block->data, block->data.size(), block->offset, &wrapper->ffi);
                uint64_t end = utils::mtime<uint64_t>();

                //LOG(INFO) << "Roundtrip: " << (end - start) << " for " << block->data.size() << " bytes";

                wrapper->cond.notify_all();
            }

            {
                unique_lock buff_guard(wrapper->mutex);
                guard.lock();

                if(block)
                {
                    if(writeRes < 0)
                    {
                        while(wrapper->buffer->blockCount() > 0)
                        {
                            wrapper->buffer->removeOldestBlock();
                        }

                        wrapper->lastError = writeRes;
                    }
                    else if(writeRes < block->data.size())
                    {
                        block->offset += writeRes;
                        block->data = block->data.substr(writeRes);
                        wrapper->buffer->insertBlock(*block);
                    }
                }

                if(wrapper->buffer->blockCount() > 0)
                {
                    m_wrJobQueue.push_back(file);
                }
                else
                {
                    wrapper->opPending = false;
                }

                updateWrBufferSize(file, wrapper->buffer->byteSize());
            }
        }

        wrapper->cond.notify_all();
    }
}

boost::shared_ptr<FileCache> BufferAgent::newFileCache(bool isBuffer)
{
    return boost::shared_ptr<FileCache>(new FileCache(config::buffers::preferedBlockSize, isBuffer));
}

} // namespace helpers
} // namespace veil

<|endoftext|>