Skip to content

Commit a7e1ce1

Browse files
dabhattimsftDarshak Bhatti
andauthored
Fix SharedMemory redirection queue and add telemetry events (#6127)
* cp * comments * event name changes * event name changes * cleanup * cp * cp --------- Co-authored-by: Darshak Bhatti <[email protected]>
1 parent 6834a7e commit a7e1ce1

6 files changed

Lines changed: 108 additions & 20 deletions

File tree

dev/AppLifecycle/AppInstance.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation and Contributors.
1+
// Copyright (c) Microsoft Corporation and Contributors.
22
// Licensed under the MIT License.
33

44
#include <pch.h>
@@ -171,23 +171,27 @@ namespace winrt::Microsoft::Windows::AppLifecycle::implementation
171171
GUID AppInstance::DequeueRedirectionRequestId()
172172
{
173173
auto releaseOnExit = m_dataMutex.acquire();
174+
AppLifecycleTelemetry::DequeueRedirectionRequestId();
174175
auto id = m_redirectionArgs.Dequeue();
175176
return id;
176177
}
177178

178179
void AppInstance::EnqueueRedirectionRequestId(GUID id)
179180
{
180181
auto releaseOnExit = m_dataMutex.acquire();
182+
AppLifecycleTelemetry::EnqueueRedirectionRequestId(id);
181183
m_redirectionArgs.Enqueue(id);
182184
}
183185

184186
void AppInstance::ProcessRedirectionRequests()
185187
{
188+
auto activity{ AppLifecycleTelemetry::ProcessRedirectionRequests::Start(m_processId, m_isCurrent) };
186189
m_innerActivated.ResetEvent();
187190

188191
GUID id;
189192
while ((id = DequeueRedirectionRequestId()) != GUID_NULL)
190193
{
194+
activity.DequeueRedirectionRequest(id);
191195
wil::unique_cotaskmem_string idString;
192196
THROW_IF_FAILED(StringFromCLSID(id, &idString));
193197

@@ -199,6 +203,7 @@ namespace winrt::Microsoft::Windows::AppLifecycle::implementation
199203

200204
// Notify the app that the redirection request is here.
201205
m_activatedEvent(*this, args);
206+
activity.RedrectionActivatedEvent(id);
202207

203208
std::wstring eventName = name + c_activatedEventNameSuffix;
204209
wil::unique_event cleanupEvent;
@@ -207,7 +212,10 @@ namespace winrt::Microsoft::Windows::AppLifecycle::implementation
207212
// If the event is missing, it means the waiter gave up. Ignore the error.
208213
cleanupEvent.SetEvent();
209214
}
215+
activity.RequestCleanupEvent(id);
210216
}
217+
218+
activity.Stop();
211219
}
212220

213221
IAsyncAction AppInstance::QueueRequest(AppLifecycle::AppActivationArguments args)
@@ -229,6 +237,7 @@ namespace winrt::Microsoft::Windows::AppLifecycle::implementation
229237

230238
GUID id;
231239
THROW_IF_FAILED(CoCreateGuid(&id));
240+
auto activity{ AppLifecycleTelemetry::QueueRequest::Start(m_processId, m_isCurrent, id) };
232241

233242
wil::unique_cotaskmem_string idString;
234243
THROW_IF_FAILED(StringFromCLSID(id, &idString));
@@ -248,9 +257,11 @@ namespace winrt::Microsoft::Windows::AppLifecycle::implementation
248257

249258
// Signal the activation.
250259
m_innerActivated.SetEvent();
260+
activity.InnerActivatedEvent(id);
251261

252262
// Wait for the other instance to open the memory mapped file before exiting and cleaning our interest in it.
253263
cleanupEvent.wait();
264+
activity.Stop();
254265
co_return;
255266
}
256267

@@ -559,11 +570,13 @@ namespace winrt::Microsoft::Windows::AppLifecycle::implementation
559570

560571
event_token AppInstance::Activated(EventHandler<Microsoft::Windows::AppLifecycle::AppActivationArguments> const& handler)
561572
{
573+
AppLifecycleTelemetry::ActivatedEventAdd(m_processId);
562574
return m_activatedEvent.add(handler);
563575
}
564576

565577
void AppInstance::Activated(event_token const& token) noexcept
566578
{
579+
AppLifecycleTelemetry::ActivatedEventRemove(m_processId);
567580
m_activatedEvent.remove(token);
568581
}
569582

dev/AppLifecycle/AppInstance.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation and Contributors.
1+
// Copyright (c) Microsoft Corporation and Contributors.
22
// Licensed under the MIT License.
33
#pragma once
44

dev/AppLifecycle/AppLifecycle.vcxitems

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
33
<PropertyGroup Label="Globals">
44
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
@@ -40,4 +40,4 @@
4040
<ClInclude Include="$(MSBuildThisFileDirectory)ValueMarshaling.h" />
4141
<Midl Include="$(MSBuildThisFileDirectory)AppLifecycle.idl" />
4242
</ItemGroup>
43-
</Project>
43+
</Project>

dev/AppLifecycle/AppLifecycleTelemetry.h

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation and Contributors.
1+
// Copyright (c) Microsoft Corporation and Contributors.
22
// Licensed under the MIT License.
33
#pragma once
44

@@ -17,4 +17,42 @@ class AppLifecycleTelemetry : public wil::TraceLoggingProvider
1717
DEFINE_COMPLIANT_MEASURES_EVENT(RedirectActivationToAsync, PDT_ProductAndServiceUsage);
1818
DEFINE_COMPLIANT_MEASURES_EVENT(ActivationRegistrationManager, PDT_ProductAndServiceUsage);
1919
DEFINE_COMPLIANT_MEASURES_EVENT(Restart, PDT_ProductAndServiceUsage);
20+
21+
DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM1(ActivatedEventAdd, PDT_ProductAndServiceUsage, uint32_t, processId);
22+
DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM1(ActivatedEventRemove, PDT_ProductAndServiceUsage, uint32_t, processId);
23+
24+
DEFINE_COMPLIANT_MEASURES_EVENT_PARAM1(EnqueueRedirectionRequestId, PDT_ProductAndServiceUsage, GUID, requestId);
25+
DEFINE_COMPLIANT_MEASURES_EVENT(DequeueRedirectionRequestId, PDT_ProductAndServiceUsage);
26+
27+
BEGIN_COMPLIANT_MEASURES_ACTIVITY_CLASS(ProcessRedirectionRequests, PDT_ProductAndServicePerformance);
28+
DEFINE_ACTIVITY_START(uint32_t processId, bool isCurrent) noexcept try
29+
{
30+
TraceLoggingClassWriteStart(
31+
ProcessRedirectionRequests,
32+
_GENERIC_PARTB_FIELDS_ENABLED,
33+
TraceLoggingUInt32(processId, "processId"),
34+
TraceLoggingBool(isCurrent, "isCurrent"));
35+
}
36+
CATCH_LOG()
37+
38+
DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_PARAM1(DequeueRedirectionRequest, PDT_ProductAndServicePerformance, GUID, requestId);
39+
DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_PARAM1(RedrectionActivatedEvent, PDT_ProductAndServicePerformance, GUID, requestId);
40+
DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_PARAM1(RequestCleanupEvent, PDT_ProductAndServicePerformance, GUID, requestId);
41+
END_ACTIVITY_CLASS();
42+
43+
BEGIN_COMPLIANT_MEASURES_ACTIVITY_CLASS(QueueRequest, PDT_ProductAndServicePerformance)
44+
DEFINE_ACTIVITY_START(uint32_t processId, bool isCurrent, GUID requestId) noexcept try
45+
{
46+
TraceLoggingClassWriteStart(
47+
QueueRequest,
48+
_GENERIC_PARTB_FIELDS_ENABLED,
49+
TraceLoggingUInt32(processId, "processId"),
50+
TraceLoggingBool(isCurrent, "isCurrent"),
51+
TraceLoggingGuid(requestId, "requestId"));
52+
}
53+
CATCH_LOG()
54+
55+
DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_PARAM1(InnerActivatedEvent, PDT_ProductAndServicePerformance, GUID, requestId);
56+
END_ACTIVITY_CLASS()
57+
2058
};

dev/AppLifecycle/RedirectionRequestQueue.h

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation and Contributors.
1+
// Copyright (c) Microsoft Corporation and Contributors.
22
// Licensed under the MIT License.
33
#pragma once
44
#include "SharedMemory.h"
@@ -7,6 +7,8 @@
77

88
namespace winrt::Microsoft::Windows::AppLifecycle::implementation
99
{
10+
constexpr size_t c_queueSize = 4096;
11+
1012
class RedirectionRequestQueue
1113
{
1214
struct QueueItem
@@ -16,15 +18,38 @@ namespace winrt::Microsoft::Windows::AppLifecycle::implementation
1618
GUID id{ 0 };
1719
};
1820

21+
// NOTE: SharedMemory layout
22+
// In shared memory, we store headpointer (sizeof(size_t)) followed by c_queueSize QueueItems.
23+
// But shared memory also stores the above requested size (head + queue) at the beginning of the memory
24+
// See SharedMemory.h:
25+
// Result of MapViewOfFile() is stored in m_view, which is of type DynamicSharedMemory<T>*.
26+
// DynamicSharedMemory has a "size" member at the beginning, followed by "data" member.
27+
// m_data.Get() returns pointer to "data" member of above DynamicSharedMemory. This is our "usable" region i.e. head + queue.
28+
// So the overall layout in memory can be represented as below:
29+
// | DynamicSharedMemory.size |-------------------------- DynamicSharedMemory.data ---------------------------|
30+
//
31+
// m_data.Get() (start of head pointer storage)
32+
// v
33+
// | DynamicSharedMemory.size | head pointer | QueueItem[0] | QueueItem[1] | ... | QueueItem[c_queueSize - 1] |
34+
// ^
35+
// m_dataStart (first QueueItem in shared memory)
36+
1937
public:
2038
void Init(const std::wstring& name)
2139
{
2240
m_name = name;
2341

2442
// We store the head pointer at the beginning of the memory, and then items in the queue after.
25-
m_data.Open(name, (sizeof(QueueItem) * 4096) + sizeof(QueueItem*));
26-
#pragma warning(suppress: 6305) // C6305: PREFast does not know m_data.Get() is compatible with "sizeof(size_t)".
27-
m_dataStart = reinterpret_cast<QueueItem*>(m_data.Get() + sizeof(size_t));
43+
auto headPointerSizeInBytes = sizeof(size_t);
44+
auto queueSizeInBytes = sizeof(QueueItem) * c_queueSize;
45+
46+
auto totalSharedMemoryDataSizeInBytes = queueSizeInBytes + headPointerSizeInBytes;
47+
48+
m_data.Open(name, totalSharedMemoryDataSizeInBytes);
49+
50+
auto sharedMemoryBase = reinterpret_cast<std::byte*>(m_data.Get());
51+
auto queueStartInSharedMemory = sharedMemoryBase + headPointerSizeInBytes;
52+
m_dataStart = reinterpret_cast<QueueItem*>(queueStartInSharedMemory);
2853
}
2954

3055
void Enqueue(const GUID& itemId)
@@ -120,23 +145,28 @@ namespace winrt::Microsoft::Windows::AppLifecycle::implementation
120145

121146
QueueItem* AllocateItem()
122147
{
123-
QueueItem* upperBounds = reinterpret_cast<QueueItem*>(m_data.Get()) + m_data.Size();
148+
// m_dataStart points to the first QueueItem in the shared memory.
149+
// The total size of the queue is c_queueSize items.
150+
// m_dataStart + c_queueSize will point to one past the end of the queue.
151+
// For example, if c_queueSize is 4, and m_dataStart is at address 0x22506a80010
152+
// then m_dataStart + c_queueSize is at address 0x0000022506a80090.
153+
// Valid items are at:
154+
// 0x0000022506a80010, 0x0000022506a80030, 0x0000022506a80050, 0x0000022506a80070
155+
// Since cur is of type QueueItem*, incrementing it moves by sizeof(QueueItem) in below loop.
156+
QueueItem* upperBounds = m_dataStart + c_queueSize;
124157
auto cur = m_dataStart;
125158

126-
#pragma warning(suppress: 6305) // C6305: PREFast does not know upperBounds was also computed in byte count so it is compatible with "sizeof(QueueItem)".
127-
while (cur < (upperBounds - sizeof(QueueItem)) && cur->inUse)
159+
while (cur < upperBounds && cur->inUse)
128160
{
129-
#pragma warning(suppress: 6305) // C6305: PREFast does not know cur was also computed in byte count so it is compatible with "sizeof(QueueItem)".
130-
cur += sizeof(QueueItem);
161+
cur++; // cur is of type QueueItem*, so ++ moves by sizeof(QueueItem).
131162
}
132163

133-
#pragma warning(suppress: 6305) // C6305: PREFast does not know upperBounds was also computed in byte count so it is compatible with "sizeof(QueueItem)".
134-
THROW_HR_IF(E_OUTOFMEMORY, cur >= (upperBounds - sizeof(QueueItem)));
164+
THROW_HR_IF(E_OUTOFMEMORY, cur >= upperBounds);
135165
return cur;
136166
}
137167

138168
std::wstring m_name;
139-
QueueItem* m_dataStart{ nullptr };
169+
QueueItem* m_dataStart{ nullptr }; // First "QueueItem" in the shared memory after the head pointer.
140170
SharedMemory<size_t> m_data;
141171
};
142172
}

dev/AppLifecycle/SharedMemory.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation and Contributors.
1+
// Copyright (c) Microsoft Corporation and Contributors.
22
// Licensed under the MIT License.
33
#pragma once
44

@@ -23,6 +23,8 @@ class SharedMemory
2323
if (createdFile)
2424
{
2525
Clear();
26+
27+
// We only store size of the request.
2628
m_view.get()->size = size;
2729
}
2830
else
@@ -31,6 +33,7 @@ class SharedMemory
3133
auto newSize = m_view.get()->size;
3234
m_view.reset();
3335
m_file.reset();
36+
3437
OpenInternal(newSize);
3538
}
3639

@@ -45,6 +48,7 @@ class SharedMemory
4548
void Clear()
4649
{
4750
// Clear only the data portion, not the size.
51+
// and Size() accounts for only requested size. See comment in Open().
4852
memset(Get(), 0, Size());
4953
}
5054

@@ -54,6 +58,7 @@ class SharedMemory
5458
Reset();
5559

5660
m_name = name;
61+
5762
OpenInternal(size);
5863
m_view.get()->size = size;
5964
}
@@ -88,11 +93,13 @@ class SharedMemory
8893
protected:
8994
bool OpenInternal(size_t size)
9095
{
91-
m_file = wil::unique_handle(CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, static_cast<DWORD>(size), m_name.c_str()));
96+
// OpenInternal needs to account for "size" member of DynamicSharedMemory struct.
97+
size_t totalSize = size + sizeof(size_t);
98+
m_file = wil::unique_handle(CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, static_cast<DWORD>(totalSize), m_name.c_str()));
9299
THROW_LAST_ERROR_IF_NULL(m_file);
93100

94101
bool createdFile = (GetLastError() != ERROR_ALREADY_EXISTS);
95-
m_view.reset(reinterpret_cast<DynamicSharedMemory<T>*>(MapViewOfFile(m_file.get(), FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, size)));
102+
m_view.reset(reinterpret_cast<DynamicSharedMemory<T>*>(MapViewOfFile(m_file.get(), FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, totalSize)));
96103
THROW_LAST_ERROR_IF_NULL(m_view);
97104

98105
return createdFile;

0 commit comments

Comments
 (0)