1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-26 02:14:22 +00:00

CoreMidi: Use RAII to avoid potential leaks of MIDI ports/endpoints

This commit is contained in:
reuk 2021-02-22 19:36:35 +00:00
parent a48a564f9e
commit 69b630a2c0

View file

@ -313,36 +313,89 @@ namespace CoreMidiHelpers
using SenderToUse = Sender<implementationStrategy>;
template <typename Resource, typename Destructor>
class ScopedMidiResource
{
public:
ScopedMidiResource() = default;
explicit ScopedMidiResource (Resource r) : contents (r, {}) {}
~ScopedMidiResource() noexcept
{
auto ref = std::get<0> (contents);
if (ref != 0)
std::get<1> (contents) (ref);
}
ScopedMidiResource (const ScopedMidiResource& other) = delete;
ScopedMidiResource& operator= (const ScopedMidiResource& other) = delete;
ScopedMidiResource (ScopedMidiResource&& other) noexcept { swap (other); }
ScopedMidiResource& operator= (ScopedMidiResource&& other) noexcept
{
swap (other);
return *this;
}
void swap (ScopedMidiResource& other) noexcept { std::swap (other.contents, contents); }
Resource operator*() const noexcept { return std::get<0> (contents); }
Resource release() noexcept
{
auto old = std::get<0> (contents);
std::get<0> (contents) = 0;
return old;
}
private:
std::tuple<Resource, Destructor> contents { {}, {} };
};
struct PortRefDestructor
{
void operator() (MIDIPortRef p) const noexcept { MIDIPortDispose (p); }
};
using ScopedPortRef = ScopedMidiResource<MIDIPortRef, PortRefDestructor>;
struct EndpointRefDestructor
{
void operator() (MIDIEndpointRef p) const noexcept { MIDIEndpointDispose (p); }
};
using ScopedEndpointRef = ScopedMidiResource<MIDIEndpointRef, EndpointRefDestructor>;
//==============================================================================
class MidiPortAndEndpoint
{
public:
MidiPortAndEndpoint (MIDIPortRef p, MIDIEndpointRef ep) noexcept
: port (p), endpoint (ep), sender (ep)
MidiPortAndEndpoint (ScopedPortRef p, ScopedEndpointRef ep) noexcept
: port (std::move (p)), endpoint (std::move (ep)), sender (*endpoint)
{}
~MidiPortAndEndpoint() noexcept
{
if (port != 0)
MIDIPortDispose (port);
// if port == nullptr, it means we created the endpoint, so it's safe to delete it
if (port == 0 && endpoint != 0)
MIDIEndpointDispose (endpoint);
// if port != 0, it means we didn't create the endpoint, so it's not safe to delete it
if (*port != 0)
endpoint.release();
}
void send (const MidiMessage& m)
{
sender.send (port, endpoint, m);
sender.send (*port, *endpoint, m);
}
void send (ump::Iterator b, ump::Iterator e)
{
sender.send (port, endpoint, b, e);
sender.send (*port, *endpoint, b, e);
}
bool canStop() const noexcept { return port != 0; }
void stop() const { CHECK_ERROR (MIDIPortDisconnectSource (port, endpoint)); }
bool canStop() const noexcept { return *port != 0; }
void stop() const { CHECK_ERROR (MIDIPortDisconnectSource (*port, *endpoint)); }
ump::MidiProtocol getProtocol() const noexcept
{
@ -350,8 +403,8 @@ namespace CoreMidiHelpers
}
private:
MIDIPortRef port;
MIDIEndpointRef endpoint;
ScopedPortRef port;
ScopedEndpointRef endpoint;
SenderToUse sender;
};
@ -1017,13 +1070,12 @@ public:
if (! CHECK_ERROR (CreatorFunctionsToUse::createInputPort (protocol, client, cfName.cfString, input->internal.get(), &port)))
continue;
if (! CHECK_ERROR (MIDIPortConnectSource (port, endpoint, nullptr)))
{
CHECK_ERROR (MIDIPortDispose (port));
continue;
}
ScopedPortRef scopedPort { port };
input->internal->portAndEndpoint = std::make_unique<MidiPortAndEndpoint> (port, endpoint);
if (! CHECK_ERROR (MIDIPortConnectSource (*scopedPort, endpoint, nullptr)))
continue;
input->internal->portAndEndpoint = std::make_unique<MidiPortAndEndpoint> (std::move (scopedPort), ScopedEndpointRef { endpoint });
return input;
}
}
@ -1049,6 +1101,7 @@ public:
ScopedCFString name (deviceName);
auto err = CreatorFunctionsToUse::createDestination (protocol, client, name.cfString, input->internal.get(), &endpoint);
ScopedEndpointRef scopedEndpoint { endpoint };
#if JUCE_IOS
if (err == kMIDINotPermitted)
@ -1066,7 +1119,7 @@ public:
if (! CHECK_ERROR (MIDIObjectSetIntegerProperty (endpoint, kMIDIPropertyUniqueID, (SInt32) deviceIdentifier)))
return {};
input->internal->portAndEndpoint = std::make_unique<MidiPortAndEndpoint> ((MIDIPortRef) 0, endpoint);
input->internal->portAndEndpoint = std::make_unique<MidiPortAndEndpoint> (ScopedPortRef{}, std::move (scopedEndpoint));
return input;
}
}
@ -1185,8 +1238,10 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (const String& deviceIdentifi
if (! CHECK_ERROR (MIDIOutputPortCreate (client, cfName.cfString, &port)))
continue;
ScopedPortRef scopedPort { port };
auto midiOutput = rawToUniquePtr (new MidiOutput (endpointInfo.name, endpointInfo.identifier));
midiOutput->internal = std::make_unique<Pimpl> (port, endpoint);
midiOutput->internal = std::make_unique<Pimpl> (std::move (scopedPort), ScopedEndpointRef { endpoint });
return midiOutput;
}
@ -1206,6 +1261,7 @@ std::unique_ptr<MidiOutput> MidiOutput::createNewDevice (const String& deviceNam
ScopedCFString name (deviceName);
auto err = CreatorFunctionsToUse::createSource (ump::PacketProtocol::MIDI_1_0, client, name.cfString, &endpoint);
ScopedEndpointRef scopedEndpoint { endpoint };
#if JUCE_IOS
if (err == kMIDINotPermitted)
@ -1222,11 +1278,11 @@ std::unique_ptr<MidiOutput> MidiOutput::createNewDevice (const String& deviceNam
auto deviceIdentifier = createUniqueIDForMidiPort (deviceName, false);
if (! CHECK_ERROR (MIDIObjectSetIntegerProperty (endpoint, kMIDIPropertyUniqueID, (SInt32) deviceIdentifier)))
if (! CHECK_ERROR (MIDIObjectSetIntegerProperty (*scopedEndpoint, kMIDIPropertyUniqueID, (SInt32) deviceIdentifier)))
return {};
auto midiOutput = rawToUniquePtr (new MidiOutput (deviceName, String (deviceIdentifier)));
midiOutput->internal = std::make_unique<Pimpl> ((UInt32) 0, endpoint);
midiOutput->internal = std::make_unique<Pimpl> (ScopedPortRef{}, std::move (scopedEndpoint));
return midiOutput;
}