add rwlock for upgrading keys, use atomic for multi-thread

Signed-off-by: code4lala <fengziteng2@huawei.com>
Change-Id: Ib74f47a667a8f052aefe085d2389377f8e8a99d7
This commit is contained in:
code4lala 2024-08-20 20:35:12 +08:00
parent beb236f28d
commit 4bdbd1e9c1
16 changed files with 369 additions and 234 deletions

View File

@ -16,6 +16,7 @@
#include "hks_event_observer.h"
#include "common_event_support.h"
#include "rwlock.h"
#ifdef HAS_OS_ACCOUNT_PART
#include "os_account_manager.h"
#endif
@ -102,10 +103,11 @@ void SystemEventSubscriber::OnReceiveEvent(const OHOS::EventFwk::CommonEventData
std::string action = want.GetAction();
// judge whether is upgrading, wait for upgrade finished
if (HksWaitIfUpgrading() != HKS_SUCCESS) {
if (HksWaitIfPowerOnUpgrading() != HKS_SUCCESS) {
HKS_LOG_E("wait on upgrading failed.");
return;
}
OHOS::Utils::UniqueReadGuard<OHOS::Utils::RWLock> readGuard(g_upgradeOrRequestLock);
if (action == OHOS::EventFwk::CommonEventSupport::COMMON_EVENT_PACKAGE_REMOVED ||
action == OHOS::EventFwk::CommonEventSupport::COMMON_EVENT_SANDBOX_PACKAGE_REMOVED) {

View File

@ -20,11 +20,14 @@
#include "hks_type.h"
#include "hks_osaccount_check.h"
#include <atomic>
#ifdef HAS_OS_ACCOUNT_PART
#include "os_account_manager.h"
#endif // HAS_OS_ACCOUNT_PART
static volatile bool g_isCeUpgradeSucc = false;
namespace {
static volatile std::atomic_bool g_isCeUpgradeSucc = false;
static bool HksIsOsAccountVerified(const int32_t userId)
{
@ -40,12 +43,18 @@ static bool HksIsOsAccountVerified(const int32_t userId)
#endif
return isVerified;
}
}
void HksCheckIfNeedTransferFile(const uint32_t storageLevel, const int32_t storeUserId)
extern "C" void HksCheckIfNeedTransferFile(const uint32_t storageLevel, const int32_t storeUserId)
{
if (!g_isCeUpgradeSucc && storageLevel == HKS_AUTH_STORAGE_LEVEL_CE && HksIsOsAccountVerified(storeUserId)) {
HksUpgradeOnUserUnlock(storeUserId);
g_isCeUpgradeSucc = true;
bool flag = false;
bool equal = std::atomic_compare_exchange_strong(&g_isCeUpgradeSucc, &flag, true);
if (!equal) {
return;
}
HKS_LOG_I("never HksCheckIfNeedTransferFile before, first time upgrade ce!");
if (storageLevel == HKS_AUTH_STORAGE_LEVEL_CE && HksIsOsAccountVerified(storeUserId)) {
OHOS::Security::Hks::HksUpgradeOnUserUnlock(storeUserId);
}
}

View File

@ -17,6 +17,7 @@
#include <ipc_skeleton.h>
#include <iservice_registry.h>
#include <mutex>
#include <string_ex.h>
#include <system_ability_definition.h>
@ -34,6 +35,7 @@
#include "hks_upgrade_lock.h"
#include "hks_util.h"
#include "huks_service_ipc_interface_code.h"
#include "rwlock.h"
#ifdef CONFIG_USE_JEMALLOC_DFX_INTF
#include "malloc.h"
@ -282,10 +284,11 @@ int HksService::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParce
}
// judge whether is upgrading, wait for upgrade finished
if (HksWaitIfUpgrading() != HKS_SUCCESS) {
if (HksWaitIfPowerOnUpgrading() != HKS_SUCCESS) {
HKS_LOG_E("wait on upgrading failed.");
return HW_SYSTEM_ERROR;
}
OHOS::Utils::UniqueReadGuard<OHOS::Utils::RWLock> readGuard(g_upgradeOrRequestLock);
ProcessRemoteRequest(code, data, reply);
@ -388,6 +391,11 @@ void MoveDirectoryTree(const char *oldDir, const char *newDir)
void HksService::OnStart()
{
HKS_LOG_I("HksService OnStart");
std::lock_guard<std::mutex> lock(runningStateLock);
if (std::atomic_load(&runningState_) == STATE_RUNNING) {
HKS_LOG_I("HksService has already started");
return;
}
MoveDirectoryTree(OLD_PATH, NEW_PATH);
#ifdef HKS_USE_RKC_IN_STANDARD
// the intermediate mine's rkc is located in INTERMEDIATE_MINE_RKC_PATH, normal keys is located in NEW_PATH
@ -395,15 +403,7 @@ void HksService::OnStart()
// the original mine's rkc and normal keys are both located in OLD_MINE_PATH, should move all expect for rkc files
MoveMineOldFile(OLD_MINE_PATH, NEW_PATH);
#endif
if (runningState_ == STATE_RUNNING) {
HKS_LOG_I("HksService has already Started");
return;
}
if (HksUpgradeLockCreate() != HKS_SUCCESS) {
HKS_LOG_E("create upgrade lock on init failed.");
return;
}
if (HksProcessConditionCreate() != HKS_SUCCESS) {
HKS_LOG_E("create process condition on init failed.");
return;
@ -411,22 +411,27 @@ void HksService::OnStart()
// lock before huks init, for the upgrading will be thread safe.
#ifdef HUKS_ENABLE_UPGRADE_KEY_STORAGE_SECURE_LEVEL
HksUpgradeLock();
{
OHOS::Utils::UniqueWriteGuard<OHOS::Utils::RWLock> writeGuard(g_upgradeOrRequestLock);
#endif
if (!Init()) {
HKS_LOG_E("Failed to init HksService");
return;
if (!Init()) {
HKS_LOG_E("Failed to init HksService");
return;
}
#ifdef SUPPORT_COMMON_EVENT
(void)AddSystemAbilityListener(COMMON_EVENT_SERVICE_ID);
#endif
// this should be excuted after huks published and listener added.
HksUpgradeOnPowerOn();
#ifdef HUKS_ENABLE_UPGRADE_KEY_STORAGE_SECURE_LEVEL
}
#ifdef SUPPORT_COMMON_EVENT
(void)AddSystemAbilityListener(COMMON_EVENT_SERVICE_ID);
HksUpgradeOnPowerOnDoneNotifyAll();
#endif
// this should be excuted after huks published and listener added.
HksUpgradeOnPowerOn();
runningState_ = STATE_RUNNING;
std::atomic_store(&runningState_, STATE_RUNNING);
IPCSkeleton::SetMaxWorkThreadNum(HUKS_IPC_THREAD_NUM);
HKS_LOG_I("HksService start success.");
}
@ -447,7 +452,8 @@ void HksService::OnRemoveSystemAbility(int32_t systemAbilityId, const std::strin
void HksService::OnStop()
{
HKS_LOG_I("HksService Service OnStop");
runningState_ = STATE_NOT_START;
std::lock_guard<std::mutex> lock(runningStateLock);
std::atomic_store(&runningState_, STATE_NOT_START);
registerToService_ = false;
#ifndef HKS_UNTRUSTED_RUNNING_ENV
HksCloseDcmFunction();

View File

@ -19,6 +19,7 @@
#include "huks_service_ipc_interface_code.h"
#include "hks_sa_interface.h"
#include <atomic>
#include "iremote_broker.h"
#include "iremote_stub.h"
#include "nocopyable.h"
@ -62,7 +63,8 @@ private:
bool Init();
bool registerToService_;
ServiceRunningState runningState_;
volatile std::atomic_int runningState_;
std::mutex runningStateLock;
static std::mutex instanceLock;
static sptr<HksService> instance;
int OnRemotePluginRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option);

View File

@ -24,7 +24,7 @@ ohos_static_library("libhuks_upgrade_core_static") {
part_name = "huks"
public_configs = [ ":huks_config" ]
sources = [ "src/hks_upgrade.c" ]
sources = [ "src/hks_upgrade.cpp" ]
deps = [
"../../../../../../frameworks/huks_standard/main/common:libhuks_common_standard_static",
@ -50,5 +50,8 @@ ohos_static_library("libhuks_upgrade_core_static") {
configs =
[ "../../../../../../frameworks/config/build:l2_standard_common_config" ]
external_deps = [ "hilog:libhilog" ]
external_deps = [
"c_utils:utils",
"hilog:libhilog",
]
}

View File

@ -16,17 +16,17 @@
#ifndef HKS_UPGRADE_H
#define HKS_UPGRADE_H
#include "hks_type.h"
#include <cstdint>
#ifdef __cplusplus
extern "C" {
#endif
namespace OHOS {
namespace Security {
namespace Hks {
void HksUpgradeOnPowerOn(void);
void HksUpgradeOnUserUnlock(uint32_t userId);
#ifdef __cplusplus
}
#endif
}
}
#endif // HKS_UPGRADE_H

View File

@ -16,29 +16,47 @@
#include "hks_upgrade.h"
#include "hks_template.h"
#include "hks_type_enum.h"
#include "hks_upgrade_lock.h"
#include "rwlock.h"
#ifdef HUKS_ENABLE_UPGRADE_KEY_STORAGE_SECURE_LEVEL
#include "hks_file_transfer.h"
#endif
namespace OHOS {
namespace Security {
namespace Hks {
void HksUpgradeOnPowerOn(void)
{
HKS_LOG_I("enter HksUpgradeOnPowerOn.");
#ifdef HUKS_ENABLE_UPGRADE_KEY_STORAGE_SECURE_LEVEL
HKS_IF_NOT_SUCC_LOGE(HksUpgradeFileTransferOnPowerOn(), "HksUpgradeFileTransfer on power on failed!")
HksUpgradeUnlock();
#endif
HKS_LOG_I("leave HksUpgradeOnPowerOn.");
}
void HksUpgradeOnUserUnlock(uint32_t userId)
{
HKS_LOG_I("enter HksUpgradeOnUserUnlock.");
#ifdef HUKS_ENABLE_UPGRADE_KEY_STORAGE_SECURE_LEVEL
HksUpgradeLock();
HKS_IF_NOT_SUCC_LOGE(HksUpgradeFileTransferOnUserUnlock(userId), "HksUpgradeFileTransfer on user unlock failed!")
HksUpgradeUnlock();
g_upgradeOrRequestLock.UnLockRead();
// Since current thread have owned read lock, the power on upgrade have ended at earlier time,
// waiting for notify is not necessary.
{
HKS_LOG_I("acquiring for write lock...");
OHOS::Utils::UniqueWriteGuard<OHOS::Utils::RWLock> writeGuard(g_upgradeOrRequestLock);
int ret = HksUpgradeFileTransferOnUserUnlock(userId);
if (ret != HKS_SUCCESS) {
HKS_LOG_E("HksUpgradeFileTransferOnUserUnlock failed! %" LOG_PUBLIC "d", ret);
} else {
HKS_LOG_I("HksUpgradeFileTransferOnUserUnlock ok!");
}
}
g_upgradeOrRequestLock.LockRead();
#endif
HKS_LOG_I("leave HksUpgradeOnUserUnlock.");
}
}
}
}

View File

@ -24,7 +24,7 @@ ohos_static_library("libhuks_upgrade_lock_static") {
part_name = "huks"
public_configs = [ ":huks_config" ]
sources = [ "src/hks_upgrade_lock.c" ]
sources = [ "src/hks_upgrade_lock.cpp" ]
deps = [
"../../../../../../frameworks/huks_standard/main/common:libhuks_common_standard_static",
@ -47,5 +47,8 @@ ohos_static_library("libhuks_upgrade_lock_static") {
configs =
[ "../../../../../../frameworks/config/build:l2_standard_common_config" ]
external_deps = [ "hilog:libhilog" ]
external_deps = [
"c_utils:utils",
"hilog:libhilog",
]
}

View File

@ -16,20 +16,24 @@
#ifndef HKS_UPGRADE_LOCK_H
#define HKS_UPGRADE_LOCK_H
#include "hks_type.h"
#include <rwlock.h>
namespace OHOS {
namespace Security {
namespace Hks {
// OnStart upgrade <==> write, first time using ce level key upgrade <==> write
// OnRemoteRequest <==> read, OnReceiveEvent <==> read
extern OHOS::Utils::RWLock g_upgradeOrRequestLock;
#ifdef __cplusplus
extern "C" {
#endif
int32_t HksProcessConditionCreate(void);
int32_t HksWaitIfUpgrading(void);
int32_t HksUpgradeLockCreate(void);
void HksUpgradeLock(void);
void HksUpgradeUnlock(void);
int32_t HksWaitIfPowerOnUpgrading(void);
void HksUpgradeOnPowerOnDoneNotifyAll(void);
#ifdef __cplusplus
}
#endif
}
}
#endif // HKS_UPGRADE_LOCK_H

View File

@ -1,105 +0,0 @@
/*
* Copyright (c) 2024-2024 Huawei Device Co., Ltd.
* 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 "hks_upgrade_lock.h"
#include "hks_condition.h"
#include "hks_mutex.h"
#include "hks_template.h"
HksMutex *g_upgradeMutex = NULL;
HksMutex *g_upgradingMutex = NULL;
volatile bool g_upgrading = false;
HksCondition *g_processCondition = NULL;
int32_t HksProcessConditionCreate(void)
{
g_processCondition = HksConditionCreate();
if (g_processCondition == NULL) {
HKS_LOG_E("create process condition failed.");
return HKS_ERROR_BAD_STATE;
}
return HKS_SUCCESS;
}
static int32_t HksProcessConditionWait(void)
{
return HksConditionWait(g_processCondition);
}
static int32_t HksProcessConditionNotifyAll(void)
{
return HksConditionNotifyAll(g_processCondition);
}
int32_t HksWaitIfUpgrading(void)
{
if (HksMutexLock(g_upgradingMutex) != 0) {
HKS_LOG_E("lock g_upgrading failed.");
}
if (g_upgrading) {
if (HksMutexUnlock(g_upgradingMutex) != 0) {
HKS_LOG_E("unlock g_upgrading failed.");
}
HKS_LOG_I("upgrading, wait...");
return HksProcessConditionWait();
}
if (HksMutexUnlock(g_upgradingMutex) != 0) {
HKS_LOG_E("unlock g_upgrading failed.");
}
return HKS_SUCCESS;
}
int32_t HksUpgradeLockCreate(void)
{
g_upgradeMutex = HksMutexCreate();
if (g_upgradeMutex == NULL) {
HKS_LOG_E("create upgrade mutex failed.");
return HKS_ERROR_BAD_STATE;
}
g_upgradingMutex = HksMutexCreate();
if (g_upgradingMutex == NULL) {
HKS_LOG_E("create g_upgrading mutex failed.");
}
return HKS_SUCCESS;
}
void HksUpgradeLock(void)
{
HKS_LOG_I("get upgrade lock");
if (HksMutexLock(g_upgradeMutex) != 0) {
HKS_LOG_E("lock upgrade failed.");
return;
}
g_upgrading = true;
}
void HksUpgradeUnlock(void)
{
HKS_LOG_I("release upgrade lock");
if (HksMutexUnlock(g_upgradeMutex) != 0) {
HKS_LOG_E("unlock upgrade failed.");
// if fail, continue so the huks can work properly
}
if (HksMutexLock(g_upgradingMutex) != 0) {
HKS_LOG_E("lock g_upgrading failed.");
}
g_upgrading = false;
if (HksMutexUnlock(g_upgradingMutex) != 0) {
HKS_LOG_E("unlock g_upgrading failed.");
}
HksProcessConditionNotifyAll();
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2024-2024 Huawei Device Co., Ltd.
* 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 "hks_upgrade_lock.h"
#include "hks_condition.h"
#include "hks_mutex.h"
#include "hks_template.h"
#include "hks_type_enum.h"
#include <rwlock.h>
#include <atomic>
namespace OHOS {
namespace Security {
namespace Hks {
static HksCondition *g_powerOnUpgradeCondition = NULL;
OHOS::Utils::RWLock g_upgradeOrRequestLock(true);
int32_t HksProcessConditionCreate(void)
{
g_powerOnUpgradeCondition = HksConditionCreate();
HKS_IF_NULL_LOGE_RETURN(g_powerOnUpgradeCondition, HKS_ERROR_BAD_STATE, "create process condition failed.")
return HKS_SUCCESS;
}
int32_t HksWaitIfPowerOnUpgrading(void)
{
int32_t ret = HksConditionWait(g_powerOnUpgradeCondition);
HKS_IF_NOT_SUCC_LOGE_RETURN(ret, HKS_ERROR_BAD_STATE,
"HksWaitIfPowerOnUpgrading HksConditionWait fail %" LOG_PUBLIC "d", ret)
return HKS_SUCCESS;
}
void HksUpgradeOnPowerOnDoneNotifyAll(void)
{
HKS_LOG_I("HksUpgradeOnPowerOnDoneNotifyAll HksConditionNotifyAll");
int32_t ret = HksConditionNotifyAll(g_powerOnUpgradeCondition);
if (ret != HKS_SUCCESS) {
HKS_LOG_E("HksUpgradeOnPowerOnDoneNotifyAll HksConditionNotifyAll fail %" LOG_PUBLIC "d", ret);
} else {
HKS_LOG_I("HksUpgradeOnPowerOnDoneNotifyAll HksConditionNotifyAll ok!");
}
}
}
}
}

View File

@ -88,10 +88,10 @@ huks_sources = [
"//base/security/huks/services/huks_standard/huks_service/main/systemapi_mock/src/hks_useridm_api_mock.cpp",
"//base/security/huks/services/huks_standard/huks_service/main/systemapi_wrap/at_wrapper/src/hks_at_api_wrap.cpp",
"//base/security/huks/services/huks_standard/huks_service/main/systemapi_wrap/hitrace_meter_wrapper/src/hitrace_meter_wrapper.cpp",
"//base/security/huks/services/huks_standard/huks_service/main/upgrade/core/src/hks_upgrade.c",
"//base/security/huks/services/huks_standard/huks_service/main/upgrade/core/src/hks_upgrade.cpp",
"//base/security/huks/services/huks_standard/huks_service/main/upgrade/file_transfer/src/hks_config_parser.c",
"//base/security/huks/services/huks_standard/huks_service/main/upgrade/file_transfer/src/hks_file_transfer.c",
"//base/security/huks/services/huks_standard/huks_service/main/upgrade/lock/src/hks_upgrade_lock.c",
"//base/security/huks/services/huks_standard/huks_service/main/upgrade/lock/src/hks_upgrade_lock.cpp",
"//base/security/huks/test/unittest/huks_standard_test/module_test/mock/idl/src/huks_access_mock.c",
"//base/security/huks/utils/crypto_adapter/hks_client_service_adapter.c",
"//base/security/huks/utils/crypto_adapter/hks_client_service_adapter_common.c",

View File

@ -1,39 +0,0 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* 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.
*/
#ifndef HKS_CONDITION_TEST_H
#define HKS_CONDITION_TEST_H
namespace Unittest::HksUtilsConditionTest {
/**
* HksConditionWait
*/
int HksConditionTest001(void);
int HksConditionTest002(void);
int HksConditionTest003(void);
/**
* HksConditionNotify
*/
int HksConditionTest004(void);
/**
* HksConditionDestroy
*/
int HksConditionTest005(void);
}
#endif

View File

@ -13,24 +13,18 @@
* limitations under the License.
*/
#include "hks_condition_test.h"
#include <algorithm>
#include <gtest/gtest.h>
#include <iterator>
#include <pthread.h>
#include <random>
#include <rwlock.h>
#include <thread>
#include <unistd.h>
#include "file_ex.h"
#include "hks_api.h"
#include "hks_condition.h"
#include "hks_log.h"
#include "hks_type.h"
struct HksCondition {
bool notified;
bool waited;
pthread_mutex_t mutex;
pthread_cond_t cond;
};
using namespace testing::ext;
namespace Unittest::HksUtilsConditionTest {
@ -76,7 +70,6 @@ HWTEST_F(HksConditionTest, HksConditionTest001, TestSize.Level0)
void NotifyCondition(HksCondition *condition)
{
sleep(1);
(void)condition;
int32_t ret = HksConditionNotify(condition);
if (ret != HKS_SUCCESS) {
HKS_LOG_E("HksConditionNotify failed, ret = %" LOG_PUBLIC "d", ret);
@ -92,7 +85,7 @@ HWTEST_F(HksConditionTest, HksConditionTest002, TestSize.Level0)
{
HKS_LOG_I("enter HksConditionTest002");
HksCondition *condition = HksConditionCreate();
EXPECT_EQ(condition == nullptr, false) << "HksConditionCreate failed";
EXPECT_NE(condition, nullptr) << "HksConditionCreate failed";
std::thread thObj(NotifyCondition, condition);
int32_t ret = HksConditionWait(condition);
EXPECT_EQ(ret, HKS_SUCCESS) << "HksConditionTest002 failed, ret = " << ret;
@ -109,8 +102,9 @@ HWTEST_F(HksConditionTest, HksConditionTest003, TestSize.Level0)
{
HKS_LOG_I("enter HksConditionTest003");
HksCondition *condition = HksConditionCreate();
condition->notified = true;
int32_t ret = HksConditionWait(condition);
int32_t ret = HksConditionNotify(condition);
EXPECT_EQ(ret, HKS_SUCCESS) << "HksConditionTest003 failed, ret = " << ret;
ret = HksConditionWait(condition);
EXPECT_EQ(ret, HKS_SUCCESS) << "HksConditionTest003 failed, ret = " << ret;
HksConditionDestroy(condition);
}
@ -124,9 +118,10 @@ HWTEST_F(HksConditionTest, HksConditionTest004, TestSize.Level0)
{
HKS_LOG_I("enter HksConditionTest004");
HksCondition *condition = HksConditionCreate();
condition->notified = true;
int32_t ret = HksConditionNotify(condition);
EXPECT_EQ(ret, HKS_SUCCESS) << "HksConditionTest004 failed, ret = " << ret;
ret = HksConditionNotify(condition);
EXPECT_EQ(ret, HKS_SUCCESS) << "HksConditionTest004 failed, ret = " << ret;
HksConditionDestroy(condition);
}
@ -140,6 +135,180 @@ HWTEST_F(HksConditionTest, HksConditionTest005, TestSize.Level0)
HKS_LOG_I("enter HksConditionTest005");
HksConditionDestroy(nullptr);
int32_t ret = HksInitialize();
ASSERT_TRUE(ret == HKS_SUCCESS);
EXPECT_EQ(ret, HKS_SUCCESS);
}
static void *WaitThread(void *p)
{
EXPECT_EQ(HksConditionWait(static_cast<HksCondition *>(p)), HKS_SUCCESS);
return nullptr;
}
/**
* @tc.name: HksConditionTest.HksConditionTest006
* @tc.desc: case fail if stuck
* @tc.type: FUNC
*/
HWTEST_F(HksConditionTest, HksConditionTest006, TestSize.Level0)
{
HKS_LOG_I("enter HksConditionTest006");
enum {
TSET_THREADS_COUNT = 10,
TEST_TIMES = 100,
};
for (int no = 0; no < TEST_TIMES; ++no) {
HksCondition *condition = HksConditionCreate();
EXPECT_NE(condition, nullptr);
pthread_t threads[TSET_THREADS_COUNT] {};
for (int i = 0; i < TSET_THREADS_COUNT; ++i) {
EXPECT_EQ(pthread_create(&threads[i], nullptr, WaitThread, condition), 0);
}
EXPECT_EQ(HksConditionNotifyAll(condition), HKS_SUCCESS);
for (int i = 0; i < TSET_THREADS_COUNT; ++i) {
EXPECT_EQ(pthread_join(threads[i], nullptr), 0);
}
HksConditionDestroy(condition);
}
}
uint64_t Fib(uint64_t n)
{
if (n < 1) {
return 0;
}
enum {
FIB_START_INDEX = 2,
};
if (n <= FIB_START_INDEX) {
return 1;
}
int current = 1;
int previous = 0;
for (uint64_t i = 1; i < n; ++i) {
int next = current + previous;
previous = current;
current = next;
}
return current;
}
static uint64_t TimeConsumingWork(uint64_t repeatTimes, uint64_t fibNumber)
{
uint64_t sum = 0;
for (uint64_t i = 0; i < repeatTimes; ++i) {
sum += Fib(fibNumber);
}
if (repeatTimes == 0) {
return Fib(fibNumber);
}
// avoid divisor zero
return sum / repeatTimes;
}
static OHOS::Utils::RWLock g_rwLock(true);
static void *OnStartTest(void *p)
{
{
OHOS::Utils::UniqueWriteGuard<OHOS::Utils::RWLock> writeGuard(g_rwLock);
enum {
CALC_FIB_TIMES = 10'000'000,
CALC_FIB_NUMBER = 40,
};
// huks sa service start, upgrade keys
EXPECT_EQ(TimeConsumingWork(CALC_FIB_TIMES, CALC_FIB_NUMBER), Fib(CALC_FIB_NUMBER));
}
EXPECT_EQ(HksConditionNotifyAll(static_cast<HksCondition *>(p)), HKS_SUCCESS);
return nullptr;
}
static void HksUpgradeOnUserUnlockTest()
{
g_rwLock.UnLockRead();
{
OHOS::Utils::UniqueWriteGuard<OHOS::Utils::RWLock> writeGuard(g_rwLock);
enum {
CALC_FIB_TIMES = 10'000'000,
CALC_FIB_NUMBER = 40,
};
// upgrade keys in case that user unlocked, or first time someone using credential-encrypted level key.
EXPECT_EQ(TimeConsumingWork(CALC_FIB_TIMES, CALC_FIB_NUMBER), Fib(CALC_FIB_NUMBER));
}
g_rwLock.LockRead();
}
static volatile std::atomic_bool g_isCeUpgradeSucc = false;
static void *OnRemoteRequestTest(void *p)
{
EXPECT_EQ(HksConditionWait(static_cast<HksCondition *>(p)), HKS_SUCCESS);
OHOS::Utils::UniqueReadGuard<OHOS::Utils::RWLock> readGuard(g_rwLock);
enum {
CALC_FIB_TIMES = 1'000'000,
CALC_FIB_NUMBER = 40,
};
// someone is invoking huks
EXPECT_EQ(TimeConsumingWork(CALC_FIB_TIMES, CALC_FIB_NUMBER), Fib(CALC_FIB_NUMBER));
enum {
IF_STORAGE_LEVEL_IS_CE = 2,
};
// someone is invoking huks for credential-encrypted level key
if (std::rand() % IF_STORAGE_LEVEL_IS_CE) {
bool flag = false;
if (std::atomic_compare_exchange_strong(&g_isCeUpgradeSucc, &flag, true)) {
HksUpgradeOnUserUnlockTest();
}
}
return nullptr;
}
static void *OnReceiveEventTest(void *p)
{
EXPECT_EQ(HksConditionWait(static_cast<HksCondition *>(p)), HKS_SUCCESS);
OHOS::Utils::UniqueReadGuard<OHOS::Utils::RWLock> readGuard(g_rwLock);
HksUpgradeOnUserUnlockTest();
return nullptr;
}
/**
* @tc.name: HksConditionTest.HksConditionTest007
* @tc.desc: case fail if stuck
* @tc.type: FUNC
*/
HWTEST_F(HksConditionTest, HksConditionTest007, TestSize.Level0)
{
HKS_LOG_I("enter HksConditionTest007");
enum {
TEST_THREADS_COUNT = 20,
TEST_TIMES = 100,
TEST_ON_RECEIVE_EVENT_THREADS_COUNT = 5,
};
for (int no = 0; no < TEST_TIMES; ++no) {
HksCondition *condition = HksConditionCreate();
EXPECT_NE(condition, nullptr);
void *(*functions[TEST_THREADS_COUNT])(void *) {};
// 1 :> OnStartTest, TEST_ON_RECEIVE_EVENT_THREADS_COUNT :> OnReceiveEventTest, others :> OnRemoteRequestTest
std::fill(std::begin(functions), std::end(functions), OnRemoteRequestTest);
std::fill_n(std::begin(functions), TEST_ON_RECEIVE_EVENT_THREADS_COUNT, OnReceiveEventTest);
functions[TEST_THREADS_COUNT - 1] = OnStartTest;
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(std::begin(functions), std::end(functions), g);
pthread_t threads[TEST_THREADS_COUNT] {};
for (int i = 0; i < TEST_THREADS_COUNT; ++i) {
EXPECT_EQ(pthread_create(&threads[i], nullptr, functions[i], condition), 0);
}
for (int i = 0; i < TEST_THREADS_COUNT; ++i) {
EXPECT_EQ(pthread_join(threads[i], nullptr), 0);
}
HksConditionDestroy(condition);
}
}
}

View File

@ -19,6 +19,10 @@
#include "hks_template.h"
#include <errno.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
@ -26,8 +30,7 @@ extern "C" {
#endif
struct HksCondition {
bool notified;
bool waited;
volatile atomic_bool notified;
pthread_mutex_t mutex;
pthread_cond_t cond;
};
@ -45,21 +48,18 @@ int32_t HksConditionWait(HksCondition *condition)
HKS_LOG_ERRNO("HksConditionWait pthread_mutex_lock fail!", ret);
return ret;
}
if (condition->notified) {
condition->notified = false;
if (atomic_load(&condition->notified)) {
int unlockRet = pthread_mutex_unlock(&condition->mutex);
if (unlockRet != 0) {
HKS_LOG_ERRNO("HksConditionWait notified pthread_mutex_unlock fail!", unlockRet);
}
return 0;
} else {
condition->waited = true;
HKS_LOG_I("HksConditionWait begin wait...");
ret = pthread_cond_wait(&condition->cond, &condition->mutex);
if (ret != 0) {
HKS_LOG_ERRNO("HksConditionWait pthread_cond_wait fail!", ret);
}
condition->waited = false;
condition->notified = false;
int unlockRet = pthread_mutex_unlock(&condition->mutex);
if (unlockRet != 0) {
HKS_LOG_ERRNO("HksConditionWait waited pthread_mutex_unlock fail!", unlockRet);
@ -78,11 +78,13 @@ int32_t HksConditionNotify(HksCondition *condition)
return ret;
}
if (!condition->waited) {
condition->notified = true;
bool flag = false;
if (atomic_compare_exchange_strong(&condition->notified, &flag, true)) {
HKS_LOG_I("never pthread_cond_signal before, first time notify!");
} else {
condition->notified = false;
HKS_LOG_W("do pthread_cond_signal again!");
}
ret = pthread_cond_signal(&condition->cond);
if (ret != 0) {
HKS_LOG_ERRNO("HksConditionNotify pthread_cond_signal fail!", ret);
@ -104,11 +106,13 @@ int32_t HksConditionNotifyAll(HksCondition *condition)
return ret;
}
if (!condition->waited) {
condition->notified = true;
bool flag = false;
if (atomic_compare_exchange_strong(&condition->notified, &flag, true)) {
HKS_LOG_I("never pthread_cond_broadcast before, first time notify!");
} else {
condition->notified = false;
HKS_LOG_W("do pthread_cond_broadcast again!");
}
ret = pthread_cond_broadcast(&condition->cond);
if (ret != 0) {
HKS_LOG_ERRNO("HksConditionNotifyAll pthread_cond_broadcast fail!", ret);
@ -124,8 +128,7 @@ HksCondition *HksConditionCreate(void)
{
HksCondition *condition = (HksCondition *)HksMalloc(sizeof(HksCondition));
HKS_IF_NULL_RETURN(condition, NULL)
condition->notified = false;
condition->waited = false;
atomic_store(&condition->notified, false);
int32_t ret = pthread_mutex_init(&condition->mutex, NULL);
if (ret != 0) {
HKS_LOG_ERRNO("HksConditionCreate pthread_mutex_init fail!", ret);

View File

@ -16,9 +16,10 @@
#ifndef HKS_CONDITION_H
#define HKS_CONDITION_H
#include "pthread.h"
#include "hks_type.h"
#include <stdint.h>
// One-time condition struct, you CAN NOT reset it after notify or notify-all,
// you can only create a new one.
typedef struct HksCondition HksCondition;
#ifdef __cplusplus