@@ -93,6 +93,9 @@ extern "C"
9393 } \
9494 } while (false )
9595
96+ // On macOS 26, sem_open fails if debugger and debugee are signed with different team ids.
97+ // Use fifos instead of semaphores to avoid this issue, https://github.com/dotnet/runtime/issues/116545
98+ #define ENABLE_RUNTIME_EVENTS_OVER_PIPES
9699#endif // __APPLE__
97100
98101#ifdef __NetBSD__
@@ -1401,21 +1404,217 @@ static uint64_t HashSemaphoreName(uint64_t a, uint64_t b)
14011404static const char *const TwoWayNamedPipePrefix = " clr-debug-pipe" ;
14021405static const char * IpcNameFormat = " %s-%d-%llu-%s" ;
14031406
1404- /* ++
1405- PAL_NotifyRuntimeStarted
1407+ #ifdef ENABLE_RUNTIME_EVENTS_OVER_PIPES
1408+ static const char * RuntimeStartupPipeName = " st" ;
1409+ static const char * RuntimeContinuePipeName = " co" ;
14061410
1407- Signals the debugger waiting for runtime startup notification to continue and
1408- waits until the debugger signals us to continue.
1411+ #define PIPE_OPEN_RETRY_DELAY_NS 500000000 // 500 ms
14091412
1410- Parameters:
1411- None
1413+ typedef enum
1414+ {
1415+ RuntimeEventsOverPipes_Disabled = 0 ,
1416+ RuntimeEventsOverPipes_Succeeded = 1 ,
1417+ RuntimeEventsOverPipes_Failed = 2 ,
1418+ } RuntimeEventsOverPipes;
14121419
1413- Return value:
1414- TRUE - successfully launched by debugger, FALSE - not launched or some failure in the handshake
1415- --*/
1420+ typedef enum
1421+ {
1422+ RuntimeEvent_Unknown = 0 ,
1423+ RuntimeEvent_Started = 1 ,
1424+ RuntimeEvent_Continue = 2 ,
1425+ } RuntimeEvent;
1426+
1427+ static
1428+ int
1429+ OpenPipe (const char * name, int mode)
1430+ {
1431+ int fd = -1 ;
1432+ int flags = mode | O_NONBLOCK;
1433+
1434+ #if defined(FD_CLOEXEC)
1435+ flags |= O_CLOEXEC;
1436+ #endif
1437+
1438+ while (fd == -1 )
1439+ {
1440+ fd = open (name, flags);
1441+ if (fd == -1 )
1442+ {
1443+ if (mode == O_WRONLY && errno == ENXIO)
1444+ {
1445+ PAL_nanosleep (PIPE_OPEN_RETRY_DELAY_NS);
1446+ continue ;
1447+ }
1448+ else if (errno == EINTR)
1449+ {
1450+ continue ;
1451+ }
1452+ else
1453+ {
1454+ break ;
1455+ }
1456+ }
1457+ }
1458+
1459+ if (fd != -1 )
1460+ {
1461+ flags = fcntl (fd, F_GETFL);
1462+ if (flags != -1 )
1463+ {
1464+ flags &= ~O_NONBLOCK;
1465+ if (fcntl (fd, F_SETFL, flags) == -1 )
1466+ {
1467+ close (fd);
1468+ fd = -1 ;
1469+ }
1470+ }
1471+ else
1472+ {
1473+ close (fd);
1474+ fd = -1 ;
1475+ }
1476+ }
1477+
1478+ return fd;
1479+ }
1480+
1481+ static
1482+ void
1483+ ClosePipe (int fd)
1484+ {
1485+ if (fd != -1 )
1486+ {
1487+ while (close (fd) < 0 && errno == EINTR);
1488+ }
1489+ }
1490+
1491+ static
1492+ RuntimeEventsOverPipes
1493+ NotifyRuntimeUsingPipes ()
1494+ {
1495+ RuntimeEventsOverPipes result = RuntimeEventsOverPipes_Disabled;
1496+ char startupPipeName[MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH];
1497+ char continuePipeName[MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH];
1498+ int startupPipeFd = -1 ;
1499+ int continuePipeFd = -1 ;
1500+ size_t offset = 0 ;
1501+
1502+ LPCSTR applicationGroupId = PAL_GetApplicationGroupId ();
1503+
1504+ PAL_GetTransportPipeName (continuePipeName, gPID , applicationGroupId, RuntimeContinuePipeName);
1505+ TRACE (" NotifyRuntimeUsingPipes: opening continue '%s' pipe\n " , continuePipeName);
1506+
1507+ continuePipeFd = OpenPipe (continuePipeName, O_RDONLY);
1508+ if (continuePipeFd == -1 )
1509+ {
1510+ if (errno == ENOENT || errno == EACCES)
1511+ {
1512+ TRACE (" NotifyRuntimeUsingPipes: pipe %s not found/accessible, runtime events over pipes disabled\n " , continuePipeName);
1513+ }
1514+ else
1515+ {
1516+ TRACE (" NotifyRuntimeUsingPipes: open(%s) failed: %d (%s)\n " , continuePipeName, errno, strerror (errno));
1517+ result = RuntimeEventsOverPipes_Failed;
1518+ }
1519+
1520+ goto exit;
1521+ }
1522+
1523+ PAL_GetTransportPipeName (startupPipeName, gPID , applicationGroupId, RuntimeStartupPipeName);
1524+ TRACE (" NotifyRuntimeUsingPipes: opening startup '%s' pipe\n " , startupPipeName);
1525+
1526+ startupPipeFd = OpenPipe (startupPipeName, O_WRONLY);
1527+ if (startupPipeFd == -1 )
1528+ {
1529+ if (errno == ENOENT || errno == EACCES)
1530+ {
1531+ TRACE (" NotifyRuntimeUsingPipes: pipe %s not found/accessible, runtime events over pipes disabled\n " , startupPipeName);
1532+ }
1533+ else
1534+ {
1535+ TRACE (" NotifyRuntimeUsingPipes: open(%s) failed: %d (%s)\n " , startupPipeName, errno, strerror (errno));
1536+ result = RuntimeEventsOverPipes_Failed;
1537+ }
1538+
1539+ goto exit;
1540+ }
1541+
1542+ TRACE (" NotifyRuntimeUsingPipes: sending started event\n " );
1543+
1544+ {
1545+ unsigned char event = (unsigned char )RuntimeEvent_Started;
1546+ unsigned char *buffer = &event;
1547+ int bytesToWrite = sizeof (event);
1548+ int bytesWritten = 0 ;
1549+
1550+ do
1551+ {
1552+ bytesWritten = write (startupPipeFd, buffer + offset, bytesToWrite - offset);
1553+ if (bytesWritten > 0 )
1554+ {
1555+ offset += bytesWritten;
1556+ }
1557+ }
1558+ while ((bytesWritten > 0 && offset < bytesToWrite) || (bytesWritten == -1 && errno == EINTR));
1559+
1560+ if (offset != bytesToWrite)
1561+ {
1562+ TRACE (" NotifyRuntimeUsingPipes: write(%s) failed: %d (%s)\n " , startupPipeName, errno, strerror (errno));
1563+ goto exit;
1564+ }
1565+ }
1566+
1567+ TRACE (" NotifyRuntimeUsingPipes: waiting on continue event\n " );
1568+
1569+ {
1570+ unsigned char event = (unsigned char )RuntimeEvent_Unknown;
1571+ unsigned char *buffer = &event;
1572+ int bytesToRead = sizeof (event);
1573+ int bytesRead = 0 ;
1574+
1575+ offset = 0 ;
1576+ do
1577+ {
1578+ bytesRead = read (continuePipeFd, buffer + offset, bytesToRead - offset);
1579+ if (bytesRead > 0 )
1580+ {
1581+ offset += bytesRead;
1582+ }
1583+ }
1584+ while ((bytesRead > 0 && offset < bytesToRead) || (bytesRead == -1 && errno == EINTR));
1585+
1586+ if (offset == bytesToRead && event == (unsigned char )RuntimeEvent_Continue)
1587+ {
1588+ TRACE (" NotifyRuntimeUsingPipes: received continue event\n " );
1589+ }
1590+ else
1591+ {
1592+ TRACE (" NotifyRuntimeUsingPipes: received invalid event\n " );
1593+ goto exit;
1594+ }
1595+ }
1596+
1597+ result = RuntimeEventsOverPipes_Succeeded;
1598+
1599+ exit:
1600+
1601+ if (startupPipeFd != -1 )
1602+ {
1603+ ClosePipe (startupPipeFd);
1604+ }
1605+
1606+ if (continuePipeFd != -1 )
1607+ {
1608+ ClosePipe (continuePipeFd);
1609+ }
1610+
1611+ return result;
1612+ }
1613+ #endif // ENABLE_RUNTIME_EVENTS_OVER_PIPES
1614+
1615+ static
14161616BOOL
1417- PALAPI
1418- PAL_NotifyRuntimeStarted ()
1617+ NotifyRuntimeUsingSemaphores ()
14191618{
14201619 char startupSemName[CLR_SEM_MAX_NAMELEN];
14211620 char continueSemName[CLR_SEM_MAX_NAMELEN];
@@ -1436,13 +1635,13 @@ PAL_NotifyRuntimeStarted()
14361635 CreateSemaphoreName (startupSemName, RuntimeStartupSemaphoreName, unambiguousProcessDescriptor, applicationGroupId);
14371636 CreateSemaphoreName (continueSemName, RuntimeContinueSemaphoreName, unambiguousProcessDescriptor, applicationGroupId);
14381637
1439- TRACE (" PAL_NotifyRuntimeStarted opening continue '%s' startup '%s'\n " , continueSemName, startupSemName);
1638+ TRACE (" NotifyRuntimeUsingSemaphores: opening continue '%s' startup '%s'\n " , continueSemName, startupSemName);
14401639
14411640 // Open the debugger startup semaphore. If it doesn't exists, then we do nothing and return
14421641 startupSem = sem_open (startupSemName, 0 );
14431642 if (startupSem == SEM_FAILED)
14441643 {
1445- TRACE (" sem_open(%s) failed: %d (%s)\n " , startupSemName, errno, strerror (errno));
1644+ TRACE (" NotifyRuntimeUsingSemaphores: sem_open(%s) failed: %d (%s)\n " , startupSemName, errno, strerror (errno));
14461645 goto exit;
14471646 }
14481647
@@ -1465,7 +1664,7 @@ PAL_NotifyRuntimeStarted()
14651664 {
14661665 if (EINTR == errno)
14671666 {
1468- TRACE (" sem_wait() failed with EINTR; re-waiting" );
1667+ TRACE (" NotifyRuntimeUsingSemaphores: sem_wait() failed with EINTR; re-waiting" );
14691668 continue ;
14701669 }
14711670 ASSERT (" sem_wait(continueSem) failed: errno is %d (%s)\n " , errno, strerror (errno));
@@ -1487,6 +1686,45 @@ PAL_NotifyRuntimeStarted()
14871686 return launched;
14881687}
14891688
1689+ /* ++
1690+ PAL_NotifyRuntimeStarted
1691+
1692+ Signals the debugger waiting for runtime startup notification to continue and
1693+ waits until the debugger signals us to continue.
1694+
1695+ Parameters:
1696+ None
1697+
1698+ Return value:
1699+ TRUE - successfully launched by debugger, FALSE - not launched or some failure in the handshake
1700+ --*/
1701+ BOOL
1702+ PALAPI
1703+ PAL_NotifyRuntimeStarted ()
1704+ {
1705+ #ifdef ENABLE_RUNTIME_EVENTS_OVER_PIPES
1706+ // Test pipes as runtime event transport.
1707+ RuntimeEventsOverPipes result = NotifyRuntimeUsingPipes ();
1708+ switch (result)
1709+ {
1710+ case RuntimeEventsOverPipes_Disabled:
1711+ TRACE (" PAL_NotifyRuntimeStarted: pipe handshake disabled, try semaphores\n " );
1712+ return NotifyRuntimeUsingSemaphores ();
1713+ case RuntimeEventsOverPipes_Failed:
1714+ TRACE (" PAL_NotifyRuntimeStarted: pipe handshake failed\n " );
1715+ return FALSE ;
1716+ case RuntimeEventsOverPipes_Succeeded:
1717+ TRACE (" PAL_NotifyRuntimeStarted: pipe handshake succeeded\n " );
1718+ return TRUE ;
1719+ default :
1720+ // Unexpected result.
1721+ return FALSE ;
1722+ }
1723+ #else
1724+ return NotifyRuntimeUsingSemaphores ();
1725+ #endif // ENABLE_RUNTIME_EVENTS_OVER_PIPES
1726+ }
1727+
14901728LPCSTR
14911729PALAPI
14921730PAL_GetApplicationGroupId ()
0 commit comments