blob: b7dc8b681fa8e85b35d284ef2c6eb86785d7ec9e [file] [log] [blame]
/*
*
* Copyright (c) 2020 Project CHIP Authors
* Copyright (c) 2018 Nest Labs, Inc.
*
* 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.
*/
/**
* @file
* Provides an implementation of the PlatformManager object
* for Darwin platforms.
*/
#include <platform/internal/CHIPDeviceLayerInternal.h>
#include <tracing/metric_macros.h>
#if !CHIP_DISABLE_PLATFORM_KVS
#include <platform/Darwin/DeviceInstanceInfoProviderImpl.h>
#include <platform/DeviceInstanceInfoProvider.h>
#endif
#include <platform/Darwin/DiagnosticDataProviderImpl.h>
#include <platform/Darwin/PlatformMetricKeys.h>
#include <platform/PlatformManager.h>
// Include the non-inline definitions for the GenericPlatformManagerImpl<> template,
#include <platform/internal/GenericPlatformManagerImpl.ipp>
#include <CoreFoundation/CoreFoundation.h>
#include <tracing/metric_event.h>
using namespace chip::Tracing::DarwinPlatform;
namespace chip {
namespace DeviceLayer {
AtomicGlobal<PlatformManagerImpl> PlatformManagerImpl::sInstance;
PlatformManagerImpl::PlatformManagerImpl() :
mWorkQueue(dispatch_queue_create("org.csa-iot.matter.workqueue",
dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL,
QOS_CLASS_USER_INITIATED, QOS_MIN_RELATIVE_PRIORITY)))
{
// Tag our queue for IsWorkQueueCurrentQueue()
dispatch_queue_set_specific(mWorkQueue, this, this, nullptr);
dispatch_suspend(mWorkQueue);
}
CHIP_ERROR PlatformManagerImpl::_InitChipStack()
{
// Initialize the configuration system.
#if !CHIP_DISABLE_PLATFORM_KVS
ReturnErrorOnFailure(Internal::PosixConfig::Init());
#endif // CHIP_DISABLE_PLATFORM_KVS
#if !CHIP_SYSTEM_CONFIG_USE_LIBEV
// Ensure there is a dispatch queue available
static_cast<System::LayerSocketsLoop &>(DeviceLayer::SystemLayer()).SetDispatchQueue(GetWorkQueue());
#endif
// Call _InitChipStack() on the generic implementation base class
// to finish the initialization process.
ReturnErrorOnFailure(Internal::GenericPlatformManagerImpl<PlatformManagerImpl>::_InitChipStack());
#if !CHIP_DISABLE_PLATFORM_KVS
// Now set up our device instance info provider. We couldn't do that
// earlier, because the generic implementation sets a generic one.
SetDeviceInstanceInfoProvider(&DeviceInstanceInfoProviderMgrImpl());
#endif // CHIP_DISABLE_PLATFORM_KVS
mStartTime = System::SystemClock().GetMonotonicTimestamp();
return CHIP_NO_ERROR;
}
CHIP_ERROR PlatformManagerImpl::_StartEventLoopTask()
{
auto expected = WorkQueueState::kSuspended;
VerifyOrReturnError(mWorkQueueState.compare_exchange_strong(expected, WorkQueueState::kRunning), CHIP_ERROR_INCORRECT_STATE);
dispatch_resume(mWorkQueue);
return CHIP_NO_ERROR;
};
CHIP_ERROR PlatformManagerImpl::_StopEventLoopTask()
{
auto expected = WorkQueueState::kRunning;
VerifyOrReturnError(mWorkQueueState.compare_exchange_strong(expected, WorkQueueState::kSuspensionPending),
CHIP_ERROR_INCORRECT_STATE);
// We need to dispatch to the work queue to ensure any currently queued jobs
// finish executing. When called from outside the work queue we also need to
// wait for them to complete before returning to the caller, so we use
// dispatch_sync in that case.
(IsWorkQueueCurrentQueue() ? dispatch_async : dispatch_sync)(mWorkQueue, ^{
dispatch_suspend(mWorkQueue);
mWorkQueueState.store(WorkQueueState::kSuspended);
auto * semaphore = mRunLoopSem;
if (semaphore != nullptr)
{
dispatch_semaphore_signal(semaphore);
}
});
return CHIP_NO_ERROR;
}
void PlatformManagerImpl::_RunEventLoop()
{
mRunLoopSem = dispatch_semaphore_create(0);
_StartEventLoopTask();
//
// Block on the semaphore till we're signalled to stop by
// _StopEventLoopTask()
//
dispatch_semaphore_wait(mRunLoopSem, DISPATCH_TIME_FOREVER);
dispatch_release(mRunLoopSem);
mRunLoopSem = nullptr;
}
void PlatformManagerImpl::_Shutdown()
{
// Call up to the base class _Shutdown() to perform the bulk of the shutdown.
GenericPlatformManagerImpl<ImplClass>::_Shutdown();
}
CHIP_ERROR PlatformManagerImpl::_PostEvent(const ChipDeviceEvent * event)
{
const ChipDeviceEvent eventCopy = *event;
dispatch_async(mWorkQueue, ^{
DispatchEvent(&eventCopy);
});
return CHIP_NO_ERROR;
}
#if CHIP_STACK_LOCK_TRACKING_ENABLED
bool PlatformManagerImpl::_IsChipStackLockedByCurrentThread() const
{
// Assume our caller knows what they are doing in terms of concurrency if the work queue is suspended.
return IsWorkQueueCurrentQueue() || mWorkQueueState.load() == WorkQueueState::kSuspended;
};
#endif
bool PlatformManagerImpl::IsWorkQueueCurrentQueue() const
{
return dispatch_get_specific(this) == this;
}
CHIP_ERROR PlatformManagerImpl::StartBleScan(BleScannerDelegate * delegate)
{
#if CONFIG_NETWORK_LAYER_BLE
ReturnErrorOnFailureWithMetric(kMetricBLEScan, Internal::BLEMgrImpl().StartScan(delegate));
#endif // CONFIG_NETWORK_LAYER_BLE
return CHIP_NO_ERROR;
}
CHIP_ERROR PlatformManagerImpl::StopBleScan()
{
#if CONFIG_NETWORK_LAYER_BLE
ReturnErrorOnFailureWithMetric(kMetricBLEScan, Internal::BLEMgrImpl().StopScan());
#endif // CONFIG_NETWORK_LAYER_BLE
return CHIP_NO_ERROR;
}
CHIP_ERROR PlatformManagerImpl::PrepareCommissioning()
{
#if CONFIG_NETWORK_LAYER_BLE
ReturnErrorOnFailureWithMetric(kMetricBLEStartPreWarmScan, Internal::BLEMgrImpl().StartScan());
#endif // CONFIG_NETWORK_LAYER_BLE
return CHIP_NO_ERROR;
}
} // namespace DeviceLayer
} // namespace chip