@@ -103,6 +103,9 @@ extern "C"
103103 } \
104104 } while (false )
105105
106+ // On macOS 26, sem_open fails if debugger and debugee are signed with different team ids.
107+ // Use fifos instead of semaphores to avoid this issue, https://github.com/dotnet/runtime/issues/116545
108+ #define ENABLE_RUNTIME_EVENTS_OVER_PIPES
106109#endif // __APPLE__
107110
108111#ifdef __NetBSD__
@@ -1430,21 +1433,217 @@ static uint64_t HashSemaphoreName(uint64_t a, uint64_t b)
14301433static const char *const TwoWayNamedPipePrefix = " clr-debug-pipe" ;
14311434static const char * IpcNameFormat = " %s-%d-%llu-%s" ;
14321435
1433- /* ++
1434- PAL_NotifyRuntimeStarted
1436+ #ifdef ENABLE_RUNTIME_EVENTS_OVER_PIPES
1437+ static const char * RuntimeStartupPipeName = " st" ;
1438+ static const char * RuntimeContinuePipeName = " co" ;
14351439
1436- Signals the debugger waiting for runtime startup notification to continue and
1437- waits until the debugger signals us to continue.
1440+ #define PIPE_OPEN_RETRY_DELAY_NS 500000000 // 500 ms
14381441
1439- Parameters:
1440- None
1442+ typedef enum
1443+ {
1444+ RuntimeEventsOverPipes_Disabled = 0 ,
1445+ RuntimeEventsOverPipes_Succeeded = 1 ,
1446+ RuntimeEventsOverPipes_Failed = 2 ,
1447+ } RuntimeEventsOverPipes;
14411448
1442- Return value:
1443- TRUE - successfully launched by debugger, FALSE - not launched or some failure in the handshake
1444- --*/
1449+ typedef enum
1450+ {
1451+ RuntimeEvent_Unknown = 0 ,
1452+ RuntimeEvent_Started = 1 ,
1453+ RuntimeEvent_Continue = 2 ,
1454+ } RuntimeEvent;
1455+
1456+ static
1457+ int
1458+ OpenPipe (const char * name, int mode)
1459+ {
1460+ int fd = -1 ;
1461+ int flags = mode | O_NONBLOCK;
1462+
1463+ #if defined(FD_CLOEXEC)
1464+ flags |= O_CLOEXEC;
1465+ #endif
1466+
1467+ while (fd == -1 )
1468+ {
1469+ fd = open (name, flags);
1470+ if (fd == -1 )
1471+ {
1472+ if (mode == O_WRONLY && errno == ENXIO)
1473+ {
1474+ PAL_nanosleep (PIPE_OPEN_RETRY_DELAY_NS);
1475+ continue ;
1476+ }
1477+ else if (errno == EINTR)
1478+ {
1479+ continue ;
1480+ }
1481+ else
1482+ {
1483+ break ;
1484+ }
1485+ }
1486+ }
1487+
1488+ if (fd != -1 )
1489+ {
1490+ flags = fcntl (fd, F_GETFL);
1491+ if (flags != -1 )
1492+ {
1493+ flags &= ~O_NONBLOCK;
1494+ if (fcntl (fd, F_SETFL, flags) == -1 )
1495+ {
1496+ close (fd);
1497+ fd = -1 ;
1498+ }
1499+ }
1500+ else
1501+ {
1502+ close (fd);
1503+ fd = -1 ;
1504+ }
1505+ }
1506+
1507+ return fd;
1508+ }
1509+
1510+ static
1511+ void
1512+ ClosePipe (int fd)
1513+ {
1514+ if (fd != -1 )
1515+ {
1516+ while (close (fd) < 0 && errno == EINTR);
1517+ }
1518+ }
1519+
1520+ static
1521+ RuntimeEventsOverPipes
1522+ NotifyRuntimeUsingPipes ()
1523+ {
1524+ RuntimeEventsOverPipes result = RuntimeEventsOverPipes_Disabled;
1525+ char startupPipeName[MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH];
1526+ char continuePipeName[MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH];
1527+ int startupPipeFd = -1 ;
1528+ int continuePipeFd = -1 ;
1529+ size_t offset = 0 ;
1530+
1531+ LPCSTR applicationGroupId = PAL_GetApplicationGroupId ();
1532+
1533+ PAL_GetTransportPipeName (continuePipeName, gPID , applicationGroupId, RuntimeContinuePipeName);
1534+ TRACE (" NotifyRuntimeUsingPipes: opening continue '%s' pipe\n " , continuePipeName);
1535+
1536+ continuePipeFd = OpenPipe (continuePipeName, O_RDONLY);
1537+ if (continuePipeFd == -1 )
1538+ {
1539+ if (errno == ENOENT || errno == EACCES)
1540+ {
1541+ TRACE (" NotifyRuntimeUsingPipes: pipe %s not found/accessible, runtime events over pipes disabled\n " , continuePipeName);
1542+ }
1543+ else
1544+ {
1545+ TRACE (" NotifyRuntimeUsingPipes: open(%s) failed: %d (%s)\n " , continuePipeName, errno, strerror (errno));
1546+ result = RuntimeEventsOverPipes_Failed;
1547+ }
1548+
1549+ goto exit;
1550+ }
1551+
1552+ PAL_GetTransportPipeName (startupPipeName, gPID , applicationGroupId, RuntimeStartupPipeName);
1553+ TRACE (" NotifyRuntimeUsingPipes: opening startup '%s' pipe\n " , startupPipeName);
1554+
1555+ startupPipeFd = OpenPipe (startupPipeName, O_WRONLY);
1556+ if (startupPipeFd == -1 )
1557+ {
1558+ if (errno == ENOENT || errno == EACCES)
1559+ {
1560+ TRACE (" NotifyRuntimeUsingPipes: pipe %s not found/accessible, runtime events over pipes disabled\n " , startupPipeName);
1561+ }
1562+ else
1563+ {
1564+ TRACE (" NotifyRuntimeUsingPipes: open(%s) failed: %d (%s)\n " , startupPipeName, errno, strerror (errno));
1565+ result = RuntimeEventsOverPipes_Failed;
1566+ }
1567+
1568+ goto exit;
1569+ }
1570+
1571+ TRACE (" NotifyRuntimeUsingPipes: sending started event\n " );
1572+
1573+ {
1574+ unsigned char event = (unsigned char )RuntimeEvent_Started;
1575+ unsigned char *buffer = &event;
1576+ int bytesToWrite = sizeof (event);
1577+ int bytesWritten = 0 ;
1578+
1579+ do
1580+ {
1581+ bytesWritten = write (startupPipeFd, buffer + offset, bytesToWrite - offset);
1582+ if (bytesWritten > 0 )
1583+ {
1584+ offset += bytesWritten;
1585+ }
1586+ }
1587+ while ((bytesWritten > 0 && offset < bytesToWrite) || (bytesWritten == -1 && errno == EINTR));
1588+
1589+ if (offset != bytesToWrite)
1590+ {
1591+ TRACE (" NotifyRuntimeUsingPipes: write(%s) failed: %d (%s)\n " , startupPipeName, errno, strerror (errno));
1592+ goto exit;
1593+ }
1594+ }
1595+
1596+ TRACE (" NotifyRuntimeUsingPipes: waiting on continue event\n " );
1597+
1598+ {
1599+ unsigned char event = (unsigned char )RuntimeEvent_Unknown;
1600+ unsigned char *buffer = &event;
1601+ int bytesToRead = sizeof (event);
1602+ int bytesRead = 0 ;
1603+
1604+ offset = 0 ;
1605+ do
1606+ {
1607+ bytesRead = read (continuePipeFd, buffer + offset, bytesToRead - offset);
1608+ if (bytesRead > 0 )
1609+ {
1610+ offset += bytesRead;
1611+ }
1612+ }
1613+ while ((bytesRead > 0 && offset < bytesToRead) || (bytesRead == -1 && errno == EINTR));
1614+
1615+ if (offset == bytesToRead && event == (unsigned char )RuntimeEvent_Continue)
1616+ {
1617+ TRACE (" NotifyRuntimeUsingPipes: received continue event\n " );
1618+ }
1619+ else
1620+ {
1621+ TRACE (" NotifyRuntimeUsingPipes: received invalid event\n " );
1622+ goto exit;
1623+ }
1624+ }
1625+
1626+ result = RuntimeEventsOverPipes_Succeeded;
1627+
1628+ exit:
1629+
1630+ if (startupPipeFd != -1 )
1631+ {
1632+ ClosePipe (startupPipeFd);
1633+ }
1634+
1635+ if (continuePipeFd != -1 )
1636+ {
1637+ ClosePipe (continuePipeFd);
1638+ }
1639+
1640+ return result;
1641+ }
1642+ #endif // ENABLE_RUNTIME_EVENTS_OVER_PIPES
1643+
1644+ static
14451645BOOL
1446- PALAPI
1447- PAL_NotifyRuntimeStarted ()
1646+ NotifyRuntimeUsingSemaphores ()
14481647{
14491648 char startupSemName[CLR_SEM_MAX_NAMELEN];
14501649 char continueSemName[CLR_SEM_MAX_NAMELEN];
@@ -1465,13 +1664,13 @@ PAL_NotifyRuntimeStarted()
14651664 CreateSemaphoreName (startupSemName, RuntimeStartupSemaphoreName, unambiguousProcessDescriptor, applicationGroupId);
14661665 CreateSemaphoreName (continueSemName, RuntimeContinueSemaphoreName, unambiguousProcessDescriptor, applicationGroupId);
14671666
1468- TRACE (" PAL_NotifyRuntimeStarted opening continue '%s' startup '%s'\n " , continueSemName, startupSemName);
1667+ TRACE (" NotifyRuntimeUsingSemaphores: opening continue '%s' startup '%s'\n " , continueSemName, startupSemName);
14691668
14701669 // Open the debugger startup semaphore. If it doesn't exists, then we do nothing and return
14711670 startupSem = sem_open (startupSemName, 0 );
14721671 if (startupSem == SEM_FAILED)
14731672 {
1474- TRACE (" sem_open(%s) failed: %d (%s)\n " , startupSemName, errno, strerror (errno));
1673+ TRACE (" NotifyRuntimeUsingSemaphores: sem_open(%s) failed: %d (%s)\n " , startupSemName, errno, strerror (errno));
14751674 goto exit;
14761675 }
14771676
@@ -1494,7 +1693,7 @@ PAL_NotifyRuntimeStarted()
14941693 {
14951694 if (EINTR == errno)
14961695 {
1497- TRACE (" sem_wait() failed with EINTR; re-waiting" );
1696+ TRACE (" NotifyRuntimeUsingSemaphores: sem_wait() failed with EINTR; re-waiting" );
14981697 continue ;
14991698 }
15001699 ASSERT (" sem_wait(continueSem) failed: errno is %d (%s)\n " , errno, strerror (errno));
@@ -1516,6 +1715,45 @@ PAL_NotifyRuntimeStarted()
15161715 return launched;
15171716}
15181717
1718+ /* ++
1719+ PAL_NotifyRuntimeStarted
1720+
1721+ Signals the debugger waiting for runtime startup notification to continue and
1722+ waits until the debugger signals us to continue.
1723+
1724+ Parameters:
1725+ None
1726+
1727+ Return value:
1728+ TRUE - successfully launched by debugger, FALSE - not launched or some failure in the handshake
1729+ --*/
1730+ BOOL
1731+ PALAPI
1732+ PAL_NotifyRuntimeStarted ()
1733+ {
1734+ #ifdef ENABLE_RUNTIME_EVENTS_OVER_PIPES
1735+ // Test pipes as runtime event transport.
1736+ RuntimeEventsOverPipes result = NotifyRuntimeUsingPipes ();
1737+ switch (result)
1738+ {
1739+ case RuntimeEventsOverPipes_Disabled:
1740+ TRACE (" PAL_NotifyRuntimeStarted: pipe handshake disabled, try semaphores\n " );
1741+ return NotifyRuntimeUsingSemaphores ();
1742+ case RuntimeEventsOverPipes_Failed:
1743+ TRACE (" PAL_NotifyRuntimeStarted: pipe handshake failed\n " );
1744+ return FALSE ;
1745+ case RuntimeEventsOverPipes_Succeeded:
1746+ TRACE (" PAL_NotifyRuntimeStarted: pipe handshake succeeded\n " );
1747+ return TRUE ;
1748+ default :
1749+ // Unexpected result.
1750+ return FALSE ;
1751+ }
1752+ #else
1753+ return NotifyRuntimeUsingSemaphores ();
1754+ #endif // ENABLE_RUNTIME_EVENTS_OVER_PIPES
1755+ }
1756+
15191757LPCSTR
15201758PALAPI
15211759PAL_GetApplicationGroupId ()
0 commit comments