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