-
Notifications
You must be signed in to change notification settings - Fork 90
fix: Refactor DConfig wrapper class generation for thread safety and … #531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: Dami-star The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
Synchronize source files from linuxdeepin/dtkcore. Source-pull-request: linuxdeepin/dtkcore#531
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR refactors the DConfig C++ wrapper class generation tool (dconfig2cpp) to improve thread safety and lifecycle management. The refactor introduces a data layer separation pattern, enhances the state machine from 3 states to 5 states, and implements atomic operations for thread-safe state transitions.
Key changes:
- Introduces a Data class (TreelandUserConfigData) to separate internal data management from the public API (TreelandUserConfig)
- Expands state machine from Invalid/Succeed/Failed to Invalid/Initializing/Succeed/Failed/Destroyed with atomic CAS operations
- Separates thread responsibilities: updateValue() in worker thread, updateProperty() in main thread with QMetaObject::invokeMethod for proper thread context
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
zccrs
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
还没有看完
Synchronize source files from linuxdeepin/dtkcore. Source-pull-request: linuxdeepin/dtkcore#531
Synchronize source files from linuxdeepin/dtkcore. Source-pull-request: linuxdeepin/dtkcore#531
Synchronize source files from linuxdeepin/dtkcore. Source-pull-request: linuxdeepin/dtkcore#531
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Synchronize source files from linuxdeepin/dtkcore. Source-pull-request: linuxdeepin/dtkcore#531
Synchronize source files from linuxdeepin/dtkcore. Source-pull-request: linuxdeepin/dtkcore#531
Synchronize source files from linuxdeepin/dtkcore. Source-pull-request: linuxdeepin/dtkcore#531
Synchronize source files from linuxdeepin/dtkcore. Source-pull-request: linuxdeepin/dtkcore#531
Synchronize source files from linuxdeepin/dtkcore. Source-pull-request: linuxdeepin/dtkcore#531
Synchronize source files from linuxdeepin/dtkcore. Source-pull-request: linuxdeepin/dtkcore#531
Synchronize source files from linuxdeepin/dtkcore. Source-pull-request: linuxdeepin/dtkcore#531
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Synchronize source files from linuxdeepin/dtkcore. Source-pull-request: linuxdeepin/dtkcore#531
Synchronize source files from linuxdeepin/dtkcore. Source-pull-request: linuxdeepin/dtkcore#531
Synchronize source files from linuxdeepin/dtkcore. Source-pull-request: linuxdeepin/dtkcore#531
…lifecycle management Problem: - Single-threaded design with weak state machine (Invalid -> Succeed/Failed) - No proper handling of object destruction during initialization - Signal emissions in worker thread context (incorrect thread context) - Fragile destructor unable to handle all cleanup scenarios Solution: 1. Introduce Data layer separation (TreelandUserConfigData + TreelandUserConfig) - Clear separation between internal data management and public API - Enables safer object lifecycle management 2. Enhance state machine (3-state -> 5-state model) - Add Initializing and Destroyed states - Use atomic CAS operations for thread-safe state transitions - States: Invalid -> Initializing -> (Succeed | Failed | Destroyed) 3. Improve async initialization and cleanup - Use QPointer for safe backref checks (prevent use-after-free) - Support 4 destruction paths: normal/failed/quick/mid-initialization - Atomic state transitions with proper signal emission guards 4. Separate thread responsibilities - updateValue(): Worker thread reads config values - updateProperty(): Main thread updates properties and emits signals - Use QMetaObject::invokeMethod for correct thread context Improvements: - Thread safety: Complete atomic operations coverage - Memory safety: QPointer guards prevent dangling pointers - Code clarity: Layered architecture with clear responsibilities - Backward compatibility: API unchanged
Synchronize source files from linuxdeepin/dtkcore. Source-pull-request: linuxdeepin/dtkcore#531
deepin pr auto review代码审查报告总体评价这是一个对DConfig配置管理相关代码的更新,主要改进了线程安全性和对象生命周期管理。代码引入了新的Data类来封装配置数据,并使用原子操作和状态机来管理配置的初始化过程。 主要改进点
潜在问题与建议1. 线程安全与竞态条件问题:在 QMetaObject::invokeMethod(this, [this, newValue, key, value]() {
if (m_userConfig && p_autoDisplayFeature != newValue) {
// m_userConfig可能在这里被设置为nullptr
Q_ASSERT(QThread::currentThread() == m_userConfig->thread());
// ...
}
});建议:在lambda捕获时使用 QPointer<dconfig_org_deepin_dtk_preference> safeUserConfig(m_userConfig);
QMetaObject::invokeMethod(this, [safeUserConfig, newValue, key, value]() {
if (safeUserConfig && safeUserConfig->m_data && safeUserConfig->m_data->p_autoDisplayFeature != newValue) {
Q_ASSERT(QThread::currentThread() == safeUserConfig->thread());
safeUserConfig->m_data->p_autoDisplayFeature = newValue;
Q_EMIT safeUserConfig->autoDisplayFeatureChanged();
Q_EMIT safeUserConfig->valueChanged(key, value);
}
});2. 错误处理改进问题:当配置初始化失败时, 建议:考虑添加错误码或错误消息参数: Q_SIGNALS:
void configInitializeFailed(const QString &errorMessage);3. 内存管理优化问题:在构造函数中,worker对象在lambda内被删除,但lambda本身可能还未执行完。 QMetaObject::invokeMethod(worker, [safeData, backend, name, appId, subpath, isGeneric, worker]() mutable {
delete worker;
worker = nullptr;
// ...
});建议:使用 QPointer<QObject> safeWorker(worker);
QMetaObject::invokeMethod(worker, [safeData, backend, name, appId, subpath, isGeneric, safeWorker]() mutable {
if (safeWorker) {
safeWorker->deleteLater();
}
// ...
});4. 性能优化问题:每次属性变更都会触发两个信号( 建议:考虑提供选项让用户选择是否需要 5. 代码可读性问题:状态转换逻辑较为复杂,特别是在构造函数和析构函数中。 建议:将状态转换逻辑封装为独立方法,提高可读性: bool tryTransitionToInitializing() {
return m_status.testAndSetOrdered(static_cast<int>(Status::Invalid),
static_cast<int>(Status::Initializing));
}
bool tryTransitionToSucceeded() {
return m_status.testAndSetOrdered(static_cast<int>(Status::Initializing),
static_cast<int>(Status::Succeeded));
}
bool tryTransitionToFailed() {
return m_status.testAndSetOrdered(static_cast<int>(Status::Initializing),
static_cast<int>(Status::Failed));
}6. 测试覆盖建议:确保以下场景有充分的单元测试覆盖:
其他建议
总结这次更新显著提高了代码的线程安全性和对象生命周期管理,但仍有改进空间。主要关注点应放在多线程环境下的竞态条件处理和错误处理机制上。建议在合并前进行充分的测试,特别是多线程场景下的压力测试。 |
| m_config.storeRelaxed(nullptr); | ||
| int oldStatus = m_data->m_status.fetchAndStoreOrdered(static_cast<int>(Data::Status::Destroyed)); | ||
|
|
||
| m_data->m_userConfig = nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
在修改m_status之前赋值
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return m_data->m_config.loadRelaxed(); | ||
| } | ||
| Q_INVOKABLE bool isInitializeSucceed() const { |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Status enum naming is inconsistent. The new state uses "Succeeded" (line 529) but the old comparison in isInitializeSucceed uses "Succeed" pattern (line 442). While this is just a rename within the same PR, the function name isInitializeSucceed should be isInitializeSucceeded for grammatical consistency with the enum value name.
| Q_INVOKABLE bool isInitializeSucceed() const { | |
| Q_INVOKABLE bool isInitializeSucceeded() const { |
| if (!safeData->m_status.testAndSetOrdered(static_cast<int>(Data::Status::Invalid), | ||
| static_cast<int>(Data::Status::Initializing))) { | ||
| // CAS failed, state already changed - userConfig destructor will handle cleanup | ||
| // Do not attempt to delete here as it would race with destructor | ||
| safeData->deleteLater(); |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the CAS operation fails at line 302-304, safeData->deleteLater() is called. However, this could cause a double-delete issue. If the CAS fails because the destructor already set the state to Destroyed, the destructor is responsible for cleaning up Data. Calling deleteLater() here could attempt to delete an object that's already being destroyed. Consider checking the actual state value after CAS failure to determine the appropriate action.
| if (!safeData->m_status.testAndSetOrdered(static_cast<int>(Data::Status::Invalid), | |
| static_cast<int>(Data::Status::Initializing))) { | |
| // CAS failed, state already changed - userConfig destructor will handle cleanup | |
| // Do not attempt to delete here as it would race with destructor | |
| safeData->deleteLater(); | |
| if (safeData && !safeData->m_status.testAndSetOrdered(static_cast<int>(Data::Status::Invalid), | |
| static_cast<int>(Data::Status::Initializing))) { | |
| // CAS failed, state already changed. | |
| // If the state is Destroyed, the destructor is responsible for cleanup. | |
| const int currentStatus = safeData->m_status.loadAcquire(); | |
| if (currentStatus != static_cast<int>(Data::Status::Destroyed)) { | |
| // Only schedule deletion if the object is still alive and not in Destroyed state. | |
| safeData->deleteLater(); | |
| } |
…lifecycle management
Problem:
Solution:
Introduce Data layer separation (TreelandUserConfigData + TreelandUserConfig)
Enhance state machine (3-state -> 5-state model)
Improve async initialization and cleanup
Separate thread responsibilities
Improvements: