Skip to content

Commit 2043c7b

Browse files
Merge pull request #266 from microsoft/pete-dev
Multithreading test and documentation update
2 parents f24f7f1 + dbb9094 commit 2043c7b

11 files changed

+213
-7
lines changed

build/staging/version/BundleInfo.wxi

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<Include>
22
<?define SetupVersionName="Developer Preview 5" ?>
3-
<?define SetupVersionNumber="1.0.24039.2143" ?>
3+
<?define SetupVersionNumber="1.0.24039.2325" ?>
44
</Include>

docs/developer-docs/Windows.Devices.Midi2/connections/MidiEndpointConnection.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Currently, in the implementation behind the scenes, the service receives each ti
5757
| `AddEndpointProcessingPlugin(plugin)` | Add an endpoint processing plugin to this connection |
5858
| `RemoveEndpointProcessingPlugin(id)` | Remove an endpoint processing plugin from this connection |
5959

60-
> Tip: In all the functions which accept a timestamp to schedule the message, **you can send a timestamp of 0 (zero) to bypass the scheduler and send the message immediately**. Otherwise, the provided timestamp is treated as an absolute time for when the message should be sent from the service. Note that the service-based scheduler (currently based on a `std::priority_queue`) gets less efficient when there are thousands of messages in it, so it's recommended that you not schedule too many messages at a time or too far out into the future.
60+
> Tip: In all the functions which accept a timestamp to schedule the message, **you can send a timestamp of 0 (zero) to bypass the scheduler and send the message immediately** or use the `MidiClock::TimestampConstantSendImmediately` static property. Otherwise, the provided timestamp is treated as an absolute time for when the message should be sent from the service. Note that the service-based scheduler (currently based on a `std::priority_queue`) gets less efficient when there are thousands of messages in it, so it's recommended that you not schedule too many messages at a time or too far out into the future.
6161
6262
## Events
6363

@@ -67,6 +67,8 @@ Currently, in the implementation behind the scenes, the service receives each ti
6767

6868
When processing the `MessageReceived` event, do so quickly. This event is synchronous. If you need to do long-running processing of incoming messages, add them to your own incoming queue structure and have them processed by another application thread.
6969

70+
> Threading: Note that the thread the `MessageReceived` callback comes in on is not the same thread which created the connection to begin with. Windows MIDI Services uses a high-priority thread in the background, one per connection. For this reason, it's best to use the event only to receive the message and store it, not to do any additional processing on the message. The TAEF test `MidiEndpointCreationThreadTests` in the `Midi2.Client.unittests` project demonstrates how this works.
71+
7072
> Note: Wire up event handlers and add message processing plugins prior to calling `Open()`.
7173
7274
## IDL

docs/developer-docs/Windows.Devices.Midi2/metadata/MidiFunctionBlockDirectionEnum.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ Indicates the message flow for a function block. Note that this is, per the spec
2121

2222
## IDL
2323

24-
MidiFunctionBlockDirectionEnum IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiFunctionBlockDirectionEnum.idl)
24+
[MidiFunctionBlockDirectionEnum IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiFunctionBlockDirectionEnum.idl)

docs/developer-docs/Windows.Devices.Midi2/metadata/MidiFunctionBlockMidi10Enum.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ Indicates the MIDI 1.0 capability restrictions for a function block. Note that W
2121

2222
## IDL
2323

24-
MidiFunctionBlockMidi10Enum IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiFunctionBlockMidi10Enum.idl)
24+
[MidiFunctionBlockMidi10Enum IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiFunctionBlockMidi10Enum.idl)

docs/developer-docs/Windows.Devices.Midi2/metadata/MidiFunctionBlockUIHintEnum.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ In general, these values should not restrict completely what you enable a user t
2323

2424
## IDL
2525

26-
MidiFunctionBlockUIHintEnum IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiFunctionBlockUIHintEnum.idl)
26+
[MidiFunctionBlockUIHintEnum IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiFunctionBlockUIHintEnum.idl)

docs/developer-docs/Windows.Devices.Midi2/metadata/MidiGroupTerminalBlockDirectionEnum.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ Indicates the message flow for a group terminal block. Note that this is, per th
2020

2121
## IDL
2222

23-
MidiGroupTerminalBlockDirectionEnum IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiGroupTerminalBlockDirectionEnum.idl)
23+
[MidiGroupTerminalBlockDirectionEnum IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiGroupTerminalBlockDirectionEnum.idl)

docs/developer-docs/Windows.Devices.Midi2/metadata/MidiGroupTerminalBlockProtocolEnum.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ Indicates the protocol specifics for the Group Terminal Block. Group terminal bl
2626

2727
## IDL
2828

29-
MidiGroupTerminalBlockProtocolEnum IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiGroupTerminalBlockProtocolEnum.idl)
29+
[MidiGroupTerminalBlockProtocolEnum IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiGroupTerminalBlockProtocolEnum.idl)

src/api/Test/Midi2.Client.unittests/Midi2.Client.unittests.vcxproj

+2
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@
151151
<ClCompile Include="MidiClockTests.cpp" />
152152
<ClCompile Include="MidiEndpointConnectionBufferTests.cpp" />
153153
<ClCompile Include="MidiEndpointConnectionTests.cpp" />
154+
<ClCompile Include="MidiEndpointCreationThreadTests.cpp" />
154155
<ClCompile Include="MidiEndpointDeviceWatcherTests.cpp" />
155156
<ClCompile Include="MidiEndpointListenerTests.cpp" />
156157
<ClCompile Include="MidiFunctionBlockMessageBuilderTests.cpp" />
@@ -165,6 +166,7 @@
165166
<ClInclude Include="MidiClockTests.h" />
166167
<ClInclude Include="MidiEndpointConnectionBufferTests.h" />
167168
<ClInclude Include="MidiEndpointConnectionTests.h" />
169+
<ClInclude Include="MidiEndpointCreationThreadTests.h" />
168170
<ClInclude Include="MidiEndpointDeviceWatcherTests.h" />
169171
<ClInclude Include="MidiEndpointListenerTests.h" />
170172
<ClInclude Include="MidiFunctionBlockMessageBuilderTests.h" />

src/api/Test/Midi2.Client.unittests/Midi2.Client.unittests.vcxproj.filters

+6
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@
5151
<ClCompile Include="MidiEndpointConnectionBufferTests.cpp">
5252
<Filter>Source Files</Filter>
5353
</ClCompile>
54+
<ClCompile Include="MidiEndpointCreationThreadTests.cpp">
55+
<Filter>Source Files</Filter>
56+
</ClCompile>
5457
</ItemGroup>
5558
<ItemGroup>
5659
<ClInclude Include="stdafx.h">
@@ -89,6 +92,9 @@
8992
<ClInclude Include="MidiEndpointConnectionBufferTests.h">
9093
<Filter>Header Files</Filter>
9194
</ClInclude>
95+
<ClInclude Include="MidiEndpointCreationThreadTests.h">
96+
<Filter>Header Files</Filter>
97+
</ClInclude>
9298
</ItemGroup>
9399
<ItemGroup>
94100
<ResourceCompile Include="Midi2ClientTests.rc">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
2+
#include "stdafx.h"
3+
4+
#include "MidiEndpointCreationThreadTests.h"
5+
6+
7+
#include <wil\resource.h>
8+
9+
10+
#define NUM_MESSAGES_TO_TRANSMIT 10
11+
12+
_Use_decl_annotations_
13+
void MidiEndpointCreationThreadTests::ReceiveThreadWorker(MidiSession session, winrt::hstring endpointId)
14+
{
15+
wil::unique_event_nothrow allMessagesReceived;
16+
allMessagesReceived.create();
17+
18+
uint32_t countMessagesReceived = 0;
19+
20+
auto connectionThreadId = GetCurrentThreadId();
21+
std::cout << "Receiver: connection thread: " << connectionThreadId << std::endl;
22+
23+
// create the connection
24+
auto connection = session.CreateEndpointConnection(endpointId);
25+
26+
auto MessageReceivedHandler = [&](IMidiMessageReceivedEventSource const& sender, MidiMessageReceivedEventArgs const& args)
27+
{
28+
UNREFERENCED_PARAMETER(sender);
29+
UNREFERENCED_PARAMETER(args);
30+
31+
auto callbackThreadId = GetCurrentThreadId();
32+
std::cout << "Message received on callback thread: " << callbackThreadId << std::endl;
33+
34+
countMessagesReceived++;
35+
36+
// this will not be true. The thread receiving messages is a high-priority thread the application does not control.
37+
//VERIFY_ARE_EQUAL(connectionThreadId, callbackThreadId);
38+
39+
if (countMessagesReceived >= NUM_MESSAGES_TO_TRANSMIT)
40+
{
41+
allMessagesReceived.SetEvent();
42+
}
43+
44+
};
45+
46+
auto revoke = connection.MessageReceived(MessageReceivedHandler);
47+
std::cout << "Receiver: Event handler created." << std::endl;
48+
49+
connection.Open();
50+
std::cout << "Receiver: Connection opened." << std::endl;
51+
52+
m_receiverReady.SetEvent();
53+
54+
std::cout << "Receiver: Waiting for messages." << std::endl;
55+
56+
if (!allMessagesReceived.wait(10000))
57+
{
58+
std::cout << "Receiver: Failure waiting for receiver to connect." << std::endl;
59+
60+
VERIFY_FAIL();
61+
}
62+
63+
std::cout << "Receiver: " << countMessagesReceived << " messages received." << std::endl;
64+
65+
m_receiveComplete.SetEvent();
66+
}
67+
68+
69+
_Use_decl_annotations_
70+
void MidiEndpointCreationThreadTests::SendThreadWorker(MidiSession session, winrt::hstring endpointId)
71+
{
72+
auto threadId = GetCurrentThreadId();
73+
74+
std::cout << "Sender thread: " << threadId << std::endl;
75+
76+
// create the connection
77+
auto connection = session.CreateEndpointConnection(endpointId);
78+
connection.Open();
79+
80+
for (uint32_t i = 0; i < NUM_MESSAGES_TO_TRANSMIT; i++)
81+
{
82+
connection.SendMessageWords(MidiClock::TimestampConstantSendImmediately(), 0x21234567);
83+
84+
}
85+
86+
std::cout << "Sender: " << NUM_MESSAGES_TO_TRANSMIT << " messages sent" << std::endl;
87+
88+
}
89+
90+
91+
92+
void MidiEndpointCreationThreadTests::TestCreateNewSessionMultithreaded()
93+
{
94+
m_receiveComplete.create();
95+
m_receiverReady.create();
96+
97+
auto sessionThreadId = GetCurrentThreadId();
98+
99+
std::cout << "Session thread: " << sessionThreadId << std::endl;
100+
101+
// create session on this thread
102+
103+
auto session = MidiSession::CreateSession(L"Multi-threaded Test");
104+
105+
// create loopback A on thread A
106+
std::thread workerThreadA(&MidiEndpointCreationThreadTests::ReceiveThreadWorker, this, session, MidiEndpointDeviceInformation::DiagnosticsLoopbackAEndpointId());
107+
workerThreadA.detach();
108+
109+
if (!m_receiverReady.wait(10000))
110+
{
111+
std::cout << "Session: Failure waiting for receiver to connect." << std::endl;
112+
113+
VERIFY_FAIL();
114+
}
115+
else
116+
{
117+
std::cout << "Session: Receiver ready notification received." << std::endl;
118+
}
119+
120+
// create loopback B on thread B
121+
std::thread workerThreadB(&MidiEndpointCreationThreadTests::SendThreadWorker, this, session, MidiEndpointDeviceInformation::DiagnosticsLoopbackBEndpointId());
122+
workerThreadB.detach();
123+
124+
if (!m_receiveComplete.wait(20000))
125+
{
126+
std::cout << "Session: Failure waiting for all messages to be received." << std::endl;
127+
128+
VERIFY_FAIL();
129+
}
130+
else
131+
{
132+
std::cout << "Session: Message receive complete notification received." << std::endl;
133+
}
134+
135+
136+
}
137+
138+
139+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License
3+
// ============================================================================
4+
// This is part of the Windows MIDI Services App API and should be used
5+
// in your Windows application via an official binary distribution.
6+
// Further information: https://github.com/microsoft/MIDI/
7+
// ============================================================================
8+
9+
10+
#pragma once
11+
12+
using namespace winrt::Windows::Devices::Midi2;
13+
14+
15+
class MidiEndpointCreationThreadTests
16+
: public WEX::TestClass<MidiEndpointCreationThreadTests>
17+
{
18+
public:
19+
20+
BEGIN_TEST_CLASS(MidiEndpointCreationThreadTests)
21+
TEST_CLASS_PROPERTY(L"TestClassification", L"Unit")
22+
TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"Windows.Devices.Midi2.dll")
23+
TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"Midi2.BluetoothMidiAbstraction.dll")
24+
TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"Midi2.DiagnosticsAbstraction.dll")
25+
TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"Midi2.KSAbstraction.dll")
26+
TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"Midi2.MidiSrvAbstraction.dll")
27+
TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"Midi2.NetworkMidiAbstraction.dll")
28+
TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"Midi2.VirtualMidiAbstraction.dll")
29+
TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"Midi2.VirtualPatchBayAbstraction.dll")
30+
TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"Minmidi.sys")
31+
TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"usbmidi2.sys")
32+
TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"MidiSrv.exe")
33+
END_TEST_CLASS()
34+
35+
//TEST_CLASS_SETUP(ClassSetup);
36+
//TEST_CLASS_CLEANUP(ClassCleanup);
37+
38+
//TEST_METHOD_SETUP(TestSetup);
39+
//TEST_METHOD_CLEANUP(TestCleanup);
40+
41+
//Generic Tests
42+
TEST_METHOD(TestCreateNewSessionMultithreaded);
43+
44+
45+
private:
46+
47+
void ReceiveThreadWorker(_In_ MidiSession session, _In_ winrt::hstring endpointId);
48+
void SendThreadWorker(_In_ MidiSession session, _In_ winrt::hstring endpointId);
49+
50+
51+
wil::unique_event_nothrow m_receiverReady;
52+
53+
wil::unique_event_nothrow m_receiveComplete;
54+
55+
56+
};
57+

0 commit comments

Comments
 (0)