diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Runtime/r3/os2 | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/r3/os2')
-rw-r--r-- | src/VBox/Runtime/r3/os2/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/os2/RTTimeSet-os2.cpp | 121 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/os2/filelock-os2.cpp | 186 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/os2/mp-os2.cpp | 125 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/os2/pipe-os2.cpp | 1083 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/os2/rtProcInitExePath-os2.cpp | 71 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/os2/sched-os2.cpp | 244 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/os2/sems-os2.cpp | 392 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/os2/serialport-os2.cpp | 755 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/os2/systemmem-os2.cpp | 80 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/os2/thread-os2.cpp | 330 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/os2/time-os2.cpp | 87 |
12 files changed, 3474 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r3/os2/Makefile.kup b/src/VBox/Runtime/r3/os2/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Runtime/r3/os2/Makefile.kup diff --git a/src/VBox/Runtime/r3/os2/RTTimeSet-os2.cpp b/src/VBox/Runtime/r3/os2/RTTimeSet-os2.cpp new file mode 100644 index 00000000..fded291b --- /dev/null +++ b/src/VBox/Runtime/r3/os2/RTTimeSet-os2.cpp @@ -0,0 +1,121 @@ +/* $Id: RTTimeSet-os2.cpp $ */ +/** @file + * IPRT - RTTimeSet, OS/2. + */ + +/* + * Contributed by knut st. osmundsen. + * + * Copyright (C) 2018-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + * -------------------------------------------------------------------- + * + * Copyright (c) 2018 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_TIME +#define INCL_DOSDATETIME +#define INCL_DOSERRORS +#include <os2.h> + +#include <iprt/time.h> +#include "internal/iprt.h" + +#include <iprt/assert.h> +#include <iprt/errcore.h> + + +RTDECL(int) RTTimeSet(PCRTTIMESPEC pTime) +{ + /* + * Convert to local time and explode it, keeping the distance + * between UTC and local. + */ + int64_t cNsLocalDelta = RTTimeLocalDeltaNanoFor(pTime); + RTTIMESPEC TimeLocal = *pTime; + RTTIME Exploded; + if (RTTimeExplode(&Exploded, RTTimeSpecAddNano(&TimeLocal, cNsLocalDelta))) + { + /* + * Fill in the OS/2 structure and make the call. + */ + DATETIME DateTime; + DateTime.hours = Exploded.u8Hour; + DateTime.minutes = Exploded.u8Minute; + DateTime.seconds = Exploded.u8Second; + DateTime.hundredths = (uint8_t)(Exploded.u32Nanosecond / (RT_NS_1SEC_64 / 100)); + DateTime.day = Exploded.u8MonthDay; + DateTime.month = Exploded.u8Month; + DateTime.year = (uint16_t)Exploded.i32Year; + DateTime.weekday = Exploded.u8WeekDay; + + /* Minutes from UTC. http://www.edm2.com/os2api/Dos/DosSetDateTime.html says + that timezones west of UTC should have a positive value. The kernel fails + the call if we're more than +/-780 min (13h) distant, so clamp it in + case of bogus TZ values. */ + DateTime.timezone = (int16_t)(-cNsLocalDelta / (int64_t)RT_NS_1MIN); + if (DateTime.timezone > 780) + DateTime.timezone = 780; + else if (DateTime.timezone < -780) + DateTime.timezone = -780; + + APIRET rc = DosSetDateTime(&DateTime); + if (rc == NO_ERROR) + return VINF_SUCCESS; + AssertMsgFailed(("rc=%u\n", rc)); + return RTErrConvertFromOS2(rc); + } + return VERR_INVALID_PARAMETER; +} + diff --git a/src/VBox/Runtime/r3/os2/filelock-os2.cpp b/src/VBox/Runtime/r3/os2/filelock-os2.cpp new file mode 100644 index 00000000..9fb93078 --- /dev/null +++ b/src/VBox/Runtime/r3/os2/filelock-os2.cpp @@ -0,0 +1,186 @@ +/* $Id: filelock-os2.cpp $ */ +/** @file + * IPRT - File Locking, OS/2. + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_FILE + +#include <errno.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/fcntl.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/time.h> + +#include <iprt/file.h> +#include <iprt/assert.h> +#include <iprt/string.h> +#include <iprt/err.h> +#include <iprt/log.h> +#include "internal/file.h" +#include "internal/fs.h" + + + + +RTR3DECL(int) RTFileLock(RTFILE File, unsigned fLock, int64_t offLock, uint64_t cbLock) +{ + Assert(offLock >= 0); + + /* Check arguments. */ + if (fLock & ~RTFILE_LOCK_MASK) + { + AssertMsgFailed(("Invalid fLock=%08X\n", fLock)); + return VERR_INVALID_PARAMETER; + } + + /* + * Validate offset. + */ + if ( sizeof(off_t) < sizeof(cbLock) + && ( (offLock >> 32) != 0 + || (cbLock >> 32) != 0 + || ((offLock + cbLock) >> 32) != 0)) + { + AssertMsgFailed(("64-bit file i/o not supported! offLock=%lld cbLock=%lld\n", offLock, cbLock)); + return VERR_NOT_SUPPORTED; + } + + /* Prepare flock structure. */ + struct flock fl; + Assert(RTFILE_LOCK_WRITE); + fl.l_type = (fLock & RTFILE_LOCK_WRITE) ? F_WRLCK : F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = (off_t)offLock; + fl.l_len = (off_t)cbLock; + fl.l_pid = 0; + + Assert(RTFILE_LOCK_WAIT); + if (fcntl(RTFileToNative(File), (fLock & RTFILE_LOCK_WAIT) ? F_SETLKW : F_SETLK, &fl) >= 0) + return VINF_SUCCESS; + + int iErr = errno; + if ( iErr == EAGAIN + || iErr == EACCES) + return VERR_FILE_LOCK_VIOLATION; + + return RTErrConvertFromErrno(iErr); +} + + +RTR3DECL(int) RTFileChangeLock(RTFILE File, unsigned fLock, int64_t offLock, uint64_t cbLock) +{ + /** @todo copied from ../win/fileio-win.cpp for now but a proper solution + * would probably be to modify kLIBC so that __fcntl_locking() first + * assumes a change lock request is made (e.g. the same region was + * previously F_RDLCK'ed and now needs to be F_WRLCK'ed or vice versa) and + * tries to use atomic locking, and only if it fails, it does the regular + * lock procedure. The alternative is to use DosSetFileLocks directly here + * which basically means copy-pasting the __fcntl_locking() source + * code :) Note that the first attempt to call RTFileLock() below assumes + * that kLIBC is patched as described above one day and gives it a chance; + * on failure, we fall back to the Win-like unlock-then-lock approach. */ + + int rc = RTFileLock(File, fLock, offLock, cbLock); + if (RT_FAILURE(rc) && rc != VERR_FILE_LOCK_VIOLATION) + return rc; + + /* Check arguments. */ + if (fLock & ~RTFILE_LOCK_MASK) + { + AssertMsgFailed(("Invalid fLock=%08X\n", fLock)); + return VERR_INVALID_PARAMETER; + } + + /* Remove old lock. */ + rc = RTFileUnlock(File, offLock, cbLock); + if (RT_FAILURE(rc)) + return rc; + + /* Set new lock. */ + rc = RTFileLock(File, fLock, offLock, cbLock); + if (RT_SUCCESS(rc)) + return rc; + + /* Try to restore old lock. */ + unsigned fLockOld = (fLock & RTFILE_LOCK_WRITE) ? fLock & ~RTFILE_LOCK_WRITE : fLock | RTFILE_LOCK_WRITE; + rc = RTFileLock(File, fLockOld, offLock, cbLock); + if (RT_SUCCESS(rc)) + return VERR_FILE_LOCK_VIOLATION; + else + return VERR_FILE_LOCK_LOST; +} + + +RTR3DECL(int) RTFileUnlock(RTFILE File, int64_t offLock, uint64_t cbLock) +{ + Assert(offLock >= 0); + + /* + * Validate offset. + */ + if ( sizeof(off_t) < sizeof(cbLock) + && ( (offLock >> 32) != 0 + || (cbLock >> 32) != 0 + || ((offLock + cbLock) >> 32) != 0)) + { + AssertMsgFailed(("64-bit file i/o not supported! offLock=%lld cbLock=%lld\n", offLock, cbLock)); + return VERR_NOT_SUPPORTED; + } + + /* Prepare flock structure. */ + struct flock fl; + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + fl.l_start = (off_t)offLock; + fl.l_len = (off_t)cbLock; + fl.l_pid = 0; + + if (fcntl(RTFileToNative(File), F_SETLK, &fl) >= 0) + return VINF_SUCCESS; + + /** @todo check error codes for non existing lock. */ + int iErr = errno; + if ( iErr == EAGAIN + || iErr == EACCES) + return VERR_FILE_LOCK_VIOLATION; + + return RTErrConvertFromErrno(iErr); +} + diff --git a/src/VBox/Runtime/r3/os2/mp-os2.cpp b/src/VBox/Runtime/r3/os2/mp-os2.cpp new file mode 100644 index 00000000..64ae06a8 --- /dev/null +++ b/src/VBox/Runtime/r3/os2/mp-os2.cpp @@ -0,0 +1,125 @@ +/* $Id: mp-os2.cpp $ */ +/** @file + * IPRT - Multiprocessor, OS/2. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define INCL_BASE +#define INCL_ERRORS +#include <os2.h> +#undef RT_MAX + +#include <iprt/mp.h> +#include <iprt/cpuset.h> +#include <iprt/assert.h> + +/** @todo RTMpCpuId() */ + +RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu) +{ + return idCpu < RTCPUSET_MAX_CPUS ? (int) idCpu : -1; +} + + +RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu) +{ + return (unsigned)iCpu < RTCPUSET_MAX_CPUS ? iCpu : NIL_RTCPUID; +} + + +RTDECL(RTCPUID) RTMpGetMaxCpuId(void) +{ + return RTCPUSET_MAX_CPUS; +} + + +RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu) +{ + RTCPUSET Set; + return RTCpuSetIsMember(RTMpGetSet(&Set), idCpu); +} + + +RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet) +{ + RTCPUID idCpu = RTMpGetCount(); + RTCpuSetEmpty(pSet); + while (idCpu-- > 0) + RTCpuSetAdd(pSet, idCpu); + return pSet; +} + + +RTDECL(RTCPUID) RTMpGetCount(void) +{ + ULONG cCpus = 1; + int rc = DosQuerySysInfo(QSV_NUMPROCESSORS, QSV_NUMPROCESSORS, &cCpus, sizeof(cCpus)); + if (rc || !cCpus) + cCpus = 1; + return cCpus; +} + + +RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu) +{ + RTCPUSET Set; + return RTCpuSetIsMember(RTMpGetOnlineSet(&Set), idCpu); +} + + +RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet) +{ + union + { + uint64_t u64; + MPAFFINITY mpaff; + } u; + + int rc = DosQueryThreadAffinity(AFNTY_SYSTEM, &u.mpaff); + if (rc) + u.u64 = 1; + return RTCpuSetFromU64(pSet, u.u64); +} + + +RTDECL(RTCPUID) RTMpGetOnlineCount(void) +{ + RTCPUSET Set; + RTMpGetOnlineSet(&Set); + return RTCpuSetCount(&Set); +} + diff --git a/src/VBox/Runtime/r3/os2/pipe-os2.cpp b/src/VBox/Runtime/r3/os2/pipe-os2.cpp new file mode 100644 index 00000000..6a4792c5 --- /dev/null +++ b/src/VBox/Runtime/r3/os2/pipe-os2.cpp @@ -0,0 +1,1083 @@ +/* $Id: pipe-os2.cpp $ */ +/** @file + * IPRT - Anonymous Pipes, OS/2 Implementation. + */ + +/* + * Copyright (C) 2010-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define INCL_ERRORS +#define INCL_DOSSEMAPHORES +#include <os2.h> + +#include <iprt/pipe.h> +#include "internal/iprt.h" + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <iprt/err.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/poll.h> +#include <iprt/process.h> +#include <iprt/thread.h> +#include <iprt/time.h> +#include "internal/pipe.h" +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** The pipe buffer size we prefer. */ +#define RTPIPE_OS2_SIZE _32K + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct RTPIPEINTERNAL +{ + /** Magic value (RTPIPE_MAGIC). */ + uint32_t u32Magic; + /** The pipe handle. */ + HPIPE hPipe; + /** Set if this is the read end, clear if it's the write end. */ + bool fRead; + /** RTPipeFromNative: Leave open. */ + bool fLeaveOpen; + /** Whether the pipe is in blocking or non-blocking mode. */ + bool fBlocking; + /** Set if the pipe is broken. */ + bool fBrokenPipe; + /** Usage counter. */ + uint32_t cUsers; + + /** The event semaphore associated with the pipe. */ + HEV hev; + /** The handle of the poll set currently polling on this pipe. + * We can only have one poller at the time (lazy bird). */ + RTPOLLSET hPollSet; + /** Critical section protecting the above members. + * (Taking the lazy/simple approach.) */ + RTCRITSECT CritSect; + +} RTPIPEINTERNAL; + + +/** + * Ensures that the pipe has a semaphore associated with it. + * + * @returns VBox status code. + * @param pThis The pipe. + */ +static int rtPipeOs2EnsureSem(RTPIPEINTERNAL *pThis) +{ + if (pThis->hev != NULLHANDLE) + return VINF_SUCCESS; + + HEV hev; + APIRET orc = DosCreateEventSem(NULL, &hev, DC_SEM_SHARED, FALSE); + if (orc == NO_ERROR) + { + orc = DosSetNPipeSem(pThis->hPipe, (HSEM)hev, 1); + if (orc == NO_ERROR) + { + pThis->hev = hev; + return VINF_SUCCESS; + } + + DosCloseEventSem(hev); + } + return RTErrConvertFromOS2(orc); +} + + +RTDECL(int) RTPipeCreate(PRTPIPE phPipeRead, PRTPIPE phPipeWrite, uint32_t fFlags) +{ + AssertPtrReturn(phPipeRead, VERR_INVALID_POINTER); + AssertPtrReturn(phPipeWrite, VERR_INVALID_POINTER); + AssertReturn(!(fFlags & ~RTPIPE_C_VALID_MASK), VERR_INVALID_PARAMETER); + + /* + * Try create and connect a pipe pair. + */ + APIRET orc; + HPIPE hPipeR; + HFILE hPipeW; + int rc; + for (;;) + { + static volatile uint32_t g_iNextPipe = 0; + char szName[128]; + RTStrPrintf(szName, sizeof(szName), "\\pipe\\iprt-pipe-%u-%u", RTProcSelf(), ASMAtomicIncU32(&g_iNextPipe)); + + /* + * Create the read end of the pipe. + */ + ULONG fPipeMode = 1 /*instance*/ | NP_TYPE_BYTE | NP_READMODE_BYTE | NP_NOWAIT; + ULONG fOpenMode = NP_ACCESS_DUPLEX | NP_WRITEBEHIND; + if (fFlags & RTPIPE_C_INHERIT_READ) + fOpenMode |= NP_INHERIT; + else + fOpenMode |= NP_NOINHERIT; + orc = DosCreateNPipe((PSZ)szName, &hPipeR, fOpenMode, fPipeMode, RTPIPE_OS2_SIZE, RTPIPE_OS2_SIZE, NP_DEFAULT_WAIT); + if (orc == NO_ERROR) + { + orc = DosConnectNPipe(hPipeR); + if (orc == ERROR_PIPE_NOT_CONNECTED || orc == NO_ERROR) + { + /* + * Connect to the pipe (the write end), attach sem below. + */ + ULONG ulAction = 0; + ULONG fOpenW = OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS; + ULONG fModeW = OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYNONE | OPEN_FLAGS_FAIL_ON_ERROR; + if (!(fFlags & RTPIPE_C_INHERIT_WRITE)) + fModeW |= OPEN_FLAGS_NOINHERIT; + orc = DosOpen((PSZ)szName, &hPipeW, &ulAction, 0 /*cbFile*/, FILE_NORMAL, + fOpenW, fModeW, NULL /*peaop2*/); + if (orc == NO_ERROR) + break; + } + DosClose(hPipeR); + } + if ( orc != ERROR_PIPE_BUSY /* already exist - compatible */ + && orc != ERROR_ACCESS_DENIED /* already exist - incompatible (?) */) + return RTErrConvertFromOS2(orc); + /* else: try again with a new name */ + } + + /* + * Create the two handles. + */ + RTPIPEINTERNAL *pThisR = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL)); + if (pThisR) + { + RTPIPEINTERNAL *pThisW = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL)); + if (pThisW) + { + /* Crit sects. */ + rc = RTCritSectInit(&pThisR->CritSect); + if (RT_SUCCESS(rc)) + { + rc = RTCritSectInit(&pThisW->CritSect); + if (RT_SUCCESS(rc)) + { + /* Initialize the structures. */ + pThisR->u32Magic = RTPIPE_MAGIC; + pThisW->u32Magic = RTPIPE_MAGIC; + pThisR->hPipe = hPipeR; + pThisW->hPipe = hPipeW; + pThisR->hev = NULLHANDLE; + pThisW->hev = NULLHANDLE; + pThisR->fRead = true; + pThisW->fRead = false; + pThisR->fLeaveOpen = false; + pThisW->fLeaveOpen = false; + pThisR->fBlocking = false; + pThisW->fBlocking = true; + //pThisR->fBrokenPipe = false; + //pThisW->fBrokenPipe = false; + //pThisR->cUsers = 0; + //pThisW->cUsers = 0; + pThisR->hPollSet = NIL_RTPOLLSET; + pThisW->hPollSet = NIL_RTPOLLSET; + + *phPipeRead = pThisR; + *phPipeWrite = pThisW; + return VINF_SUCCESS; + } + + RTCritSectDelete(&pThisR->CritSect); + } + RTMemFree(pThisW); + } + else + rc = VERR_NO_MEMORY; + RTMemFree(pThisR); + } + else + rc = VERR_NO_MEMORY; + + /* Don't call DosDisConnectNPipe! */ + DosClose(hPipeW); + DosClose(hPipeR); + return rc; +} + + +RTDECL(int) RTPipeCloseEx(RTPIPE hPipe, bool fLeaveOpen) +{ + RTPIPEINTERNAL *pThis = hPipe; + if (pThis == NIL_RTPIPE) + return VINF_SUCCESS; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + + /* + * Do the cleanup. + */ + AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTPIPE_MAGIC, RTPIPE_MAGIC), VERR_INVALID_HANDLE); + RTCritSectEnter(&pThis->CritSect); + Assert(pThis->cUsers == 0); + + /* Don't call DosDisConnectNPipe! */ + if (!fLeaveOpen && !pThis->fLeaveOpen) + DosClose(pThis->hPipe); + pThis->hPipe = (HPIPE)-1; + + if (pThis->hev != NULLHANDLE) + { + DosCloseEventSem(pThis->hev); + pThis->hev = NULLHANDLE; + } + + RTCritSectLeave(&pThis->CritSect); + RTCritSectDelete(&pThis->CritSect); + + RTMemFree(pThis); + + return VINF_SUCCESS; +} + + +RTDECL(int) RTPipeClose(RTPIPE hPipe) +{ + return RTPipeCloseEx(hPipe, false /*fLeaveOpen*/); +} + + +RTDECL(int) RTPipeFromNative(PRTPIPE phPipe, RTHCINTPTR hNativePipe, uint32_t fFlags) +{ + AssertPtrReturn(phPipe, VERR_INVALID_POINTER); + AssertReturn(!(fFlags & ~RTPIPE_N_VALID_MASK_FN), VERR_INVALID_PARAMETER); + AssertReturn(!!(fFlags & RTPIPE_N_READ) != !!(fFlags & RTPIPE_N_WRITE), VERR_INVALID_PARAMETER); + + /* + * Get and validate the pipe handle info. + */ + HPIPE hNative = (HPIPE)hNativePipe; + ULONG ulType = 0; + ULONG ulAttr = 0; + APIRET orc = DosQueryHType(hNative, &ulType, &ulAttr); + AssertMsgReturn(orc == NO_ERROR, ("%d\n", orc), RTErrConvertFromOS2(orc)); + AssertReturn((ulType & 0x7) == HANDTYPE_PIPE, VERR_INVALID_HANDLE); + +#if 0 + union + { + PIPEINFO PipeInfo; + uint8_t abPadding[sizeof(PIPEINFO) + 127]; + } Buf; + orc = DosQueryNPipeInfo(hNative, 1, &Buf, sizeof(Buf)); + if (orc != NO_ERROR) + { + /* Sorry, anonymous pips are not supported. */ + AssertMsgFailed(("%d\n", orc)); + return VERR_INVALID_HANDLE; + } + AssertReturn(Buf.PipeInfo.cbMaxInst == 1, VERR_INVALID_HANDLE); +#endif + + ULONG fPipeState = 0; + orc = DosQueryNPHState(hNative, &fPipeState); + if (orc != NO_ERROR) + { + /* Sorry, anonymous pips are not supported. */ + AssertMsgFailed(("%d\n", orc)); + return VERR_INVALID_HANDLE; + } + AssertReturn(!(fPipeState & NP_TYPE_MESSAGE), VERR_INVALID_HANDLE); + AssertReturn(!(fPipeState & NP_READMODE_MESSAGE), VERR_INVALID_HANDLE); + AssertReturn((fPipeState & 0xff) == 1, VERR_INVALID_HANDLE); + + ULONG fFileState = 0; + orc = DosQueryFHState(hNative, &fFileState); + AssertMsgReturn(orc == NO_ERROR, ("%d\n", orc), VERR_INVALID_HANDLE); + AssertMsgReturn( (fFileState & 0x3) == (fFlags & RTPIPE_N_READ ? OPEN_ACCESS_READONLY : OPEN_ACCESS_WRITEONLY) + || (fFileState & 0x3) == OPEN_ACCESS_READWRITE + , ("%#x\n", fFileState), VERR_INVALID_HANDLE); + + /* + * Looks kind of OK. Fix the inherit flag. + */ + orc = DosSetFHState(hNative, (fFileState & (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_CACHE)) + | (fFlags & RTPIPE_N_INHERIT ? 0 : OPEN_FLAGS_NOINHERIT)); + AssertMsgReturn(orc == NO_ERROR, ("%d\n", orc), RTErrConvertFromOS2(orc)); + + + /* + * Create a handle so we can try rtPipeQueryInfo on it + * and see if we need to duplicate it to make that call work. + */ + RTPIPEINTERNAL *pThis = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL)); + if (!pThis) + return VERR_NO_MEMORY; + int rc = RTCritSectInit(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + pThis->u32Magic = RTPIPE_MAGIC; + pThis->hPipe = hNative; + pThis->hev = NULLHANDLE; + pThis->fRead = RT_BOOL(fFlags & RTPIPE_N_READ); + pThis->fLeaveOpen = RT_BOOL(fFlags & RTPIPE_N_LEAVE_OPEN); + pThis->fBlocking = !(fPipeState & NP_NOWAIT); + //pThis->fBrokenPipe = false; + //pThis->cUsers = 0; + pThis->hPollSet = NIL_RTPOLLSET; + + *phPipe = pThis; + return VINF_SUCCESS; + + //RTCritSectDelete(&pThis->CritSect); + } + RTMemFree(pThis); + return rc; +} + +RTDECL(RTHCINTPTR) RTPipeToNative(RTPIPE hPipe) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, -1); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, -1); + + return (RTHCINTPTR)pThis->hPipe; +} + +/** + * Prepare blocking mode. + * + * @returns IPRT status code. + * @retval VERR_WRONG_ORDER if simultaneous non-blocking and blocking access is + * attempted. + * + * @param pThis The pipe handle. + * + * @remarks Caller owns the critical section. + */ +static int rtPipeTryBlocking(RTPIPEINTERNAL *pThis) +{ + if (!pThis->fBlocking) + { + if (pThis->cUsers != 0) + return VERR_WRONG_ORDER; + + APIRET orc = DosSetNPHState(pThis->hPipe, NP_WAIT | NP_READMODE_BYTE); + if (orc != NO_ERROR) + { + if (orc != ERROR_BROKEN_PIPE && orc != ERROR_PIPE_NOT_CONNECTED) + return RTErrConvertFromOS2(orc); + pThis->fBrokenPipe = true; + } + pThis->fBlocking = true; + } + + pThis->cUsers++; + return VINF_SUCCESS; +} + + +/** + * Prepare non-blocking mode. + * + * @returns IPRT status code. + * @retval VERR_WRONG_ORDER if simultaneous non-blocking and blocking access is + * attempted. + * + * @param pThis The pipe handle. + */ +static int rtPipeTryNonBlocking(RTPIPEINTERNAL *pThis) +{ + if (pThis->fBlocking) + { + if (pThis->cUsers != 0) + return VERR_WRONG_ORDER; + + APIRET orc = DosSetNPHState(pThis->hPipe, NP_NOWAIT | NP_READMODE_BYTE); + if (orc != NO_ERROR) + { + if (orc != ERROR_BROKEN_PIPE && orc != ERROR_PIPE_NOT_CONNECTED) + return RTErrConvertFromOS2(orc); + pThis->fBrokenPipe = true; + } + pThis->fBlocking = false; + } + + pThis->cUsers++; + return VINF_SUCCESS; +} + + +/** + * Checks if the read pipe has been broken. + * + * @returns true if broken, false if no. + * @param pThis The pipe handle (read). + */ +static bool rtPipeOs2IsBroken(RTPIPEINTERNAL *pThis) +{ + Assert(pThis->fRead); + +#if 0 + /* + * Query it via the semaphore. Not sure how fast this is... + */ + PIPESEMSTATE aStates[3]; RT_ZERO(aStates); + APIRET orc = DosQueryNPipeSemState(pThis->hev, &aStates[0], sizeof(aStates)); + if (orc == NO_ERROR) + { + if (aStates[0].fStatus == NPSS_CLOSE) + return true; + if (aStates[0].fStatus == NPSS_RDATA) + return false; + } + AssertMsgFailed(("%d / %d\n", orc, aStates[0].fStatus)); + + /* + * Fall back / alternative method. + */ +#endif + ULONG cbActual = 0; + ULONG ulState = 0; + AVAILDATA Avail = { 0, 0 }; + APIRET orc = DosPeekNPipe(pThis->hPipe, NULL, 0, &cbActual, &Avail, &ulState); + if (orc != NO_ERROR) + { + if (orc != ERROR_PIPE_BUSY) + AssertMsgFailed(("%d\n", orc)); + return false; + } + + return ulState != NP_STATE_CONNECTED; +} + + +RTDECL(int) RTPipeRead(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(pThis->fRead, VERR_ACCESS_DENIED); + AssertPtr(pcbRead); + AssertPtr(pvBuf); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + rc = rtPipeTryNonBlocking(pThis); + if (RT_SUCCESS(rc)) + { + RTCritSectLeave(&pThis->CritSect); + + ULONG cbActual = 0; + APIRET orc = DosRead(pThis->hPipe, pvBuf, cbToRead, &cbActual); + if (orc == NO_ERROR) + { + if (cbActual || !cbToRead || !rtPipeOs2IsBroken(pThis)) + *pcbRead = cbActual; + else + rc = VERR_BROKEN_PIPE; + } + else if (orc == ERROR_NO_DATA) + { + *pcbRead = 0; + rc = VINF_TRY_AGAIN; + } + else + rc = RTErrConvertFromOS2(orc); + + RTCritSectEnter(&pThis->CritSect); + if (rc == VERR_BROKEN_PIPE) + pThis->fBrokenPipe = true; + pThis->cUsers--; + } + else + rc = VERR_WRONG_ORDER; + RTCritSectLeave(&pThis->CritSect); + } + return rc; +} + + +RTDECL(int) RTPipeReadBlocking(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(pThis->fRead, VERR_ACCESS_DENIED); + AssertPtr(pvBuf); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + rc = rtPipeTryBlocking(pThis); + if (RT_SUCCESS(rc)) + { + RTCritSectLeave(&pThis->CritSect); + + size_t cbTotalRead = 0; + while (cbToRead > 0) + { + ULONG cbActual = 0; + APIRET orc = DosRead(pThis->hPipe, pvBuf, cbToRead, &cbActual); + if (orc != NO_ERROR) + { + rc = RTErrConvertFromOS2(orc); + break; + } + if (!cbActual && rtPipeOs2IsBroken(pThis)) + { + rc = VERR_BROKEN_PIPE; + break; + } + + /* advance */ + pvBuf = (char *)pvBuf + cbActual; + cbTotalRead += cbActual; + cbToRead -= cbActual; + } + + if (pcbRead) + { + *pcbRead = cbTotalRead; + if ( RT_FAILURE(rc) + && cbTotalRead) + rc = VINF_SUCCESS; + } + + RTCritSectEnter(&pThis->CritSect); + if (rc == VERR_BROKEN_PIPE) + pThis->fBrokenPipe = true; + pThis->cUsers--; + } + else + rc = VERR_WRONG_ORDER; + RTCritSectLeave(&pThis->CritSect); + } + return rc; +} + + +/** + * Gets the available write buffer size of the pipe. + * + * @returns Number of bytes, 1 on failure. + * @param pThis The pipe handle. + */ +static ULONG rtPipeOs2GetSpace(RTPIPEINTERNAL *pThis) +{ + Assert(!pThis->fRead); + +#if 0 /* Not sure which is more efficient, neither are really optimal, I fear. */ + /* + * Query via semaphore state. + * This will walk the list of active named pipes... + */ + /** @todo Check how hev and hpipe are associated, if complicated, use the + * alternative method below. */ + PIPESEMSTATE aStates[3]; RT_ZERO(aStates); + APIRET orc = DosQueryNPipeSemState((HSEM)pThis->hev, &aStates[0], sizeof(aStates)); + if (orc == NO_ERROR) + { + if (aStates[0].fStatus == NPSS_WSPACE) + return aStates[0].usAvail; + if (aStates[1].fStatus == NPSS_WSPACE) + return aStates[1].usAvail; + return 0; + } + AssertMsgFailed(("%d / %d\n", orc, aStates[0].fStatus)); + +#else + /* + * Query via the pipe info. + * This will have to lookup and store the pipe name. + */ + union + { + PIPEINFO PipeInfo; + uint8_t abPadding[sizeof(PIPEINFO) + 127]; + } Buf; + APIRET orc = DosQueryNPipeInfo(pThis->hPipe, 1, &Buf, sizeof(Buf)); + if (orc == NO_ERROR) + return Buf.PipeInfo.cbOut; + AssertMsgFailed(("%d\n", orc)); +#endif + + return 1; +} + + +RTDECL(int) RTPipeWrite(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED); + AssertPtr(pcbWritten); + AssertPtr(pvBuf); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + rc = rtPipeTryNonBlocking(pThis); + if (RT_SUCCESS(rc)) + { + if (cbToWrite > 0) + { + ULONG cbActual = 0; + APIRET orc = DosWrite(pThis->hPipe, pvBuf, cbToWrite, &cbActual); + if (orc == NO_ERROR && cbActual == 0) + { + /* Retry with the request adjusted to the available buffer space. */ + ULONG cbAvail = rtPipeOs2GetSpace(pThis); + orc = DosWrite(pThis->hPipe, pvBuf, RT_MIN(cbAvail, cbToWrite), &cbActual); + } + + if (orc == NO_ERROR) + { + *pcbWritten = cbActual; + if (cbActual == 0) + rc = VINF_TRY_AGAIN; + } + else + { + rc = RTErrConvertFromOS2(orc); + if (rc == VERR_PIPE_NOT_CONNECTED) + rc = VERR_BROKEN_PIPE; + } + } + else + *pcbWritten = 0; + + if (rc == VERR_BROKEN_PIPE) + pThis->fBrokenPipe = true; + pThis->cUsers--; + } + else + rc = VERR_WRONG_ORDER; + RTCritSectLeave(&pThis->CritSect); + } + return rc; +} + + +RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED); + AssertPtr(pvBuf); + AssertPtrNull(pcbWritten); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + rc = rtPipeTryBlocking(pThis); + if (RT_SUCCESS(rc)) + { + RTCritSectLeave(&pThis->CritSect); + + size_t cbTotalWritten = 0; + while (cbToWrite > 0) + { + ULONG cbActual = 0; + APIRET orc = DosWrite(pThis->hPipe, pvBuf, cbToWrite, &cbActual); + if (orc != NO_ERROR) + { + rc = RTErrConvertFromOS2(orc); + if (rc == VERR_PIPE_NOT_CONNECTED) + rc = VERR_BROKEN_PIPE; + break; + } + pvBuf = (char const *)pvBuf + cbActual; + cbToWrite -= cbActual; + cbTotalWritten += cbActual; + } + + if (pcbWritten) + { + *pcbWritten = cbTotalWritten; + if ( RT_FAILURE(rc) + && cbTotalWritten) + rc = VINF_SUCCESS; + } + + RTCritSectEnter(&pThis->CritSect); + if (rc == VERR_BROKEN_PIPE) + pThis->fBrokenPipe = true; + pThis->cUsers--; + } + else + rc = VERR_WRONG_ORDER; + RTCritSectLeave(&pThis->CritSect); + } + return rc; +} + + +RTDECL(int) RTPipeFlush(RTPIPE hPipe) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED); + + APIRET orc = DosResetBuffer(pThis->hPipe); + if (orc != NO_ERROR) + { + int rc = RTErrConvertFromOS2(orc); + if (rc == VERR_BROKEN_PIPE) + { + RTCritSectEnter(&pThis->CritSect); + pThis->fBrokenPipe = true; + RTCritSectLeave(&pThis->CritSect); + } + return rc; + } + return VINF_SUCCESS; +} + + +RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + + uint64_t const StartMsTS = RTTimeMilliTS(); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_FAILURE(rc)) + return rc; + + rc = rtPipeOs2EnsureSem(pThis); + if (RT_SUCCESS(rc) && cMillies > 0) + { + /* Stop polling attempts if we might block. */ + if (pThis->hPollSet == NIL_RTPOLLSET) + pThis->hPollSet = (RTPOLLSET)(uintptr_t)0xbeef0042; + else + rc = VERR_WRONG_ORDER; + } + if (RT_SUCCESS(rc)) + { + for (unsigned iLoop = 0;; iLoop++) + { + /* + * Check the handle state. + */ + APIRET orc; + if (cMillies > 0) + { + ULONG ulIgnore; + orc = DosResetEventSem(pThis->hev, &ulIgnore); + AssertMsg(orc == NO_ERROR || orc == ERROR_ALREADY_RESET, ("%d\n", orc)); + } + + PIPESEMSTATE aStates[4]; RT_ZERO(aStates); + orc = DosQueryNPipeSemState((HSEM)pThis->hev, &aStates[0], sizeof(aStates)); + if (orc != NO_ERROR) + { + rc = RTErrConvertFromOS2(orc); + break; + } + int i = 0; + if (pThis->fRead) + while (aStates[i].fStatus == NPSS_WSPACE) + i++; + else + while (aStates[i].fStatus == NPSS_RDATA) + i++; + if (aStates[i].fStatus == NPSS_CLOSE) + break; + Assert(aStates[i].fStatus == NPSS_WSPACE || aStates[i].fStatus == NPSS_RDATA || aStates[i].fStatus == NPSS_EOI); + if ( aStates[i].fStatus != NPSS_EOI + && aStates[i].usAvail > 0) + break; + + /* + * Check for timeout. + */ + ULONG cMsMaxWait = SEM_INDEFINITE_WAIT; + if (cMillies != RT_INDEFINITE_WAIT) + { + uint64_t cElapsed = RTTimeMilliTS() - StartMsTS; + if (cElapsed >= cMillies) + { + rc = VERR_TIMEOUT; + break; + } + cMsMaxWait = cMillies - (uint32_t)cElapsed; + } + + /* + * Wait. + */ + RTCritSectLeave(&pThis->CritSect); + orc = DosWaitEventSem(pThis->hev, cMsMaxWait); + RTCritSectEnter(&pThis->CritSect); + if (orc != NO_ERROR && orc != ERROR_TIMEOUT && orc != ERROR_SEM_TIMEOUT ) + { + rc = RTErrConvertFromOS2(orc); + break; + } + } + + if (rc == VERR_BROKEN_PIPE) + pThis->fBrokenPipe = true; + if (cMillies > 0) + pThis->hPollSet = NIL_RTPOLLSET; + } + + RTCritSectLeave(&pThis->CritSect); + return rc; +} + + +RTDECL(int) RTPipeQueryReadable(RTPIPE hPipe, size_t *pcbReadable) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(pThis->fRead, VERR_PIPE_NOT_READ); + AssertPtrReturn(pcbReadable, VERR_INVALID_POINTER); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_FAILURE(rc)) + return rc; + + ULONG cbActual = 0; + ULONG ulState = 0; + AVAILDATA Avail = { 0, 0 }; + APIRET orc = DosPeekNPipe(pThis->hPipe, NULL, 0, &cbActual, &Avail, &ulState); + if (orc == NO_ERROR) + { + if (Avail.cbpipe > 0 || ulState == NP_STATE_CONNECTED) + *pcbReadable = Avail.cbpipe; + else + rc = VERR_PIPE_NOT_CONNECTED; /*??*/ + } + else + rc = RTErrConvertFromOS2(orc); + + RTCritSectLeave(&pThis->CritSect); + return rc; +} + + +RTDECL(int) RTPipeQueryInfo(RTPIPE hPipe, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, 0); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0); + + int rc = RTCritSectEnter(&pThis->CritSect); + AssertRCReturn(rc, 0); + + rtPipeFakeQueryInfo(pObjInfo, enmAddAttr, pThis->fRead); + + if (pThis->fRead) + { + ULONG cbActual = 0; + ULONG ulState = 0; + AVAILDATA Avail = { 0, 0 }; + APIRET orc = DosPeekNPipe(pThis->hPipe, NULL, 0, &cbActual, &Avail, &ulState); + if (orc == NO_ERROR && (Avail.cbpipe > 0 || ulState == NP_STATE_CONNECTED)) + pObjInfo->cbObject = Avail.cbpipe; + } + else + pObjInfo->cbObject = rtPipeOs2GetSpace(pThis); + pObjInfo->cbAllocated = RTPIPE_OS2_SIZE; /** @todo this isn't necessarily true if we didn't create it... but, whatever */ + + RTCritSectLeave(&pThis->CritSect); + return VINF_SUCCESS; +} + + +int rtPipePollGetHandle(RTPIPE hPipe, uint32_t fEvents, PRTHCINTPTR phNative) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + + AssertReturn(!(fEvents & RTPOLL_EVT_READ) || pThis->fRead, VERR_INVALID_PARAMETER); + AssertReturn(!(fEvents & RTPOLL_EVT_WRITE) || !pThis->fRead, VERR_INVALID_PARAMETER); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + rc = rtPipeOs2EnsureSem(pThis); + if (RT_SUCCESS(rc)) + *phNative = (RTHCINTPTR)pThis->hev; + RTCritSectLeave(&pThis->CritSect); + } + return rc; +} + + +/** + * Checks for pending events. + * + * @returns Event mask or 0. + * @param pThis The pipe handle. + * @param fEvents The desired events. + * @param fResetEvtSem Whether to reset the event semaphore. + */ +static uint32_t rtPipePollCheck(RTPIPEINTERNAL *pThis, uint32_t fEvents, bool fResetEvtSem) +{ + /* + * Reset the event semaphore if we're gonna wait. + */ + APIRET orc; + ULONG ulIgnore; + if (fResetEvtSem) + { + orc = DosResetEventSem(pThis->hev, &ulIgnore); + AssertMsg(orc == NO_ERROR || orc == ERROR_ALREADY_RESET, ("%d\n", orc)); + } + + /* + * Check for events. + */ + uint32_t fRetEvents = 0; + if (pThis->fBrokenPipe) + fRetEvents |= RTPOLL_EVT_ERROR; + else if (pThis->fRead) + { + ULONG cbActual = 0; + ULONG ulState = 0; + AVAILDATA Avail = { 0, 0 }; + orc = DosPeekNPipe(pThis->hPipe, NULL, 0, &cbActual, &Avail, &ulState); + if (orc != NO_ERROR) + { + fRetEvents |= RTPOLL_EVT_ERROR; + if (orc == ERROR_BROKEN_PIPE || orc == ERROR_PIPE_NOT_CONNECTED) + pThis->fBrokenPipe = true; + } + else if (Avail.cbpipe > 0) + fRetEvents |= RTPOLL_EVT_READ; + else if (ulState != NP_STATE_CONNECTED) + { + fRetEvents |= RTPOLL_EVT_ERROR; + pThis->fBrokenPipe = true; + } + } + else + { + PIPESEMSTATE aStates[4]; RT_ZERO(aStates); + orc = DosQueryNPipeSemState((HSEM)pThis->hev, &aStates[0], sizeof(aStates)); + if (orc == NO_ERROR) + { + int i = 0; + while (aStates[i].fStatus == NPSS_RDATA) + i++; + if (aStates[i].fStatus == NPSS_CLOSE) + { + fRetEvents |= RTPOLL_EVT_ERROR; + pThis->fBrokenPipe = true; + } + else if ( aStates[i].fStatus == NPSS_WSPACE + && aStates[i].usAvail > 0) + fRetEvents |= RTPOLL_EVT_WRITE; + } + else + { + fRetEvents |= RTPOLL_EVT_ERROR; + if (orc == ERROR_BROKEN_PIPE || orc == ERROR_PIPE_NOT_CONNECTED) + pThis->fBrokenPipe = true; + } + } + + return fRetEvents & (fEvents | RTPOLL_EVT_ERROR); +} + + +uint32_t rtPipePollStart(RTPIPE hPipe, RTPOLLSET hPollSet, uint32_t fEvents, bool fFinalEntry, bool fNoWait) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, UINT32_MAX); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, UINT32_MAX); + + int rc = RTCritSectEnter(&pThis->CritSect); + AssertRCReturn(rc, UINT32_MAX); + + /* Check that this is the only current use of this pipe. */ + uint32_t fRetEvents; + if ( pThis->cUsers == 0 + || pThis->hPollSet == NIL_RTPOLLSET) + { + fRetEvents = rtPipePollCheck(pThis, fEvents, fNoWait); + if (!fRetEvents && !fNoWait) + { + /* Mark the set busy while waiting. */ + pThis->cUsers++; + pThis->hPollSet = hPollSet; + } + } + else + { + AssertFailed(); + fRetEvents = UINT32_MAX; + } + + RTCritSectLeave(&pThis->CritSect); + return fRetEvents; +} + + +uint32_t rtPipePollDone(RTPIPE hPipe, uint32_t fEvents, bool fFinalEntry, bool fHarvestEvents) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, 0); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0); + + int rc = RTCritSectEnter(&pThis->CritSect); + AssertRCReturn(rc, 0); + + Assert(pThis->cUsers > 0); + + /* harvest events. */ + uint32_t fRetEvents = rtPipePollCheck(pThis, fEvents, false); + + /* update counters. */ + pThis->cUsers--; + pThis->hPollSet = NIL_RTPOLLSET; + + RTCritSectLeave(&pThis->CritSect); + return fRetEvents; +} diff --git a/src/VBox/Runtime/r3/os2/rtProcInitExePath-os2.cpp b/src/VBox/Runtime/r3/os2/rtProcInitExePath-os2.cpp new file mode 100644 index 00000000..bd5e249a --- /dev/null +++ b/src/VBox/Runtime/r3/os2/rtProcInitExePath-os2.cpp @@ -0,0 +1,71 @@ +/* $Id: rtProcInitExePath-os2.cpp $ */ +/** @file + * IPRT - rtProcInitName, OS/2. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_PROCESS +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#include <iprt/string.h> +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <iprt/path.h> +#include "internal/process.h" +#include "internal/path.h" + + +DECLHIDDEN(int) rtProcInitExePath(char *pszPath, size_t cchPath) +{ + /* + * Query the image name from the dynamic linker, convert and return it. + */ + _execname(pszPath, cchPath); + + char const *pszTmp; + int rc = rtPathFromNative(&pszTmp, pszPath, NULL); + AssertMsgRCReturn(rc, ("rc=%Rrc pszLink=\"%s\"\nhex: %.*Rhxs\n", rc, pszPath, cchPath, pszPath), rc); + if (pszTmp != pszPath) + { + rc = RTStrCopy(pszPath, cchPath, pszTmp); + rtPathFreeIprt(pszTmp, pszPath); + } + return rc; +} + diff --git a/src/VBox/Runtime/r3/os2/sched-os2.cpp b/src/VBox/Runtime/r3/os2/sched-os2.cpp new file mode 100644 index 00000000..25eb75c3 --- /dev/null +++ b/src/VBox/Runtime/r3/os2/sched-os2.cpp @@ -0,0 +1,244 @@ +/* $Id: sched-os2.cpp $ */ +/** @file + * IPRT - Scheduling, OS/2 + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/** @def OS2_SCHED_ENABLED + * Enables the priority scheme. */ +#define OS2_SCHED_ENABLED + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_THREAD +#define INCL_BASE +#define INCL_ERRORS +#include <os2.h> + +#include <iprt/thread.h> +#include <iprt/log.h> +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include "internal/sched.h" +#include "internal/thread.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Configuration of one priority. + */ +typedef struct +{ + /** The priority. */ + RTPROCPRIORITY enmPriority; + /** The name of this priority. */ + const char *pszName; + /** Array scheduler attributes corresponding to each of the thread types. */ + struct + { + /** For sanity include the array index. */ + RTTHREADTYPE enmType; + /** The OS/2 priority class. */ + ULONG ulClass; + /** The OS/2 priority delta. */ + ULONG ulDelta; + } aTypes[RTTHREADTYPE_END]; +} PROCPRIORITY; + +/** Matches any process priority class. */ +#define ANY_PROCESS_PRIORITY_CLASS (~0U) + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** + * Array of static priority configurations. + */ +static const PROCPRIORITY g_aPriorities[] = +{ + { + RTPROCPRIORITY_FLAT, "Flat", + { + { RTTHREADTYPE_INVALID, ~0, ~0 }, + { RTTHREADTYPE_INFREQUENT_POLLER, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_EMULATION, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_DEFAULT, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_GUI, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_MAIN_WORKER, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_VRDP_IO, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_DEBUGGER, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_MSG_PUMP, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_IO, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_TIMER, PRTYC_REGULAR, 0 } + } + }, + { + RTPROCPRIORITY_LOW, "Low", + { + { RTTHREADTYPE_INVALID, ~0 }, + { RTTHREADTYPE_INFREQUENT_POLLER, PRTYC_IDLETIME, 0 }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, PRTYC_IDLETIME, 0 }, + { RTTHREADTYPE_EMULATION, PRTYC_IDLETIME, 0 }, + { RTTHREADTYPE_DEFAULT, PRTYC_IDLETIME, 30 }, + { RTTHREADTYPE_GUI, PRTYC_IDLETIME, 30 }, + { RTTHREADTYPE_MAIN_WORKER, PRTYC_IDLETIME, 30 }, + { RTTHREADTYPE_VRDP_IO, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_DEBUGGER, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_MSG_PUMP, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_IO, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_TIMER, PRTYC_REGULAR, 0 } + } + }, + { + RTPROCPRIORITY_NORMAL, "Normal", + { + { RTTHREADTYPE_INVALID, ~0 }, + { RTTHREADTYPE_INFREQUENT_POLLER, PRTYC_IDLETIME, 30 }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, PRTYC_IDLETIME, 31 }, + { RTTHREADTYPE_EMULATION, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_DEFAULT, PRTYC_REGULAR, 5 }, + { RTTHREADTYPE_GUI, PRTYC_REGULAR, 10 }, + { RTTHREADTYPE_MAIN_WORKER, PRTYC_REGULAR, 12 }, + { RTTHREADTYPE_VRDP_IO, PRTYC_REGULAR, 15 }, + { RTTHREADTYPE_DEBUGGER, PRTYC_REGULAR, 20 }, + { RTTHREADTYPE_MSG_PUMP, PRTYC_REGULAR, 25 }, + { RTTHREADTYPE_IO, PRTYC_FOREGROUNDSERVER, 5 }, + { RTTHREADTYPE_TIMER, PRTYC_TIMECRITICAL, 0 } + } + }, + { + RTPROCPRIORITY_HIGH, "High", + { + { RTTHREADTYPE_INVALID, ~0 }, + { RTTHREADTYPE_INFREQUENT_POLLER, PRTYC_IDLETIME, 30 }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_EMULATION, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_DEFAULT, PRTYC_REGULAR, 15 }, + { RTTHREADTYPE_GUI, PRTYC_REGULAR, 20 }, + { RTTHREADTYPE_MAIN_WORKER, PRTYC_REGULAR, 25 }, + { RTTHREADTYPE_VRDP_IO, PRTYC_REGULAR, 30 }, + { RTTHREADTYPE_DEBUGGER, PRTYC_TIMECRITICAL, 2 }, + { RTTHREADTYPE_MSG_PUMP, PRTYC_TIMECRITICAL, 3 }, + { RTTHREADTYPE_IO, PRTYC_TIMECRITICAL, 4 }, + { RTTHREADTYPE_TIMER, PRTYC_TIMECRITICAL, 5 } + } + } +}; + +/** + * The dynamic default priority configuration. + * + * This can be recalulated at runtime depending on what the + * system allow us to do. Presently we don't do this as it's + * generally not a bit issue on OS/2 hosts. + */ +static PROCPRIORITY g_aDefaultPriority = +{ + RTPROCPRIORITY_LOW, "Default", + { + { RTTHREADTYPE_INVALID, ~0 }, + { RTTHREADTYPE_INFREQUENT_POLLER, PRTYC_IDLETIME, 30 }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, PRTYC_IDLETIME, 31 }, + { RTTHREADTYPE_EMULATION, PRTYC_REGULAR, 0 }, + { RTTHREADTYPE_DEFAULT, PRTYC_REGULAR, 5 }, + { RTTHREADTYPE_GUI, PRTYC_REGULAR, 10 }, + { RTTHREADTYPE_MAIN_WORKER, PRTYC_REGULAR, 12 }, + { RTTHREADTYPE_VRDP_IO, PRTYC_REGULAR, 15 }, + { RTTHREADTYPE_DEBUGGER, PRTYC_REGULAR, 20 }, + { RTTHREADTYPE_MSG_PUMP, PRTYC_REGULAR, 25 }, + { RTTHREADTYPE_IO, PRTYC_FOREGROUNDSERVER, 5 }, + { RTTHREADTYPE_TIMER, PRTYC_TIMECRITICAL, 0 } + } +}; + + +/** Pointer to the current priority configuration. */ +static const PROCPRIORITY *g_pProcessPriority = &g_aDefaultPriority; + + +/** + * Calculate the scheduling properties for all the threads in the default + * process priority, assuming the current thread have the type enmType. + * + * @returns iprt status code. + * @param enmType The thread type to be assumed for the current thread. + */ +DECLHIDDEN(int) rtSchedNativeCalcDefaultPriority(RTTHREADTYPE enmType) +{ + Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END); + return VINF_SUCCESS; +} + + +DECLHIDDEN(int) rtProcNativeSetPriority(RTPROCPRIORITY enmPriority) +{ + Assert(enmPriority > RTPROCPRIORITY_INVALID && enmPriority < RTPROCPRIORITY_LAST); + + if (enmPriority == RTPROCPRIORITY_DEFAULT) + { + g_pProcessPriority = &g_aDefaultPriority; + return VINF_SUCCESS; + } + + for (size_t i = 0; i < RT_ELEMENTS(g_aPriorities); i++) + if (g_aPriorities[i].enmPriority == enmPriority) + { + g_pProcessPriority = &g_aPriorities[i]; + return VINF_SUCCESS; + } + + AssertFailedReturn(VERR_INTERNAL_ERROR); +} + + +DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType) +{ + Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END); + AssertMsg(g_pProcessPriority && g_pProcessPriority->aTypes[enmType].enmType == enmType, + ("enmType=%d entry=%d\n", enmType, g_pProcessPriority->aTypes[enmType].enmType)); + +#ifdef OS2_SCHED_ENABLED + APIRET rc = DosSetPriority(PRTYS_THREAD, g_pProcessPriority->aTypes[enmType].ulClass, g_pProcessPriority->aTypes[enmType].ulDelta, (ULONG)pThread->Core.Key & 0xffff /*tid*/); + AssertMsg(rc == NO_ERROR, ("%d\n", rc)); + return RTErrConvertFromOS2(rc); +#else + return VINF_SUCCESS; +#endif +} + diff --git a/src/VBox/Runtime/r3/os2/sems-os2.cpp b/src/VBox/Runtime/r3/os2/sems-os2.cpp new file mode 100644 index 00000000..d4b8136e --- /dev/null +++ b/src/VBox/Runtime/r3/os2/sems-os2.cpp @@ -0,0 +1,392 @@ +/* $Id: sems-os2.cpp $ */ +/** @file + * IPRT - Semaphores, OS/2. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define INCL_DOSSEMAPHORES +#define INCL_ERRORS +#include <os2.h> +#undef RT_MAX + +#include <iprt/semaphore.h> +#include <iprt/assert.h> +#include <iprt/err.h> + + +/** Converts semaphore to OS/2 handle. */ +#define SEM2HND(Sem) ((LHANDLE)(uintptr_t)Sem) + + + +RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem) +{ + return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL); +} + + +RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...) +{ + AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER); + Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL)); + + /* + * Create the semaphore. + * (Auto reset, not signaled, private event object.) + */ + HEV hev; + int rc = DosCreateEventSem(NULL, &hev, DCE_AUTORESET | DCE_POSTONE, 0); + if (!rc) + { + *phEventSem = (RTSEMEVENT)(void *)hev; + return VINF_SUCCESS; + } + return RTErrConvertFromOS2(rc); +} + + +RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem) +{ + if (hEventSem == NIL_RTSEMEVENT) + return VINF_SUCCESS; + + /* + * Close semaphore handle. + */ + int rc = DosCloseEventSem(SEM2HND(hEventSem)); + if (!rc) + return VINF_SUCCESS; + AssertMsgFailed(("Destroy hEventSem %p failed, rc=%d\n", hEventSem, rc)); + return RTErrConvertFromOS2(rc); +} + + +RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies) +{ + /* + * Wait for condition. + */ + int rc = DosWaitEventSem(SEM2HND(hEventSem), cMillies == RT_INDEFINITE_WAIT ? SEM_INDEFINITE_WAIT : cMillies); + switch (rc) + { + case NO_ERROR: return VINF_SUCCESS; + case ERROR_SEM_TIMEOUT: + case ERROR_TIMEOUT: return VERR_TIMEOUT; + case ERROR_INTERRUPT: return VERR_INTERRUPTED; + default: + { + AssertMsgFailed(("Wait on hEventSem %p failed, rc=%d\n", hEventSem, rc)); + return RTErrConvertFromOS2(rc); + } + } +} + + +RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem) +{ + /* + * Signal the object. + */ + int rc = DosPostEventSem(SEM2HND(hEventSem)); + switch (rc) + { + case NO_ERROR: + case ERROR_ALREADY_POSTED: + case ERROR_TOO_MANY_POSTS: + return VINF_SUCCESS; + default: + return RTErrConvertFromOS2(rc); + } +} + + +RTDECL(void) RTSemEventSetSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread) +{ +/** @todo implement RTSemEventSetSignaller and friends for OS/2 */ +} + + +RTDECL(void) RTSemEventAddSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread) +{ + +} + + +RTDECL(void) RTSemEventRemoveSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread) +{ + +} + + + + +RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem) +{ + return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL); +} + + +RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass, + const char *pszNameFmt, ...) +{ + AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER); + + /* + * Create the semaphore. + * (Manual reset, not signaled, private event object.) + */ + HEV hev; + int rc = DosCreateEventSem(NULL, &hev, 0, FALSE); + if (!rc) + { + *phEventMultiSem = (RTSEMEVENTMULTI)(void *)hev; + return VINF_SUCCESS; + } + return RTErrConvertFromOS2(rc); +} + + +RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem) +{ + if (hEventMultiSem == NIL_RTSEMEVENTMULTI) + return VINF_SUCCESS; + + /* + * Close semaphore handle. + */ + int rc = DosCloseEventSem(SEM2HND(hEventMultiSem)); + if (!rc) + return VINF_SUCCESS; + AssertMsgFailed(("Destroy hEventMultiSem %p failed, rc=%d\n", hEventMultiSem, rc)); + return RTErrConvertFromOS2(rc); +} + + +RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem) +{ + /* + * Signal the object. + */ + int rc = DosPostEventSem(SEM2HND(hEventMultiSem)); + switch (rc) + { + case NO_ERROR: + case ERROR_ALREADY_POSTED: + case ERROR_TOO_MANY_POSTS: + return VINF_SUCCESS; + default: + return RTErrConvertFromOS2(rc); + } +} + + +RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem) +{ + /* + * Reset the object. + */ + ULONG ulIgnore; + int rc = DosResetEventSem(SEM2HND(hEventMultiSem), &ulIgnore); + switch (rc) + { + case NO_ERROR: + case ERROR_ALREADY_RESET: + return VINF_SUCCESS; + default: + return RTErrConvertFromOS2(rc); + } +} + + +RTDECL(int) RTSemEventMultiWaitNoResume(RTSEMEVENTMULTI hEventMultiSem, RTMSINTERVAL cMillies) +{ + /* + * Wait for condition. + */ + int rc = DosWaitEventSem(SEM2HND(hEventMultiSem), cMillies == RT_INDEFINITE_WAIT ? SEM_INDEFINITE_WAIT : cMillies); + switch (rc) + { + case NO_ERROR: return VINF_SUCCESS; + case ERROR_SEM_TIMEOUT: + case ERROR_TIMEOUT: return VERR_TIMEOUT; + case ERROR_INTERRUPT: return VERR_INTERRUPTED; + default: + { + AssertMsgFailed(("Wait on hEventMultiSem %p failed, rc=%d\n", hEventMultiSem, rc)); + return RTErrConvertFromOS2(rc); + } + } +} + + +RTDECL(void) RTSemEventMultiSetSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread) +{ + /** @todo implement RTSemEventMultiSetSignaller on OS/2 */ +} + + +RTDECL(void) RTSemEventMultiAddSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread) +{ +} + + +RTDECL(void) RTSemEventMultiRemoveSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread) +{ +} + + + +#undef RTSemMutexCreate +RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem) +{ + return RTSemMutexCreateEx(phMutexSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL); +} + + +RTDECL(int) RTSemMutexCreateEx(PRTSEMMUTEX phMutexSem, uint32_t fFlags, + RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...) +{ + AssertReturn(!(fFlags & ~RTSEMMUTEX_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER); + + /* + * Create the semaphore. + */ + HMTX hmtx; + int rc = DosCreateMutexSem(NULL, &hmtx, 0, FALSE); + if (!rc) + { + /** @todo implement lock validation of OS/2 mutex semaphores. */ + *phMutexSem = (RTSEMMUTEX)(void *)hmtx; + return VINF_SUCCESS; + } + + return RTErrConvertFromOS2(rc); +} + + +RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem) +{ + if (hMutexSem == NIL_RTSEMMUTEX) + return VINF_SUCCESS; + + /* + * Close semaphore handle. + */ + int rc = DosCloseMutexSem(SEM2HND(hMutexSem)); + if (!rc) + return VINF_SUCCESS; + AssertMsgFailed(("Destroy hMutexSem %p failed, rc=%d\n", hMutexSem, rc)); + return RTErrConvertFromOS2(rc); +} + + + +RTDECL(uint32_t) RTSemMutexSetSubClass(RTSEMMUTEX hMutexSem, uint32_t uSubClass) +{ +#if 0 /** @todo def RTSEMMUTEX_STRICT */ + /* + * Validate. + */ + RTSEMMUTEXINTERNAL *pThis = hMutexSem; + AssertPtrReturn(pThis, RTLOCKVAL_SUB_CLASS_INVALID); + AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID); + + return RTLockValidatorRecExclSetSubClass(&pThis->ValidatorRec, uSubClass); +#else + return RTLOCKVAL_SUB_CLASS_INVALID; +#endif +} + + +#undef RTSemMutexRequestNoResume +RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies) +{ + /* + * Lock mutex semaphore. + */ + int rc = DosRequestMutexSem(SEM2HND(hMutexSem), cMillies == RT_INDEFINITE_WAIT ? SEM_INDEFINITE_WAIT : cMillies); + switch (rc) + { + case NO_ERROR: return VINF_SUCCESS; + case ERROR_SEM_TIMEOUT: + case ERROR_TIMEOUT: return VERR_TIMEOUT; + case ERROR_INTERRUPT: return VERR_INTERRUPTED; + case ERROR_SEM_OWNER_DIED: return VERR_SEM_OWNER_DIED; + default: + { + AssertMsgFailed(("Wait on hMutexSem %p failed, rc=%d\n", hMutexSem, rc)); + return RTErrConvertFromOS2(rc); + } + } +} + +RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ +// RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API(); +// return rtSemMutexRequestNoResume(hMutexSem, cMillies, &SrcPos); + return RTSemMutexRequestNoResume(hMutexSem, cMillies); +} + + +RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem) +{ + /* + * Unlock mutex semaphore. + */ + int rc = DosReleaseMutexSem(SEM2HND(hMutexSem)); + if (!rc) + return VINF_SUCCESS; + AssertMsgFailed(("Release hMutexSem %p failed, rc=%d\n", hMutexSem, rc)); + return RTErrConvertFromOS2(rc); +} + + +RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem) +{ + /* + * Unlock mutex semaphore. + */ + PID pid; + TID tid; + ULONG cRecursions; + int rc = DosQueryMutexSem(SEM2HND(hMutexSem), &pid, &tid, &cRecursions); + if (!rc) + return cRecursions != 0; + AssertMsgFailed(("DosQueryMutexSem %p failed, rc=%d\n", hMutexSem, rc)); + return rc == ERROR_SEM_OWNER_DIED; +} + diff --git a/src/VBox/Runtime/r3/os2/serialport-os2.cpp b/src/VBox/Runtime/r3/os2/serialport-os2.cpp new file mode 100644 index 00000000..95158dea --- /dev/null +++ b/src/VBox/Runtime/r3/os2/serialport-os2.cpp @@ -0,0 +1,755 @@ +/* $Id: serialport-os2.cpp $ */ +/** @file + * IPRT - Serial Port API, OS/2 Implementation. + */ + +/* + * Copyright (C) 2017-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define INCL_BASE +#define INCL_DOSFILEMGR +#define INCL_ERRORS +#define INCL_DOS +#define INCL_DOSDEVIOCTL +#define INCL_DOSDEVICES +#include <os2.h> +#undef RT_MAX + +#include <iprt/serialport.h> +#include "internal/iprt.h" + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/cdefs.h> +#include <iprt/err.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/time.h> +#include "internal/magics.h" + + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + +/** + * Returned data structure for ASYNC_EXTGETBAUDRATE. + */ +typedef struct OS2EXTGETBAUDRATEDATA +{ + /** Current bit rate. */ + ULONG uBitRateCur; + /** Fraction of the current bit rate. */ + BYTE bBitRateCurFrac; + /** Minimum supported bit rate. */ + ULONG uBitRateMin; + /** Fraction of the minimum bit rate. */ + BYTE bBitRateCurMin; + /** Maximum supported bit rate. */ + ULONG uBitRateMax; + /** Fraction of the maximum bit rate. */ + BYTE bBitRateCurMax; +} OS2EXTGETBAUDRATEDATA; +/** Pointer to the get extended baud rate data packet. */ +typedef OS2EXTGETBAUDRATEDATA *POS2EXTGETBAUDRATEDATA; + + +/** + * Data packet for the ASYNC_EXTSETBAUDRATE ioctl. + */ +typedef struct OS2EXTSETBAUDRATEDATA +{ + /** Current bit rate. */ + ULONG uBitRate; + /** Fraction of the current bit rate. */ + BYTE bBitRateFrac; +} OS2EXTSETBAUDRATEDATA; +/** Pointer to the set extended baud rate data packet. */ +typedef OS2EXTSETBAUDRATEDATA *POS2EXTSETBAUDRATEDATA; + + +/** + * Data packet for the ASYNC_GETLINECTRL ioctl. + */ +typedef struct OS2GETLINECTRLDATA +{ + /** Returns the current amount of data bits in a symbol used for the communication. */ + BYTE bDataBits; + /** Current parity setting. */ + BYTE bParity; + /** Current number of stop bits. */ + BYTE bStopBits; + /** Flag whether a break condition is currently transmitted on the line. */ + BYTE bTxBrk; +} OS2GETLINECTRLDATA; +/** Pointer to the get line control data packet. */ +typedef OS2GETLINECTRLDATA *POS2GETLINECTRLDATA; + + +/** + * Data packet for the ASYNC_SETLINECTRL ioctl. + */ +typedef struct OS2SETLINECTRLDATA +{ + /** Returns the current amount of data bits in a symbol used for the communication. */ + BYTE bDataBits; + /** Current parity setting. */ + BYTE bParity; + /** Current number of stop bits. */ + BYTE bStopBits; +} OS2SETLINECTRLDATA; +/** Pointer to the get line control data packet. */ +typedef OS2SETLINECTRLDATA *POS2SETLINECTRLDATA; + + +/** + * Internal serial port state. + */ +typedef struct RTSERIALPORTINTERNAL +{ + /** Magic value (RTSERIALPORT_MAGIC). */ + uint32_t u32Magic; + /** Flags given while opening the serial port. */ + uint32_t fOpenFlags; + /** The file descriptor of the serial port. */ + HFILE hDev; + /** Flag whether blocking mode is currently enabled. */ + bool fBlocking; + /** Flag whether RTSerialPortEvtPoll() was interrupted by RTSerialPortEvtPollInterrupt(). */ + volatile bool fInterrupt; +} RTSERIALPORTINTERNAL; +/** Pointer to the internal serial port state. */ +typedef RTSERIALPORTINTERNAL *PRTSERIALPORTINTERNAL; + + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ + +/** Indicator whether the CTS input is set/clear. */ +#define OS2_GET_MODEM_INPUT_CTS RT_BIT(4) +/** Indicator whether the DSR input is set/clear. */ +#define OS2_GET_MODEM_INPUT_DSR RT_BIT(5) +/** Indicator whether the RI input is set/clear. */ +#define OS2_GET_MODEM_INPUT_RI RT_BIT(6) +/** Indicator whether the DCD input is set/clear. */ +#define OS2_GET_MODEM_INPUT_DCD RT_BIT(7) + +/** There is something to read on the serial port. */ +#define OS2_GET_COMM_EVT_RX RT_BIT(0) +/** A receive timeout interrupt was generated on the serial port during a read request. */ +#define OS2_GET_COMM_EVT_RTI RT_BIT(1) +/** The transmit queue for the serial port is empty. */ +#define OS2_GET_COMM_EVT_TX_EMPTY RT_BIT(2) +/** The CTS signal changes state. */ +#define OS2_GET_COMM_EVT_CTS_CHG RT_BIT(3) +/** The DSR signal changes state. */ +#define OS2_GET_COMM_EVT_DSR_CHG RT_BIT(4) +/** The DCD signal changes state. */ +#define OS2_GET_COMM_EVT_DCD_CHG RT_BIT(5) +/** A break condition was detected on the serial port. */ +#define OS2_GET_COMM_EVT_BRK RT_BIT(6) +/** A parity, framing or receive hardware overrun error occurred. */ +#define OS2_GET_COMM_EVT_COMM_ERR RT_BIT(7) +/** Trailing edge ring indicator was detected. */ +#define OS2_GET_COMM_EVT_RI_TRAIL_EDGE RT_BIT(8) + + +/********************************************************************************************************************************* +* Global variables * +*********************************************************************************************************************************/ +/** OS/2 parity value to IPRT parity enum. */ +static RTSERIALPORTPARITY s_aParityConvTbl[] = +{ + RTSERIALPORTPARITY_NONE, + RTSERIALPORTPARITY_ODD, + RTSERIALPORTPARITY_EVEN, + RTSERIALPORTPARITY_MARK, + RTSERIALPORTPARITY_SPACE +}; + +/** OS/2 data bits value to IPRT data bits enum. */ +static RTSERIALPORTDATABITS s_aDataBitsConvTbl[] = +{ + RTSERIALPORTDATABITS_INVALID, + RTSERIALPORTDATABITS_INVALID, + RTSERIALPORTDATABITS_INVALID, + RTSERIALPORTDATABITS_INVALID, + RTSERIALPORTDATABITS_INVALID, + RTSERIALPORTDATABITS_5BITS, + RTSERIALPORTDATABITS_6BITS, + RTSERIALPORTDATABITS_7BITS, + RTSERIALPORTDATABITS_8BITS +}; + +/** OS/2 stop bits value to IPRT stop bits enum. */ +static RTSERIALPORTSTOPBITS s_aStopBitsConvTbl[] = +{ + RTSERIALPORTSTOPBITS_ONE, + RTSERIALPORTSTOPBITS_ONEPOINTFIVE, + RTSERIALPORTSTOPBITS_TWO +}; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ + +/** + * The slow path of rtSerialPortSwitchBlockingMode that does the actual switching. + * + * @returns IPRT status code. + * @param pThis The internal serial port instance data. + * @param fBlocking The desired mode of operation. + * @remarks Do not call directly. + * + * @note Affects only read behavior. + */ +static int rtSerialPortSwitchBlockingModeSlow(PRTSERIALPORTINTERNAL pThis, bool fBlocking) +{ + DCBINFO DcbInfo; + ULONG cbDcbInfo = sizeof(DcbInfo); + ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_GETDCBINFO, NULL, 0, NULL, &DcbInfo, cbDcbInfo, &cbDcbInfo); + if (!rcOs2) + { + DcbInfo.fbTimeout &= ~0x06; + DcbInfo.fbTimeout |= fBlocking ? 0x04 : 0x06; + rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_SETDCBINFO, &DcbInfo, cbDcbInfo, &cbDcbInfo, NULL, 0, NULL); + if (rcOs2) + return RTErrConvertFromOS2(rcOs2); + } + else + return RTErrConvertFromOS2(rcOs2); + + pThis->fBlocking = fBlocking; + return VINF_SUCCESS; +} + + +/** + * Switches the serial port to the desired blocking mode if necessary. + * + * @returns IPRT status code. + * @param pThis The internal serial port instance data. + * @param fBlocking The desired mode of operation. + * + * @note Affects only read behavior. + */ +DECLINLINE(int) rtSerialPortSwitchBlockingMode(PRTSERIALPORTINTERNAL pThis, bool fBlocking) +{ + if (pThis->fBlocking != fBlocking) + return rtSerialPortSwitchBlockingModeSlow(pThis, fBlocking); + return VINF_SUCCESS; +} + + +RTDECL(int) RTSerialPortOpen(PRTSERIALPORT phSerialPort, const char *pszPortAddress, uint32_t fFlags) +{ + AssertPtrReturn(phSerialPort, VERR_INVALID_POINTER); + AssertPtrReturn(pszPortAddress, VERR_INVALID_POINTER); + AssertReturn(*pszPortAddress != '\0', VERR_INVALID_PARAMETER); + AssertReturn(!(fFlags & ~RTSERIALPORT_OPEN_F_VALID_MASK), VERR_INVALID_PARAMETER); + AssertReturn((fFlags & RTSERIALPORT_OPEN_F_READ) || (fFlags & RTSERIALPORT_OPEN_F_WRITE), + VERR_INVALID_PARAMETER); + + int rc = VINF_SUCCESS; + PRTSERIALPORTINTERNAL pThis = (PRTSERIALPORTINTERNAL)RTMemAllocZ(sizeof(*pThis)); + if (pThis) + { + ULONG fOpenMode = OPEN_SHARE_DENYREADWRITE + | OPEN_FLAGS_SEQUENTIAL + | OPEN_FLAGS_NOINHERIT + | OPEN_FLAGS_FAIL_ON_ERROR; + + if ((fFlags & RTSERIALPORT_OPEN_F_READ) && !(fFlags & RTSERIALPORT_OPEN_F_WRITE)) + fOpenMode |= OPEN_ACCESS_READONLY; + else if (!(fFlags & RTSERIALPORT_OPEN_F_READ) && (fFlags & RTSERIALPORT_OPEN_F_WRITE)) + fOpenMode |= OPEN_ACCESS_WRITEONLY; + else + fOpenMode |= OPEN_ACCESS_READWRITE; + + pThis->u32Magic = RTSERIALPORT_MAGIC; + pThis->fOpenFlags = fFlags; + pThis->fInterrupt = false; + pThis->fBlocking = true; + + ULONG uAction = 0; + ULONG rcOs2 = DosOpen((const UCHAR *)pszPortAddress, &pThis->hDev, &uAction, 0, FILE_NORMAL, FILE_OPEN, fOpenMode, NULL); + if (!rcOs2) + { + /* Switch to a known read blocking mode. */ + rc = rtSerialPortSwitchBlockingMode(pThis, false); + if (RT_SUCCESS(rc)) + { + *phSerialPort = pThis; + return VINF_SUCCESS; + } + + DosClose(pThis->hDev); + } + else + rc = RTErrConvertFromOS2(rcOs2); + + RTMemFree(pThis); + } + else + rc = VERR_NO_MEMORY; + + return rc; +} + + +RTDECL(int) RTSerialPortClose(RTSERIALPORT hSerialPort) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + if (pThis == NIL_RTSERIALPORT) + return VINF_SUCCESS; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + + /* + * Do the cleanup. + */ + AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSERIALPORT_MAGIC_DEAD, RTSERIALPORT_MAGIC), VERR_INVALID_HANDLE); + + DosClose(pThis->hDev); + RTMemFree(pThis); + return VINF_SUCCESS; +} + + +RTDECL(RTHCINTPTR) RTSerialPortToNative(RTSERIALPORT hSerialPort) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, -1); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, -1); + + return pThis->hDev; +} + + +RTDECL(int) RTSerialPortRead(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER); + + int rc = rtSerialPortSwitchBlockingMode(pThis, true); + if (RT_SUCCESS(rc)) + { + /* + * Attempt read. + */ + ULONG cbRead = 0; + ULONG rcOs2 = DosRead(pThis->hDev, pvBuf, cbToRead, &cbRead); + if (!rcOs2) + { + if (pcbRead) + /* caller can handle partial read. */ + *pcbRead = cbRead; + else + { + /* Caller expects all to be read. */ + while (cbToRead > cbRead) + { + ULONG cbReadPart = 0; + rcOs2 = DosRead(pThis->hDev, (uint8_t *)pvBuf + cbRead, cbToRead - cbRead, &cbReadPart); + if (rcOs2) + return RTErrConvertFromOS2(rcOs2); + + cbRead += cbReadPart; + } + } + } + else + rc = RTErrConvertFromOS2(rcOs2); + } + + return rc; +} + + +RTDECL(int) RTSerialPortReadNB(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcbRead, VERR_INVALID_POINTER); + + *pcbRead = 0; + + int rc = rtSerialPortSwitchBlockingMode(pThis, false); + if (RT_SUCCESS(rc)) + { + ULONG cbThisRead = 0; + ULONG rcOs2 = DosRead(pThis->hDev, pvBuf, cbToRead, &cbThisRead); + if (!rcOs2) + { + *pcbRead = cbThisRead; + + if (cbThisRead == 0) + rc = VINF_TRY_AGAIN; + } + else + rc = RTErrConvertFromOS2(rcOs2); + } + + return rc; +} + + +RTDECL(int) RTSerialPortWrite(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER); + + /* + * Attempt write. + */ + int rc = VINF_SUCCESS; + ULONG cbThisWritten = 0; + ULONG rcOs2 = DosWrite(pThis->hDev, pvBuf, cbToWrite, &cbThisWritten); + if (!rcOs2) + { + if (pcbWritten) + /* caller can handle partial write. */ + *pcbWritten = cbThisWritten; + else + { + /** @todo Wait for TX empty and loop. */ + rc = VERR_NOT_SUPPORTED; + } + } + else + rc = RTErrConvertFromOS2(rcOs2); + + return rc; +} + + +RTDECL(int) RTSerialPortWriteNB(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER); + + *pcbWritten = 0; + + int rc = VINF_SUCCESS; + ULONG cbThisWritten = 0; + ULONG rcOs2 = DosWrite(pThis->hDev, pvBuf, cbToWrite, &cbThisWritten); + if (!rcOs2) + { + *pcbWritten = cbThisWritten; + if (!cbThisWritten) + rc = VINF_TRY_AGAIN; + } + else + rc = RTErrConvertFromOS2(rcOs2); + + return rc; +} + + +RTDECL(int) RTSerialPortCfgQueryCurrent(RTSERIALPORT hSerialPort, PRTSERIALPORTCFG pCfg) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + + int rc = VINF_SUCCESS; + OS2EXTGETBAUDRATEDATA ExtBaudRate; + ULONG cbExtBaudRate = sizeof(ExtBaudRate); + ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_EXTGETBAUDRATE, NULL, 0, NULL, &ExtBaudRate, cbExtBaudRate, &cbExtBaudRate); + if (!rcOs2) + { + OS2GETLINECTRLDATA LineCtrl; + ULONG cbLineCtrl = sizeof(LineCtrl); + rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_GETLINECTRL, NULL, 0, NULL, &LineCtrl, cbLineCtrl, &cbLineCtrl); + if (!rcOs2) + { + pCfg->uBaudRate = ExtBaudRate.uBitRateCur; + if (LineCtrl.bParity < RT_ELEMENTS(s_aParityConvTbl)) + pCfg->enmParity = s_aParityConvTbl[LineCtrl.bParity]; + else + rc = VERR_IPE_UNEXPECTED_STATUS; + + if ( RT_SUCCESS(rc) + && LineCtrl.bDataBits < RT_ELEMENTS(s_aDataBitsConvTbl) + && s_aDataBitsConvTbl[LineCtrl.bDataBits] != RTSERIALPORTDATABITS_INVALID) + pCfg->enmDataBitCount = s_aDataBitsConvTbl[LineCtrl.bDataBits]; + else + rc = VERR_IPE_UNEXPECTED_STATUS; + + if ( RT_SUCCESS(rc) + && LineCtrl.bStopBits < RT_ELEMENTS(s_aStopBitsConvTbl)) + pCfg->enmStopBitCount = s_aStopBitsConvTbl[LineCtrl.bStopBits]; + else + rc = VERR_IPE_UNEXPECTED_STATUS; + } + else + rc = RTErrConvertFromOS2(rcOs2); + } + else + rc = RTErrConvertFromOS2(rcOs2); + + return rc; +} + + +RTDECL(int) RTSerialPortCfgSet(RTSERIALPORT hSerialPort, PCRTSERIALPORTCFG pCfg, PRTERRINFO pErrInfo) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + + int rc = VINF_SUCCESS; + OS2EXTSETBAUDRATEDATA ExtBaudRate; + OS2SETLINECTRLDATA LineCtrl; + ULONG cbExtBaudRate = sizeof(ExtBaudRate); + ULONG cbLineCtrl = sizeof(LineCtrl); + + ExtBaudRate.uBitRate = pCfg->uBaudRate; + ExtBaudRate.bBitRateFrac = 0; + + BYTE idx = 0; + while (idx < RT_ELEMENTS(s_aParityConvTbl)) + { + if (s_aParityConvTbl[idx] == pCfg->enmParity) + { + LineCtrl.bParity = idx; + break; + } + idx++; + } + AssertReturn(idx < RT_ELEMENTS(s_aParityConvTbl), VERR_INTERNAL_ERROR); + + idx = 0; + while (idx < RT_ELEMENTS(s_aDataBitsConvTbl)) + { + if (s_aDataBitsConvTbl[idx] == pCfg->enmDataBitCount) + { + LineCtrl.bDataBits = idx; + break; + } + idx++; + } + AssertReturn(idx < RT_ELEMENTS(s_aDataBitsConvTbl), VERR_INTERNAL_ERROR); + + idx = 0; + while (idx < RT_ELEMENTS(s_aStopBitsConvTbl)) + { + if (s_aStopBitsConvTbl[idx] == pCfg->enmStopBitCount) + { + LineCtrl.bStopBits = idx; + break; + } + idx++; + } + AssertReturn(idx < RT_ELEMENTS(s_aStopBitsConvTbl), VERR_INTERNAL_ERROR); + + ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_EXTSETBAUDRATE, &ExtBaudRate, cbExtBaudRate, &cbExtBaudRate, NULL, 0, NULL); + if (!rcOs2) + { + rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_SETLINECTRL, &LineCtrl, cbLineCtrl, &cbLineCtrl, NULL, 0, NULL); + if (rcOs2) + rc = RTErrConvertFromOS2(rcOs2); + } + else + rc = RTErrConvertFromOS2(rcOs2); + + return rc; +} + + +RTDECL(int) RTSerialPortEvtPoll(RTSERIALPORT hSerialPort, uint32_t fEvtMask, uint32_t *pfEvtsRecv, + RTMSINTERVAL msTimeout) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(!(fEvtMask & ~RTSERIALPORT_EVT_F_VALID_MASK), VERR_INVALID_PARAMETER); + AssertPtrReturn(pfEvtsRecv, VERR_INVALID_POINTER); + + *pfEvtsRecv = 0; + + /* + * We need to kind of busy wait here as there is, to my knowledge, no API + * to wait for a COM event. + * + * @todo Adaptive waiting + * @todo Handle rollover after 48days eventually + */ + int rc = VINF_SUCCESS; + uint64_t tsStart = RTTimeSystemMilliTS(); + do + { + if (ASMAtomicXchgBool(&pThis->fInterrupt, false)) + { + rc = VERR_INTERRUPTED; + break; + } + + USHORT fCommEvt = 0; + ULONG cbCommEvt = sizeof(fCommEvt); + ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_GETCOMMEVENT, NULL, 0, NULL, + &fCommEvt, cbCommEvt, &cbCommEvt); + if (!rcOs2) + { + AssertReturn(cbCommEvt = sizeof(fCommEvt), VERR_IPE_UNEXPECTED_STATUS); + + if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX) + && (fCommEvt & OS2_GET_COMM_EVT_RX)) + *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_RX; + + /** @todo Is there something better to indicate that there is room in the queue instead of queue is empty? */ + if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX) + && (fCommEvt & OS2_GET_COMM_EVT_TX_EMPTY)) + *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX; + + if ( (fEvtMask & RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED) + && (fCommEvt & (OS2_GET_COMM_EVT_CTS_CHG | OS2_GET_COMM_EVT_DSR_CHG | OS2_GET_COMM_EVT_DCD_CHG))) + *pfEvtsRecv |= RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED; + + if ( (fEvtMask & RTSERIALPORT_EVT_F_BREAK_DETECTED) + && (fCommEvt & OS2_GET_COMM_EVT_BRK)) + *pfEvtsRecv |= RTSERIALPORT_EVT_F_BREAK_DETECTED; + + if (*pfEvtsRecv != 0) + break; + } + else + { + rc = RTErrConvertFromOS2(rcOs2); + break; + } + + uint64_t tsNow = RTTimeSystemMilliTS(); + if ( msTimeout == RT_INDEFINITE_WAIT + || tsNow - tsStart < msTimeout) + DosSleep(1); + else + rc = VERR_TIMEOUT; + } while (RT_SUCCESS(rc)); + + return rc; +} + + +RTDECL(int) RTSerialPortEvtPollInterrupt(RTSERIALPORT hSerialPort) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + + ASMAtomicXchgBool(&pThis->fInterrupt, true); + return VINF_SUCCESS; +} + + +RTDECL(int) RTSerialPortChgBreakCondition(RTSERIALPORT hSerialPort, bool fSet) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + + ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, fSet ? ASYNC_SETBREAKON : ASYNC_SETBREAKOFF, + NULL, 0, NULL, NULL, 0, NULL); + + return RTErrConvertFromOS2(rcOs2); +} + + +RTDECL(int) RTSerialPortChgStatusLines(RTSERIALPORT hSerialPort, uint32_t fClear, uint32_t fSet) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + + MODEMSTATUS MdmSts; + ULONG cbMdmSts = sizeof(MdmSts); + + MdmSts.fbModemOn = (fSet & RTSERIALPORT_CHG_STS_LINES_F_RTS ? 0x02 : 0x00) + | (fSet & RTSERIALPORT_CHG_STS_LINES_F_DTR ? 0x01 : 0x00); + MdmSts.fbModemOff = 0xff; + MdmSts.fbModemOff &= ~( (fClear & RTSERIALPORT_CHG_STS_LINES_F_RTS ? 0x02 : 0x00) + | (fClear & RTSERIALPORT_CHG_STS_LINES_F_DTR ? 0x01 : 0x00)); + + ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_SETMODEMCTRL, &MdmSts, cbMdmSts, &cbMdmSts, NULL, 0, NULL); + + return RTErrConvertFromOS2(rcOs2); +} + + +RTDECL(int) RTSerialPortQueryStatusLines(RTSERIALPORT hSerialPort, uint32_t *pfStsLines) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pfStsLines, VERR_INVALID_POINTER); + + *pfStsLines = 0; + + int rc = VINF_SUCCESS; + BYTE fStsLines = 0; + ULONG cbStsLines = sizeof(fStsLines); + ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_GETMODEMINPUT, NULL, 0, NULL, &fStsLines, cbStsLines, &cbStsLines); + if (!rcOs2) + { + AssertReturn(cbStsLines == sizeof(BYTE), VERR_IPE_UNEXPECTED_STATUS); + + *pfStsLines |= (fStsLines & OS2_GET_MODEM_INPUT_DCD) ? RTSERIALPORT_STS_LINE_DCD : 0; + *pfStsLines |= (fStsLines & OS2_GET_MODEM_INPUT_RI) ? RTSERIALPORT_STS_LINE_RI : 0; + *pfStsLines |= (fStsLines & OS2_GET_MODEM_INPUT_DSR) ? RTSERIALPORT_STS_LINE_DSR : 0; + *pfStsLines |= (fStsLines & OS2_GET_MODEM_INPUT_CTS) ? RTSERIALPORT_STS_LINE_CTS : 0; + } + else + rc = RTErrConvertFromOS2(rcOs2); + + return rc; +} + diff --git a/src/VBox/Runtime/r3/os2/systemmem-os2.cpp b/src/VBox/Runtime/r3/os2/systemmem-os2.cpp new file mode 100644 index 00000000..71eb1adf --- /dev/null +++ b/src/VBox/Runtime/r3/os2/systemmem-os2.cpp @@ -0,0 +1,80 @@ +/* $Id: systemmem-os2.cpp $ */ +/** @file + * IPRT - RTSystemQueryTotalRam, OS/2 ring-3. + */ + +/* + * Copyright (C) 2010-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define INCL_DOSMISC +#define INCL_ERRORS +#include <os2.h> +#undef RT_MAX + +#include <iprt/system.h> +#include "internal/iprt.h" + +#include <iprt/errcore.h> +#include <iprt/assert.h> +#include <iprt/string.h> + + +RTDECL(int) RTSystemQueryTotalRam(uint64_t *pcb) +{ + AssertPtrReturn(pcb, VERR_INVALID_POINTER); + + ULONG cbMem; + APIRET rc = DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM, &cbMem, sizeof(cbMem)); + if (rc != NO_ERROR) + return RTErrConvertFromOS2(rc); + + *pcb = cbMem; + return VINF_SUCCESS; +} + + +RTDECL(int) RTSystemQueryAvailableRam(uint64_t *pcb) +{ + AssertPtrReturn(pcb, VERR_INVALID_POINTER); + + ULONG cbAvailMem; + APIRET rc = DosQuerySysInfo(QSV_TOTAVAILMEM, QSV_TOTAVAILMEM, &cbAvailMem, sizeof(cbAvailMem)); + if (rc != NO_ERROR) + return RTErrConvertFromOS2(rc); + + *pcb = cbAvailMem; + return VINF_SUCCESS; +} + diff --git a/src/VBox/Runtime/r3/os2/thread-os2.cpp b/src/VBox/Runtime/r3/os2/thread-os2.cpp new file mode 100644 index 00000000..366f4803 --- /dev/null +++ b/src/VBox/Runtime/r3/os2/thread-os2.cpp @@ -0,0 +1,330 @@ +/* $Id: thread-os2.cpp $ */ +/** @file + * IPRT - Threads, OS/2. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_THREAD +#define INCL_BASE +#include <os2.h> +#undef RT_MAX + +#include <errno.h> +#include <process.h> +#include <stdlib.h> +#include <signal.h> +#include <InnoTekLIBC/FastInfoBlocks.h> +#include <InnoTekLIBC/thread.h> + +#include <iprt/thread.h> +#include <iprt/log.h> +#include <iprt/assert.h> +#include <iprt/alloc.h> +#include <iprt/asm-amd64-x86.h> +#include <iprt/cpuset.h> +#include <iprt/string.h> +#include <iprt/err.h> +#include "internal/thread.h" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Pointer to thread local memory which points to the current thread. */ +static PRTTHREADINT *g_ppCurThread; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static void rtThreadNativeMain(void *pvArgs); + + +DECLHIDDEN(int) rtThreadNativeInit(void) +{ + /* + * Allocate thread local memory. + */ + PULONG pul; + int rc = DosAllocThreadLocalMemory(1, &pul); + if (rc) + return VERR_NO_TLS_FOR_SELF; + g_ppCurThread = (PRTTHREADINT *)(void *)pul; + return VINF_SUCCESS; +} + + +static void rtThreadOs2BlockSigAlarm(void) +{ + /* + * Block SIGALRM - required for timer-posix.cpp. + * This is done to limit harm done by OSes which doesn't do special SIGALRM scheduling. + * It will not help much if someone creates threads directly using pthread_create. :/ + */ + sigset_t SigSet; + sigemptyset(&SigSet); + sigaddset(&SigSet, SIGALRM); + sigprocmask(SIG_BLOCK, &SigSet, NULL); +} + + +DECLHIDDEN(void) rtThreadNativeReInitObtrusive(void) +{ + rtThreadOs2BlockSigAlarm(); +} + + +DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread) +{ + + *g_ppCurThread = pThread; + return VINF_SUCCESS; +} + + +DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread) +{ + if (pThread == *g_ppCurThread) + *g_ppCurThread = NULL; +} + + +/** + * Wrapper which unpacks the params and calls thread function. + */ +static void rtThreadNativeMain(void *pvArgs) +{ + rtThreadOs2BlockSigAlarm(); + + /* + * Call common main. + */ + PRTTHREADINT pThread = (PRTTHREADINT)pvArgs; + *g_ppCurThread = pThread; + +#ifdef fibGetTidPid + rtThreadMain(pThread, fibGetTidPid(), &pThread->szName[0]); +#else + rtThreadMain(pThread, _gettid(), &pThread->szName[0]); +#endif + + *g_ppCurThread = NULL; + _endthread(); +} + + +DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThread, PRTNATIVETHREAD pNativeThread) +{ + /* + * Default stack size. + */ + if (!pThread->cbStack) + pThread->cbStack = 512*1024; + + /* + * Create the thread. + */ + int iThreadId = _beginthread(rtThreadNativeMain, NULL, pThread->cbStack, pThread); + if (iThreadId > 0) + { +#ifdef fibGetTidPid + *pNativeThread = iThreadId | (fibGetPid() << 16); +#else + *pNativeThread = iThreadId; +#endif + return VINF_SUCCESS; + } + return RTErrConvertFromErrno(errno); +} + + +RTDECL(RTTHREAD) RTThreadSelf(void) +{ + PRTTHREADINT pThread = *g_ppCurThread; + if (pThread) + return (RTTHREAD)pThread; + /** @todo import alien threads? */ + return NULL; +} + + +RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void) +{ +#ifdef fibGetTidPid + return fibGetTidPid(); +#else + return _gettid(); +#endif +} + + +RTDECL(int) RTThreadSleep(RTMSINTERVAL cMillies) +{ + LogFlow(("RTThreadSleep: cMillies=%d\n", cMillies)); + DosSleep(cMillies); + LogFlow(("RTThreadSleep: returning (cMillies=%d)\n", cMillies)); + return VINF_SUCCESS; +} + + +RTDECL(int) RTThreadSleepNoLog(RTMSINTERVAL cMillies) +{ + DosSleep(cMillies); + return VINF_SUCCESS; +} + + +RTDECL(bool) RTThreadYield(void) +{ + uint64_t u64TS = ASMReadTSC(); + DosSleep(0); + u64TS = ASMReadTSC() - u64TS; + bool fRc = u64TS > 1750; + LogFlow(("RTThreadYield: returning %d (%llu ticks)\n", fRc, u64TS)); + return fRc; +} + + +RTR3DECL(int) RTThreadGetAffinity(PRTCPUSET pCpuSet) +{ + union + { + uint64_t u64; + MPAFFINITY mpaff; + } u; + + APIRET rc = DosQueryThreadAffinity(AFNTY_THREAD, &u.mpaff); + if (!rc) + { + RTCpuSetFromU64(pCpuSet, u.u64); + return VINF_SUCCESS; + } + return RTErrConvertFromOS2(rc); +} + + +RTR3DECL(int) RTThreadSetAffinity(PCRTCPUSET pCpuSet) +{ + union + { + uint64_t u64; + MPAFFINITY mpaff; + } u; + u.u64 = pCpuSet ? RTCpuSetToU64(pCpuSet) : UINT64_MAX; + int rc = DosSetThreadAffinity(&u.mpaff); + if (!rc) + return VINF_SUCCESS; + return RTErrConvertFromOS2(rc); +} + + +RTR3DECL(RTTLS) RTTlsAlloc(void) +{ + AssertCompile(NIL_RTTLS == -1); + return __libc_TLSAlloc(); +} + + +RTR3DECL(int) RTTlsAllocEx(PRTTLS piTls, PFNRTTLSDTOR pfnDestructor) +{ + int rc; + int iTls = __libc_TLSAlloc(); + if (iTls != -1) + { + if ( !pfnDestructor + || __libc_TLSDestructor(iTls, (void (*)(void *, int, unsigned))pfnDestructor, 0) != -1) + { + *piTls = iTls; + return VINF_SUCCESS; + } + + rc = RTErrConvertFromErrno(errno); + __libc_TLSFree(iTls); + } + else + rc = RTErrConvertFromErrno(errno); + + *piTls = NIL_RTTLS; + return rc; +} + + +RTR3DECL(int) RTTlsFree(RTTLS iTls) +{ + if (iTls == NIL_RTTLS) + return VINF_SUCCESS; + if (__libc_TLSFree(iTls) != -1) + return VINF_SUCCESS; + return RTErrConvertFromErrno(errno); +} + + +RTR3DECL(void *) RTTlsGet(RTTLS iTls) +{ + return __libc_TLSGet(iTls); +} + + +RTR3DECL(int) RTTlsGetEx(RTTLS iTls, void **ppvValue) +{ + int rc = VINF_SUCCESS; + void *pv = __libc_TLSGet(iTls); + if (RT_UNLIKELY(!pv)) + { + errno = 0; + pv = __libc_TLSGet(iTls); + if (!pv && errno) + rc = RTErrConvertFromErrno(errno); + } + + *ppvValue = pv; + return rc; +} + + +RTR3DECL(int) RTTlsSet(RTTLS iTls, void *pvValue) +{ + if (__libc_TLSSet(iTls, pvValue) != -1) + return VINF_SUCCESS; + return RTErrConvertFromErrno(errno); +} + + +RTR3DECL(int) RTThreadGetExecutionTimeMilli(uint64_t *pKernelTime, uint64_t *pUserTime) +{ + return VERR_NOT_IMPLEMENTED; +} diff --git a/src/VBox/Runtime/r3/os2/time-os2.cpp b/src/VBox/Runtime/r3/os2/time-os2.cpp new file mode 100644 index 00000000..e06b9fcd --- /dev/null +++ b/src/VBox/Runtime/r3/os2/time-os2.cpp @@ -0,0 +1,87 @@ +/* $Id: time-os2.cpp $ */ +/** @file + * IPRT - Time, OS/2. + */ + +/* + * Contributed by knut st. osmundsen. + * + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + * -------------------------------------------------------------------- + * + * This code is based on: + * + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_TIME +#include <InnoTekLIBC/FastInfoBlocks.h> + +#include <iprt/time.h> +#include "internal/time.h" + +/** @todo mscount will roll over after ~48 days. */ + +RTDECL(uint64_t) RTTimeSystemNanoTS(void) +{ + return fibGetMsCount() * UINT64_C(10000000); +} + + +RTDECL(uint64_t) RTTimeSystemMilliTS(void) +{ + return fibGetMsCount(); +} + |