From 3f31e431addcd1adbb5a4ad36a0b8baa415f770c Mon Sep 17 00:00:00 2001 From: Jianmin Zhao Date: Tue, 21 Jul 2026 19:26:15 -0700 Subject: [PATCH 1/2] Use AtomicRetained for _replicator. This to respond to CBSE-23263. --- Replicator/c4IncomingReplicator.hh | 2 +- Replicator/c4RemoteReplicator.hh | 4 +-- Replicator/c4ReplicatorImpl.cc | 53 ++++++++++++++++-------------- Replicator/c4ReplicatorImpl.hh | 5 +-- 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/Replicator/c4IncomingReplicator.hh b/Replicator/c4IncomingReplicator.hh index bf07aeaca2..f6b156013f 100644 --- a/Replicator/c4IncomingReplicator.hh +++ b/Replicator/c4IncomingReplicator.hh @@ -44,7 +44,7 @@ namespace litecore { // are not the _actual_ addresses of the object, but rather the pointer to // its Logging virtual table since inside of _logVerbose this is all that // is known. - _logVerbose("C4IncomingRepl %p created Repl %p", (Logging*)this, (Logging*)_replicator.get()); + _logVerbose("C4IncomingRepl %p created Repl %p", (Logging*)this, (Logging*)_replicator.get().get()); _openSocket = nullptr; } diff --git a/Replicator/c4RemoteReplicator.hh b/Replicator/c4RemoteReplicator.hh index 470ab0ed46..6f3f30b40f 100644 --- a/Replicator/c4RemoteReplicator.hh +++ b/Replicator/c4RemoteReplicator.hh @@ -59,7 +59,7 @@ namespace litecore { void start(bool reset) noexcept override { LOCK(_mutex); - if ( _replicator ) return; + if ( _replicator.get() ) return; _retryCount = 0; if ( !_restart(reset) ) { UNLOCK(); @@ -139,7 +139,7 @@ namespace litecore { // are not the _actual_ addresses of the object, but rather the pointer to // its Logging virtual table since inside of _logVerbose this is all that // is known. - _logVerbose("C4RemoteRepl %p created Repl %p", (Logging*)this, (Logging*)_replicator.get()); + _logVerbose("C4RemoteRepl %p created Repl %p", (Logging*)this, (Logging*)_replicator.get().get()); } // Both `start` and `retry` end up calling this. diff --git a/Replicator/c4ReplicatorImpl.cc b/Replicator/c4ReplicatorImpl.cc index d01e1d5c86..65712cbd8f 100644 --- a/Replicator/c4ReplicatorImpl.cc +++ b/Replicator/c4ReplicatorImpl.cc @@ -46,7 +46,7 @@ namespace litecore { // Tear down the Replicator instance -- this is important in the case where it was // never started, because otherwise there will be a bunch of ref cycles that cause many // objects (including C4Databases) to be leaked. [CBL-524] - if ( _replicator ) _replicator->terminate(); + if ( auto repl = _replicator.get() ) repl->terminate(); } void C4ReplicatorImpl::start(bool reset) noexcept { @@ -58,7 +58,7 @@ namespace litecore { return; } - if ( !_replicator ) { + if ( !_replicator.get() ) { clearCorrelationID(); if ( !_start(reset) ) { UNLOCK(); @@ -155,9 +155,9 @@ namespace litecore { return; } - if ( _replicator ) { + if ( auto repl = _replicator.get() ) { _status.level = kC4Stopping; - _replicator->stop(); + repl->stop(); } else if ( _status.level != kC4Stopped ) { _status.level = kC4Stopped; _status.progress = {}; @@ -207,21 +207,20 @@ namespace litecore { return _peerTLSCertificate; } - void C4ReplicatorImpl::_registerBLIPHandlersNow(BLIPHandlerSpecs specs) { - for ( auto& s : specs ) - _replicator->registerBLIPHandler(std::move(s.profile), s.atBeginning, std::move(s.handler)); + void C4ReplicatorImpl::_registerBLIPHandlersNow(Replicator* repl, BLIPHandlerSpecs specs) { + for ( auto& s : specs ) repl->registerBLIPHandler(std::move(s.profile), s.atBeginning, std::move(s.handler)); } void C4ReplicatorImpl::registerBLIPHandlers(BLIPHandlerSpecs const& specs) { LOCK(_mutex); - if ( _replicator ) _registerBLIPHandlersNow(specs); + if ( auto repl = _replicator.get() ) _registerBLIPHandlersNow(repl, specs); else _pendingHandlers.insert(_pendingHandlers.end(), specs.begin(), specs.end()); } void C4ReplicatorImpl::sendBLIPRequest(blip::MessageBuilder& request) { LOCK(_mutex); - _replicator->sendBLIPRequest(request); + if ( auto repl = _replicator.get() ) repl->sendBLIPRequest(request); } #endif @@ -275,7 +274,7 @@ namespace litecore { } bool C4ReplicatorImpl::_start(bool reset) noexcept { - if ( !_replicator ) { + if ( !_replicator.get() ) { try { createReplicator(); } catch ( exception& x ) { @@ -284,30 +283,32 @@ namespace litecore { return false; } } + auto repl = _replicator.get(); + if ( !repl ) return false; setStatusFlag(kC4Suspended, false); - logInfo("Starting Replicator %s with config: {%s} and endpoint: %.*s", _replicator->loggingName().c_str(), - std::string(*_options).c_str(), SPLAT(_replicator->remoteURL())); + logInfo("Starting Replicator %s with config: {%s} and endpoint: %.*s", repl->loggingName().c_str(), + std::string(*_options).c_str(), SPLAT(repl->remoteURL())); _selfRetain = this; // keep myself alive till Replicator stops - updateStatusFromReplicator(_replicator->status()); + updateStatusFromReplicator(repl->status()); _responseHeaders = nullptr; #ifdef COUCHBASE_ENTERPRISE _peerTLSCertificateData = nullopt; _peerTLSCertificate = nullptr; - _registerBLIPHandlersNow(std::move(_pendingHandlers)); + _registerBLIPHandlersNow(repl, std::move(_pendingHandlers)); _pendingHandlers.clear(); #endif - _replicator->start(reset); + repl->start(reset); return true; } void C4ReplicatorImpl::_suspend() noexcept { // called with _mutex locked - if ( _replicator ) { + if ( auto repl = _replicator.get() ) { _status.level = kC4Stopping; - _replicator->stop(); + repl->stop(); } } @@ -330,16 +331,17 @@ namespace litecore { bool stopped, resume = false; { LOCK(_mutex); - if ( repl != _replicator ) return; + auto replicator = _replicator.get(); + if ( repl != replicator ) return; // invariant: repl != nullptr if ( !_correlationID ) - if ( auto corrID = _replicator->getCorrelationID() ) setCorrelationID(corrID); + if ( auto corrID = replicator->getCorrelationID() ) setCorrelationID(corrID); auto oldLevel = _status.level; updateStatusFromReplicator((C4ReplicatorStatus)newStatus); if ( _status.level > kC4Connecting && oldLevel <= kC4Connecting ) { // Connected! By now we know the HTTP headers and (optional) peer cert: - _responseHeaders = _replicator->httpResponse().second.encode(); + _responseHeaders = replicator->httpResponse().second.encode(); #ifdef COUCHBASE_ENTERPRISE if ( !_peerTLSCertificateData.has_value() ) _peerTLSCertificateData = nullslice; // definitely no peer cert @@ -347,7 +349,8 @@ namespace litecore { handleConnected(); } if ( _status.level == kC4Stopped ) { - _replicator->terminate(); + replicator->terminate(); + replicator = nullptr; _replicator = nullptr; if ( statusFlag(kC4Suspended) ) { // If suspended, go to Offline state when Replicator stops @@ -375,7 +378,8 @@ namespace litecore { void C4ReplicatorImpl::replicatorDocumentsEnded(Replicator* repl, const std::vector>& revs) { - if ( repl != _replicator ) return; + auto replicator = _replicator.get(); + if ( repl != replicator ) return; auto nRevs = revs.size(); std::vector docsEnded; @@ -394,7 +398,8 @@ namespace litecore { } void C4ReplicatorImpl::replicatorBlobProgress(Replicator* repl, const Replicator::BlobProgress& p) { - if ( repl != _replicator ) return; + auto replicator = _replicator.get(); + if ( repl != replicator ) return; auto onBlob = _onBlobProgress.load(); if ( onBlob ) onBlob(this, (p.dir == Dir::kPushing), p.collSpec, p.docID, p.docProperty, p.key, p.bytesCompleted, @@ -495,7 +500,7 @@ namespace litecore { alloc_slice C4ReplicatorImpl::correlationID() const noexcept { LOCK(_mutex); if ( _correlationID ) return _correlationID; - if ( _replicator ) return _replicator->getCorrelationID(); + if ( auto repl = _replicator.get() ) return repl->getCorrelationID(); return {}; } } // namespace litecore diff --git a/Replicator/c4ReplicatorImpl.hh b/Replicator/c4ReplicatorImpl.hh index 5b74e2d7e0..d5707bac2e 100644 --- a/Replicator/c4ReplicatorImpl.hh +++ b/Replicator/c4ReplicatorImpl.hh @@ -12,6 +12,7 @@ #pragma once +#include "AtomicRetained.hh" #include "c4Replicator.hh" #include "c4Database.hh" #include "c4Internal.hh" @@ -154,7 +155,7 @@ namespace litecore { mutable std::mutex _mutex; DatabaseOrPool const _database; Retained _options; - Retained _replicator; + AtomicRetained _replicator; C4ReplicatorStatus _status{kC4Stopped}; bool _activeWhenSuspended{false}; bool _cancelStop{false}; @@ -166,7 +167,7 @@ namespace litecore { private: #ifdef COUCHBASE_ENTERPRISE - void _registerBLIPHandlersNow(BLIPHandlerSpecs); + void _registerBLIPHandlersNow(Replicator* repl, BLIPHandlerSpecs); #endif class PendingDocuments; From e6d96219522e30098dfaa59ce3818933af3cfdc6 Mon Sep 17 00:00:00 2001 From: Jim Borden Date: Fri, 17 Jul 2026 09:56:32 +0900 Subject: [PATCH 2/2] Branch for making an Android ASAN build This was confirmed working via a manual test inside adb shell, as long as the required dep libs are present as specified in the Android documentation --- CMakeLists.txt | 1 + cmake/platform_android.cmake | 23 +++++++++++++++++++++++ jenkins/build_server_android.sh | 2 ++ 3 files changed, 26 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f9084774bf..a089e84a6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,6 +87,7 @@ set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS ) option(CODE_COVERAGE_ENABLED "Set whether or not code coverage information should be generated" OFF) +option(LITECORE_SANITIZE "Enables address and undefined-behavior sanitizers (Clang only)" OFF) option(LITECORE_PERF_TESTING_MODE "Build LiteCore with more things public than in production to facilitate perf testing" OFF) option(BUILD_ENTERPRISE "Set whether or not to build enterprise edition" OFF) option(LITECORE_DISABLE_ICU "Disables ICU linking" OFF) diff --git a/cmake/platform_android.cmake b/cmake/platform_android.cmake index 9c675c7810..0871b9fd33 100644 --- a/cmake/platform_android.cmake +++ b/cmake/platform_android.cmake @@ -51,12 +51,33 @@ endfunction() function(setup_litecore_build) setup_litecore_build_linux() + if (LITECORE_SANITIZE AND NOT CODE_COVERAGE_ENABLED) + set(SANITIZER_COMPILE_FLAGS -fsanitize=address -fno-omit-frame-pointer) + + target_link_options( + LiteCore PRIVATE + -fsanitize=address + ) + + foreach(otherVariant FleeceBase FleeceObjects FleeceStatic LiteCoreStatic) + target_compile_options( + ${otherVariant} PRIVATE + ${SANITIZER_COMPILE_FLAGS} + ) + endforeach() + endif() + foreach(liteCoreVariant LiteCoreObjects LiteCoreUnitTesting) target_compile_definitions( ${liteCoreVariant} PRIVATE -DLITECORE_USES_ICU=1 ) + target_compile_options( + ${liteCoreVariant} PRIVATE + ${SANITIZER_COMPILE_FLAGS} + ) + target_include_directories( ${liteCoreVariant} PRIVATE LiteCore/Android @@ -68,6 +89,8 @@ function(setup_litecore_build) ) endforeach() + + target_compile_options( CouchbaseSqlite3 PRIVATE -DSQLITE_UNLINK_AFTER_CLOSE diff --git a/jenkins/build_server_android.sh b/jenkins/build_server_android.sh index e6efc1f764..bac5b1c685 100755 --- a/jenkins/build_server_android.sh +++ b/jenkins/build_server_android.sh @@ -164,6 +164,8 @@ ${CMAKE} \ -DEMBEDDED_MDNS=ON \ -DCMAKE_INSTALL_PREFIX=`pwd`/install \ -DCMAKE_BUILD_TYPE=Debug \ + -DANDROID_STL=c++_shared \ + -DLITECORE_SANITIZE=ON \ -DVERSION=${VERSION} \ -DBLD_NUM=${BLD_NUM} \ -S ../couchbase-lite-core