// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// SchedulerPolicyBase.cpp
//
// Scheduler policy implementation
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#include "concrtinternal.h"
namespace Concurrency
{
namespace details
{
///
/// Internal list of scheduler policy defaults.
///
const unsigned int PolicyDefaults[] =
{
::Concurrency::ThreadScheduler, // SchedulerKind
MaxExecutionResources, // MaxConcurrency
1, // MinConcurrency
1, // TargetOversubscriptionFactor
8, // LocalContextCacheSize
0, // ContextStackSize
THREAD_PRIORITY_NORMAL, // ContextPriority
EnhanceScheduleGroupLocality, // SchedulingProtocol
ProgressFeedbackEnabled, // DynamicProgressFeedback
InitializeWinRTAsMTA, // WinRTInitialization
};
///
/// Internal map from policy keys to descriptive strings.
///
const char* const PolicyElementKeyStrings[] =
{
"SchedulerKind",
"MaxConcurrency",
"MinConcurrency",
"TargetOversubscriptionFactor",
"LocalContextCacheSize",
"ContextStackSize",
"ContextPriority",
"SchedulingProtocol",
"DynamicProgressFeedback",
"WinRTInitialization",
"MaxPolicyElementKey"
};
}
///
/// Creates a new default scheduler policy.
///
SchedulerPolicy::SchedulerPolicy()
{
_Initialize(0, NULL);
}
///
/// Creates a new scheduler policy that uses a named-parameter style of initialization. Unnamed parameters take defaults described above.
///
SchedulerPolicy::SchedulerPolicy(size_t _PolicyKeyCount, ...)
{
va_list args;
va_start(args, _PolicyKeyCount);
_Initialize(_PolicyKeyCount, &args);
}
///
/// Initializes the scheduler policy.
///
void SchedulerPolicy::_Initialize(size_t _PolicyKeyCount, va_list *_PArgs)
{
size_t bagSize = sizeof(unsigned int) * Concurrency::MaxPolicyElementKey;
_PolicyBag *pPolicyBag = _concrt_new _PolicyBag;
_M_pPolicyBag = pPolicyBag;
try
{
memcpy(pPolicyBag->_M_values._M_pPolicyBag, PolicyDefaults, bagSize);
for (size_t i = 0; i < _PolicyKeyCount; i++)
{
PolicyElementKey key = va_arg(*_PArgs, PolicyElementKey);
unsigned int value = va_arg(*_PArgs, unsigned int);
if ( !_ValidPolicyKey(key))
throw invalid_scheduler_policy_key(_StringFromPolicyKey(key));
if ( !_ValidPolicyValue(key, value))
throw invalid_scheduler_policy_value(_StringFromPolicyKey(key));
pPolicyBag->_M_values._M_pPolicyBag[key] = value;
}
if (!_AreConcurrencyLimitsValid())
{
throw invalid_scheduler_policy_thread_specification();
}
if (!_ArePolicyCombinationsValid())
{
throw invalid_scheduler_policy_value();
}
_ResolvePolicyValues();
}
catch (...)
{
delete pPolicyBag;
throw;
}
}
///
/// The most convenient way to define a new scheduler policy is to copy
/// an existing policy and modify it. The copy constructor is also needed
/// for all the usual reasons.
///
SchedulerPolicy::SchedulerPolicy(const SchedulerPolicy &srcPolicy)
{
_M_pPolicyBag = _concrt_new _PolicyBag;
_Assign(srcPolicy);
}
///
/// The most convenient way to define a new scheduler policy is to copy
/// an existing policy and modify it. The copy constructor is also needed
/// for all the usual reasons.
///
SchedulerPolicy& SchedulerPolicy::operator=(const SchedulerPolicy &rhsPolicy)
{
_Assign(rhsPolicy);
return *this;
}
///
/// Make this policy a copy of the source policy.
///
void SchedulerPolicy::_Assign(const SchedulerPolicy &rhsPolicy)
{
size_t bagSize = sizeof(unsigned int) * Concurrency::MaxPolicyElementKey;
memcpy(_M_pPolicyBag->_M_values._M_pPolicyBag, rhsPolicy._M_pPolicyBag->_M_values._M_pPolicyBag, bagSize);
}
///
/// Destroys a scheduler policy.
///
SchedulerPolicy::~SchedulerPolicy()
{
delete _M_pPolicyBag;
}
///
/// Retrieve the value of the supplied policy key.
///
///
/// [in] The policy key.
///
///
/// The policy key value for the key, if is a supported key.
///
///
/// The function will throw "invalid_scheduler_policy_key" for any key that is not supported.
///
unsigned int SchedulerPolicy::GetPolicyValue(PolicyElementKey key) const
{
if (!_ValidPolicyKey(key))
{
throw invalid_scheduler_policy_key(_StringFromPolicyKey(key));
}
return _M_pPolicyBag->_M_values._M_pPolicyBag[key];
}
///
/// Set the value of the supplied policy key and return the old value.
///
///
/// [in] The policy key.
///
///
/// [in] The value for the policy key.
///
///
/// The old policy key value for the key, if is a supported key.
///
///
/// The function will throw "invalid_scheduler_policy_key" for any key that is not supported,
/// and "invalid_scheduler_policy_value" for a value that is not supported for a valid key.
///
unsigned int SchedulerPolicy::SetPolicyValue(PolicyElementKey key, unsigned int value)
{
if (!_ValidPolicyKey(key)
|| key == ::Concurrency::MinConcurrency
|| key == ::Concurrency::MaxConcurrency)
{
throw invalid_scheduler_policy_key(_StringFromPolicyKey(key));
}
if (!_ValidPolicyValue(key, value))
{
throw invalid_scheduler_policy_value(_StringFromPolicyKey(key));
}
unsigned int oldValue = GetPolicyValue(key);
_M_pPolicyBag->_M_values._M_pPolicyBag[key] = value;
_ResolvePolicyValues();
return oldValue;
}
///
/// Set the value of the supplied policy key and return the old value.
///
///
/// The value for MinConcurrency.
///
///
/// The value for MaxConcurrency.
///
///
/// The function will throw "invalid_scheduler_policy_value" if:
/// _MaxConcurrency != MaxExecutionResources && _MinConcurrency > _MaxConcurrency
///
void SchedulerPolicy::SetConcurrencyLimits(unsigned int _MinConcurrency, unsigned int _MaxConcurrency)
{
if (!_ValidPolicyValue(::Concurrency::MaxConcurrency, _MaxConcurrency))
throw invalid_scheduler_policy_value(_StringFromPolicyKey(::Concurrency::MaxConcurrency));
if (!_ValidPolicyValue(::Concurrency::MinConcurrency, _MinConcurrency))
throw invalid_scheduler_policy_value(_StringFromPolicyKey(::Concurrency::MinConcurrency));
if (!_AreConcurrencyLimitsValid(_MinConcurrency, _MaxConcurrency))
throw invalid_scheduler_policy_thread_specification();
if (!_ArePolicyCombinationsValid())
throw invalid_scheduler_policy_value();
_M_pPolicyBag->_M_values._M_pPolicyBag[::Concurrency::MaxConcurrency] = _MaxConcurrency;
_M_pPolicyBag->_M_values._M_pPolicyBag[::Concurrency::MinConcurrency] = _MinConcurrency;
_ResolvePolicyValues();
}
///
/// Resolves some of the policy keys that are set to defaults, based on the characteristics of the underlying system.
///
void SchedulerPolicy::_ResolvePolicyValues()
{
// Resolve the SchedulerKind policy key value.
_M_pPolicyBag->_M_values._M_pPolicyBag[::Concurrency::SchedulerKind] = ::Concurrency::ThreadScheduler;
// Resolve MinConcurrency and MaxConcurrency, if either of them are set to the special value MaxExecutionResources.
unsigned int coreCount = ::Concurrency::GetProcessorCount();
ASSERT((coreCount > 0) && (coreCount <= INT_MAX));
if (_M_pPolicyBag->_M_values._M_pPolicyBag[MinConcurrency] == MaxExecutionResources)
{
if (_M_pPolicyBag->_M_values._M_pPolicyBag[MaxConcurrency] == MaxExecutionResources)
{
// [1] Both the keys are set to MaxExecutionResources.
_M_pPolicyBag->_M_values._M_pPolicyBag[MinConcurrency] = _M_pPolicyBag->_M_values._M_pPolicyBag[MaxConcurrency] = coreCount;
}
else
{
// [2] MinConcurrency is set to MaxExecutionResources.
_M_pPolicyBag->_M_values._M_pPolicyBag[MinConcurrency] = (_M_pPolicyBag->_M_values._M_pPolicyBag[MaxConcurrency] < coreCount) ?
_M_pPolicyBag->_M_values._M_pPolicyBag[MaxConcurrency] : coreCount;
}
}
else if (_M_pPolicyBag->_M_values._M_pPolicyBag[MaxConcurrency] == MaxExecutionResources)
{
// [3] MaxConcurrency is set to MaxExecutionResources.
_M_pPolicyBag->_M_values._M_pPolicyBag[MaxConcurrency] = (_M_pPolicyBag->_M_values._M_pPolicyBag[MinConcurrency] > coreCount) ?
_M_pPolicyBag->_M_values._M_pPolicyBag[MinConcurrency] : coreCount;
}
ASSERT(_M_pPolicyBag->_M_values._M_pPolicyBag[MaxConcurrency] >= _M_pPolicyBag->_M_values._M_pPolicyBag[MinConcurrency]);
}
const char* SchedulerPolicy::_StringFromPolicyKey(unsigned int index)
{
if (index > ::Concurrency::MaxPolicyElementKey)
index = ::Concurrency::MaxPolicyElementKey;
return PolicyElementKeyStrings[index];
}
bool SchedulerPolicy::_ValidPolicyKey(PolicyElementKey key)
{
return (key >= SchedulerKind && key < MaxPolicyElementKey);
}
bool SchedulerPolicy::_ValidPolicyValue(PolicyElementKey key, unsigned int value)
{
bool valid = true;
switch (key)
{
case ::Concurrency::SchedulerKind:
if ( value != ::Concurrency::ThreadScheduler )
{
valid = false;
}
break;
case ::Concurrency::ContextPriority:
{
int priority = (int)value;
//
// The win32 api accepts values [-7, 7), 15 and -15 for threads other than the current thread.
// In addition, we define a special value INHERIT_THREAD_PRIORITY, whereby the internal contexts
// inherit the priority of the thread creating the scheduler
//
if ( !(priority >= -7 && priority < 7
|| priority == 15
|| priority == -15
|| priority == INHERIT_THREAD_PRIORITY))
{
valid = false;
}
}
break;
case ::Concurrency::SchedulingProtocol:
if ( !(value == ::Concurrency::EnhanceScheduleGroupLocality
|| value == ::Concurrency::EnhanceForwardProgress))
{
valid = false;
}
break;
case ::Concurrency::MaxConcurrency:
if ( !((value > 0 && value <= INT_MAX) || value == MaxExecutionResources))
{
valid = false;
}
break;
case ::Concurrency::MinConcurrency:
if ( !((value <= INT_MAX) || value == MaxExecutionResources))
{
valid = false;
}
break;
case ::Concurrency::LocalContextCacheSize:
case ::Concurrency::ContextStackSize:
if ( !(value <= INT_MAX))
{
valid = false;
}
break;
case ::Concurrency::TargetOversubscriptionFactor:
if ( !(value > 0 && value <= INT_MAX))
{
valid = false;
}
break;
case ::Concurrency::DynamicProgressFeedback:
if ( !(value == ::Concurrency::ProgressFeedbackEnabled || value == ::Concurrency::ProgressFeedbackDisabled))
{
valid = false;
}
break;
case ::Concurrency::WinRTInitialization:
if ( !(value == ::Concurrency::InitializeWinRTAsMTA || value == ::Concurrency::DoNotInitializeWinRT))
{
valid = false;
}
break;
case ::Concurrency::MaxPolicyElementKey:
default:
terminate();
}
return valid;
}
void SchedulerPolicy::_ValidateConcRTPolicy() const
{
unsigned int minConcurrency = GetPolicyValue(::Concurrency::MinConcurrency);
if (minConcurrency == 0)
{
throw invalid_scheduler_policy_value(_StringFromPolicyKey(::Concurrency::MinConcurrency));
}
::Concurrency::DynamicProgressFeedbackType dynamicProgress =
(::Concurrency::DynamicProgressFeedbackType) GetPolicyValue(::Concurrency::DynamicProgressFeedback);
if (dynamicProgress == ProgressFeedbackDisabled)
{
throw invalid_scheduler_policy_value(_StringFromPolicyKey(::Concurrency::DynamicProgressFeedback));
}
}
///
/// Test a policy's concurrency limits.
///
bool SchedulerPolicy::_AreConcurrencyLimitsValid(unsigned int _MinConcurrency, unsigned int _MaxConcurrency)
{
// For concurrency limits that are != MaxExecutionResource, plug into the equation: _MinConcurrency <= _MaxConcurrency,
// and return false, if it does not hold.
// Validate Max
if ((_MaxConcurrency != MaxExecutionResources)
&& (_MinConcurrency != MaxExecutionResources) && (_MaxConcurrency < _MinConcurrency))
{
return false;
}
return true;
}
///
/// Test a policy's concurrency limits.
///
bool SchedulerPolicy::_AreConcurrencyLimitsValid() const
{
return _AreConcurrencyLimitsValid(GetPolicyValue(::Concurrency::MinConcurrency),
GetPolicyValue(::Concurrency::MaxConcurrency));
}
///
/// Test a policy's concurrency limits.
///
bool SchedulerPolicy::_ArePolicyCombinationsValid() const
{
return true;
}
} // namespace Concurrency