Skip to content

Commit fb1cd6b

Browse files
committed
Draft for ensuring exclusive main thread operation between host and plug-in when using IPC
1 parent 8d9354e commit fb1cd6b

File tree

5 files changed

+201
-2
lines changed

5 files changed

+201
-2
lines changed

IPC/ARAIPCEncoding.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,10 @@ constexpr auto kBindToDocumentControllerMethodID { MethodID::createWithARAGlobal
10891089
constexpr auto kCleanupBindingMethodID { MethodID::createWithARAGlobalMessageID<6> () };
10901090
constexpr auto kUninitializeARAMethodID { MethodID::createWithARAGlobalMessageID<7> () };
10911091

1092+
ARA_DRAFT constexpr auto kLockDistributedMainThreadMethodID { MethodID::createWithARAGlobalMessageID<kReservedMessageIDRangeEnd - 3> () };
1093+
ARA_DRAFT constexpr auto kTryLockDistributedMainThreadMethodID { MethodID::createWithARAGlobalMessageID<kReservedMessageIDRangeEnd - 2> () };
1094+
ARA_DRAFT constexpr auto kUnlockDistributedMainThreadMethodID { MethodID::createWithARAGlobalMessageID<kReservedMessageIDRangeEnd - 1> () };
1095+
10921096

10931097
// for debugging: translate IDs of messages sent by the host back to human-readable text
10941098
inline const char* decodeHostMessageID (const MessageID messageID)
@@ -1181,6 +1185,13 @@ inline const char* decodePlugInMessageID (const MessageID messageID)
11811185
{
11821186
switch (messageID)
11831187
{
1188+
#define ARA_IPC_GLOBAL_MESSAGE_CASE(methodID) \
1189+
case methodID.getMessageID (): return #methodID;
1190+
ARA_IPC_GLOBAL_MESSAGE_CASE (kLockDistributedMainThreadMethodID);
1191+
ARA_IPC_GLOBAL_MESSAGE_CASE (kTryLockDistributedMainThreadMethodID);
1192+
ARA_IPC_GLOBAL_MESSAGE_CASE (kUnlockDistributedMainThreadMethodID);
1193+
#undef ARA_IPC_GLOBAL_MESSAGE_CASE
1194+
11841195
#define ARA_IPC_HOST_INTERFACE_CASE(StructT, member) \
11851196
case ARA_IPC_HOST_METHOD_ID(StructT, member).getMessageID (): return #StructT "::" #member;
11861197
ARA_IPC_HOST_INTERFACE_CASE (ARAAudioAccessControllerInterface, createAudioReaderForSource)

IPC/ARAIPCProxyHost.cpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,8 @@ using namespace ProxyHostImpl;
627627

628628

629629
std::vector<const ARAFactory*> _factories {};
630-
ARAIPCBindingHandler _bindingHandler {};
630+
ARAIPCBindingHandler _bindingHandler { nullptr };
631+
ProxyHost* _currentProxyHost { nullptr }; // there only can be one host at any time
631632

632633
void ARAIPCProxyHostAddFactory (const ARAFactory* factory)
633634
{
@@ -659,12 +660,40 @@ ARABool ARAIPCProxyHostCurrentThreadActsAsMainThread ()
659660
return (Connection::currentThreadActsAsMainThread ()) ? kARATrue : kARAFalse;
660661
}
661662

663+
void ARAIPCProxyHostLockDistributedMainThread ()
664+
{
665+
ARA_INTERNAL_ASSERT (_currentProxyHost != nullptr);
666+
_currentProxyHost->remoteCall (kLockDistributedMainThreadMethodID);
667+
}
668+
669+
ARABool ARAIPCProxyHostTryLockDistributedMainThread ()
670+
{
671+
ARA_INTERNAL_ASSERT (_currentProxyHost != nullptr);
672+
ARABool reply;
673+
_currentProxyHost->remoteCall (reply, kTryLockDistributedMainThreadMethodID);
674+
return reply;
675+
}
676+
677+
void ARAIPCProxyHostUnlockDistributedMainThread ()
678+
{
679+
ARA_INTERNAL_ASSERT (_currentProxyHost != nullptr);
680+
_currentProxyHost->remoteCall (kUnlockDistributedMainThreadMethodID);
681+
}
662682

663683
/*******************************************************************************/
664684

665685
ProxyHost::ProxyHost (Connection* connection)
666686
: RemoteCaller (connection)
667-
{}
687+
{
688+
ARA_INTERNAL_ASSERT (_currentProxyHost == nullptr);
689+
_currentProxyHost = this;
690+
}
691+
692+
ProxyHost::~ProxyHost ()
693+
{
694+
ARA_INTERNAL_ASSERT (_currentProxyHost == this);
695+
_currentProxyHost = nullptr;
696+
}
668697

669698
void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder,
670699
MessageEncoder* const replyEncoder)

IPC/ARAIPCProxyHost.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class ProxyHost : public MessageHandler, public RemoteCaller
4343
{
4444
protected:
4545
explicit ProxyHost (Connection* connection);
46+
~ProxyHost () override;
4647

4748
public:
4849
void handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder,
@@ -69,6 +70,16 @@ void ARAIPCProxyHostSetBindingHandler(ARAIPCBindingHandler handler);
6970
ARA_DRAFT ARABool ARAIPCProxyHostCurrentThreadActsAsMainThread ();
7071

7172

73+
//! draft for synchronizing host and plug-in main thread
74+
//! the lock must be taken each time the plug-in thread wakes up, in order to prevent the host main
75+
//! thread from interfering with the main thread operation in the plug-in
76+
//! try-lock can be used for operations which can be simply aborted if the host is currently busy,
77+
//! e.g. when a repeating timer fires
78+
ARA_DRAFT void ARAIPCProxyHostLockDistributedMainThread(void);
79+
ARA_DRAFT ARABool ARAIPCProxyHostTryLockDistributedMainThread(void);
80+
ARA_DRAFT void ARAIPCProxyHostUnlockDistributedMainThread(void);
81+
82+
7283
#if defined(__cplusplus)
7384
} // extern "C"
7485
} // namespace IPC

IPC/ARAIPCProxyPlugIn.cpp

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525
#include "ARA_Library/Dispatch/ARAPlugInDispatch.h"
2626
#include "ARA_Library/Dispatch/ARAHostDispatch.h"
2727

28+
#include <condition_variable>
2829
#include <cstring>
2930
#include <map>
31+
#include <mutex>
3032
#include <set>
3133
#include <string>
3234

@@ -1296,6 +1298,115 @@ struct RemoteFactory
12961298
std::map<std::string, RemoteFactory> _factories {};
12971299

12981300

1301+
class DistributedMainThreadLock
1302+
{
1303+
private:
1304+
// for some reason, Xcode thinks this is no constant expression...
1305+
// static constexpr ARAIPCConnectionRef _dummyLocalConnectionRef { reinterpret_cast<ARAIPCConnectionRef> (-1) };
1306+
const ARAIPCConnectionRef _dummyLocalConnectionRef { reinterpret_cast<ARAIPCConnectionRef> (-1) };
1307+
1308+
template<bool tryLock>
1309+
bool _lockImpl (ARAIPCConnectionRef connectionRef)
1310+
{
1311+
std::unique_lock <std::mutex> lock { _lock };
1312+
if (_lockingConnection == nullptr)
1313+
{
1314+
ARA_INTERNAL_ASSERT (_recursionCount == 0);
1315+
_lockingConnection = connectionRef;
1316+
return true;
1317+
}
1318+
else if (_lockingConnection == connectionRef)
1319+
{
1320+
++_recursionCount;
1321+
return true;
1322+
}
1323+
else
1324+
{
1325+
if (tryLock)
1326+
return false;
1327+
1328+
_condition.wait (lock, [this, connectionRef]
1329+
{ return (_lockingConnection == nullptr) || (_lockingConnection == connectionRef); });
1330+
if (_lockingConnection == nullptr)
1331+
{
1332+
ARA_INTERNAL_ASSERT (_recursionCount == 0);
1333+
_lockingConnection = connectionRef;
1334+
}
1335+
else
1336+
{
1337+
ARA_INTERNAL_ASSERT (_lockingConnection == connectionRef);
1338+
++_recursionCount;
1339+
}
1340+
return true;
1341+
}
1342+
}
1343+
1344+
void _unlockImpl (ARAIPCConnectionRef connectionRef)
1345+
{
1346+
std::unique_lock <std::mutex> lock { _lock };
1347+
ARA_INTERNAL_ASSERT (_lockingConnection == connectionRef);
1348+
if (_recursionCount > 0)
1349+
{
1350+
--_recursionCount;
1351+
}
1352+
else
1353+
{
1354+
_lockingConnection = nullptr;
1355+
_condition.notify_all ();
1356+
}
1357+
}
1358+
1359+
public:
1360+
DistributedMainThreadLock () = default;
1361+
1362+
void lockFromLocalMainThread ()
1363+
{
1364+
_lockImpl<false> (_dummyLocalConnectionRef);
1365+
ARA_IPC_LOG ("distributed main thread was locked from host.");
1366+
}
1367+
bool tryLockFromLocalMainThread ()
1368+
{
1369+
const auto result { _lockImpl<true> (_dummyLocalConnectionRef) };
1370+
if (result)
1371+
ARA_IPC_LOG ("distributed main thread was try-locked from host.");
1372+
else
1373+
ARA_IPC_LOG ("distributed main thread try-lock from host failed.");
1374+
return result;
1375+
}
1376+
void unlockFromLocalMainThread ()
1377+
{
1378+
ARA_IPC_LOG ("distributed main thread will be unlocked from host.");
1379+
_unlockImpl (_dummyLocalConnectionRef);
1380+
}
1381+
1382+
void lockFromRemoteMainThread (ARAIPCConnectionRef connectionRef)
1383+
{
1384+
_lockImpl<false> (connectionRef);
1385+
ARA_IPC_LOG ("distributed main thread was locked from remote.");
1386+
}
1387+
bool tryLockFromRemoteMainThread (ARAIPCConnectionRef connectionRef)
1388+
{
1389+
const auto result { _lockImpl<true> (connectionRef) };
1390+
if (result)
1391+
ARA_IPC_LOG ("distributed main thread was try-locked from remote.");
1392+
else
1393+
ARA_IPC_LOG ("distributed main thread try-lock from remote failed.");
1394+
return result;
1395+
}
1396+
void unlockFromRemoteMainThread (ARAIPCConnectionRef connectionRef)
1397+
{
1398+
ARA_IPC_LOG ("distributed main thread will be unlocked from remote.");
1399+
_unlockImpl (connectionRef);
1400+
}
1401+
1402+
private:
1403+
std::mutex _lock;
1404+
std::condition_variable _condition;
1405+
ARAIPCConnectionRef _lockingConnection { nullptr };
1406+
int _recursionCount { 0 };
1407+
} _distributedMainThreadLock {};
1408+
1409+
12991410
/*******************************************************************************/
13001411

13011412
} // namespace ProxyPlugInImpl
@@ -1433,6 +1544,20 @@ ARABool ARAIPCProxyPlugInCurrentThreadActsAsMainThread ()
14331544
return (Connection::currentThreadActsAsMainThread ()) ? kARATrue : kARAFalse;
14341545
}
14351546

1547+
void ARAIPCProxyPlugInLockDistributedMainThread ()
1548+
{
1549+
_distributedMainThreadLock.lockFromLocalMainThread ();
1550+
}
1551+
1552+
ARABool ARAIPCProxyPlugInTryLockDistributedMainThread ()
1553+
{
1554+
return (_distributedMainThreadLock.tryLockFromLocalMainThread ()) ? kARATrue : kARAFalse;
1555+
}
1556+
1557+
void ARAIPCProxyPlugInUnlockDistributedMainThread ()
1558+
{
1559+
_distributedMainThreadLock.unlockFromLocalMainThread ();
1560+
}
14361561

14371562
/*******************************************************************************/
14381563

@@ -1841,6 +1966,19 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag
18411966

18421967
documentController->getHostPlaybackController ()->requestEnableCycle (enable != kARAFalse);
18431968
}
1969+
else if (messageID == kLockDistributedMainThreadMethodID)
1970+
{
1971+
_distributedMainThreadLock.lockFromRemoteMainThread (toIPCRef (getConnection ()));
1972+
}
1973+
else if (messageID == kTryLockDistributedMainThreadMethodID)
1974+
{
1975+
auto result { _distributedMainThreadLock.tryLockFromRemoteMainThread (toIPCRef (getConnection ())) };
1976+
encodeReply (replyEncoder, (result) ? kARATrue : kARAFalse);
1977+
}
1978+
else if (messageID == kUnlockDistributedMainThreadMethodID)
1979+
{
1980+
_distributedMainThreadLock.unlockFromRemoteMainThread (toIPCRef (getConnection ()));
1981+
}
18441982
else
18451983
{
18461984
ARA_INTERNAL_ASSERT (false && "unhandled message ID");

IPC/ARAIPCProxyPlugIn.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,16 @@ void ARAIPCProxyPlugInUninitializeARA(ARAIPCConnectionRef connectionRef, const A
8585
ARA_DRAFT ARABool ARAIPCProxyPlugInCurrentThreadActsAsMainThread ();
8686

8787

88+
//! draft for synchronizing host and plug-in main thread
89+
//! the lock must be taken each time the main thread wakes up, in order to prevent the plug-in main
90+
//! thread from interfering with the main thread operation in the host
91+
//! try-lock can be used for operations which can be simply aborted if the plug-in is currently busy,
92+
//! e.g. when a repeating timer fires
93+
ARA_DRAFT void ARAIPCProxyPlugInLockDistributedMainThread(void);
94+
ARA_DRAFT ARABool ARAIPCProxyPlugInTryLockDistributedMainThread(void);
95+
ARA_DRAFT void ARAIPCProxyPlugInUnlockDistributedMainThread(void);
96+
97+
8898
#if defined(__cplusplus)
8999
} // extern "C"
90100
} // namespace IPC

0 commit comments

Comments
 (0)