Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
a783517
Fix HTTP client torn reads and response memory leak
bmehta001 Apr 28, 2026
28cf17d
Fix WorkerThread shutdown: safe cleanup and diagnostics
bmehta001 Apr 28, 2026
a355ec5
Make m_runningLatency and m_scheduledUploadTime atomic
bmehta001 Apr 28, 2026
de46cb2
Fix static-destruction-order crash in Logger destructor
bmehta001 Apr 28, 2026
706a01f
Use cleaner shutdown and scheduler synchronization fixes
bmehta001 Apr 30, 2026
0b27717
Avoid holding TPM scheduler mutex during cancel
bmehta001 Apr 30, 2026
2cdf817
Address runtime review comments
bmehta001 May 4, 2026
95519ef
Apply force-scheduled latency when running cancel fails
bmehta001 May 4, 2026
11820ae
Merge branch 'main' into bhamehta/runtime-fixes
bmehta001 May 6, 2026
68f4dd0
Simplify TPM cancellation cleanup
bmehta001 May 11, 2026
4a8cc9d
Simplify TPM force scheduling test
bmehta001 May 11, 2026
5638972
Keep TPM cancellation comment wording
bmehta001 May 11, 2026
05bd377
Address runtime review comments
bmehta001 May 11, 2026
2c559d0
Clean up runtime logging follow-ups
bmehta001 May 12, 2026
b0ad7d8
Merge branch 'main' into bhamehta/runtime-fixes
bmehta001 May 20, 2026
2241c38
Merge branch 'main' into bhamehta/runtime-fixes
bmehta001 May 20, 2026
eb3bfff
Merge branch 'main' into bhamehta/runtime-fixes
bmehta001 Jun 1, 2026
a111e11
Merge branch 'main' into bhamehta/runtime-fixes
bmehta001 Jun 1, 2026
42cfa76
Merge remote-tracking branch 'msft/main' into bhamehta/runtime-fixes
bmehta001 Jun 3, 2026
042f077
Merge branch 'main' into bhamehta/runtime-fixes
bmehta001 Jun 9, 2026
9ae10ec
pal: return a no-op handle when a scheduled task is dropped
bmehta001 Jun 9, 2026
e9b1957
tpm/tests: address Copilot round feedback (printf cast + test suite n…
bmehta001 Jun 10, 2026
6429fef
Merge branch 'main' into bhamehta/runtime-fixes
bmehta001 Jun 10, 2026
797ede0
Declare ITaskDispatcher::QueueWithResult after Cancel (preserve vtabl…
bmehta001 Jun 13, 2026
9762f94
Address Copilot on #1429: don't claim binary/ABI compatibility in vta…
bmehta001 Jun 13, 2026
b5ba867
HttpClient_WinInet: close session handle even when request handle is …
bmehta001 Jun 22, 2026
652e5e5
Offline storage: guard empty-filter delete + propagate SQLite store f…
bmehta001 Jun 22, 2026
325c55b
Add MemoryStorage empty-filter delete regression test
bmehta001 Jun 22, 2026
d9640b7
Address review comment: propagate synchronous disk store failures
bmehta001 Jun 22, 2026
f1b3381
Prevent event loss when a disk write fails during Flush()
bmehta001 Jun 23, 2026
45e9d55
Address Copilot comment: NoopTaskDispatcher::Cancel returns found-state
bmehta001 Jun 23, 2026
bab7b42
Address Copilot comments: rename flush test for precision
bmehta001 Jun 23, 2026
e9c7ee3
Merge remote-tracking branch 'msft/main' into bhamehta/fix-storage-da…
bmehta001 Jun 23, 2026
84e49a6
Fold the SQLite batch-flush optimization into the data-safety change …
bmehta001 Jun 23, 2026
e1e7c4e
Address Copilot: make StoreRecords fully all-or-nothing on invalid re…
bmehta001 Jun 23, 2026
40fd118
Address Copilot: re-queue the flush batch only on a zero store result
bmehta001 Jun 23, 2026
97fee8e
Merge branch 'main' into bhamehta/fix-storage-data-safety
bmehta001 Jun 24, 2026
8935ef2
Merge branch 'main' into bhamehta/runtime-fixes
bmehta001 Jun 30, 2026
937d3ac
Fix PrivacyGuard JNI UAF, RoInitialize leak, and missing low_battery …
bmehta001 Jul 2, 2026
82cffa1
Fix GetAndReserveRecords data race (#1221) and SQLite shutdown leak (…
bmehta001 Jul 2, 2026
03cf210
Balance RoInitialize with an RAII guard (Copilot round-1)
bmehta001 Jul 2, 2026
2905ca7
Add test for low_battery transmit-profile powerState (#312)
bmehta001 Jul 2, 2026
e8db589
Guard checkpoint-on-flush against null disk storage (Copilot round-3)
bmehta001 Jul 2, 2026
62ffbbd
Merge branch 'main' into bhamehta/fix-storage-data-safety
bmehta001 Jul 2, 2026
dd9e023
Add teardown-during-in-flight-upload smoke test
bmehta001 Jul 6, 2026
6be37b1
Fix teardown deadlock: always signal flush completion
bmehta001 Jul 8, 2026
80b9c80
Merge remote-tracking branch 'msft/main' into bhamehta/runtime-fixes
bmehta001 Jul 8, 2026
ce1699e
Drain pending tasks in the worker on shutdown to avoid a self-Join leak
bmehta001 Jul 8, 2026
e6769f1
Assert the /slow/ endpoint rewrite in the teardown smoke test
bmehta001 Jul 8, 2026
cc8ece8
Cast chrono counts to long long in %lld LOG_TRACE calls
bmehta001 Jul 9, 2026
c10f636
Drop issue-number reference from teardown smoke-test comment
bmehta001 Jul 9, 2026
21233a6
Drop issue-number reference from metastats opt-in comments
bmehta001 Jul 9, 2026
31877df
Merge branch 'main' into bhamehta/fix-storage-data-safety
bmehta001 Jul 9, 2026
689b61a
Fix data-loss and queue-wedge in SQLite batched flush
bmehta001 Jul 10, 2026
6d2dd1a
Fix out-of-bounds timer access in transmit-profile debug logging
bmehta001 Jul 10, 2026
f200af9
Address Copilot review: correct Flush comment and size_t format speci…
bmehta001 Jul 10, 2026
46e8b1d
Merge remote-tracking branch 'msft/main' into bhamehta/runtime-fixes
bmehta001 Jul 10, 2026
099348f
Fix use-after-free when the last worker reference is released on its …
bmehta001 Jul 10, 2026
be00ea0
Make worker self-dispose detection survive a prior detach()
bmehta001 Jul 11, 2026
9dd565a
Address review: portable worker-id storage and fix thread-id logging UB
bmehta001 Jul 12, 2026
b9d9d03
Avoid public queue-result dispatcher virtual
bmehta001 Jul 13, 2026
c86e954
Harden batched flush retry handling
bmehta001 Jul 13, 2026
7cdf393
Handle latency-off drops and share disk validation
bmehta001 Jul 31, 2026
7ef8109
Fix deferred task lifetime tracking and shutdown cleanup
bmehta001 Jul 31, 2026
d325700
Guard OfflineStorageHandler::Flush against leaking StartActivity on e…
bmehta001 Aug 1, 2026
074c6e4
Leak LogManagerFactory and PAL singletons to avoid static-destruction…
bmehta001 Aug 1, 2026
0885742
Merge branch 'main' into bhamehta/fix-storage-data-safety
bmehta001 Aug 3, 2026
aae640e
Merge upstream main into runtime fixes
bmehta001 Aug 4, 2026
d803615
Fix shutdown and flush review findings
bmehta001 Aug 5, 2026
fece2b2
Guard flush exception completion
bmehta001 Aug 5, 2026
36e4927
Merge branch 'main' into bhamehta/runtime-fixes
bmehta001 Aug 5, 2026
b2bd27b
Align vcpkg iOS deployment target
bmehta001 Aug 6, 2026
ca440fc
Harden Apple packaging integration
bmehta001 Aug 6, 2026
c96f7de
Migrate Apple builds to canonical CMake variables
bmehta001 Aug 7, 2026
bfc2f6a
Harden teardown and preserve failed flush records
bmehta001 Aug 7, 2026
e942b93
Merge remote-tracking branch 'origin/bhamehta/runtime-fixes' into bha…
bmehta001 Aug 7, 2026
159645b
Harden flush and worker teardown recovery
bmehta001 Aug 7, 2026
e49cb76
Merge PR #1491 batched offline storage flush
bmehta001 Aug 7, 2026
ab40f93
Make activity cleanup non-throwing
bmehta001 Aug 7, 2026
f6853b6
Merge origin/main into runtime fixes
bmehta001 Aug 7, 2026
8d45721
Add direct test standard library includes
bmehta001 Aug 7, 2026
c5ed4dc
Rollback batched storage when an insert throws
bmehta001 Aug 7, 2026
dd09470
Merge latest Microsoft main
bmehta001 Aug 8, 2026
62471df
Handle nil Apple responses during cancellation
bmehta001 Aug 9, 2026
afb12aa
Keep Apple requests alive through cancellation callbacks
bmehta001 Aug 9, 2026
c3c1ce3
Fix SQLite batch accounting and benchmark
bmehta001 Aug 9, 2026
c457bb6
Prevent SIGPIPE from killing the test process on peer reset
bmehta001 Aug 9, 2026
1056ed7
Always release SQLite storage during shutdown
bmehta001 Aug 9, 2026
880c5df
Use stable worker identity for self-cancellation
bmehta001 Aug 9, 2026
660aa2a
Bound offline storage flush batches
bmehta001 Aug 9, 2026
21b645f
Bound offline flush batches to prevent CI timeouts
bmehta001 Aug 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/api/LogConfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace MAT_NS_BEGIN {
{ CFG_BOOL_ENABLE_ANALYTICS, false },
{ CFG_INT_CACHE_FILE_SIZE, 3145728 },
{ CFG_INT_RAM_QUEUE_SIZE, 524288 },
{ CFG_BOOL_ENABLE_BATCHED_STORAGE_FLUSH, true },
{ CFG_BOOL_ENABLE_MULTITENANT, true },
{ CFG_BOOL_ENABLE_DB_DROP_IF_FULL, false },
{ CFG_INT_MAX_TEARDOWN_TIME, 0 },
Expand Down Expand Up @@ -51,6 +52,7 @@ namespace MAT_NS_BEGIN {
{ CFG_BOOL_ENABLE_ANALYTICS, src.enableLifecycleSession },
{ CFG_INT_CACHE_FILE_SIZE, src.cacheFileSizeLimitInBytes },
{ CFG_INT_RAM_QUEUE_SIZE, src.cacheMemorySizeLimitInBytes },
{ CFG_BOOL_ENABLE_BATCHED_STORAGE_FLUSH, true },
{ CFG_BOOL_ENABLE_MULTITENANT, src.multiTenantEnabled },
{ CFG_INT_MAX_TEARDOWN_TIME, src.maxTeardownUploadTimeInSec },
{ CFG_INT_MAX_PENDING_REQ, src.maxPendingHTTPRequests },
Expand Down Expand Up @@ -128,4 +130,3 @@ namespace MAT_NS_BEGIN {
}

} MAT_NS_END

10 changes: 9 additions & 1 deletion lib/api/LogManagerFactory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,15 @@ namespace MAT_NS_BEGIN {

// C++11 Magic Statics (N2660)
static LogManagerFactory& instance() {
static LogManagerFactory impl;
// Deliberately never destroyed. LogManagerProvider::Release() must be
// able to walk this factory's registries during process teardown, but
// a normal function-local static's destruction order relative to that
// teardown call is unspecified -- if this were destroyed first,
// Release() would walk already-freed std::map nodes (a downstream
// consumer observed this as EXC_BAD_ACCESS in release() at process
// exit). Leaking one small, fixed-size object avoids the ordering
// hazard entirely; the OS reclaims it when the process exits.
static LogManagerFactory& impl = *new LogManagerFactory();
return impl;
}

Expand Down
56 changes: 43 additions & 13 deletions lib/api/LogManagerImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#pragma warning(disable : 4459)
#endif
#include "LogManagerImpl.hpp"
#include <cstdio>
#include "mat/config.h"

#include "offline/LogSessionDataProvider.hpp"
Expand Down Expand Up @@ -368,9 +369,27 @@ namespace MAT_NS_BEGIN

LogManagerImpl::~LogManagerImpl() noexcept
{
FlushAndTeardown();
LOCKGUARD(ILogManagerInternal::managers_lock);
ILogManagerInternal::managers.erase(this);
try
{
FlushAndTeardown();
}
catch (const std::exception& e)
{
std::fprintf(stderr, "Log manager teardown failed: %s\n", e.what());
}
catch (...)
{
std::fputs("Log manager teardown failed with an unknown exception\n", stderr);
}
try
{
LOCKGUARD(ILogManagerInternal::managers_lock);
ILogManagerInternal::managers.erase(this);
}
catch (...)
{
std::fputs("Log manager registry cleanup failed\n", stderr);
}
}

size_t LogManagerImpl::GetDeadLoggerCount()
Expand Down Expand Up @@ -959,19 +978,30 @@ namespace MAT_NS_BEGIN
return true;
}

void LogManagerImpl::EndActivity()
void LogManagerImpl::EndActivity() noexcept
{
std::unique_lock<std::mutex> lock(m_pause_mutex);
if (m_pause_active_count == 0) {
return;
try
{
std::unique_lock<std::mutex> lock(m_pause_mutex);
if (m_pause_active_count == 0) {
return;
}
m_pause_active_count -= 1;
if (m_pause_active_count > 0) {
return;
}
if (m_pause_state == PauseState::Pausing) {
m_pause_state = PauseState::Paused;
m_pause_cv.notify_all();
}
}
m_pause_active_count -= 1;
if (m_pause_active_count > 0) {
return;
catch (const std::exception& e)
{
std::fprintf(stderr, "Failed to end telemetry activity: %s\n", e.what());
}
if (m_pause_state == PauseState::Pausing) {
m_pause_state = PauseState::Paused;
m_pause_cv.notify_all();
catch (...)
{
std::fputs("Failed to end telemetry activity\n", stderr);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/api/LogManagerImpl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ namespace MAT_NS_BEGIN
virtual void ResumeActivity() override;
virtual void WaitPause() override;
virtual bool StartActivity() override;
virtual void EndActivity() override;
virtual void EndActivity() noexcept override;

protected:
std::unique_ptr<ITelemetrySystem>& GetSystem();
Expand Down
3 changes: 2 additions & 1 deletion lib/api/Logger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ namespace MAT_NS_BEGIN

Logger::~Logger() noexcept
{
LOG_TRACE("%p: Destroyed", this);
// Intentionally empty — logging here triggers a static-destruction-order
// crash on iOS simulator (recursive_mutex used after teardown).
}

ISemanticContext* Logger::GetSemanticContext() const
Expand Down
2 changes: 1 addition & 1 deletion lib/config/RuntimeConfig_Default.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace MAT_NS_BEGIN
{CFG_BOOL_ENABLE_ANALYTICS, false},
{CFG_INT_CACHE_FILE_SIZE, 3145728},
{CFG_INT_RAM_QUEUE_SIZE, 524288},
{CFG_BOOL_ENABLE_BATCHED_STORAGE_FLUSH, true},
{CFG_BOOL_ENABLE_MULTITENANT, true},
{CFG_BOOL_ENABLE_DB_DROP_IF_FULL, false},
{CFG_INT_MAX_TEARDOWN_TIME, 1},
Expand Down Expand Up @@ -233,4 +234,3 @@ namespace MAT_NS_BEGIN

}
MAT_NS_END

18 changes: 4 additions & 14 deletions lib/http/HttpClient_Apple.mm
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,11 @@ void HandleResponse(NSData* data, NSURLResponse* response, NSError* error)
NSHTTPURLResponse *httpResp = static_cast<NSHTTPURLResponse*>(response);
auto simpleResponse = new SimpleHttpResponse { NextRespId() };

simpleResponse->m_statusCode = static_cast<unsigned int>(httpResp.statusCode);
simpleResponse->m_statusCode = httpResp != nil
? static_cast<unsigned int>(httpResp.statusCode)
: 0;

NSDictionary *responseHeaders = [httpResp allHeaderFields];
NSDictionary *responseHeaders = httpResp != nil ? [httpResp allHeaderFields] : nil;
for (id key in responseHeaders)
{
simpleResponse->m_headers.add([key UTF8String], [responseHeaders[key] UTF8String]);
Expand Down Expand Up @@ -299,7 +301,6 @@ void Cancel()
LOG_TRACE("HTTP request=%p id=%s being aborted...", request, id.c_str());
request->Cancel();
}
m_requests.erase(id);
}
}
}
Expand All @@ -317,17 +318,6 @@ void Cancel()
for (const auto &id : ids)
CancelRequestAsync(id);

for (;;)
{
{
std::lock_guard<std::mutex> lock(m_requestsMtx);
if (m_requests.empty())
{
return;
}
}
PAL::sleep(100);
}
}

void HttpClient_Apple::Erase(IHttpRequest* req)
Expand Down
3 changes: 3 additions & 0 deletions lib/http/HttpClient_WinInet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ class WinInetRequestWrapper
if (m_hWinInetRequest != nullptr)
{
::InternetCloseHandle(m_hWinInetRequest);
}
if (m_hWinInetSession != nullptr)
{
::InternetCloseHandle(m_hWinInetSession);
}
}
Expand Down
3 changes: 0 additions & 3 deletions lib/http/HttpResponseDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,11 @@ namespace MAT_NS_BEGIN {
break;

case HttpResult_Aborted:
ctx->httpResponse = nullptr;
outcome = Abort;
break;

case HttpResult_LocalFailure:
case HttpResult_NetworkFailure:
ctx->httpResponse = nullptr;
outcome = RetryNetwork;
break;
}
Expand Down Expand Up @@ -132,7 +130,6 @@ namespace MAT_NS_BEGIN {
evt.param2 = ctx->recordIdsAndTenantIds.size();
DispatchEvent(evt);
}
ctx->httpResponse = nullptr;
// eventsRejected(ctx); // FIXME: [MG] - investigate why ctx gets corrupt after eventsRejected
requestAborted(ctx);
break;
Expand Down
7 changes: 6 additions & 1 deletion lib/include/public/ILogConfiguration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ namespace MAT_NS_BEGIN
/// </summary>
static constexpr const char* const CFG_INT_RAM_QUEUE_BUFFERS = "maxDBFlushQueues";

/// <summary>
/// Batch records when flushing the RAM queue to disk storage.
/// Set to false to use per-record disk stores during flush.
/// </summary>
static constexpr const char* const CFG_BOOL_ENABLE_BATCHED_STORAGE_FLUSH = "enableBatchedStorageFlush";

/// <summary>
/// SQLite DB will be checkpointed when flushing.
/// </summary>
Expand Down Expand Up @@ -481,4 +487,3 @@ namespace MAT_NS_BEGIN
}
MAT_NS_END
#endif

2 changes: 1 addition & 1 deletion lib/include/public/ITaskDispatcher.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,11 @@ namespace MAT_NS_BEGIN
/// <param name="waitTime">Amount of time to wait for if the task is currently executing</param>
/// <returns>True if successfully cancelled, else false</returns>
virtual bool Cancel(Task* task, uint64_t waitTime = 0) = 0;

};

/// @endcond

} MAT_NS_END

#endif // ITASKDISPATCHER_HPP

24 changes: 18 additions & 6 deletions lib/jni/PrivacyGuard_jni.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,22 @@ Java_com_microsoft_applications_events_PrivacyGuard_nativeInitializePrivacyGuard
InitializationConfiguration config(
reinterpret_cast<ILogger*>(iLoggerNativePtr),
CommonDataContext{});
// InitializationConfiguration holds const char* pointers, so the backing
// std::string storage must outlive the PrivacyGuard construction below.
std::string notificationEventName, semanticContextEventName, summaryEventName;
if (NotificationEventName != nullptr) {
config.NotificationEventName = JStringToStdString(env, NotificationEventName).c_str();
notificationEventName = JStringToStdString(env, NotificationEventName);
config.NotificationEventName = notificationEventName.c_str();
}

if (SemanticContextEventName != nullptr) {
config.SemanticContextNotificationEventName = JStringToStdString(env, SemanticContextEventName).c_str();
semanticContextEventName = JStringToStdString(env, SemanticContextEventName);
config.SemanticContextNotificationEventName = semanticContextEventName.c_str();
}

if (SummaryEventName != nullptr) {
config.SummaryEventName = JStringToStdString(env, SummaryEventName).c_str();
summaryEventName = JStringToStdString(env, SummaryEventName);
config.SummaryEventName = summaryEventName.c_str();
}

config.UseEventFieldPrefix = static_cast<bool>(UseEventFieldPrefix);
Expand Down Expand Up @@ -119,16 +125,22 @@ Java_com_microsoft_applications_events_PrivacyGuard_nativeInitializePrivacyGuard
machineIds,
outOfScopeIdentifiers));

// InitializationConfiguration holds const char* pointers, so the backing
// std::string storage must outlive the PrivacyGuard construction below.
std::string notificationEventName, semanticContextEventName, summaryEventName;
if (NotificationEventName != NULL) {
config.NotificationEventName = JStringToStdString(env, NotificationEventName).c_str();
notificationEventName = JStringToStdString(env, NotificationEventName);
config.NotificationEventName = notificationEventName.c_str();
}

if (SemanticContextEventName != NULL) {
config.SemanticContextNotificationEventName = JStringToStdString(env, SemanticContextEventName).c_str();
semanticContextEventName = JStringToStdString(env, SemanticContextEventName);
config.SemanticContextNotificationEventName = semanticContextEventName.c_str();
}

if (SummaryEventName != NULL) {
config.SummaryEventName = JStringToStdString(env, SummaryEventName).c_str();
summaryEventName = JStringToStdString(env, SummaryEventName);
config.SummaryEventName = summaryEventName.c_str();
}

config.UseEventFieldPrefix = static_cast<bool>(UseEventFieldPrefix);
Expand Down
10 changes: 10 additions & 0 deletions lib/offline/MemoryStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,16 @@ namespace MAT_NS_BEGIN {

void MemoryStorage::DeleteRecords(const std::map<std::string, std::string> & whereFilter)
{
// An empty filter matches every record. Never silently wipe the whole
// in-memory queue from a no-op predicate; callers must use
// DeleteAllRecords() for an intentional full clear. This mirrors the
// fail-closed behavior of OfflineStorage_SQLite::DeleteRecords.
if (whereFilter.empty())
{
LOG_WARN("DeleteRecords called with an empty filter; ignoring to avoid deleting all records.");
return;
}

auto matcher = [&](const StorageRecord &r, const std::map<std::string, std::string> & whereFilter)
{
bool matched = true;
Expand Down
Loading
Loading