blob: 9f55f3b824f3dee62e1220fee6f002b6209b6c4e [file] [log] [blame]
/*
*
* Copyright (c) 2020 Project CHIP Authors
* Copyright (c) 2018 Nest Labs, Inc.
* All rights reserved.
*
* 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 Tizen platforms.
*/
/**
* Note: Use public include for PlatformManager which includes our local
* platform/<PLATFORM>/PlatformManager.h after defining interface
* class. */
#include <platform/PlatformManager.h>
#include <condition_variable>
#include <mutex>
#include <glib.h>
#include <lib/core/CHIPError.h>
#include <lib/support/CodeUtils.h>
#include <platform/DeviceInstanceInfoProvider.h>
#include <platform/Tizen/DeviceInstanceInfoProviderImpl.h>
#include "PosixConfig.h"
#include "SystemInfo.h"
#include "platform/internal/GenericPlatformManagerImpl.h"
#include "platform/internal/GenericPlatformManagerImpl.ipp"
#include "platform/internal/GenericPlatformManagerImpl_POSIX.h"
#include "platform/internal/GenericPlatformManagerImpl_POSIX.ipp"
namespace chip {
namespace DeviceLayer {
PlatformManagerImpl PlatformManagerImpl::sInstance;
namespace {
struct GLibMatterContextInvokeData
{
CHIP_ERROR (*mFunc)(void *);
void * mFuncUserData;
CHIP_ERROR mFuncResult;
// Sync primitives to wait for the function to be executed
std::mutex mDoneMutex;
std::condition_variable mDoneCond;
bool mDone = false;
};
void * GLibMainLoopThread(void * userData)
{
GMainLoop * loop = static_cast<GMainLoop *>(userData);
GMainContext * context = g_main_loop_get_context(loop);
g_main_context_push_thread_default(context);
g_main_loop_run(loop);
return nullptr;
}
} // namespace
CHIP_ERROR PlatformManagerImpl::_InitChipStack()
{
auto * context = g_main_context_new();
mGLibMainLoop = g_main_loop_new(context, FALSE);
mGLibMainLoopThread = g_thread_new("gmain-matter", GLibMainLoopThread, mGLibMainLoop);
g_main_context_unref(context);
{
// Wait for the GLib main loop to start. It is required that the GLib Matter
// context is acquired by the g_main_loop_run() before any other GLib function
// is called. Otherwise, the GLibMatterContextInvokeSync() might run callback
// functions on the wrong thread.
GLibMatterContextInvokeData invokeData{};
auto * idleSource = g_idle_source_new();
g_source_set_callback(
idleSource,
[](void * userData_) {
auto * data = reinterpret_cast<GLibMatterContextInvokeData *>(userData_);
std::unique_lock<std::mutex> lock(data->mDoneMutex);
data->mDone = true;
data->mDoneCond.notify_one();
return G_SOURCE_REMOVE;
},
&invokeData, nullptr);
g_source_attach(idleSource, g_main_loop_get_context(mGLibMainLoop));
g_source_unref(idleSource);
std::unique_lock<std::mutex> lock(invokeData.mDoneMutex);
invokeData.mDoneCond.wait(lock, [&invokeData]() { return invokeData.mDone; });
}
ReturnErrorOnFailure(Internal::PosixConfig::Init());
ReturnErrorOnFailure(Internal::GenericPlatformManagerImpl_POSIX<PlatformManagerImpl>::_InitChipStack());
// Now set up our device instance info provider. We couldn't do that
// earlier, because the generic implementation sets a generic one.
SetDeviceInstanceInfoProvider(&DeviceInstanceInfoProviderMgrImpl());
mStartTime = System::SystemClock().GetMonotonicTimestamp();
Internal::PlatformVersion version;
ReturnErrorOnFailure(Internal::SystemInfo::GetPlatformVersion(version));
ChipLogProgress(DeviceLayer, "Tizen Version: %d.%d", version.mMajor, version.mMinor);
return CHIP_NO_ERROR;
}
void PlatformManagerImpl::_Shutdown()
{
Internal::GenericPlatformManagerImpl_POSIX<PlatformManagerImpl>::_Shutdown();
g_main_loop_quit(mGLibMainLoop);
g_main_loop_unref(mGLibMainLoop);
g_thread_join(mGLibMainLoopThread);
}
CHIP_ERROR PlatformManagerImpl::_GLibMatterContextInvokeSync(CHIP_ERROR (*func)(void *), void * userData)
{
GLibMatterContextInvokeData invokeData{ func, userData };
g_main_context_invoke_full(
g_main_loop_get_context(mGLibMainLoop), G_PRIORITY_HIGH_IDLE,
[](void * userData_) {
auto * data = reinterpret_cast<GLibMatterContextInvokeData *>(userData_);
VerifyOrExit(g_main_context_get_thread_default() != nullptr,
ChipLogError(DeviceLayer, "GLib thread default main context is not set");
data->mFuncResult = CHIP_ERROR_INCORRECT_STATE);
data->mFuncResult = data->mFunc(data->mFuncUserData);
exit:
data->mDoneMutex.lock();
data->mDone = true;
data->mDoneMutex.unlock();
data->mDoneCond.notify_one();
return G_SOURCE_REMOVE;
},
&invokeData, nullptr);
std::unique_lock<std::mutex> lock(invokeData.mDoneMutex);
invokeData.mDoneCond.wait(lock, [&invokeData]() { return invokeData.mDone; });
return invokeData.mFuncResult;
}
} // namespace DeviceLayer
} // namespace chip