terminal/src/cascadia/TerminalConnection/ConnectionStateHolder.h
Michael Niksa d711d731d7
Apply audit mode to TerminalConnection/Core/Settings and WinCon… (#4016)
## Summary of the Pull Request
- Enables auditing of some Terminal libraries (Connection, Core, Settings)
- Also audit WinConPTY.LIB since Connection depends on it

## PR Checklist
* [x] Rolls audit out to more things
* [x] I work here
* [x] Tests should still pass
* [x] Am core contributor

## Detailed Description of the Pull Request / Additional comments
This is turning on the auditing of these projects (as enabled by the heavier lifting in the other refactor) and then cleaning up the remaining warnings.

## Validation Steps Performed
- [x] Built it
- [x] Ran the tests
2020-01-03 10:44:27 -08:00

96 lines
3.9 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "../inc/cppwinrt_utils.h"
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
template<typename T>
struct ConnectionStateHolder
{
public:
ConnectionState State() const noexcept { return _connectionState; }
TYPED_EVENT(StateChanged, ITerminalConnection, winrt::Windows::Foundation::IInspectable);
protected:
#pragma warning(push)
#pragma warning(disable : 26447) // Analyzer is still upset about noexcepts throwing even with function level try.
// Method Description:
// - Attempt to transition to and signal the specified connection state.
// The transition will only be effected if the state is "beyond" the current state.
// Arguments:
// - state: the new state
// Return Value:
// Whether we've successfully transitioned to the new state.
bool _transitionToState(const ConnectionState state) noexcept
try
{
{
std::lock_guard<std::mutex> stateLock{ _stateMutex };
// only allow movement up the state gradient
if (state < _connectionState)
{
return false;
}
_connectionState = state;
}
// Dispatch the event outside of lock.
#pragma warning(suppress : 26491) // We can't avoid static_cast downcast because this is template magic.
_StateChangedHandlers(*static_cast<T*>(this), nullptr);
return true;
}
CATCH_FAIL_FAST()
// Method Description:
// - Returns whether the state is one of the N specified states.
// Arguments:
// - "...": the states
// Return Value:
// Whether we're in one of the states.
template<typename... Args>
bool _isStateOneOf(const Args&&... args) const noexcept
try
{
// Dark magic! This function uses C++17 fold expressions. These fold expressions roughly expand as follows:
// (... OP (expression_using_args))
// into ->
// expression_using_args[0] OP expression_using_args[1] OP expression_using_args[2] OP (etc.)
// We use that to first check that All Args types are ConnectionState (type[0] == ConnectionState && type[1] == ConnectionState && etc.)
// and then later to check that the current state is one of the passed-in ones:
// (_state == args[0] || _state == args[1] || etc.)
static_assert((... && std::is_same<Args, ConnectionState>::value), "all queried connection states must be from the ConnectionState enum");
std::lock_guard<std::mutex> stateLock{ _stateMutex };
return (... || (_connectionState == args));
}
CATCH_FAIL_FAST()
// Method Description:
// - Returns whether the state has reached or surpassed the specified state.
// Arguments:
// - state; the state to check against
// Return Value:
// Whether we're at or beyond the specified state
bool _isStateAtOrBeyond(const ConnectionState state) const noexcept
try
{
std::lock_guard<std::mutex> stateLock{ _stateMutex };
return _connectionState >= state;
}
CATCH_FAIL_FAST()
#pragma warning(pop)
// Method Description:
// - (Convenience:) Returns whether we're "connected".
// Return Value:
// Whether we're "connected"
bool _isConnected() const noexcept
{
return _isStateOneOf(ConnectionState::Connected);
}
private:
std::atomic<ConnectionState> _connectionState{ ConnectionState::NotConnected };
mutable std::mutex _stateMutex;
};
}